[llvm] e901b02 - [BasicBlockUtils] Fixed LoopInfo update in UpdateAnalysisInformation() (#177147)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 03:53:58 PDT 2026


Author: Yevgeny Rouban
Date: 2026-03-30T10:53:52Z
New Revision: e901b0295dae50a30ed736661ec86aded9693db9

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

LOG: [BasicBlockUtils] Fixed LoopInfo update in UpdateAnalysisInformation() (#177147)

SplitLandingPadPredecessors() results in an irreducible loop
and makes LoopInfo invalid. Verification results in a crash:
Assertion `CB != OutsideLoopPreds[i] && "Loop has multiple entry
points!"' failed.

Created a new test with a broken LoopInfo after
SplitLandingPadPredecessors().
The test @split-lp-predecessors-test() after
SplitBlockPredecessors(catch_dest, { loop }, "", DT, LI) changes to
the following IR where the loop {%catch_dest} gets into irreducible
loop {%catch_dest.split-lp, %catch_dest}:

```
define void @split-lp-predecessors-test() personality ptr null {
entry:
  invoke void @foo()
          to label %loop unwind label %catch_dest.split-lp

loop:                                             ; preds = %latch, %entry
  invoke void @foo()
          to label %latch unwind label %catch_dest1

latch:                                            ; preds = %loop
  br label %loop

catch_dest1:                                      ; preds = %loop
  %lpad = landingpad i32
          cleanup
  br label %catch_dest

catch_dest.split-lp:                              ; preds = %entry, %catch_dest
  %lpad.split-lp = landingpad i32
          cleanup
  br label %catch_dest

catch_dest:                                       ; preds = %catch_dest.split-lp, %catch_dest1
  invoke void @foo()
          to label %exit unwind label %catch_dest.split-lp

exit:                                             ; preds = %catch_dest
  ret void
}
```

Irreducible loops must be removed from LoopInfo.

When UpdateAnalysisInformation() detects an irreducible loop created
then the loop is erased from LoopInfo.

See the issue #163922 for details.

Added: 
    

Modified: 
    llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
    llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index cdb3896e81778..425faf57f8b63 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -1057,6 +1057,15 @@ BasicBlock *llvm::SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt,
   return SplitBlockImpl(Old, SplitPt, DTU, /*DT=*/nullptr, LI, MSSAU, BBName);
 }
 
+static bool hasReachableLoopEntryToHeader(const Loop &L,
+                                          const DominatorTree &DT) {
+  for (const BasicBlock *Pred : predecessors(L.getHeader()))
+    if (!L.contains(Pred) && DT.isReachableFromEntry(Pred))
+      return true;
+
+  return false;
+}
+
 /// Update DominatorTree, LoopInfo, and LCCSA analysis information.
 /// Invalidates DFS Numbering when DTU or DT is provided.
 static void UpdateAnalysisInformation(BasicBlock *OldBB, BasicBlock *NewBB,
@@ -1166,8 +1175,15 @@ static void UpdateAnalysisInformation(BasicBlock *OldBB, BasicBlock *NewBB,
       InnermostPredLoop->addBasicBlockToLoop(NewBB, *LI);
   } else {
     L->addBasicBlockToLoop(NewBB, *LI);
-    if (SplitMakesNewLoopHeader)
-      L->moveToHeader(NewBB);
+    if (SplitMakesNewLoopHeader) {
+      // The old header might still have a loop entry. If so the loop becomes
+      // irreducible and must be erased. Otherwise the NewBB becomes the loop
+      // header.
+      if (hasReachableLoopEntryToHeader(*L, *DT))
+        LI->erase(L);
+      else
+        L->moveToHeader(NewBB);
+    }
   }
 }
 

diff  --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index 0856ac698e5a0..a91d67ff6c550 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -456,6 +456,98 @@ define i32 @basic_func(i1 %cond) {
   EXPECT_TRUE(DT.verify());
 }
 
+TEST(BasicBlockUtils, SplitLandingPadPredecessors) {
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"IR(
+declare void @foo()
+
+define void @split-lp-predecessors-test() personality ptr null {
+entry:
+  invoke void @foo()
+          to label %loop unwind label %catch_dest
+
+loop:
+  invoke void @foo()
+          to label %latch unwind label %catch_dest
+
+latch:
+  br label %loop
+
+catch_dest:
+  %lp = landingpad i32
+          cleanup
+  invoke void @foo()
+          to label %exit unwind label %catch_dest
+
+exit:
+  ret void
+}
+)IR");
+  Function *F = M->getFunction("split-lp-predecessors-test");
+  DominatorTree DT(*F);
+  LoopInfo LI(DT);
+
+  EXPECT_TRUE(DT.verify());
+  LI.verify(DT);
+  SplitBlockPredecessors(getBasicBlockByName(*F, "catch_dest"),
+                         {getBasicBlockByName(*F, "loop")}, "", &DT, &LI);
+
+  EXPECT_TRUE(DT.verify());
+  LI.verify(DT);
+  EXPECT_EQ(LI.getLoopFor(getBasicBlockByName(*F, "catch_dest")), nullptr);
+}
+
+TEST(BasicBlockUtils, SplitLandingPadPredecessorsLoopStruct) {
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"IR(
+declare void @foo()
+
+define void @split-lp-predecessors-test(i1 %flag) personality ptr null {
+entry:
+  br label %superloop
+
+superloop:
+  invoke void @foo()
+          to label %loop unwind label %catch_dest
+
+loop:
+  invoke void @foo()
+          to label %latch unwind label %catch_dest
+
+latch:
+  br label %loop
+
+catch_dest:
+  %lp = landingpad i32
+          cleanup
+  br label %loop2
+
+loop2:
+  br i1 %flag, label %loop2, label %loop2_exit
+
+loop2_exit:
+  invoke void @foo()
+          to label %exit unwind label %catch_dest
+
+exit:
+  br label %superloop
+}
+)IR");
+  Function *F = M->getFunction("split-lp-predecessors-test");
+  DominatorTree DT(*F);
+  LoopInfo LI(DT);
+
+  EXPECT_TRUE(DT.verify());
+  LI.verify(DT);
+  SplitBlockPredecessors(getBasicBlockByName(*F, "catch_dest"),
+                         {getBasicBlockByName(*F, "loop")}, "", &DT, &LI);
+
+  EXPECT_TRUE(DT.verify());
+  LI.verify(DT);
+  EXPECT_EQ(LI.getLoopFor(getBasicBlockByName(*F, "catch_dest")),
+            LI.getLoopFor(getBasicBlockByName(*F, "superloop")));
+}
+
 TEST(BasicBlockUtils, SplitCriticalEdge) {
   LLVMContext C;
   std::unique_ptr<Module> M = parseIR(C, R"IR(


        


More information about the llvm-commits mailing list