[llvm] Adding the support for vectorization of loops with load based tripcount (PR #209390)
Ramkumar Ramachandra via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 18 13:57:12 PDT 2026
================
@@ -3175,13 +3176,171 @@ void LoopAccessInfo::collectStridedAccess(Value *MemAccess) {
SymbolicStrides[Ptr] = cast<SCEVUnknown>(StrideBase);
}
-LoopAccessInfo::LoopAccessInfo(Loop *L, ScalarEvolution *SE,
- const TargetTransformInfo *TTI,
- const TargetLibraryInfo *TLI, AAResults *AA,
- DominatorTree *DT, LoopInfo *LI,
- AssumptionCache *AC, bool AllowPartial)
+static bool isBoundLoadSafeToLoadAtCtx(Value *Ptr, Type *Ty, Align Alignment,
+ Instruction *CtxI, DominatorTree *DT,
+ AssumptionCache *AC) {
+ const DataLayout &DL = CtxI->getDataLayout();
+ for (DomTreeNode *Node = DT->getNode(CtxI->getParent()); Node;
+ Node = Node->getIDom()) {
+ BasicBlock *BB = Node->getBlock();
+ Instruction *ScanFrom =
+ (BB == CtxI->getParent()) ? CtxI : BB->getTerminator();
+ if (isSafeToLoadUnconditionally(Ptr, Ty, Alignment, DL, ScanFrom, AC, DT)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool isSafeToHoistBoundLoad(Instruction *I,
+ const SmallPtrSetImpl<Value *> &ModifiedPtrs,
+ Instruction *CtxI, DominatorTree *DT,
+ AssumptionCache *AC) {
+ if (auto *LI = dyn_cast<LoadInst>(I)) {
+ if (!LI->isSimple()) {
+ return false;
+ }
+ Value *Ptr = LI->getPointerOperand();
+ if (ModifiedPtrs.count(Ptr)) {
+ return false;
+ }
+ if (!isBoundLoadSafeToLoadAtCtx(Ptr, LI->getType(), LI->getAlign(), CtxI,
+ DT, AC)) {
+ return false;
+ }
+ return true;
+ }
+
+ // Limiting down the kind of instructions we can hoist to avoid any unforeseen
+ // side effects.
+ if (I->isBinaryOp() || I->isUnaryOp() || I->isCast() ||
+ isa<GetElementPtrInst>(I)) {
+ return isSafeToSpeculativelyExecute(I, CtxI, AC, DT);
+ }
+ return false;
+}
+
+LLVM_ABI bool llvm::collectInvariantLoadsBoundChain(
+ Loop *L, ScalarEvolution *SE, DominatorTree *DT, AssumptionCache *AC,
+ SmallVectorImpl<Instruction *> &HoistedDeps,
+ SmallVectorImpl<LoadInst *> &BoundLoads) {
+ HoistedDeps.clear();
+ BoundLoads.clear();
+
+ BasicBlock *ExitingBB = L->getExitingBlock();
+ if (!ExitingBB || ExitingBB != L->getLoopLatch()) {
+ return false;
+ }
+ auto *ExitBranch = dyn_cast<CondBrInst>(ExitingBB->getTerminator());
+ if (!ExitBranch) {
+ return false;
+ }
+ auto *ExitCmp = dyn_cast<ICmpInst>(ExitBranch->getCondition());
+ if (!ExitCmp) {
+ return false;
+ }
+ PHINode *IndVar = L->getInductionVariable(*SE);
+ if (!IndVar) {
+ return false;
+ }
+ Value *StepInst = IndVar->getIncomingValueForBlock(L->getLoopLatch());
+ auto IsIVOrStep = [&](Value *V) { return V == IndVar || V == StepInst; };
+
+ Value *DynamicUpperBound = nullptr;
+ if (IsIVOrStep(ExitCmp->getOperand(0))) {
+ DynamicUpperBound = ExitCmp->getOperand(1);
+ } else if (IsIVOrStep(ExitCmp->getOperand(1))) {
+ DynamicUpperBound = ExitCmp->getOperand(0);
+ } else {
+ return false;
+ }
+
+ /// Anything written inside the loop is a "not-invariant" pointer; the bound
+ /// load cannot share a pointer with such writes.
+ /// This is to prune out cases like for (i = 0; i < *Len; ++i) { Len[i] =
+ /// ..*.. }
+ SmallPtrSet<Value *, 16> ModifiedPtrs;
+ for (BasicBlock *BB : L->getBlocks()) {
+ for (Instruction &I : *BB) {
+ if (auto *SI = dyn_cast<StoreInst>(&I)) {
+ ModifiedPtrs.insert(SI->getPointerOperand());
+ }
+ }
+ }
+
+ BasicBlock *Preheader = L->getLoopPreheader();
+ if (!Preheader) {
+ return false;
+ }
+ Instruction *CtxI = Preheader->getTerminator();
+
+ // The bound must depend on at least one in-loop load (otherwise the loop
+ // would already be countable and this is unnecessary).
+ SmallVector<Instruction *, 16> Worklist;
+ SmallPtrSet<Instruction *, 16> VisitedForHoisting;
+ SmallPtrSet<Instruction *, 16> VisitedForChecking;
+
+ if (auto *I = dyn_cast<Instruction>(DynamicUpperBound)) {
+ Worklist.push_back(I);
+ }
+
+ while (!Worklist.empty()) {
+ Instruction *I = Worklist.back();
+
+ if (VisitedForHoisting.count(I)) {
+ Worklist.pop_back();
+ continue;
+ }
+
+ if (!L->contains(I->getParent())) {
+ Worklist.pop_back();
+ continue;
+ }
+
+ if (VisitedForChecking.insert(I).second) {
+ if (!isSafeToHoistBoundLoad(I, ModifiedPtrs, CtxI, DT, AC)) {
+ return false;
+ }
+
+ if (auto *LI = dyn_cast<LoadInst>(I)) {
+ BoundLoads.push_back(LI);
+ }
+
+ for (Use &U : I->operands()) {
+ if (auto *OpI = dyn_cast<Instruction>(U.get())) {
+ if (L->contains(OpI)) {
+ Worklist.push_back(OpI);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ Worklist.pop_back();
+
+ VisitedForHoisting.insert(I);
+
+ HoistedDeps.push_back(I);
+ }
+
+ return !BoundLoads.empty() && !HoistedDeps.empty();
+}
+
+LoopAccessInfo::LoopAccessInfo(
+ Loop *L, ScalarEvolution *SE, const TargetTransformInfo *TTI,
+ const TargetLibraryInfo *TLI, AAResults *AA, DominatorTree *DT,
+ LoopInfo *LI, AssumptionCache *AC, bool AllowPartial,
+ ArrayRef<const SCEVTripCountInvariantPredicate *> TripCountInvariantPreds)
: PSE(std::make_unique<PredicatedScalarEvolution>(*SE, *L)),
- PtrRtChecking(nullptr), TheLoop(L), AllowPartial(AllowPartial) {
+ PtrRtChecking(nullptr), TheLoop(L), AllowPartial(AllowPartial),
+ TripCountInvariantPreds(TripCountInvariantPreds) {
+
+ for (const SCEVTripCountInvariantPredicate *P : TripCountInvariantPreds) {
+ PSE->addTripCountInvariantPredicate(P->getTripCountLoad(),
+ P->getTripCountInvariantLoad());
+ }
----------------
artagnon wrote:
Why do we need a separate kind of predicate? Can we not just pass the predicate to the public helper in LAA, say llvm::collectInvariantLoadsBoundChain?
https://github.com/llvm/llvm-project/pull/209390
More information about the llvm-commits
mailing list