Skip to content

Commit 43561ad

Browse files
authored
[BasicBlockUtils] Handle funclets when detaching EH pad blocks (llvm#157363)
Fixes llvm#148052 . When removing EH Pad blocks, the value defined by them becomes poison. These poison values are then used by `catchret` and `cleanupret`, which is invalid. This commit replaces those unreachable `catchret` and `cleanupret` instructions with `unreachable`.
1 parent 6578772 commit 43561ad

File tree

2 files changed

+212
-1
lines changed

2 files changed

+212
-1
lines changed

llvm/lib/Transforms/Utils/BasicBlockUtils.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ static cl::opt<unsigned> MaxDeoptOrUnreachableSuccessorCheckDepth(
5858
"is followed by a block that either has a terminating "
5959
"deoptimizing call or is terminated with an unreachable"));
6060

61+
static void replaceFuncletPadsRetWithUnreachable(Instruction &I) {
62+
assert(isa<FuncletPadInst>(I) && "Instruction must be a funclet pad!");
63+
for (User *User : make_early_inc_range(I.users())) {
64+
Instruction *ReturnInstr = dyn_cast<Instruction>(User);
65+
if (isa<CatchReturnInst>(ReturnInstr) ||
66+
isa<CleanupReturnInst>(ReturnInstr)) {
67+
BasicBlock *ReturnInstrBB = ReturnInstr->getParent();
68+
ReturnInstr->eraseFromParent();
69+
new UnreachableInst(ReturnInstrBB->getContext(), ReturnInstrBB);
70+
}
71+
}
72+
}
73+
6174
void llvm::detachDeadBlocks(
6275
ArrayRef<BasicBlock *> BBs,
6376
SmallVectorImpl<DominatorTree::UpdateType> *Updates,
@@ -75,7 +88,36 @@ void llvm::detachDeadBlocks(
7588
// Zap all the instructions in the block.
7689
while (!BB->empty()) {
7790
Instruction &I = BB->back();
78-
// If this instruction is used, replace uses with an arbitrary value.
91+
// Exception handling funclets need to be explicitly addressed.
92+
// These funclets must begin with cleanuppad or catchpad and end with
93+
// cleanupred or catchret. The return instructions can be in different
94+
// basic blocks than the pad instruction. If we would only delete the
95+
// first block, the we would have possible cleanupret and catchret
96+
// instructions with poison arguments, which wouldn't be valid.
97+
if (isa<FuncletPadInst>(I))
98+
replaceFuncletPadsRetWithUnreachable(I);
99+
100+
// Catchswitch instructions have handlers, that must be catchpads and
101+
// an unwind label, that is either a catchpad or catchswitch.
102+
if (CatchSwitchInst *CSI = dyn_cast<CatchSwitchInst>(&I)) {
103+
// Iterating over the handlers and the unwind basic block and processing
104+
// catchpads. If the unwind label is a catchswitch, we just replace the
105+
// label with poison later on.
106+
for (unsigned I = 0; I < CSI->getNumSuccessors(); I++) {
107+
BasicBlock *SucBlock = CSI->getSuccessor(I);
108+
Instruction &SucFstInst = *(SucBlock->getFirstNonPHIIt());
109+
if (isa<FuncletPadInst>(SucFstInst)) {
110+
replaceFuncletPadsRetWithUnreachable(SucFstInst);
111+
// There may be catchswitch instructions using the catchpad.
112+
// Just replace those with poison.
113+
if (!SucFstInst.use_empty())
114+
SucFstInst.replaceAllUsesWith(
115+
PoisonValue::get(SucFstInst.getType()));
116+
SucFstInst.eraseFromParent();
117+
}
118+
}
119+
}
120+
79121
// Because control flow can't get here, we don't care what we replace the
80122
// value with. Note that since this block is unreachable, and all values
81123
// contained within it must dominate their uses, that all uses will
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes=simplifycfg -S < %s | FileCheck %s
3+
4+
; cleanuppad/cleanupret
5+
6+
define void @unreachable_cleanuppad_linear(i64 %shapes.1) personality ptr null {
7+
; CHECK-LABEL: define void @unreachable_cleanuppad_linear(
8+
; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
9+
; CHECK-NEXT: [[START:.*:]]
10+
; CHECK-NEXT: [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
11+
; CHECK-NEXT: ret void
12+
;
13+
start:
14+
%_7 = icmp ult i64 0, %shapes.1
15+
ret void
16+
17+
funclet:
18+
%cleanuppad = cleanuppad within none []
19+
br label %funclet_end
20+
21+
funclet_end:
22+
cleanupret from %cleanuppad unwind to caller
23+
}
24+
25+
define void @unreachable_cleanuppad_multiple_predecessors(i64 %shapes.1) personality ptr null {
26+
; CHECK-LABEL: define void @unreachable_cleanuppad_multiple_predecessors(
27+
; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
28+
; CHECK-NEXT: [[START:.*:]]
29+
; CHECK-NEXT: [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
30+
; CHECK-NEXT: ret void
31+
;
32+
start:
33+
%_7 = icmp ult i64 0, %shapes.1
34+
ret void
35+
36+
funclet:
37+
%cleanuppad = cleanuppad within none []
38+
switch i64 %shapes.1, label %otherwise [ i64 0, label %one
39+
i64 1, label %two
40+
i64 42, label %three ]
41+
one:
42+
br label %funclet_end
43+
44+
two:
45+
br label %funclet_end
46+
47+
three:
48+
br label %funclet_end
49+
50+
otherwise:
51+
br label %funclet_end
52+
53+
funclet_end:
54+
cleanupret from %cleanuppad unwind to caller
55+
}
56+
57+
; catchpad/catchret
58+
59+
define void @unreachable_catchpad_linear(i64 %shapes.1) personality ptr null {
60+
; CHECK-LABEL: define void @unreachable_catchpad_linear(
61+
; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
62+
; CHECK-NEXT: [[START:.*:]]
63+
; CHECK-NEXT: [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
64+
; CHECK-NEXT: ret void
65+
;
66+
start:
67+
%_7 = icmp ult i64 0, %shapes.1
68+
ret void
69+
70+
dispatch:
71+
%cs = catchswitch within none [label %funclet] unwind to caller
72+
73+
funclet:
74+
%cleanuppad = catchpad within %cs []
75+
br label %funclet_end
76+
77+
78+
funclet_end:
79+
catchret from %cleanuppad to label %unreachable
80+
81+
unreachable:
82+
unreachable
83+
}
84+
85+
define void @unreachable_catchpad_multiple_predecessors(i64 %shapes.1) personality ptr null {
86+
; CHECK-LABEL: define void @unreachable_catchpad_multiple_predecessors(
87+
; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
88+
; CHECK-NEXT: [[START:.*:]]
89+
; CHECK-NEXT: [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
90+
; CHECK-NEXT: ret void
91+
;
92+
start:
93+
%_7 = icmp ult i64 0, %shapes.1
94+
ret void
95+
96+
dispatch:
97+
%cs = catchswitch within none [label %funclet] unwind to caller
98+
99+
funclet:
100+
%cleanuppad = catchpad within %cs []
101+
switch i64 %shapes.1, label %otherwise [ i64 0, label %one
102+
i64 1, label %two
103+
i64 42, label %three ]
104+
one:
105+
br label %funclet_end
106+
107+
two:
108+
br label %funclet_end
109+
110+
three:
111+
br label %funclet_end
112+
113+
otherwise:
114+
br label %funclet_end
115+
116+
funclet_end:
117+
catchret from %cleanuppad to label %unreachable
118+
119+
unreachable:
120+
unreachable
121+
}
122+
123+
; Issue reproducer
124+
125+
define void @gh148052(i64 %shapes.1) personality ptr null {
126+
; CHECK-LABEL: define void @gh148052(
127+
; CHECK-SAME: i64 [[SHAPES_1:%.*]]) personality ptr null {
128+
; CHECK-NEXT: [[START:.*:]]
129+
; CHECK-NEXT: [[_7:%.*]] = icmp ult i64 0, [[SHAPES_1]]
130+
; CHECK-NEXT: call void @llvm.assume(i1 [[_7]])
131+
; CHECK-NEXT: ret void
132+
;
133+
start:
134+
%_7 = icmp ult i64 0, %shapes.1
135+
br i1 %_7, label %bb1, label %panic
136+
137+
bb1:
138+
%_11 = icmp ult i64 0, %shapes.1
139+
br i1 %_11, label %bb3, label %panic1
140+
141+
panic:
142+
unreachable
143+
144+
bb3:
145+
ret void
146+
147+
panic1:
148+
invoke void @func(i64 0, i64 0, ptr null)
149+
to label %unreachable unwind label %funclet_bb14
150+
151+
funclet_bb14:
152+
%cleanuppad = cleanuppad within none []
153+
br label %bb13
154+
155+
unreachable:
156+
unreachable
157+
158+
bb10:
159+
cleanupret from %cleanuppad5 unwind to caller
160+
161+
funclet_bb10:
162+
%cleanuppad5 = cleanuppad within none []
163+
br label %bb10
164+
165+
bb13:
166+
cleanupret from %cleanuppad unwind label %funclet_bb10
167+
}
168+
169+
declare void @func(i64, i64, ptr)

0 commit comments

Comments
 (0)