[llvm] 52452aa - [CFG] Support CycleInfo in isPotentiallyReachable() (#187681)

via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 20 07:12:41 PDT 2026


Author: Nikita Popov
Date: 2026-03-20T14:12:36Z
New Revision: 52452aa447d2a7b0eba61c117a21bc118494991d

URL: https://github.com/llvm/llvm-project/commit/52452aa447d2a7b0eba61c117a21bc118494991d
DIFF: https://github.com/llvm/llvm-project/commit/52452aa447d2a7b0eba61c117a21bc118494991d.diff

LOG: [CFG] Support CycleInfo in isPotentiallyReachable() (#187681)

Essentially do the same thing as for LoopInfo. Anything inside a cycle
is mutually reachable, and the cycle can be replaced by its exit blocks
in the walk.

An interesting additional thing we could do for CycleInfo (but not
LoopInfo) is to early exit the walk if the stop block is not in a cycle
and dominates the start block. I've not included this in this patch to
keep the implementation the same as for LoopInfo to start with.

Added: 
    

Modified: 
    llvm/include/llvm/ADT/GenericCycleImpl.h
    llvm/include/llvm/ADT/GenericCycleInfo.h
    llvm/include/llvm/Analysis/CFG.h
    llvm/lib/Analysis/CFG.cpp
    llvm/unittests/Analysis/CFGTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h
index aa43430b22834..1a28fb1cef921 100644
--- a/llvm/include/llvm/ADT/GenericCycleImpl.h
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -283,8 +283,8 @@ template <typename ContextT> class GenericCycleInfoCompute {
 };
 
 template <typename ContextT>
-auto GenericCycleInfo<ContextT>::getTopLevelParentCycle(BlockT *Block)
-    -> CycleT * {
+auto GenericCycleInfo<ContextT>::getTopLevelParentCycle(
+    const BlockT *Block) const -> CycleT * {
   CycleT *Cycle = getCycle(Block);
   return Cycle ? Cycle->TopLevelCycle : nullptr;
 }

diff  --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h
index bfa7cb5731e20..aba3db95d6df8 100644
--- a/llvm/include/llvm/ADT/GenericCycleInfo.h
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -302,7 +302,7 @@ template <typename ContextT> class GenericCycleInfo {
   CycleT *getSmallestCommonCycle(CycleT *A, CycleT *B) const;
   CycleT *getSmallestCommonCycle(BlockT *A, BlockT *B) const;
   unsigned getCycleDepth(const BlockT *Block) const;
-  CycleT *getTopLevelParentCycle(BlockT *Block);
+  CycleT *getTopLevelParentCycle(const BlockT *Block) const;
 
   /// Assumes that \p Cycle is the innermost cycle containing \p Block.
   /// \p Block will be appended to \p Cycle and all of its parent cycles.

diff  --git a/llvm/include/llvm/Analysis/CFG.h b/llvm/include/llvm/Analysis/CFG.h
index f1e8cb5225caa..0baea511ee922 100644
--- a/llvm/include/llvm/Analysis/CFG.h
+++ b/llvm/include/llvm/Analysis/CFG.h
@@ -22,6 +22,7 @@
 namespace llvm {
 
 class BasicBlock;
+class CycleInfo;
 class DominatorTree;
 class Function;
 class Instruction;
@@ -70,7 +71,8 @@ LLVM_ABI bool isCriticalEdge(const Instruction *TI, const BasicBlock *Succ,
 LLVM_ABI bool isPotentiallyReachable(
     const Instruction *From, const Instruction *To,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet = nullptr,
-    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
+    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr,
+    const CycleInfo *CI = nullptr);
 
 /// Determine whether block 'To' is reachable from 'From', returning
 /// true if uncertain.
@@ -81,7 +83,8 @@ LLVM_ABI bool isPotentiallyReachable(
 LLVM_ABI bool isPotentiallyReachable(
     const BasicBlock *From, const BasicBlock *To,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet = nullptr,
-    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
+    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr,
+    const CycleInfo *CI = nullptr);
 
 /// Determine whether there is at least one path from a block in
 /// 'Worklist' to 'StopBB' without passing through any blocks in
@@ -95,7 +98,8 @@ LLVM_ABI bool isPotentiallyReachable(
 LLVM_ABI bool isPotentiallyReachableFromMany(
     SmallVectorImpl<BasicBlock *> &Worklist, const BasicBlock *StopBB,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
-    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
+    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr,
+    const CycleInfo *CI = nullptr);
 
 /// Determine whether there is a potentially a path from at least one block in
 /// 'Worklist' to at least one block in 'StopSet' within a single function
@@ -107,7 +111,8 @@ LLVM_ABI bool isManyPotentiallyReachableFromMany(
     SmallVectorImpl<BasicBlock *> &Worklist,
     const SmallPtrSetImpl<const BasicBlock *> &StopSet,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
-    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr);
+    const DominatorTree *DT = nullptr, const LoopInfo *LI = nullptr,
+    const CycleInfo *CI = nullptr);
 
 /// Return true if the control flow in \p RPOTraversal is irreducible.
 ///

diff  --git a/llvm/lib/Analysis/CFG.cpp b/llvm/lib/Analysis/CFG.cpp
index da74691a6e6a5..e466ab64ef6c9 100644
--- a/llvm/lib/Analysis/CFG.cpp
+++ b/llvm/lib/Analysis/CFG.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Analysis/CFG.h"
+#include "llvm/Analysis/CycleAnalysis.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/IntrinsicInst.h"
@@ -144,7 +145,12 @@ template <class StopSetT>
 static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
                             const StopSetT &StopSet,
                             const SmallPtrSetImpl<BasicBlock *> *ExclusionSet,
-                            const DominatorTree *DT, const LoopInfo *LI) {
+                            const DominatorTree *DT, const LoopInfo *LI,
+                            const CycleInfo *CI) {
+  // If both LI and CI are passed, use CI, which gives us more information.
+  if (CI)
+    LI = nullptr;
+
   // When a stop block is unreachable, it's dominated from everywhere,
   // regardless of whether there's a path between the two blocks.
   if (DT) {
@@ -172,6 +178,14 @@ static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
     }
   }
 
+  SmallPtrSet<const Cycle *, 8> CyclesWithHoles;
+  if (CI && ExclusionSet) {
+    for (auto *BB : *ExclusionSet) {
+      if (const Cycle *C = CI->getTopLevelParentCycle(BB))
+        CyclesWithHoles.insert(C);
+    }
+  }
+
   SmallPtrSet<const Loop *, 2> StopLoops;
   if (LI) {
     for (auto *StopSetBB : StopSet) {
@@ -180,6 +194,14 @@ static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
     }
   }
 
+  SmallPtrSet<const Cycle *, 2> StopCycles;
+  if (CI) {
+    for (auto *StopSetBB : StopSet) {
+      if (const Cycle *C = CI->getTopLevelParentCycle(StopSetBB))
+        StopCycles.insert(C);
+    }
+  }
+
   unsigned Limit = DefaultMaxBBsToExplore;
   SmallPtrSet<const BasicBlock*, 32> Visited;
   do {
@@ -197,16 +219,25 @@ static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
         return true;
     }
 
-    const Loop *Outer = nullptr;
+    const Loop *OuterL = nullptr;
     if (LI) {
-      Outer = getOutermostLoop(LI, BB);
+      OuterL = getOutermostLoop(LI, BB);
       // If we're in a loop with a hole, not all blocks in the loop are
       // reachable from all other blocks. That implies we can't simply jump to
       // the loop's exit blocks, as that exit might need to pass through an
       // excluded block. Clear Outer so we process BB's successors.
-      if (LoopsWithHoles.count(Outer))
-        Outer = nullptr;
-      if (StopLoops.contains(Outer))
+      if (LoopsWithHoles.count(OuterL))
+        OuterL = nullptr;
+      else if (StopLoops.contains(OuterL))
+        return true;
+    }
+
+    const Cycle *OuterC = nullptr;
+    if (CI) {
+      OuterC = CI->getTopLevelParentCycle(BB);
+      if (CyclesWithHoles.count(OuterC))
+        OuterC = nullptr;
+      else if (StopCycles.contains(OuterC))
         return true;
     }
 
@@ -216,11 +247,13 @@ static bool isReachableImpl(SmallVectorImpl<BasicBlock *> &Worklist,
       return true;
     }
 
-    if (Outer) {
+    if (OuterL) {
       // All blocks in a single loop are reachable from all other blocks. From
       // any of these blocks, we can skip directly to the exits of the loop,
       // ignoring any other blocks inside the loop body.
-      Outer->getExitBlocks(Worklist);
+      OuterL->getExitBlocks(Worklist);
+    } else if (OuterC) {
+      OuterC->getExitBlocks(Worklist);
     } else {
       Worklist.append(succ_begin(BB), succ_end(BB));
     }
@@ -249,25 +282,25 @@ template <class T> class SingleEntrySet {
 bool llvm::isPotentiallyReachableFromMany(
     SmallVectorImpl<BasicBlock *> &Worklist, const BasicBlock *StopBB,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
-    const LoopInfo *LI) {
+    const LoopInfo *LI, const CycleInfo *CI) {
   return isReachableImpl<SingleEntrySet<const BasicBlock *>>(
       Worklist, SingleEntrySet<const BasicBlock *>(StopBB), ExclusionSet, DT,
-      LI);
+      LI, CI);
 }
 
 bool llvm::isManyPotentiallyReachableFromMany(
     SmallVectorImpl<BasicBlock *> &Worklist,
     const SmallPtrSetImpl<const BasicBlock *> &StopSet,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
-    const LoopInfo *LI) {
+    const LoopInfo *LI, const CycleInfo *CI) {
   return isReachableImpl<SmallPtrSetImpl<const BasicBlock *>>(
-      Worklist, StopSet, ExclusionSet, DT, LI);
+      Worklist, StopSet, ExclusionSet, DT, LI, CI);
 }
 
 bool llvm::isPotentiallyReachable(
     const BasicBlock *A, const BasicBlock *B,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
-    const LoopInfo *LI) {
+    const LoopInfo *LI, const CycleInfo *CI) {
   assert(A->getParent() == B->getParent() &&
          "This analysis is function-local!");
 
@@ -285,13 +318,13 @@ bool llvm::isPotentiallyReachable(
   SmallVector<BasicBlock*, 32> Worklist;
   Worklist.push_back(const_cast<BasicBlock*>(A));
 
-  return isPotentiallyReachableFromMany(Worklist, B, ExclusionSet, DT, LI);
+  return isPotentiallyReachableFromMany(Worklist, B, ExclusionSet, DT, LI, CI);
 }
 
 bool llvm::isPotentiallyReachable(
     const Instruction *A, const Instruction *B,
     const SmallPtrSetImpl<BasicBlock *> *ExclusionSet, const DominatorTree *DT,
-    const LoopInfo *LI) {
+    const LoopInfo *LI, const CycleInfo *CI) {
   assert(A->getParent()->getParent() == B->getParent()->getParent() &&
          "This analysis is function-local!");
 
@@ -303,15 +336,25 @@ bool llvm::isPotentiallyReachable(
     // blocks.
     BasicBlock *BB = const_cast<BasicBlock *>(A->getParent());
 
-    // If the block is in a loop then we can reach any instruction in the block
-    // from any other instruction in the block by going around a backedge.
-    if (LI && LI->getLoopFor(BB) != nullptr)
-      return true;
-
     // If A comes before B, then B is definitively reachable from A.
     if (A == B || A->comesBefore(B))
       return true;
 
+    // If the block is in a cycle (and there are no excluded blocks), then we
+    // can reach any instruction in the block from any other instruction in the
+    // block by going around a backedge.
+    if (!ExclusionSet || ExclusionSet->empty()) {
+      // If cycle info is available, we can know for sure whether or not a
+      // block is part of a cycle.
+      if (CI)
+        return CI->getCycle(BB) != nullptr;
+
+      // If only loop info is available, even if the block is not part of a
+      // natural loop, it may still be part of an irreducible cycle.
+      if (LI && LI->getLoopFor(BB) != nullptr)
+        return true;
+    }
+
     // Can't be in a loop if it's the entry block -- the entry block may not
     // have predecessors.
     if (BB->isEntryBlock())
@@ -326,11 +369,11 @@ bool llvm::isPotentiallyReachable(
     }
 
     return isPotentiallyReachableFromMany(Worklist, B->getParent(),
-                                          ExclusionSet, DT, LI);
+                                          ExclusionSet, DT, LI, CI);
   }
 
-  return isPotentiallyReachable(
-      A->getParent(), B->getParent(), ExclusionSet, DT, LI);
+  return isPotentiallyReachable(A->getParent(), B->getParent(), ExclusionSet,
+                                DT, LI, CI);
 }
 
 static bool instructionDoesNotReturn(const Instruction &I) {

diff  --git a/llvm/unittests/Analysis/CFGTest.cpp b/llvm/unittests/Analysis/CFGTest.cpp
index 7b61413cc48e6..a9938b381f9db 100644
--- a/llvm/unittests/Analysis/CFGTest.cpp
+++ b/llvm/unittests/Analysis/CFGTest.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/Analysis/CFG.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/CycleAnalysis.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/IR/Dominators.h"
@@ -81,6 +82,7 @@ class IsPotentiallyReachableTest : public testing::Test {
                                      &ID, nullptr, true, true);
          PassRegistry::getPassRegistry()->registerPass(*PI, true);
          initializeLoopInfoWrapperPassPass(*PassRegistry::getPassRegistry());
+         initializeCycleInfoWrapperPassPass(*PassRegistry::getPassRegistry());
          initializeDominatorTreeWrapperPassPass(
              *PassRegistry::getPassRegistry());
          return 0;
@@ -89,6 +91,7 @@ class IsPotentiallyReachableTest : public testing::Test {
       void getAnalysisUsage(AnalysisUsage &AU) const override {
         AU.setPreservesAll();
         AU.addRequired<LoopInfoWrapperPass>();
+        AU.addRequired<CycleInfoWrapperPass>();
         AU.addRequired<DominatorTreeWrapperPass>();
       }
 
@@ -97,6 +100,7 @@ class IsPotentiallyReachableTest : public testing::Test {
           return false;
 
         LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+        CycleInfo *CI = &getAnalysis<CycleInfoWrapperPass>().getResult();
         DominatorTree *DT =
             &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
         EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, nullptr),
@@ -107,6 +111,13 @@ class IsPotentiallyReachableTest : public testing::Test {
                   ExpectedResult);
         EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, LI),
                   ExpectedResult);
+        EXPECT_EQ(
+            isPotentiallyReachable(A, B, &ExclusionSet, nullptr, nullptr, CI),
+            ExpectedResult);
+        EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, DT, nullptr, CI),
+                  ExpectedResult);
+        EXPECT_EQ(isPotentiallyReachable(A, B, &ExclusionSet, nullptr, LI, CI),
+                  ExpectedResult);
         return false;
       }
       bool ExpectedResult;


        


More information about the llvm-commits mailing list