[llvm] Extending LoopVersioningLICM to handle cases where loopbound is invariant (PR #192902)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 02:14:50 PDT 2026


================
@@ -507,39 +638,149 @@ bool LoopVersioningLICM::isLegalForVersioning() {
   return true;
 }
 
+Instruction *LoopVersioningLICM::insertRuntimeCheckPlaceholder(Loop *L) {
+  // Preheader for our checks
+  BasicBlock *Preheader = L->getLoopPreheader();
+
+  // Determine the insertion point.
+  Instruction *InsertPt = Preheader->getTerminator();
+  LLVMContext &Context = CurLoop->getHeader()->getParent()->getContext();
+  // Create the comparison.
+  Instruction *Placeholder = nullptr;
+  Placeholder = ICmpInst::Create(Instruction::ICmp, ICmpInst::ICMP_EQ,
+                                 ConstantInt::get(Type::getInt32Ty(Context), 1),
+                                 ConstantInt::get(Type::getInt32Ty(Context), 1),
+                                 "runtime.check.placeholder", InsertPt->getIterator());
+
+  return Placeholder;
+}
+
+void LoopVersioningLICM::generateAndAddRuntimeChecks(DominatorTree *DT) {
+  BasicBlock *RuntimeCheckBB = PlaceholderForRuntimeChecks->getParent();
+
+  const auto &RtPtrChecking = *LAI->getRuntimePointerChecking();
+
+  SCEVExpander Exp1(*RtPtrChecking.getSE(), "induction");
+  
+  auto RPC = LAI->getRuntimePointerChecking();
+
+  Value *MemRuntimeChecks = llvm::addRuntimeChecks(PlaceholderForRuntimeChecks, 
+    CurLoop, RPC->getChecks(), Exp1);
+
+  SCEVExpander Exp2(*SE, "scev.check");
+  
+  const SCEVPredicate &Preds(LAI->getPSE().getPredicate());
+  Value *SCEVRuntimeChecks = Exp2.expandCodeForPredicate(&Preds, PlaceholderForRuntimeChecks);
+
+  IRBuilder<InstSimplifyFolder> Builder(RuntimeCheckBB->getContext(), 
+                InstSimplifyFolder(RuntimeCheckBB->getModule()->getDataLayout()));
+  
+  Value *RuntimeCheck = nullptr;
+  if (MemRuntimeChecks && SCEVRuntimeChecks) {
+    Builder.SetInsertPoint(PlaceholderForRuntimeChecks);
+    RuntimeCheck = Builder.CreateOr(MemRuntimeChecks, SCEVRuntimeChecks, "lver.safe");
+  } else {
+    RuntimeCheck = MemRuntimeChecks ? MemRuntimeChecks : SCEVRuntimeChecks;
+  }
+
+  RuntimeCheckBB->setName(CurLoop->getHeader()->getName() + ".lver.check");
+
+  CondBrInst *BI = dyn_cast<CondBrInst>(RuntimeCheckBB->getTerminator());
+  assert(BI && "Expected conditional branch in runtime check block");
+
+  assert(RuntimeCheck && "called even though we don't need "
+    "any runtime checks");
+
+  PlaceholderForRuntimeChecks->replaceAllUsesWith(RuntimeCheck);
+  PlaceholderForRuntimeChecks->eraseFromParent();
+}
+
 bool LoopVersioningLICM::run(DominatorTree *DT) {
   // Do not do the transformation if disabled by metadata.
   if (hasLICMVersioningTransformation(CurLoop) & TM_Disable)
     return false;
-
   bool Changed = false;
-
   // Check feasiblity of LoopVersioningLICM.
   // If versioning found to be feasible and beneficial then proceed
   // else simply return, by cleaning up memory.
   if (isLegalForVersioning()) {
+    PlaceholderForRuntimeChecks = nullptr;
+    if (IsDynamicBound) {
+      PlaceholderForRuntimeChecks = insertRuntimeCheckPlaceholder(CurLoop);
+      if (!PlaceholderForRuntimeChecks) {
+        LLVM_DEBUG(dbgs() << "No runtime check created\n");
+        return false;
+      }
+    }
     // Do loop versioning.
     // Create memcheck for memory accessed inside loop.
     // Clone original loop, and set blocks properly.
     LoopVersioning LVer(*LAI, LAI->getRuntimePointerChecking()->getChecks(),
                         CurLoop, &LI, DT, SE);
-    LVer.versionLoop();
+    LVer.versionLoop(PlaceholderForRuntimeChecks);
+    
+    // Add the extra checks for dynamic bounds
+    if (IsDynamicBound) {
+      for (auto p = HoistedDependencies.rbegin(); p != HoistedDependencies.rend(); ++p) {
+        Instruction *I = *p;
+        if (isa<LoadInst>(I)) {
+          // Clone the load to the preheader instead of moving it. The original
+          // dead load is intentionally kept in the loop body so that the subsequent LAA recomputation sees 
+          // the load and generates runtime pointer checks for its address range. The cleanup passes will erase it later. 
+          LoadInst *LI = cast<LoadInst>(I);
+          LoadInst *NewLI = new LoadInst(
+            LI->getType(), LI->getPointerOperand(), LI->getName() + ".hoisted",
+            false, LI->getAlign(), LI->getOrdering(), LI->getSyncScopeID(),
+            PlaceholderForRuntimeChecks->getIterator()
+          );
+
+          NewLI->copyMetadata(*I);
+          I->replaceAllUsesWith(NewLI);
+        } else {
+          I->moveBefore(PlaceholderForRuntimeChecks->getIterator());
+        }
+      }
+
+// #ifdef EXPENSIVE_CHECKS
+      assert(!verifyFunction(*LVer.getVersionedLoop()->getHeader()->getParent(), &dbgs())
+             && "Verification failure after dynamic bounds");
+// #endif
+
+      SE->forgetLoop(CurLoop);
+      // Recompute LoopAccessInfo after hoisting the dynamic bound
+      LoopAccessInfoManager FreshLAIs(*SE, *AA, *DT, LI, nullptr, nullptr, AC);
+      LAI = &FreshLAIs.getInfo(*CurLoop);
----------------
artagnon wrote:

```suggestion
      LAIs.invalidate();
      LAI = &LAIs.getInfo(*CurLoop);
```

https://github.com/llvm/llvm-project/pull/192902


More information about the llvm-commits mailing list