[llvm] Extending LoopVersioningLICM to handle cases where loopbound is invarint (PR #192794)

via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 18 07:54:01 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Manish Srivastava (mk-srivastava)

<details>
<summary>Changes</summary>

The changes extending the LoopVersioningLICM for the cases discussed in the discussion: https://github.com/llvm/llvm-project/issues/112060

---

Patch is 75.56 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192794.diff


12 Files Affected:

- (modified) llvm/include/llvm/Transforms/Utils/LoopVersioning.h (+5-2) 
- (modified) llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp (+336-8) 
- (modified) llvm/lib/Transforms/Utils/LoopVersioning.cpp (+27-17) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-array-element.ll (+139) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-chained-loads.ll (+86) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-fcmp-exit.ll (+57) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-lambda.ll (+142) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-loop-varying.ll (+79) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-simple.ll (+139) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-stored-pointer.ll (+72) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-two-loads.ll (+158) 
- (added) llvm/test/Transforms/LoopVersioningLICM/dynamic-bound-volatile-load.ll (+67) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/LoopVersioning.h b/llvm/include/llvm/Transforms/Utils/LoopVersioning.h
index ea4fe27c90f5c..ec07fb9259b6a 100644
--- a/llvm/include/llvm/Transforms/Utils/LoopVersioning.h
+++ b/llvm/include/llvm/Transforms/Utils/LoopVersioning.h
@@ -61,11 +61,14 @@ class LoopVersioning {
   ///        analyze L
   ///        if versioning is necessary version L
   ///        transform L
-  void versionLoop() { versionLoop(findDefsUsedOutsideOfLoop(VersionedLoop)); }
+  void versionLoop(Instruction *UserRuntimeCheck = nullptr) { versionLoop(findDefsUsedOutsideOfLoop(VersionedLoop), UserRuntimeCheck); }
 
   /// Same but if the client has already precomputed the set of values
   /// used outside the loop, this API will allows passing that.
-  void versionLoop(const SmallVectorImpl<Instruction *> &DefsUsedOutside);
+  /// \p UserMemRuntimeCheck allows the caller to supply a pre-built runtime
+  /// check instruction instead of having LoopVersioning generate one.
+  void versionLoop(const SmallVectorImpl<Instruction *> &DefsUsedOutside,
+                   Instruction *UserMemRuntimeCheck = nullptr);
 
   /// Returns the versioned loop.  Control flows here if pointers in the
   /// loop don't alias (i.e. all memchecks passed).  (This loop is actually the
diff --git a/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp b/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
index 3aed643ee8065..8d973eb32189a 100644
--- a/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopVersioningLICM.cpp
@@ -70,19 +70,23 @@
 #include "llvm/Analysis/LoopPass.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/ScalarEvolutionExpressions.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/MDBuilder.h"
 #include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/Value.h"
+#include "llvm/IR/Verifier.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/Utils/LoopUtils.h"
 #include "llvm/Transforms/Utils/LoopVersioning.h"
+#include "llvm/Transforms/Utils/ScalarEvolutionExpander.h"
 #include <cassert>
 
 using namespace llvm;
@@ -116,8 +120,8 @@ struct LoopVersioningLICM {
   LoopVersioningLICM(AliasAnalysis *AA, ScalarEvolution *SE,
                      OptimizationRemarkEmitter *ORE,
                      LoopAccessInfoManager &LAIs, LoopInfo &LI,
-                     Loop *CurLoop)
-      : AA(AA), SE(SE), LAIs(LAIs), LI(LI), CurLoop(CurLoop),
+                     AssumptionCache *AC, Loop *CurLoop)
+      : AA(AA), SE(SE), LAIs(LAIs), LI(LI), AC(AC), CurLoop(CurLoop),
         LoopDepthThreshold(LVLoopDepthThreshold),
         InvariantThreshold(LVInvarThreshold), ORE(ORE) {}
 
@@ -138,9 +142,17 @@ struct LoopVersioningLICM {
 
   LoopInfo &LI;
 
+  AssumptionCache *AC;
+
   // The current loop we are working on.
   Loop *CurLoop;
 
+  // Dynamic bound marker.
+  bool IsDynamicBound = false;
+
+  // Hoistable dependencies
+  SmallVector<Instruction *, 16> HoistedDependencies;
+
   // Maximum loop nest threshold
   unsigned LoopDepthThreshold;
 
@@ -156,6 +168,9 @@ struct LoopVersioningLICM {
   // Read only loop marker.
   bool IsReadOnlyLoop = true;
 
+  // Placeholder instruction for inserting runtime checks in the preheader.
+  Instruction *PlaceholderForRuntimeChecks = nullptr;
+
   // OptimizationRemarkEmitter
   OptimizationRemarkEmitter *ORE;
 
@@ -165,6 +180,11 @@ struct LoopVersioningLICM {
   bool legalLoopMemoryAccesses();
   bool isLoopAlreadyVisited();
   bool instructionSafeForVersioning(Instruction *I);
+  bool isSafeToHoistBoundDep(Instruction *I,
+                              const SmallPtrSetImpl<Value *> &ModifiedPtrs);
+  Instruction *insertRuntimeCheckPlaceholder(Loop *L);
+  bool generateAndAddRuntimeChecks(DominatorTree *DT);
+  void printLoopsWithPreheaders(Loop *VersionedLoop, Loop *NonVersionedLoop);
 };
 
 } // end anonymous namespace
@@ -213,12 +233,165 @@ bool LoopVersioningLICM::legalLoopStructure() {
   // to generate the bound checks.
   const SCEV *ExitCount = SE->getBackedgeTakenCount(CurLoop);
   if (isa<SCEVCouldNotCompute>(ExitCount)) {
+    IsDynamicBound = true;
     LLVM_DEBUG(dbgs() << "    loop does not have trip count\n");
-    return false;
+    LLVM_DEBUG(dbgs() << "    checking the possibility of dynamic trip count\n");
+
+    HoistedDependencies.clear();
+
+    // Check if the loop trip count is dynamic
+    BasicBlock *ExitBlock = CurLoop->getExitingBlock();
+    assert(ExitBlock && "Exiting block guaranteed by earlier getExitingBlock() check");
+    CondBrInst *ExitBranch = dyn_cast<CondBrInst>(ExitBlock->getTerminator());
+    if (!ExitBranch) {
+      LLVM_DEBUG(dbgs() << "    loop exit condition is not a conditional branch\n");
+      return false;
+    }
+    ICmpInst *ExitCmp = dyn_cast<ICmpInst>(ExitBranch->getCondition());
+    if (!ExitCmp) {
+      LLVM_DEBUG(dbgs() << "    loop exit condition is not an icmp instruction\n");
+      return false;
+    }
+    
+    PHINode *IndVar = CurLoop->getInductionVariable(*SE);
+    if (!IndVar) {
+      LLVM_DEBUG(dbgs() << "    unable to find loop induction variable\n");
+      return false;
+    }
+    LLVM_DEBUG(dbgs() << "    Induction variable: " << *IndVar << "\n");
+
+    Value *StepInst = IndVar->getIncomingValueForBlock(CurLoop->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 {
+      LLVM_DEBUG(dbgs() << "    unable to identify dynamic bound operand\n");
+      return false;
+    }
+    LLVM_DEBUG(dbgs() << "    Dynamic upper bound: " << *DynamicUpperBound
+                      << "\n");
+    
+    SmallVector<Instruction *, 16> Worklist;
+    
+    SmallPtrSet<Value *, 16> ModifiedPtrs;
+    for (BasicBlock *BB : CurLoop->getBlocks()) {
+      for (Instruction &I : *BB) {
+        if (StoreInst *SI = dyn_cast<StoreInst>(&I))
+          ModifiedPtrs.insert(SI->getPointerOperand());
+      }
+    }
+
+    if (Instruction *I = dyn_cast<Instruction>(DynamicUpperBound)) {
+      Worklist.push_back(I);
+    }
+    
+    SmallPtrSet<Instruction *, 16> Visited;
+
+    while(!Worklist.empty()) {
+      Instruction *I = Worklist.pop_back_val();
+      if (!Visited.insert(I).second)
+        continue;
+
+      if (!CurLoop->contains(I->getParent()))
+        continue;
+
+      if (!isSafeToHoistBoundDep(I, ModifiedPtrs))
+        return false;
+
+      for (Use &U : I->operands()) {
+        if (Instruction *OpI = dyn_cast<Instruction>(U.get())) {
+          if (CurLoop->contains(OpI)) {
+            Worklist.push_back(OpI);
+          }
+        }
+      }
+
+      HoistedDependencies.push_back(I);
+    }
+    return true;
   }
   return true;
 }
 
+/// Check if an instruction in the dynamic bound dependency chain is safe to
+/// hoist. Only side-effect-free arithmetic, bitwise, cast, comparison, GEP,
+/// and simple load instructions are allowed.
+bool LoopVersioningLICM::isSafeToHoistBoundDep(
+    Instruction *I, const SmallPtrSetImpl<Value *> &ModifiedPtrs) {
+  switch (I->getOpcode()) {
+  case Instruction::Load: {
+    LoadInst *LI = cast<LoadInst>(I);
+    if (!LI->isSimple()) {
+      LLVM_DEBUG(dbgs() << "    Found a non-simple load, as a dependency"
+                           " of exit condition\n");
+      return false;
+    }
+    Value *Ptr = LI->getPointerOperand();
+    if (ModifiedPtrs.count(Ptr)) {
+      LLVM_DEBUG(dbgs() << "    Bound candidate is invalid - pointer is"
+                           " stored to within loop: " << *LI << "\n");
+      return false;
+    }
+    return true;
+  }
+  // Integer arithmetic
+  case Instruction::Add:
+  case Instruction::Sub:
+  case Instruction::Mul:
+  case Instruction::UDiv:
+  case Instruction::SDiv:
+  case Instruction::URem:
+  case Instruction::SRem:
+  // Floating-point arithmetic
+  case Instruction::FAdd:
+  case Instruction::FSub:
+  case Instruction::FMul:
+  case Instruction::FDiv:
+  case Instruction::FRem:
+  case Instruction::FNeg:
+  // Bitwise operations
+  case Instruction::Shl:
+  case Instruction::LShr:
+  case Instruction::AShr:
+  case Instruction::And:
+  case Instruction::Or:
+  case Instruction::Xor:
+  // Integer casts
+  case Instruction::SExt:
+  case Instruction::ZExt:
+  case Instruction::Trunc:
+  // FP casts
+  case Instruction::FPExt:
+  case Instruction::FPTrunc:
+  case Instruction::FPToSI:
+  case Instruction::FPToUI:
+  case Instruction::SIToFP:
+  case Instruction::UIToFP:
+  // Pointer / bitcast
+  case Instruction::BitCast:
+  case Instruction::IntToPtr:
+  case Instruction::PtrToInt:
+  case Instruction::GetElementPtr:
+  // Comparisons
+  case Instruction::ICmp:
+  case Instruction::FCmp:
+    return true;
+  default:
+    LLVM_DEBUG(dbgs() << "    Unsupported instruction in bound dependency"
+                         " chain: " << *I << "\n");
+    return false;
+  }
+}
+
 /// Check memory accesses in loop and confirms it's good for
 /// LoopVersioningLICM.
 bool LoopVersioningLICM::legalLoopMemoryAccesses() {
@@ -413,7 +586,7 @@ bool LoopVersioningLICM::legalLoopInstructions() {
     LLVM_DEBUG(dbgs() << "    Found a read-only loop!\n");
     return false;
   }
-  // Profitability check:
+  // Profitablity check:
   // Check invariant threshold, should be in limit.
   if (InvariantCounter * 100 < InvariantThreshold * LoadAndStoreCounter) {
     LLVM_DEBUG(
@@ -507,39 +680,193 @@ bool LoopVersioningLICM::isLegalForVersioning() {
   return true;
 }
 
+Instruction *LoopVersioningLICM::insertRuntimeCheckPlaceholder(Loop *L) {
+  // Create a preheader for our checks
+  BasicBlock *Preheader = L->getLoopPreheader();
+  if (!Preheader)
+    return nullptr;
+
+  // 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;
+}
+
+bool 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");
+
+  PlaceholderForRuntimeChecks->replaceAllUsesWith(RuntimeCheck);
+  PlaceholderForRuntimeChecks->eraseFromParent();
+
+  return RuntimeCheck != nullptr;
+}
+
+void LoopVersioningLICM::printLoopsWithPreheaders(
+    Loop *VersionedLoop, Loop *NonVersionedLoop) {
+  std::string DL;
+  if (CurLoop) {
+    raw_string_ostream OS(DL);
+    if (const DebugLoc LoopDbgLoc = CurLoop->getStartLoc())
+      LoopDbgLoc.print(OS);
+    else
+      OS << CurLoop->getHeader()->getParent()->getParent()->getModuleIdentifier();
+  }
+
+  auto printLoop = [&DL](Loop *L){
+    if (!L)
+      return;
+    dbgs() << ">>>>>>>>>>  Loop: ";
+    dbgs() << "Loop starts at: " << DL;
+    dbgs() << "\n";
+    BasicBlock *Header = L->getHeader();
+    BasicBlock *Preheader = L->getLoopPreheader();
+    if (Preheader)
+      Header = Preheader;
+    dbgs() << "Check :" << *Header->getSinglePredecessor() <<"\n"; 
+    if (Preheader) {
+      dbgs() << "Preheader: " << *Preheader << "\n";
+    } else {
+      dbgs() << "No preheader\n";
+    }
+    L->dumpVerbose();
+    dbgs() << "<<<<<<<<<<<<<< Loop: ";
+    dbgs() << "Loop starts at: " << DL;
+    dbgs() << "\n";
+  };
+  
+  dbgs() << "\n--- NonVersioned Loop ---\n";
+  printLoop(NonVersionedLoop);
+  dbgs() << "\n--- Versioned Loop ---\n";
+  printLoop(VersionedLoop);
+}
+
 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);
+
+      generateAndAddRuntimeChecks(DT);
+
+// #ifdef EXPENSIVE_CHECKS
+      assert(!verifyFunction(*LVer.getVersionedLoop()->getHeader()->getParent(), &dbgs())
+             && "Verification failure after runtime checks");
+// #endif
+
+      // LAI isn't used anymore, so clear it to avoid dangling reference
+      LAI = nullptr;
+    }
     // Set Loop Versioning metaData for original loop.
     addStringMetadataToLoop(LVer.getNonVersionedLoop(), LICMVersioningMetaData);
     // Set Loop Versioning metaData for version loop.
     addStringMetadataToLoop(LVer.getVersionedLoop(), LICMVersioningMetaData);
+
     // Set "llvm.mem.parallel_loop_access" metaData to versioned loop.
     // FIXME: "llvm.mem.parallel_loop_access" annotates memory access
     // instructions, not loops.
     addStringMetadataToLoop(LVer.getVersionedLoop(),
                             "llvm.mem.parallel_loop_access");
+
     // Update version loop with aggressive aliasing assumption.
     LVer.annotateLoopWithNoAlias();
     Changed = true;
+
+    LLVM_DEBUG(dbgs() << "Successfully transformed the code\n");
+    LLVM_DEBUG(printLoopsWithPreheaders(LVer.getVersionedLoop(),
+                                       LVer.getNonVersionedLoop()));
   }
   return Changed;
 }
 
+namespace llvm {
+
 PreservedAnalyses LoopVersioningLICMPass::run(Loop &L, LoopAnalysisManager &AM,
                                               LoopStandardAnalysisResults &LAR,
                                               LPMUpdater &U) {
@@ -550,7 +877,8 @@ PreservedAnalyses LoopVersioningLICMPass::run(Loop &L, LoopAnalysisManager &AM,
   OptimizationRemarkEmitter ORE(F);
 
   LoopAccessInfoManager LAIs(*SE, *AA, *DT, LAR.LI, nullptr, nullptr, &LAR.AC);
-  if (!LoopVersioningLICM(AA, SE, &ORE, LAIs, LAR.LI, &L).run(DT))
+  if (!LoopVersioningLICM(AA, SE, &ORE, LAIs, LAR.LI, &LAR.AC, &L).run(DT))
     return PreservedAnalyses::all();
   return getLoopPassPreservedAnalyses();
 }
+} // namespace llvm
diff --git a/llvm/lib/Transforms/Utils/LoopVersioning.cpp b/llvm/lib/Transforms/Utils/LoopVersioning.cpp
index b90466a8c49cf..6696e3c6a6b0e 100644
--- a/llvm/lib/Transforms/Utils/LoopVersioning.cpp
+++ b/llvm/lib/Transforms/Utils/LoopVersioning.cpp
@@ -48,7 +48,7 @@ LoopVersioning::LoopVersioning(const LoopAccessInfo &LAI,
       LAI(LAI), LI(LI), DT(DT), SE(SE) {}
 
 void LoopVersioning::versionLoop(
-    const SmallVectorImpl<Instruction *> &DefsUsedOutside) {
+    const SmallVectorImpl<Instruction *> &DefsUsedOutside, Instruction *UserRuntimeCheck) {
   assert(VersionedLoop->getUniqueExitBlock() && "No single exit block");
   assert(VersionedLoop->isLoopSimplifyForm() &&
          "Loop is not in loop-simplify form");
@@ -59,27 +59,37 @@ void LoopVersioning::versionLoop(
 
   // Add the memcheck in the original preheader (this is empty initially).
   BasicBlock *RuntimeCheckBB = VersionedLoop->getLoopPreheader();
-  const auto &RtPtrChecking = *LAI.getRuntimePointerChecking();
-
-  SCEVExpander Exp2(*RtPtrChecking.getSE(), "induction");
-  MemRuntimeCheck = addRuntimeChecks(RuntimeCheckBB->getTerminator(),
-                                     VersionedLoop, AliasChecks, Exp2);
 
-  SCEVExpander Exp(*SE, "scev.check");
-  SCEVRuntimeCheck =
-      Exp.expandCodeForPredicate(&Preds, RuntimeCheckBB->getTerminator());
+  if (!UserRuntimeCheck) {
+    const auto &RtPtrChecking = *LAI.getRuntimePointerChecking();
+
+    SCEVExpander Exp2(*RtPtrChecking.getSE(), "induction");
+    MemRuntimeCheck = addRuntimeChecks(RuntimeCheckBB->getTerminator(),
+                                      VersionedLoop, AliasChecks, Exp2);
+    SCEVExpander Exp(*SE, "scev.check");
+    SCEVRuntimeCheck =
+        Exp.expandCodeForPredicate(&Preds, RuntimeCheckBB->getTerminator());
+
+    IRBuilder<InstSimplifyFolder> Builder(
+        RuntimeCheckBB->getContext(),
+        InstSimplifyFolder(RuntimeCheckBB->getDataLayout()));
+    if (MemRuntimeCheck && SCEVRuntimeCheck) {
+      Builder.SetInsertPoint(RuntimeCheckBB->getTerminator());
+      RuntimeCheck =
+          Builder.CreateOr(MemRuntimeCheck, SCEVRuntimeCheck, "lver.safe");
+    } else
+      RuntimeCheck = MemRuntimeCheck ? MemRuntimeCheck : SCEVRuntimeCheck;
+
+    Exp.eraseDeadInstructions(SCEVRuntimeCheck);
+  }
+  else {
+    RuntimeCheck = UserRuntimeCheck;
+    RuntimeCheckBB = UserRuntimeCheck->getParent();
+  }
 
   IRBuilder<InstSimplifyFolder> Builder(
       RuntimeCheckBB->ge...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list