[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.
This commit is contained in:
Yevgeny Rouban 2026-03-30 17:53:52 +07:00 committed by GitHub
parent c7908d3320
commit e901b0295d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 110 additions and 2 deletions

View file

@ -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);
}
}
}

View file

@ -456,6 +456,98 @@ bb1:
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(