[llvm] 544171d - [LoopSafetyInfo] Store Loop and automatically compute (NFC) (#221238)

via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 7 02:28:35 PDT 2026


Author: Nikita Popov
Date: 2026-09-07T09:28:29Z
New Revision: 544171da142fd5a380a943c9f7ea5150b9f2a293

URL: https://github.com/llvm/llvm-project/commit/544171da142fd5a380a943c9f7ea5150b9f2a293
DIFF: https://github.com/llvm/llvm-project/commit/544171da142fd5a380a943c9f7ea5150b9f2a293.diff

LOG: [LoopSafetyInfo] Store Loop and automatically compute (NFC) (#221238)

This makes two changes:

* LoopSafetyInfo is bound to a single loop. Store the Loop in the ctor,
so it does not need to be passed to individual queries.
* Automatically compute LoopSafetyInfo on construction, instead of in a
separate call. This is mostly to clarify that (contrary to the doc
comment) nobody is actually trying to invalidate LoopSafetyInfo by
rerunning computeLoopSafetyInfo().

This makes it easier to change implementation details, like computing
information lazily.

Added: 
    

Modified: 
    llvm/include/llvm/Analysis/MustExecute.h
    llvm/lib/Analysis/MustExecute.cpp
    llvm/lib/Transforms/Scalar/LICM.cpp
    llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
    llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
    llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp
    llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h
index af78d8e5e982d..7e64d0622a62a 100644
--- a/llvm/include/llvm/Analysis/MustExecute.h
+++ b/llvm/include/llvm/Analysis/MustExecute.h
@@ -50,13 +50,8 @@ class raw_ostream;
 /// isGuaranteedToExecute below, but some callers bailout or fallback to
 /// alternate reasoning if a loop contains any implicit control flow.
 /// NOTE: LoopSafetyInfo contains cached information regarding loops and their
-/// particular blocks. This information is only dropped on invocation of
-/// computeLoopSafetyInfo. If the loop or any of its block is deleted, or if
-/// any thrower instructions have been added or removed from them, or if the
-/// control flow has changed, or in case of other meaningful modifications, the
-/// LoopSafetyInfo needs to be recomputed. If a meaningful modifications to the
-/// loop were made and the info wasn't recomputed properly, the behavior of all
-/// methods except for computeLoopSafetyInfo is undefined.
+/// particular blocks. Cached information may not be valid after control flow
+/// changes.
 class LoopSafetyInfo {
   // Used to update funclet bundle operands.
   DenseMap<BasicBlock *, ColorVector> BlockColors;
@@ -65,9 +60,14 @@ class LoopSafetyInfo {
   // loop is entered.
   mutable DenseMap<const BasicBlock *, bool> GuaranteedToExecute;
 
+  bool allLoopPathsLeadToBlockImpl(const BasicBlock *BB,
+                                   const DominatorTree *DT) const;
+
 protected:
+  const Loop *CurLoop;
+
   /// Computes block colors.
-  LLVM_ABI void computeBlockColors(const Loop *CurLoop);
+  LLVM_ABI void computeBlockColors();
 
 public:
   /// Returns block colors map that is used to update funclet operand bundles.
@@ -85,29 +85,16 @@ class LoopSafetyInfo {
   virtual bool anyBlockMayThrow() const = 0;
 
   /// Return true if we must reach the block \p BB under assumption that the
-  /// loop \p CurLoop is entered.
-  LLVM_ABI bool allLoopPathsLeadToBlock(const Loop *CurLoop,
-                                        const BasicBlock *BB,
+  /// loop is entered.
+  LLVM_ABI bool allLoopPathsLeadToBlock(const BasicBlock *BB,
                                         const DominatorTree *DT) const;
 
-  LLVM_ABI bool allLoopPathsLeadToBlockImpl(const Loop *CurLoop,
-                                            const BasicBlock *BB,
-                                            const DominatorTree *DT) const;
-
-  /// Computes safety information for a loop checks loop body & header for
-  /// the possibility of may throw exception, it takes LoopSafetyInfo and loop
-  /// as argument. Updates safety information in LoopSafetyInfo argument.
-  /// Note: This is defined to clear and reinitialize an already initialized
-  /// LoopSafetyInfo.  Some callers rely on this fact.
-  virtual void computeLoopSafetyInfo(const Loop *CurLoop) = 0;
-
   /// Returns true if the instruction in a loop is guaranteed to execute at
   /// least once (under the assumption that the loop is entered).
   virtual bool isGuaranteedToExecute(const Instruction &Inst,
-                                     const DominatorTree *DT,
-                                     const Loop *CurLoop) const = 0;
+                                     const DominatorTree *DT) const = 0;
 
-  LoopSafetyInfo() = default;
+  LoopSafetyInfo(const Loop *CurLoop) : CurLoop(CurLoop) {}
 
   virtual ~LoopSafetyInfo() = default;
 };
@@ -121,16 +108,19 @@ class LLVM_ABI SimpleLoopSafetyInfo : public LoopSafetyInfo {
                                // may throw.
   bool HeaderMayThrow = false; // Same as previous, but specific to loop header
 
+  void computeLoopSafetyInfo();
+
 public:
+  explicit SimpleLoopSafetyInfo(const Loop *L) : LoopSafetyInfo(L) {
+    computeLoopSafetyInfo();
+  }
+
   bool blockMayThrow(const BasicBlock *BB) const override;
 
   bool anyBlockMayThrow() const override;
 
-  void computeLoopSafetyInfo(const Loop *CurLoop) override;
-
   bool isGuaranteedToExecute(const Instruction &Inst,
-                             const DominatorTree *DT,
-                             const Loop *CurLoop) const override;
+                             const DominatorTree *DT) const override;
 };
 
 /// This implementation of LoopSafetyInfo use ImplicitControlFlowTracking to
@@ -146,26 +136,27 @@ class LLVM_ABI ICFLoopSafetyInfo : public LoopSafetyInfo {
   // Contains information about instruction that may possibly write memory.
   mutable MemoryWriteTracking MW;
 
+  void computeLoopSafetyInfo();
+
 public:
+  explicit ICFLoopSafetyInfo(const Loop *L) : LoopSafetyInfo(L) {
+    computeLoopSafetyInfo();
+  }
+
   bool blockMayThrow(const BasicBlock *BB) const override;
 
   bool anyBlockMayThrow() const override;
 
-  void computeLoopSafetyInfo(const Loop *CurLoop) override;
-
   bool isGuaranteedToExecute(const Instruction &Inst,
-                             const DominatorTree *DT,
-                             const Loop *CurLoop) const override;
+                             const DominatorTree *DT) const override;
 
   /// Returns true if we could not execute a memory-modifying instruction before
-  /// we enter \p BB under assumption that \p CurLoop is entered.
-  bool doesNotWriteMemoryBefore(const BasicBlock *BB, const Loop *CurLoop)
-      const;
+  /// we enter \p BB under assumption that the loop is entered.
+  bool doesNotWriteMemoryBefore(const BasicBlock *BB) const;
 
   /// Returns true if we could not execute a memory-modifying instruction before
-  /// we execute \p I under assumption that \p CurLoop is entered.
-  bool doesNotWriteMemoryBefore(const Instruction &I, const Loop *CurLoop)
-      const;
+  /// we execute \p I under assumption that the loop is entered.
+  bool doesNotWriteMemoryBefore(const Instruction &I) const;
 
   /// Inform the safety info that we are planning to insert a new instruction
   /// \p Inst into the basic block \p BB. It will make all cache updates to keep

diff  --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp
index cbdeaf8d990d8..2462ef716dd52 100644
--- a/llvm/lib/Analysis/MustExecute.cpp
+++ b/llvm/lib/Analysis/MustExecute.cpp
@@ -46,7 +46,7 @@ bool SimpleLoopSafetyInfo::anyBlockMayThrow() const {
   return MayThrow;
 }
 
-void SimpleLoopSafetyInfo::computeLoopSafetyInfo(const Loop *CurLoop) {
+void SimpleLoopSafetyInfo::computeLoopSafetyInfo() {
   assert(CurLoop != nullptr && "CurLoop can't be null");
   BasicBlock *Header = CurLoop->getHeader();
   // Iterate over header and compute safety info.
@@ -63,7 +63,7 @@ void SimpleLoopSafetyInfo::computeLoopSafetyInfo(const Loop *CurLoop) {
       break;
   }
 
-  computeBlockColors(CurLoop);
+  computeBlockColors();
 }
 
 bool ICFLoopSafetyInfo::blockMayThrow(const BasicBlock *BB) const {
@@ -74,7 +74,7 @@ bool ICFLoopSafetyInfo::anyBlockMayThrow() const {
   return MayThrow;
 }
 
-void ICFLoopSafetyInfo::computeLoopSafetyInfo(const Loop *CurLoop) {
+void ICFLoopSafetyInfo::computeLoopSafetyInfo() {
   assert(CurLoop != nullptr && "CurLoop can't be null");
   ICF.clear();
   MW.clear();
@@ -85,7 +85,7 @@ void ICFLoopSafetyInfo::computeLoopSafetyInfo(const Loop *CurLoop) {
       MayThrow = true;
       break;
     }
-  computeBlockColors(CurLoop);
+  computeBlockColors();
 }
 
 void ICFLoopSafetyInfo::insertInstructionTo(const Instruction *Inst,
@@ -99,7 +99,7 @@ void ICFLoopSafetyInfo::removeInstruction(const Instruction *Inst) {
   MW.removeInstruction(Inst);
 }
 
-void LoopSafetyInfo::computeBlockColors(const Loop *CurLoop) {
+void LoopSafetyInfo::computeBlockColors() {
   // Compute funclet colors if we might sink/hoist in a function with a funclet
   // personality routine.
   Function *Fn = CurLoop->getHeader()->getParent();
@@ -195,8 +195,7 @@ static void collectTransitivePredecessors(
   }
 }
 
-bool LoopSafetyInfo::allLoopPathsLeadToBlock(const Loop *CurLoop,
-                                             const BasicBlock *BB,
+bool LoopSafetyInfo::allLoopPathsLeadToBlock(const BasicBlock *BB,
                                              const DominatorTree *DT) const {
   assert(CurLoop->contains(BB) && "Should only be called for loop blocks!");
 
@@ -206,12 +205,12 @@ bool LoopSafetyInfo::allLoopPathsLeadToBlock(const Loop *CurLoop,
 
   auto [It, Inserted] = GuaranteedToExecute.try_emplace(BB, false);
   if (Inserted)
-    It->second = allLoopPathsLeadToBlockImpl(CurLoop, BB, DT);
+    It->second = allLoopPathsLeadToBlockImpl(BB, DT);
   return It->second;
 }
 
 bool LoopSafetyInfo::allLoopPathsLeadToBlockImpl(
-    const Loop *CurLoop, const BasicBlock *BB, const DominatorTree *DT) const {
+    const BasicBlock *BB, const DominatorTree *DT) const {
   // Collect all transitive predecessors of BB in the same loop. This set will
   // be a subset of the blocks within the loop.
   SmallPtrSet<const BasicBlock *, 4> Predecessors;
@@ -271,9 +270,8 @@ bool LoopSafetyInfo::allLoopPathsLeadToBlockImpl(
 
 /// Returns true if the instruction in a loop is guaranteed to execute at least
 /// once.
-bool SimpleLoopSafetyInfo::isGuaranteedToExecute(const Instruction &Inst,
-                                                 const DominatorTree *DT,
-                                                 const Loop *CurLoop) const {
+bool SimpleLoopSafetyInfo::isGuaranteedToExecute(
+    const Instruction &Inst, const DominatorTree *DT) const {
   // If the instruction is in the header block for the loop (which is very
   // common), it is always guaranteed to dominate the exit blocks.  Since this
   // is a common case, and can save some work, check it now.
@@ -287,18 +285,16 @@ bool SimpleLoopSafetyInfo::isGuaranteedToExecute(const Instruction &Inst,
 
   // If there is a path from header to exit or latch that doesn't lead to our
   // instruction's block, return false.
-  return allLoopPathsLeadToBlock(CurLoop, Inst.getParent(), DT);
+  return allLoopPathsLeadToBlock(Inst.getParent(), DT);
 }
 
 bool ICFLoopSafetyInfo::isGuaranteedToExecute(const Instruction &Inst,
-                                              const DominatorTree *DT,
-                                              const Loop *CurLoop) const {
+                                              const DominatorTree *DT) const {
   return !ICF.isDominatedByICFIFromSameBlock(&Inst) &&
-         allLoopPathsLeadToBlock(CurLoop, Inst.getParent(), DT);
+         allLoopPathsLeadToBlock(Inst.getParent(), DT);
 }
 
-bool ICFLoopSafetyInfo::doesNotWriteMemoryBefore(const BasicBlock *BB,
-                                                 const Loop *CurLoop) const {
+bool ICFLoopSafetyInfo::doesNotWriteMemoryBefore(const BasicBlock *BB) const {
   assert(CurLoop->contains(BB) && "Should only be called for loop blocks!");
 
   // Fast path: there are no instructions before header.
@@ -317,22 +313,20 @@ bool ICFLoopSafetyInfo::doesNotWriteMemoryBefore(const BasicBlock *BB,
   return true;
 }
 
-bool ICFLoopSafetyInfo::doesNotWriteMemoryBefore(const Instruction &I,
-                                                 const Loop *CurLoop) const {
+bool ICFLoopSafetyInfo::doesNotWriteMemoryBefore(const Instruction &I) const {
   auto *BB = I.getParent();
   assert(CurLoop->contains(BB) && "Should only be called for loop blocks!");
   return !MW.isDominatedByMemoryWriteFromSameBlock(&I) &&
-         doesNotWriteMemoryBefore(BB, CurLoop);
+         doesNotWriteMemoryBefore(BB);
 }
 
 static bool isMustExecuteIn(const Instruction &I, Loop *L, DominatorTree *DT) {
   // TODO: merge these two routines.  For the moment, we display the best
   // result obtained by *either* implementation.  This is a bit unfair since no
   // caller actually gets the full power at the moment.
-  SimpleLoopSafetyInfo LSI;
-  LSI.computeLoopSafetyInfo(L);
-  return LSI.isGuaranteedToExecute(I, DT, L) ||
-    isGuaranteedToExecuteForEveryIteration(&I, L);
+  SimpleLoopSafetyInfo LSI(L);
+  return LSI.isGuaranteedToExecute(I, DT) ||
+         isGuaranteedToExecuteForEveryIteration(&I, L);
 }
 
 namespace {

diff  --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index ee4c7603fdd2d..bc12aca7c1086 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -460,8 +460,7 @@ bool LoopInvariantCodeMotion::runOnLoop(Loop *L, AAResults *AA, LoopInfo *LI,
   BasicBlock *Preheader = L->getLoopPreheader();
 
   // Compute loop safety information.
-  ICFLoopSafetyInfo SafetyInfo;
-  SafetyInfo.computeLoopSafetyInfo(L);
+  ICFLoopSafetyInfo SafetyInfo(L);
 
   // We want to visit all of the instructions in this loop... that are not parts
   // of our subloops (they have already had their invariants hoisted out of
@@ -986,8 +985,8 @@ bool llvm::hoistRegion(DomTreeNode *N, AAResults *AA, LoopInfo *LI,
                match(&I, m_Intrinsic<Intrinsic::invariant_start>());
       };
       auto MustExecuteWithoutWritesBefore = [&](Instruction &I) {
-        return SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop) &&
-               SafetyInfo->doesNotWriteMemoryBefore(I, CurLoop);
+        return SafetyInfo->isGuaranteedToExecute(I, DT) &&
+               SafetyInfo->doesNotWriteMemoryBefore(I);
       };
       if ((IsInvariantStart(I) || isGuard(&I)) &&
           CurLoop->hasLoopInvariantOperands(&I) &&
@@ -1799,7 +1798,7 @@ static void hoist(Instruction &I, const DominatorTree *DT, const Loop *CurLoop,
       // The check on hasMetadataOtherThanDebugLoc is to prevent us from burning
       // time in isGuaranteedToExecute if we don't actually have anything to
       // drop.  It is a compile time optimization, not required for correctness.
-      !SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop)) {
+      !SafetyInfo->isGuaranteedToExecute(I, DT)) {
     I.dropUBImplyingAttrsAndMetadata();
   }
 
@@ -1832,8 +1831,7 @@ static bool isSafeToExecuteUnconditionally(
       isSafeToSpeculativelyExecute(&Inst, CtxI, AC, DT, TLI))
     return true;
 
-  bool GuaranteedToExecute =
-      SafetyInfo->isGuaranteedToExecute(Inst, DT, CurLoop);
+  bool GuaranteedToExecute = SafetyInfo->isGuaranteedToExecute(Inst, DT);
 
   if (!GuaranteedToExecute) {
     auto *LI = dyn_cast<LoadInst>(&Inst);
@@ -2132,7 +2130,7 @@ bool llvm::promoteLoopAccessesToScalars(
 
         if (!LoadIsGuaranteedToExecute)
           LoadIsGuaranteedToExecute =
-              SafetyInfo->isGuaranteedToExecute(*UI, DT, CurLoop);
+              SafetyInfo->isGuaranteedToExecute(*UI, DT);
 
         // Note that proving a load safe to speculate requires proving
         // sufficient alignment at the target location.  Proving it guaranteed
@@ -2162,8 +2160,7 @@ bool llvm::promoteLoopAccessesToScalars(
         // alignment than any other guaranteed stores, in which case we can
         // raise the alignment on the promoted store.
         Align InstAlignment = Store->getAlign();
-        bool GuaranteedToExecute =
-            SafetyInfo->isGuaranteedToExecute(*UI, DT, CurLoop);
+        bool GuaranteedToExecute = SafetyInfo->isGuaranteedToExecute(*UI, DT);
         StoreIsGuaranteedToExecute |= GuaranteedToExecute;
         if (GuaranteedToExecute) {
           DereferenceableInPH = true;
@@ -2365,7 +2362,7 @@ collectPromotionCandidates(MemorySSA *MSSA, AliasAnalysis *AA,
     if (IsPotentiallyPromotable(I)) {
       AttemptingPromotion.insert(I);
       if (StoreInst *SI = dyn_cast<StoreInst>(I);
-          SI && !SafetyInfo->isGuaranteedToExecute(*SI, DT, L)) {
+          SI && !SafetyInfo->isGuaranteedToExecute(*SI, DT)) {
         // Promotion requires inserting a new store at the loop exits; we need
         // to prove that store doesn't alias anything, in addition to proving
         // aliasing for the stores we're removing. The new store is executed

diff  --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 4e5cd8462cc4d..dbe114f7c220a 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -403,8 +403,7 @@ bool LoopIdiomRecognize::runOnCountableLoop() {
 
   // The following transforms hoist stores/memsets into the loop pre-header.
   // Give up if the loop has instructions that may throw.
-  SimpleLoopSafetyInfo SafetyInfo;
-  SafetyInfo.computeLoopSafetyInfo(CurLoop);
+  SimpleLoopSafetyInfo SafetyInfo(CurLoop);
   if (SafetyInfo.anyBlockMayThrow())
     return false;
 

diff  --git a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
index ba61521e25d8d..7f6a08454b749 100644
--- a/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
+++ b/llvm/lib/Transforms/Scalar/SimpleLoopUnswitch.cpp
@@ -2138,9 +2138,8 @@ static void unswitchNontrivialInvariants(
     else {
       // It is only legal to preserve make.implicit metadata if we are
       // guaranteed no reach implicit null check after following this branch.
-      ICFLoopSafetyInfo SafetyInfo;
-      SafetyInfo.computeLoopSafetyInfo(&L);
-      if (!SafetyInfo.isGuaranteedToExecute(TI, &DT, &L))
+      ICFLoopSafetyInfo SafetyInfo(&L);
+      if (!SafetyInfo.isGuaranteedToExecute(TI, &DT))
         TI.setMetadata(LLVMContext::MD_make_implicit, nullptr);
     }
   }
@@ -3297,9 +3296,8 @@ static bool shouldInsertFreeze(Loop &L, Instruction &TI, DominatorTree &DT,
   if (!FreezeLoopUnswitchCond)
     return false;
 
-  ICFLoopSafetyInfo SafetyInfo;
-  SafetyInfo.computeLoopSafetyInfo(&L);
-  if (SafetyInfo.isGuaranteedToExecute(TI, &DT, &L))
+  ICFLoopSafetyInfo SafetyInfo(&L);
+  if (SafetyInfo.isGuaranteedToExecute(TI, &DT))
     return false;
 
   Value *Cond;

diff  --git a/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp b/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp
index b04cfd1a33b7c..fe34d3ea58e6a 100644
--- a/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnrollAndJam.cpp
@@ -948,8 +948,7 @@ bool llvm::isSafeToUnrollAndJam(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
   }
 
   // Check the loop safety info for exceptions.
-  SimpleLoopSafetyInfo LSI;
-  LSI.computeLoopSafetyInfo(L);
+  SimpleLoopSafetyInfo LSI(L);
   if (LSI.anyBlockMayThrow()) {
     LLVM_DEBUG(dbgs() << "Won't unroll-and-jam; Something may throw\n");
     return false;

diff  --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
index e05147ab7a3e5..0c080d9434ea8 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
@@ -1844,12 +1844,11 @@ bool LoopVectorizationLegality::canUncountableExitConditionLoadBeMoved(
     return false;
   }
 
-  ICFLoopSafetyInfo SafetyInfo;
-  SafetyInfo.computeLoopSafetyInfo(TheLoop);
+  ICFLoopSafetyInfo SafetyInfo(TheLoop);
   LoadInst *Load = cast<LoadInst>(L);
   // We need to know that load will be executed before we can hoist a
   // copy out to run just before the first iteration.
-  if (!SafetyInfo.isGuaranteedToExecute(*Load, DT, TheLoop)) {
+  if (!SafetyInfo.isGuaranteedToExecute(*Load, DT)) {
     reportVectorizationFailure(
         "Load for uncountable exit not guaranteed to execute",
         "ConditionalUncountableExitLoad", ORE, TheLoop);


        


More information about the llvm-commits mailing list