Skip to content

Commit a034e65

Browse files
authoredJan 31, 2024
[CVP] Check whether the default case is reachable (#79993)
This patch eliminates unreachable default cases using context-sensitive range information.
1 parent 8e77390 commit a034e65

File tree

3 files changed

+341
-5
lines changed

3 files changed

+341
-5
lines changed
 

‎llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
371371
{ // Scope for SwitchInstProfUpdateWrapper. It must not live during
372372
// ConstantFoldTerminator() as the underlying SwitchInst can be changed.
373373
SwitchInstProfUpdateWrapper SI(*I);
374+
unsigned ReachableCaseCount = 0;
374375

375376
for (auto CI = SI->case_begin(), CE = SI->case_end(); CI != CE;) {
376377
ConstantInt *Case = CI->getCaseValue();
@@ -407,6 +408,33 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
407408

408409
// Increment the case iterator since we didn't delete it.
409410
++CI;
411+
++ReachableCaseCount;
412+
}
413+
414+
BasicBlock *DefaultDest = SI->getDefaultDest();
415+
if (ReachableCaseCount > 1 &&
416+
!isa<UnreachableInst>(DefaultDest->getFirstNonPHIOrDbg())) {
417+
ConstantRange CR = LVI->getConstantRangeAtUse(I->getOperandUse(0),
418+
/*UndefAllowed*/ false);
419+
// The default dest is unreachable if all cases are covered.
420+
if (!CR.isSizeLargerThan(ReachableCaseCount)) {
421+
BasicBlock *NewUnreachableBB =
422+
BasicBlock::Create(BB->getContext(), "default.unreachable",
423+
BB->getParent(), DefaultDest);
424+
new UnreachableInst(BB->getContext(), NewUnreachableBB);
425+
426+
DefaultDest->removePredecessor(BB);
427+
SI->setDefaultDest(NewUnreachableBB);
428+
429+
if (SuccessorsCount[DefaultDest] == 1)
430+
DTU.applyUpdatesPermissive(
431+
{{DominatorTree::Delete, BB, DefaultDest}});
432+
DTU.applyUpdatesPermissive(
433+
{{DominatorTree::Insert, BB, NewUnreachableBB}});
434+
435+
++NumDeadCases;
436+
Changed = true;
437+
}
410438
}
411439
}
412440

@@ -1227,6 +1255,12 @@ CorrelatedValuePropagationPass::run(Function &F, FunctionAnalysisManager &AM) {
12271255
if (!Changed) {
12281256
PA = PreservedAnalyses::all();
12291257
} else {
1258+
#if defined(EXPENSIVE_CHECKS)
1259+
assert(DT->verify(DominatorTree::VerificationLevel::Full));
1260+
#else
1261+
assert(DT->verify(DominatorTree::VerificationLevel::Fast));
1262+
#endif // EXPENSIVE_CHECKS
1263+
12301264
PA.preserve<DominatorTreeAnalysis>();
12311265
PA.preserve<LazyValueAnalysis>();
12321266
}

‎llvm/test/Transforms/CorrelatedValuePropagation/basic.ll

+6-5
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ define i32 @switch_range(i32 %cond) {
442442
; CHECK-NEXT: entry:
443443
; CHECK-NEXT: [[S:%.*]] = urem i32 [[COND:%.*]], 3
444444
; CHECK-NEXT: [[S1:%.*]] = add nuw nsw i32 [[S]], 1
445-
; CHECK-NEXT: switch i32 [[S1]], label [[UNREACHABLE:%.*]] [
445+
; CHECK-NEXT: switch i32 [[S1]], label [[DEFAULT_UNREACHABLE:%.*]] [
446446
; CHECK-NEXT: i32 1, label [[EXIT1:%.*]]
447447
; CHECK-NEXT: i32 2, label [[EXIT2:%.*]]
448448
; CHECK-NEXT: i32 3, label [[EXIT1]]
@@ -451,6 +451,8 @@ define i32 @switch_range(i32 %cond) {
451451
; CHECK-NEXT: ret i32 1
452452
; CHECK: exit2:
453453
; CHECK-NEXT: ret i32 2
454+
; CHECK: default.unreachable:
455+
; CHECK-NEXT: unreachable
454456
; CHECK: unreachable:
455457
; CHECK-NEXT: ret i32 0
456458
;
@@ -513,10 +515,9 @@ define i8 @switch_defaultdest_multipleuse(i8 %t0) {
513515
; CHECK-NEXT: entry:
514516
; CHECK-NEXT: [[O:%.*]] = or i8 [[T0:%.*]], 1
515517
; CHECK-NEXT: [[R:%.*]] = srem i8 1, [[O]]
516-
; CHECK-NEXT: switch i8 [[R]], label [[EXIT:%.*]] [
517-
; CHECK-NEXT: i8 0, label [[EXIT]]
518-
; CHECK-NEXT: i8 1, label [[EXIT]]
519-
; CHECK-NEXT: ]
518+
; CHECK-NEXT: br label [[EXIT:%.*]]
519+
; CHECK: default.unreachable:
520+
; CHECK-NEXT: unreachable
520521
; CHECK: exit:
521522
; CHECK-NEXT: ret i8 0
522523
;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=correlated-propagation -S | FileCheck %s
3+
4+
define i32 @test_unreachable_default(i32 noundef %num) {
5+
; CHECK-LABEL: define i32 @test_unreachable_default(
6+
; CHECK-SAME: i32 noundef [[NUM:%.*]]) {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: [[SUB:%.*]] = add i32 [[NUM]], -120
9+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 3
10+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 2
11+
; CHECK-NEXT: switch i32 [[COND]], label [[DEFAULT_UNREACHABLE:%.*]] [
12+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
13+
; CHECK-NEXT: i32 1, label [[SW_BB2:%.*]]
14+
; CHECK-NEXT: i32 2, label [[SW_BB4:%.*]]
15+
; CHECK-NEXT: ]
16+
; CHECK: sw.bb:
17+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @call0()
18+
; CHECK-NEXT: br label [[CLEANUP:%.*]]
19+
; CHECK: sw.bb2:
20+
; CHECK-NEXT: [[CALL3:%.*]] = call i32 @call1()
21+
; CHECK-NEXT: br label [[CLEANUP]]
22+
; CHECK: sw.bb4:
23+
; CHECK-NEXT: [[CALL5:%.*]] = call i32 @call2()
24+
; CHECK-NEXT: br label [[CLEANUP]]
25+
; CHECK: default.unreachable:
26+
; CHECK-NEXT: unreachable
27+
; CHECK: sw.default:
28+
; CHECK-NEXT: [[CALL6:%.*]] = call i32 @call3()
29+
; CHECK-NEXT: br label [[CLEANUP]]
30+
; CHECK: cleanup:
31+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CALL6]], [[SW_DEFAULT:%.*]] ], [ [[CALL5]], [[SW_BB4]] ], [ [[CALL3]], [[SW_BB2]] ], [ [[CALL]], [[SW_BB]] ]
32+
; CHECK-NEXT: ret i32 [[RETVAL_0]]
33+
;
34+
entry:
35+
%sub = add i32 %num, -120
36+
%cmp = icmp ult i32 %sub, 3
37+
%cond = select i1 %cmp, i32 %sub, i32 2
38+
switch i32 %cond, label %sw.default [
39+
i32 0, label %sw.bb
40+
i32 1, label %sw.bb2
41+
i32 2, label %sw.bb4
42+
]
43+
44+
sw.bb:
45+
%call = call i32 @call0()
46+
br label %cleanup
47+
48+
sw.bb2:
49+
%call3 = call i32 @call1()
50+
br label %cleanup
51+
52+
sw.bb4:
53+
%call5 = call i32 @call2()
54+
br label %cleanup
55+
56+
sw.default:
57+
%call6 = call i32 @call3()
58+
br label %cleanup
59+
60+
cleanup:
61+
%retval.0 = phi i32 [ %call6, %sw.default ], [ %call5, %sw.bb4 ], [ %call3, %sw.bb2 ], [ %call, %sw.bb ]
62+
ret i32 %retval.0
63+
}
64+
65+
define i32 @test_unreachable_default_shared_edge(i32 noundef %num) {
66+
; CHECK-LABEL: define i32 @test_unreachable_default_shared_edge(
67+
; CHECK-SAME: i32 noundef [[NUM:%.*]]) {
68+
; CHECK-NEXT: entry:
69+
; CHECK-NEXT: [[SUB:%.*]] = add i32 [[NUM]], -120
70+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 3
71+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 2
72+
; CHECK-NEXT: switch i32 [[COND]], label [[DEFAULT_UNREACHABLE:%.*]] [
73+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
74+
; CHECK-NEXT: i32 1, label [[SW_BB2:%.*]]
75+
; CHECK-NEXT: i32 2, label [[SW_BB4:%.*]]
76+
; CHECK-NEXT: ]
77+
; CHECK: sw.bb:
78+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @call0()
79+
; CHECK-NEXT: br label [[CLEANUP:%.*]]
80+
; CHECK: sw.bb2:
81+
; CHECK-NEXT: [[CALL3:%.*]] = call i32 @call1()
82+
; CHECK-NEXT: br label [[CLEANUP]]
83+
; CHECK: default.unreachable:
84+
; CHECK-NEXT: unreachable
85+
; CHECK: sw.bb4:
86+
; CHECK-NEXT: [[CALL5:%.*]] = call i32 @call4(i32 [[SUB]])
87+
; CHECK-NEXT: br label [[CLEANUP]]
88+
; CHECK: cleanup:
89+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CALL5]], [[SW_BB4]] ], [ [[CALL3]], [[SW_BB2]] ], [ [[CALL]], [[SW_BB]] ]
90+
; CHECK-NEXT: ret i32 [[RETVAL_0]]
91+
;
92+
entry:
93+
%sub = add i32 %num, -120
94+
%cmp = icmp ult i32 %sub, 3
95+
%cond = select i1 %cmp, i32 %sub, i32 2
96+
switch i32 %cond, label %sw.bb4 [
97+
i32 0, label %sw.bb
98+
i32 1, label %sw.bb2
99+
i32 2, label %sw.bb4
100+
]
101+
102+
sw.bb:
103+
%call = call i32 @call0()
104+
br label %cleanup
105+
106+
sw.bb2:
107+
%call3 = call i32 @call1()
108+
br label %cleanup
109+
110+
sw.bb4:
111+
%val = phi i32 [ %sub, %entry ], [ %sub, %entry ]
112+
%call5 = call i32 @call4(i32 %val)
113+
br label %cleanup
114+
115+
cleanup:
116+
%retval.0 = phi i32 [ %call5, %sw.bb4 ], [ %call3, %sw.bb2 ], [ %call, %sw.bb ]
117+
ret i32 %retval.0
118+
}
119+
120+
; Negative tests
121+
122+
define i32 @test_reachable_default(i32 noundef %num) {
123+
; CHECK-LABEL: define i32 @test_reachable_default(
124+
; CHECK-SAME: i32 noundef [[NUM:%.*]]) {
125+
; CHECK-NEXT: entry:
126+
; CHECK-NEXT: [[SUB:%.*]] = add i32 [[NUM]], -120
127+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 3
128+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 4
129+
; CHECK-NEXT: switch i32 [[COND]], label [[SW_DEFAULT:%.*]] [
130+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
131+
; CHECK-NEXT: i32 1, label [[SW_BB2:%.*]]
132+
; CHECK-NEXT: i32 2, label [[SW_BB4:%.*]]
133+
; CHECK-NEXT: ]
134+
; CHECK: sw.bb:
135+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @call0()
136+
; CHECK-NEXT: br label [[CLEANUP:%.*]]
137+
; CHECK: sw.bb2:
138+
; CHECK-NEXT: [[CALL3:%.*]] = call i32 @call1()
139+
; CHECK-NEXT: br label [[CLEANUP]]
140+
; CHECK: sw.bb4:
141+
; CHECK-NEXT: [[CALL5:%.*]] = call i32 @call2()
142+
; CHECK-NEXT: br label [[CLEANUP]]
143+
; CHECK: sw.default:
144+
; CHECK-NEXT: [[CALL6:%.*]] = call i32 @call3()
145+
; CHECK-NEXT: br label [[CLEANUP]]
146+
; CHECK: cleanup:
147+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CALL6]], [[SW_DEFAULT]] ], [ [[CALL5]], [[SW_BB4]] ], [ [[CALL3]], [[SW_BB2]] ], [ [[CALL]], [[SW_BB]] ]
148+
; CHECK-NEXT: ret i32 [[RETVAL_0]]
149+
;
150+
entry:
151+
%sub = add i32 %num, -120
152+
%cmp = icmp ult i32 %sub, 3
153+
%cond = select i1 %cmp, i32 %sub, i32 4
154+
switch i32 %cond, label %sw.default [
155+
i32 0, label %sw.bb
156+
i32 1, label %sw.bb2
157+
i32 2, label %sw.bb4
158+
]
159+
160+
sw.bb:
161+
%call = call i32 @call0()
162+
br label %cleanup
163+
164+
sw.bb2:
165+
%call3 = call i32 @call1()
166+
br label %cleanup
167+
168+
sw.bb4:
169+
%call5 = call i32 @call2()
170+
br label %cleanup
171+
172+
sw.default:
173+
%call6 = call i32 @call3()
174+
br label %cleanup
175+
176+
cleanup:
177+
%retval.0 = phi i32 [ %call6, %sw.default ], [ %call5, %sw.bb4 ], [ %call3, %sw.bb2 ], [ %call, %sw.bb ]
178+
ret i32 %retval.0
179+
}
180+
181+
define i32 @test_unreachable_default_cond_may_be_undef(i32 %num) {
182+
; CHECK-LABEL: define i32 @test_unreachable_default_cond_may_be_undef(
183+
; CHECK-SAME: i32 [[NUM:%.*]]) {
184+
; CHECK-NEXT: entry:
185+
; CHECK-NEXT: [[SUB:%.*]] = add i32 [[NUM]], -120
186+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 3
187+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 2
188+
; CHECK-NEXT: switch i32 [[COND]], label [[SW_DEFAULT:%.*]] [
189+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
190+
; CHECK-NEXT: i32 1, label [[SW_BB2:%.*]]
191+
; CHECK-NEXT: i32 2, label [[SW_BB4:%.*]]
192+
; CHECK-NEXT: ]
193+
; CHECK: sw.bb:
194+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @call0()
195+
; CHECK-NEXT: br label [[CLEANUP:%.*]]
196+
; CHECK: sw.bb2:
197+
; CHECK-NEXT: [[CALL3:%.*]] = call i32 @call1()
198+
; CHECK-NEXT: br label [[CLEANUP]]
199+
; CHECK: sw.bb4:
200+
; CHECK-NEXT: [[CALL5:%.*]] = call i32 @call2()
201+
; CHECK-NEXT: br label [[CLEANUP]]
202+
; CHECK: sw.default:
203+
; CHECK-NEXT: [[CALL6:%.*]] = call i32 @call3()
204+
; CHECK-NEXT: br label [[CLEANUP]]
205+
; CHECK: cleanup:
206+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CALL6]], [[SW_DEFAULT]] ], [ [[CALL5]], [[SW_BB4]] ], [ [[CALL3]], [[SW_BB2]] ], [ [[CALL]], [[SW_BB]] ]
207+
; CHECK-NEXT: ret i32 [[RETVAL_0]]
208+
;
209+
entry:
210+
%sub = add i32 %num, -120
211+
%cmp = icmp ult i32 %sub, 3
212+
%cond = select i1 %cmp, i32 %sub, i32 2
213+
switch i32 %cond, label %sw.default [
214+
i32 0, label %sw.bb
215+
i32 1, label %sw.bb2
216+
i32 2, label %sw.bb4
217+
]
218+
219+
sw.bb:
220+
%call = call i32 @call0()
221+
br label %cleanup
222+
223+
sw.bb2:
224+
%call3 = call i32 @call1()
225+
br label %cleanup
226+
227+
sw.bb4:
228+
%call5 = call i32 @call2()
229+
br label %cleanup
230+
231+
sw.default:
232+
%call6 = call i32 @call3()
233+
br label %cleanup
234+
235+
cleanup:
236+
%retval.0 = phi i32 [ %call6, %sw.default ], [ %call5, %sw.bb4 ], [ %call3, %sw.bb2 ], [ %call, %sw.bb ]
237+
ret i32 %retval.0
238+
}
239+
240+
define i32 @test_default_is_already_unreachable(i32 %num) {
241+
; CHECK-LABEL: define i32 @test_default_is_already_unreachable(
242+
; CHECK-SAME: i32 [[NUM:%.*]]) {
243+
; CHECK-NEXT: entry:
244+
; CHECK-NEXT: [[SUB:%.*]] = add i32 [[NUM]], -120
245+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[SUB]], 3
246+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 2
247+
; CHECK-NEXT: switch i32 [[COND]], label [[SW_DEFAULT:%.*]] [
248+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
249+
; CHECK-NEXT: i32 1, label [[SW_BB2:%.*]]
250+
; CHECK-NEXT: i32 2, label [[SW_BB4:%.*]]
251+
; CHECK-NEXT: ]
252+
; CHECK: sw.bb:
253+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @call0()
254+
; CHECK-NEXT: br label [[CLEANUP:%.*]]
255+
; CHECK: sw.bb2:
256+
; CHECK-NEXT: [[CALL3:%.*]] = call i32 @call1()
257+
; CHECK-NEXT: br label [[CLEANUP]]
258+
; CHECK: sw.bb4:
259+
; CHECK-NEXT: [[CALL5:%.*]] = call i32 @call2()
260+
; CHECK-NEXT: br label [[CLEANUP]]
261+
; CHECK: sw.default:
262+
; CHECK-NEXT: unreachable
263+
; CHECK: cleanup:
264+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[CALL5]], [[SW_BB4]] ], [ [[CALL3]], [[SW_BB2]] ], [ [[CALL]], [[SW_BB]] ]
265+
; CHECK-NEXT: ret i32 [[RETVAL_0]]
266+
;
267+
entry:
268+
%sub = add i32 %num, -120
269+
%cmp = icmp ult i32 %sub, 3
270+
%cond = select i1 %cmp, i32 %sub, i32 2
271+
switch i32 %cond, label %sw.default [
272+
i32 0, label %sw.bb
273+
i32 1, label %sw.bb2
274+
i32 2, label %sw.bb4
275+
]
276+
277+
sw.bb:
278+
%call = call i32 @call0()
279+
br label %cleanup
280+
281+
sw.bb2:
282+
%call3 = call i32 @call1()
283+
br label %cleanup
284+
285+
sw.bb4:
286+
%call5 = call i32 @call2()
287+
br label %cleanup
288+
289+
sw.default:
290+
unreachable
291+
292+
cleanup:
293+
%retval.0 = phi i32 [ %call5, %sw.bb4 ], [ %call3, %sw.bb2 ], [ %call, %sw.bb ]
294+
ret i32 %retval.0
295+
}
296+
297+
declare i32 @call0()
298+
declare i32 @call1()
299+
declare i32 @call2()
300+
declare i32 @call3()
301+
declare i32 @call4(i32)

0 commit comments

Comments
 (0)