Skip to content

Commit 1259c05

Browse files
authored
[InstCombine] Canonicalize switch(X << C) into switch(X) (#77068)
This patch canonicalizes `switch(X << C)` to `switch(X)`. If the shift may wrap, an and instruction will be created to mask out all of the shifted bits. Alive2: https://alive2.llvm.org/ce/z/wSsL5y NOTE: We can relax the one-use constraint. But I don't see any benefit in my benchmark. Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=a776740d6296520b8bde156aa3f8d9ecb32cddd9&to=6dd783b9f90ae5f258102d732953567d7e317c02&stat=instructions%3Au |stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang| |--|--|--|--|--|--|--| |-0.00%|+0.01%|-0.02%|-0.01%|+0.02%|-0.00%|+0.01%|
1 parent e42edb5 commit 1259c05

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3220,6 +3220,33 @@ Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
32203220
return replaceOperand(SI, 0, Op0);
32213221
}
32223222

3223+
uint64_t ShiftAmt;
3224+
if (match(Cond, m_Shl(m_Value(Op0), m_ConstantInt(ShiftAmt))) &&
3225+
ShiftAmt < Op0->getType()->getScalarSizeInBits() &&
3226+
all_of(SI.cases(), [&](const auto &Case) {
3227+
return Case.getCaseValue()->getValue().countr_zero() >= ShiftAmt;
3228+
})) {
3229+
// Change 'switch (X << 2) case 4:' into 'switch (X) case 1:'.
3230+
OverflowingBinaryOperator *Shl = cast<OverflowingBinaryOperator>(Cond);
3231+
if (Shl->hasNoUnsignedWrap() || Shl->hasNoSignedWrap() ||
3232+
Shl->hasOneUse()) {
3233+
Value *NewCond = Op0;
3234+
if (!Shl->hasNoUnsignedWrap() && !Shl->hasNoSignedWrap()) {
3235+
// If the shift may wrap, we need to mask off the shifted bits.
3236+
unsigned BitWidth = Op0->getType()->getScalarSizeInBits();
3237+
NewCond = Builder.CreateAnd(
3238+
Op0, APInt::getLowBitsSet(BitWidth, BitWidth - ShiftAmt));
3239+
}
3240+
for (auto Case : SI.cases()) {
3241+
const APInt &CaseVal = Case.getCaseValue()->getValue();
3242+
APInt ShiftedCase = Shl->hasNoSignedWrap() ? CaseVal.ashr(ShiftAmt)
3243+
: CaseVal.lshr(ShiftAmt);
3244+
Case.setValue(ConstantInt::get(SI.getContext(), ShiftedCase));
3245+
}
3246+
return replaceOperand(SI, 0, NewCond);
3247+
}
3248+
}
3249+
32233250
KnownBits Known = computeKnownBits(Cond, 0, &SI);
32243251
unsigned LeadingKnownZeros = Known.countMinLeadingZeros();
32253252
unsigned LeadingKnownOnes = Known.countMinLeadingOnes();
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
define i1 @test_switch_with_shl_mask(i32 %a) {
5+
; CHECK-LABEL: define i1 @test_switch_with_shl_mask(
6+
; CHECK-SAME: i32 [[A:%.*]]) {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[A]] to i8
9+
; CHECK-NEXT: switch i8 [[TRUNC]], label [[SW_DEFAULT:%.*]] [
10+
; CHECK-NEXT: i8 0, label [[SW_BB:%.*]]
11+
; CHECK-NEXT: i8 1, label [[SW_BB]]
12+
; CHECK-NEXT: i8 -128, label [[SW_BB]]
13+
; CHECK-NEXT: ]
14+
; CHECK: sw.bb:
15+
; CHECK-NEXT: ret i1 true
16+
; CHECK: sw.default:
17+
; CHECK-NEXT: ret i1 false
18+
;
19+
entry:
20+
%b = shl i32 %a, 24
21+
switch i32 %b, label %sw.default [
22+
i32 0, label %sw.bb
23+
i32 16777216, label %sw.bb
24+
i32 2147483648, label %sw.bb
25+
]
26+
27+
sw.bb:
28+
ret i1 true
29+
sw.default:
30+
ret i1 false
31+
}
32+
33+
define i1 @test_switch_with_shl_nuw_multiuse(i32 %a) {
34+
; CHECK-LABEL: define i1 @test_switch_with_shl_nuw_multiuse(
35+
; CHECK-SAME: i32 [[A:%.*]]) {
36+
; CHECK-NEXT: entry:
37+
; CHECK-NEXT: [[B:%.*]] = shl nuw i32 [[A]], 24
38+
; CHECK-NEXT: call void @use(i32 [[B]])
39+
; CHECK-NEXT: switch i32 [[A]], label [[SW_DEFAULT:%.*]] [
40+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
41+
; CHECK-NEXT: i32 1, label [[SW_BB]]
42+
; CHECK-NEXT: i32 128, label [[SW_BB]]
43+
; CHECK-NEXT: ]
44+
; CHECK: sw.bb:
45+
; CHECK-NEXT: ret i1 true
46+
; CHECK: sw.default:
47+
; CHECK-NEXT: ret i1 false
48+
;
49+
entry:
50+
%b = shl nuw i32 %a, 24
51+
call void @use(i32 %b)
52+
switch i32 %b, label %sw.default [
53+
i32 0, label %sw.bb
54+
i32 16777216, label %sw.bb
55+
i32 2147483648, label %sw.bb
56+
]
57+
58+
sw.bb:
59+
ret i1 true
60+
sw.default:
61+
ret i1 false
62+
}
63+
64+
define i1 @test_switch_with_shl_nsw_multiuse(i32 %a) {
65+
; CHECK-LABEL: define i1 @test_switch_with_shl_nsw_multiuse(
66+
; CHECK-SAME: i32 [[A:%.*]]) {
67+
; CHECK-NEXT: entry:
68+
; CHECK-NEXT: [[B:%.*]] = shl nsw i32 [[A]], 24
69+
; CHECK-NEXT: call void @use(i32 [[B]])
70+
; CHECK-NEXT: switch i32 [[A]], label [[SW_DEFAULT:%.*]] [
71+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
72+
; CHECK-NEXT: i32 1, label [[SW_BB]]
73+
; CHECK-NEXT: i32 -128, label [[SW_BB]]
74+
; CHECK-NEXT: ]
75+
; CHECK: sw.bb:
76+
; CHECK-NEXT: ret i1 true
77+
; CHECK: sw.default:
78+
; CHECK-NEXT: ret i1 false
79+
;
80+
entry:
81+
%b = shl nsw i32 %a, 24
82+
call void @use(i32 %b)
83+
switch i32 %b, label %sw.default [
84+
i32 0, label %sw.bb
85+
i32 16777216, label %sw.bb
86+
i32 2147483648, label %sw.bb
87+
]
88+
89+
sw.bb:
90+
ret i1 true
91+
sw.default:
92+
ret i1 false
93+
}
94+
95+
; Negative tests
96+
97+
define i1 @test_switch_with_shl_mask_multiuse(i32 %a) {
98+
; CHECK-LABEL: define i1 @test_switch_with_shl_mask_multiuse(
99+
; CHECK-SAME: i32 [[A:%.*]]) {
100+
; CHECK-NEXT: entry:
101+
; CHECK-NEXT: [[B:%.*]] = shl i32 [[A]], 24
102+
; CHECK-NEXT: call void @use(i32 [[B]])
103+
; CHECK-NEXT: switch i32 [[B]], label [[SW_DEFAULT:%.*]] [
104+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
105+
; CHECK-NEXT: i32 16777216, label [[SW_BB]]
106+
; CHECK-NEXT: i32 -2147483648, label [[SW_BB]]
107+
; CHECK-NEXT: ]
108+
; CHECK: sw.bb:
109+
; CHECK-NEXT: ret i1 true
110+
; CHECK: sw.default:
111+
; CHECK-NEXT: ret i1 false
112+
;
113+
entry:
114+
%b = shl i32 %a, 24
115+
call void @use(i32 %b)
116+
switch i32 %b, label %sw.default [
117+
i32 0, label %sw.bb
118+
i32 16777216, label %sw.bb
119+
i32 2147483648, label %sw.bb
120+
]
121+
122+
sw.bb:
123+
ret i1 true
124+
sw.default:
125+
ret i1 false
126+
}
127+
128+
define i1 @test_switch_with_shl_mask_unknown_shamt(i32 %a, i32 %shamt) {
129+
; CHECK-LABEL: define i1 @test_switch_with_shl_mask_unknown_shamt(
130+
; CHECK-SAME: i32 [[A:%.*]], i32 [[SHAMT:%.*]]) {
131+
; CHECK-NEXT: entry:
132+
; CHECK-NEXT: [[B:%.*]] = shl i32 [[A]], [[SHAMT]]
133+
; CHECK-NEXT: switch i32 [[B]], label [[SW_DEFAULT:%.*]] [
134+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
135+
; CHECK-NEXT: i32 16777216, label [[SW_BB]]
136+
; CHECK-NEXT: i32 -2147483648, label [[SW_BB]]
137+
; CHECK-NEXT: ]
138+
; CHECK: sw.bb:
139+
; CHECK-NEXT: ret i1 true
140+
; CHECK: sw.default:
141+
; CHECK-NEXT: ret i1 false
142+
;
143+
entry:
144+
%b = shl i32 %a, %shamt
145+
switch i32 %b, label %sw.default [
146+
i32 0, label %sw.bb
147+
i32 16777216, label %sw.bb
148+
i32 2147483648, label %sw.bb
149+
]
150+
151+
sw.bb:
152+
ret i1 true
153+
sw.default:
154+
ret i1 false
155+
}
156+
157+
define i1 @test_switch_with_shl_mask_poison(i32 %a) {
158+
; CHECK-LABEL: define i1 @test_switch_with_shl_mask_poison(
159+
; CHECK-SAME: i32 [[A:%.*]]) {
160+
; CHECK-NEXT: entry:
161+
; CHECK-NEXT: switch i32 poison, label [[SW_DEFAULT:%.*]] [
162+
; CHECK-NEXT: i32 0, label [[SW_BB:%.*]]
163+
; CHECK-NEXT: i32 16777216, label [[SW_BB]]
164+
; CHECK-NEXT: i32 -2147483648, label [[SW_BB]]
165+
; CHECK-NEXT: ]
166+
; CHECK: sw.bb:
167+
; CHECK-NEXT: ret i1 true
168+
; CHECK: sw.default:
169+
; CHECK-NEXT: ret i1 false
170+
;
171+
entry:
172+
%b = shl i32 %a, 32
173+
switch i32 %b, label %sw.default [
174+
i32 0, label %sw.bb
175+
i32 16777216, label %sw.bb
176+
i32 2147483648, label %sw.bb
177+
]
178+
179+
sw.bb:
180+
ret i1 true
181+
sw.default:
182+
ret i1 false
183+
}
184+
185+
declare void @use(i32)

0 commit comments

Comments
 (0)