Skip to content

Commit 4810051

Browse files
committed
[Inline][Cloning] Reliably remove unreachable blocks during cloning (PR53206)
The pruning cloner already tries to remove unreachable blocks. The original cloning process will simplify instructions and constant terminators, and only clone blocks that are reachable at that point. However, phi nodes can only be simplified after everything has been cloned. For that reason, additional blocks may become unreachable after phi simplification. The code does try to handle this as well, but only removes blocks that don't have predecessors. It misses unreachable cycles. This can cause issues if SEH exception handling code is part of an unreachable cycle, as the inliner is not prepared to deal with that. This patch instead performs an explicit scan for reachable blocks, and drops everything else. Fixes #53206. Differential Revision: https://reviews.llvm.org/D118449
1 parent 0801940 commit 4810051

File tree

2 files changed

+78
-26
lines changed

2 files changed

+78
-26
lines changed

llvm/lib/Transforms/Utils/CloneFunction.cpp

+27-26
Original file line numberDiff line numberDiff line change
@@ -694,38 +694,39 @@ void llvm::CloneAndPruneIntoFromInst(Function *NewFunc, const Function *OldFunc,
694694
VMap[OrigV] = I;
695695
}
696696

697+
// Simplify conditional branches and switches with a constant operand. We try
698+
// to prune these out when cloning, but if the simplification required
699+
// looking through PHI nodes, those are only available after forming the full
700+
// basic block. That may leave some here, and we still want to prune the dead
701+
// code as early as possible.
702+
Function::iterator Begin = cast<BasicBlock>(VMap[StartingBB])->getIterator();
703+
for (BasicBlock &BB : make_range(Begin, NewFunc->end()))
704+
ConstantFoldTerminator(&BB);
705+
706+
// Some blocks may have become unreachable as a result. Find and delete them.
707+
{
708+
SmallPtrSet<BasicBlock *, 16> ReachableBlocks;
709+
SmallVector<BasicBlock *, 16> Worklist;
710+
Worklist.push_back(&*Begin);
711+
while (!Worklist.empty()) {
712+
BasicBlock *BB = Worklist.pop_back_val();
713+
if (ReachableBlocks.insert(BB).second)
714+
append_range(Worklist, successors(BB));
715+
}
716+
717+
SmallVector<BasicBlock *, 16> UnreachableBlocks;
718+
for (BasicBlock &BB : make_range(Begin, NewFunc->end()))
719+
if (!ReachableBlocks.contains(&BB))
720+
UnreachableBlocks.push_back(&BB);
721+
DeleteDeadBlocks(UnreachableBlocks);
722+
}
723+
697724
// Now that the inlined function body has been fully constructed, go through
698725
// and zap unconditional fall-through branches. This happens all the time when
699726
// specializing code: code specialization turns conditional branches into
700727
// uncond branches, and this code folds them.
701-
Function::iterator Begin = cast<BasicBlock>(VMap[StartingBB])->getIterator();
702728
Function::iterator I = Begin;
703729
while (I != NewFunc->end()) {
704-
// We need to simplify conditional branches and switches with a constant
705-
// operand. We try to prune these out when cloning, but if the
706-
// simplification required looking through PHI nodes, those are only
707-
// available after forming the full basic block. That may leave some here,
708-
// and we still want to prune the dead code as early as possible.
709-
//
710-
// Do the folding before we check if the block is dead since we want code
711-
// like
712-
// bb:
713-
// br i1 undef, label %bb, label %bb
714-
// to be simplified to
715-
// bb:
716-
// br label %bb
717-
// before we call I->getSinglePredecessor().
718-
ConstantFoldTerminator(&*I);
719-
720-
// Check if this block has become dead during inlining or other
721-
// simplifications. Note that the first block will appear dead, as it has
722-
// not yet been wired up properly.
723-
if (I != Begin && (pred_empty(&*I) || I->getSinglePredecessor() == &*I)) {
724-
BasicBlock *DeadBB = &*I++;
725-
DeleteDeadBlock(DeadBB);
726-
continue;
727-
}
728-
729730
BranchInst *BI = dyn_cast<BranchInst>(I->getTerminator());
730731
if (!BI || BI->isConditional()) {
731732
++I;
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S -inline < %s | FileCheck %s
3+
4+
; Check that the exception handling code is fully pruned, and does not
5+
; leave behind invalid IR.
6+
7+
define internal void @foo() personality i32 (...)* undef {
8+
entry:
9+
br i1 false, label %join, label %split
10+
11+
split:
12+
br label %join
13+
14+
join:
15+
%phi = phi i64 [ 1, %split ], [ 0, %entry ]
16+
%cmp = icmp ugt i64 1, %phi
17+
br i1 %cmp, label %invoke1, label %exit
18+
19+
invoke1:
20+
invoke void undef()
21+
to label %exit unwind label %cleanup1
22+
23+
cleanup1:
24+
%pad1 = cleanuppad within none []
25+
br label %cleanup1.cont
26+
27+
cleanup1.cont:
28+
br i1 undef, label %cleanupret, label %invoke2
29+
30+
invoke2:
31+
invoke void undef() [ "funclet"(token %pad1) ]
32+
to label %cleanup1.cont unwind label %cleanup2
33+
34+
cleanup2:
35+
%pad2 = cleanuppad within %pad1 []
36+
unreachable
37+
38+
cleanupret:
39+
unreachable
40+
41+
exit:
42+
ret void
43+
}
44+
45+
define void @test() personality i32 (...)* undef {
46+
; CHECK-LABEL: @test(
47+
; CHECK-NEXT: ret void
48+
;
49+
call void @foo()
50+
ret void
51+
}

0 commit comments

Comments
 (0)