[llvm] [BasicBlockUtils] Fixed LoopInfo update in UpdateAnalysisInformation() (PR #177147)
Yevgeny Rouban via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 4 04:32:21 PST 2026
https://github.com/yrouban updated https://github.com/llvm/llvm-project/pull/177147
>From ed90835770c03354b1a4fe8f5cb19d23df1c30ee 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/2] [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.
---
.../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 0856ac698e5a0..f937bc16a53b1 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) {
@@ -456,6 +458,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 7140b33bbeff3b0338ee406eda079af975848d4e 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/2] [BasicBlockUtils] Fixed LoopInfo update in
UpdateAnalysisInformation()
When UpdateAnalysisInformation() detects an irreducible loop created
then the loop is erased from LoopInfo.
---
llvm/lib/Transforms/Utils/BasicBlockUtils.cpp | 18 +++++-
.../Transforms/Utils/BasicBlockUtilsTest.cpp | 56 ++++++++++++++++++-
2 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
index 6472e1771ec73..d0cb47c531b3f 100644
--- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
+++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
@@ -1054,6 +1054,14 @@ BasicBlock *llvm::SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt,
return SplitBlockImpl(Old, SplitPt, DTU, /*DT=*/nullptr, LI, MSSAU, BBName);
}
+static bool hasReachableLoopEntry(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,
@@ -1163,8 +1171,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.
+ LI->erase(L);
+ }
}
}
diff --git a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
index f937bc16a53b1..45aa37b63d095 100644
--- a/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
+++ b/llvm/unittests/Transforms/Utils/BasicBlockUtilsTest.cpp
@@ -496,7 +496,61 @@ 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, 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);
+ LLVM_DEBUG(LI.print(dbgs()));
+ SplitBlockPredecessors(getBasicBlockByName(*F, "catch_dest"),
+ {getBasicBlockByName(*F, "loop")}, "", &DT, &LI);
+
+ EXPECT_TRUE(DT.verify());
+ LLVM_DEBUG(F->dump(); LI.print(dbgs()));
+ LI.verify(DT);
+ EXPECT_EQ(LI.getLoopFor(getBasicBlockByName(*F, "catch_dest")),
+ LI.getLoopFor(getBasicBlockByName(*F, "superloop")));
}
TEST(BasicBlockUtils, SplitCriticalEdge) {
More information about the llvm-commits
mailing list