Skip to content

Commit 394e321

Browse files
authored
Reland "[CVP] Check whether the default case is reachable (#79993)" (#96089)
This patch reverts #81585 as #78582 has been landed. Now clang works well with reproducer #79993 (comment).
1 parent fd88089 commit 394e321

File tree

3 files changed

+339
-5
lines changed

3 files changed

+339
-5
lines changed

llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
366366
{ // Scope for SwitchInstProfUpdateWrapper. It must not live during
367367
// ConstantFoldTerminator() as the underlying SwitchInst can be changed.
368368
SwitchInstProfUpdateWrapper SI(*I);
369+
unsigned ReachableCaseCount = 0;
369370

370371
for (auto CI = SI->case_begin(), CE = SI->case_end(); CI != CE;) {
371372
ConstantInt *Case = CI->getCaseValue();
@@ -402,6 +403,31 @@ static bool processSwitch(SwitchInst *I, LazyValueInfo *LVI,
402403

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

@@ -1283,6 +1309,12 @@ CorrelatedValuePropagationPass::run(Function &F, FunctionAnalysisManager &AM) {
12831309
if (!Changed) {
12841310
PA = PreservedAnalyses::all();
12851311
} else {
1312+
#if defined(EXPENSIVE_CHECKS)
1313+
assert(DT->verify(DominatorTree::VerificationLevel::Full));
1314+
#else
1315+
assert(DT->verify(DominatorTree::VerificationLevel::Fast));
1316+
#endif // EXPENSIVE_CHECKS
1317+
12861318
PA.preserve<DominatorTreeAnalysis>();
12871319
PA.preserve<LazyValueAnalysis>();
12881320
}

llvm/test/Transforms/CorrelatedValuePropagation/basic.ll

Lines changed: 6 additions & 5 deletions
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
;
Lines changed: 301 additions & 0 deletions
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)