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

Yevgeny Rouban via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 28 21:49:19 PST 2026


https://github.com/yrouban updated https://github.com/llvm/llvm-project/pull/177147

>From b866fa92b1cbe1d6a7298acd58aa787585093fcc Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Wed, 21 Jan 2026 13:13:56 +0300
Subject: [PATCH 1/4] [BasicBlockUtils] Test with broken LoopInfo after
 SplitLandingPadPredecessors(). NFC

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.
---
 .../Transforms/Utils/BasicBlockUtilsTest.cpp  | 43 +++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index 00d9e9ff81e05..e8b16424d3cd3 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -26,6 +26,8 @@
 #include "llvm/Support/SourceMgr.h"
 #include "gtest/gtest.h"
 
+#define DEBUG_TYPE "basic-block-utils-tests"
+
 using namespace llvm;
 
 static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
@@ -400,6 +402,47 @@ 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());
+  LLVM_DEBUG(F->dump(); LI.print(dbgs()));
+  EXPECT_DEATH({LI.verify(DT);}, "Assertion.*Loop has multiple entry points!");
+}
+
 TEST(BasicBlockUtils, SplitCriticalEdge) {
   LLVMContext C;
   std::unique_ptr<Module> M = parseIR(C, R"IR(

>From ed5422f1016a5c4f6a63fbcfb64da55d6d0e9fea Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Wed, 21 Jan 2026 13:15:50 +0300
Subject: [PATCH 2/4] [BasicBlockUtils] Fixed LoopInfo update in
 UpdateAnalysisInformation()

When UpdateAnalysisInformation() detects an irreducible loop created
then the loop is removed from LoopInfo.
---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 32 +++++++++++++++++--
 .../Transforms/Utils/BasicBlockUtilsTest.cpp  |  3 +-
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index b0c04086f5e84..b9d817ade443d 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -1109,6 +1109,28 @@ BasicBlock *llvm::splitBlockBefore(BasicBlock *Old, BasicBlock::iterator SplitPt
   return New;
 }
 
+static bool HasReachableLoopEntry(const Loop &L, const DominatorTree &DT) {
+  for (const auto Pred : inverse_children<const BasicBlock *>(L.getHeader()))
+    if (!L.contains(Pred) && DT.isReachableFromEntry(Pred))
+      return true;
+
+  return false;
+}
+
+// Destroy the loop.
+static void Destroy(Loop *L, LoopInfo &LI) {
+  auto *PerentL = L->getParentLoop();
+  for (auto *BB : L->getBlocks())
+    LI.changeLoopFor(BB, PerentL);
+
+  if (PerentL)
+    PerentL->removeChildLoop(L);
+  else
+    LI.removeLoop(llvm::find(LI, L));
+
+  LI.destroy(L);
+}
+
 /// Update DominatorTree, LoopInfo, and LCCSA analysis information.
 /// Invalidates DFS Numbering when DTU or DT is provided.
 static void UpdateAnalysisInformation(BasicBlock *OldBB, BasicBlock *NewBB,
@@ -1218,8 +1240,14 @@ static void UpdateAnalysisInformation(BasicBlock *OldBB, BasicBlock *NewBB,
       InnermostPredLoop->addBasicBlockToLoop(NewBB, *LI);
   } else {
     L->addBasicBlockToLoop(NewBB, *LI);
-    if (SplitMakesNewLoopHeader)
-      L->moveToHeader(NewBB);
+    if (SplitMakesNewLoopHeader) {
+      if (!HasReachableLoopEntry(*L, *DT))
+        // Old header lost all entries.
+        L->moveToHeader(NewBB);
+      else
+        // Both OldBB and NewBB have loop entries.
+        Destroy(L, *LI);
+    }
   }
 }
 
diff --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index e8b16424d3cd3..d905d31184a4d 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -440,7 +440,8 @@ define void @split-lp-predecessors-test() personality ptr null {
 
   EXPECT_TRUE(DT.verify());
   LLVM_DEBUG(F->dump(); LI.print(dbgs()));
-  EXPECT_DEATH({LI.verify(DT);}, "Assertion.*Loop has multiple entry points!");
+  LI.verify(DT);
+  EXPECT_EQ(LI.getLoopFor(getBasicBlockByName(*F, "catch_dest")), nullptr);
 }
 
 TEST(BasicBlockUtils, SplitCriticalEdge) {

>From 390a112d590360b09a93a54725db3195a05d7ecd Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Thu, 29 Jan 2026 08:41:49 +0300
Subject: [PATCH 3/4] fixup! [BasicBlockUtils] Fixed LoopInfo update in
 UpdateAnalysisInformation()

---
 llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index b9d817ade443d..1a82f77ab4d86 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -1109,7 +1109,7 @@ BasicBlock *llvm::splitBlockBefore(BasicBlock *Old, BasicBlock::iterator SplitPt
   return New;
 }
 
-static bool HasReachableLoopEntry(const Loop &L, const DominatorTree &DT) {
+static bool hasReachableLoopEntry(const Loop &L, const DominatorTree &DT) {
   for (const auto Pred : inverse_children<const BasicBlock *>(L.getHeader()))
     if (!L.contains(Pred) && DT.isReachableFromEntry(Pred))
       return true;
@@ -1119,12 +1119,12 @@ static bool HasReachableLoopEntry(const Loop &L, const DominatorTree &DT) {
 
 // Destroy the loop.
 static void Destroy(Loop *L, LoopInfo &LI) {
-  auto *PerentL = L->getParentLoop();
+  auto *ParentL = L->getParentLoop();
   for (auto *BB : L->getBlocks())
-    LI.changeLoopFor(BB, PerentL);
+    LI.changeLoopFor(BB, ParentL);
 
-  if (PerentL)
-    PerentL->removeChildLoop(L);
+  if (ParentL)
+    ParentL->removeChildLoop(L);
   else
     LI.removeLoop(llvm::find(LI, L));
 
@@ -1241,7 +1241,7 @@ static void UpdateAnalysisInformation(BasicBlock *OldBB, BasicBlock *NewBB,
   } else {
     L->addBasicBlockToLoop(NewBB, *LI);
     if (SplitMakesNewLoopHeader) {
-      if (!HasReachableLoopEntry(*L, *DT))
+      if (!hasReachableLoopEntry(*L, *DT))
         // Old header lost all entries.
         L->moveToHeader(NewBB);
       else

>From b7b01ecc745f5db6b0b466ec550ca71dda32b7a8 Mon Sep 17 00:00:00 2001
From: Yevgeny Rouban <yrouban at gmail.com>
Date: Thu, 29 Jan 2026 08:42:51 +0300
Subject: [PATCH 4/4] amend! [BasicBlockUtils] Test with broken LoopInfo after
 SplitLandingPadPredecessors(). NFC

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.

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.



More information about the llvm-commits mailing list