Skip to content

Commit 7c3bcc3

Browse files
authored
[InstCombine] Fold switch(zext/sext(X)) into switch(X) (#76988)
This patch folds `switch(zext/sext(X))` into `switch(X)`. The original motivation of this patch is to optimize a pattern found in cvc5. For example: ``` %bf.load.i = load i16, ptr %d_kind.i, align 8 %bf.clear.i = and i16 %bf.load.i, 1023 %bf.cast.i = zext nneg i16 %bf.clear.i to i32 switch i32 %bf.cast.i, label %if.else [ i32 335, label %if.then i32 303, label %if.then ] if.then: ; preds = %entry, %entry %d_children.i.i = getelementptr inbounds %"class.cvc5::internal::expr::NodeValue", ptr %0, i64 0, i32 3 %cmp.i.i.i.i.i = icmp eq i16 %bf.clear.i, 1023 %cond.i.i.i.i.i = select i1 %cmp.i.i.i.i.i, i32 -1, i32 %bf.cast.i ``` `%cmp.i.i.i.i.i` always evaluates to false because `%bf.clear.i` can only be 335 or 303. Folding `switch i32 %bf.cast.i` to `switch i16 %bf.clear.i` will help `CVP` to handle this case. See also #76928 (comment). Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=7954c57124b495fbdc73674d71f2e366e4afe522&to=502b13ed34e561d995ae1f724cf06d20008bd86f&stat=instructions:u |stage1-O3|stage1-ReleaseThinLTO|stage1-ReleaseLTO-g|stage1-O0-g|stage2-O3|stage2-O0-g|stage2-clang| |--|--|--|--|--|--|--| |+0.03%|+0.06%|+0.07%|+0.00%|-0.02%|-0.03%|+0.02%|
1 parent 5121e2c commit 7c3bcc3

File tree

4 files changed

+202
-17
lines changed

4 files changed

+202
-17
lines changed

llvm/lib/Transforms/InstCombine/InstructionCombining.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3247,6 +3247,25 @@ Instruction *InstCombinerImpl::visitSwitchInst(SwitchInst &SI) {
32473247
}
32483248
}
32493249

3250+
// Fold switch(zext/sext(X)) into switch(X) if possible.
3251+
if (match(Cond, m_ZExtOrSExt(m_Value(Op0)))) {
3252+
bool IsZExt = isa<ZExtInst>(Cond);
3253+
Type *SrcTy = Op0->getType();
3254+
unsigned NewWidth = SrcTy->getScalarSizeInBits();
3255+
3256+
if (all_of(SI.cases(), [&](const auto &Case) {
3257+
const APInt &CaseVal = Case.getCaseValue()->getValue();
3258+
return IsZExt ? CaseVal.isIntN(NewWidth)
3259+
: CaseVal.isSignedIntN(NewWidth);
3260+
})) {
3261+
for (auto &Case : SI.cases()) {
3262+
APInt TruncatedCase = Case.getCaseValue()->getValue().trunc(NewWidth);
3263+
Case.setValue(ConstantInt::get(SI.getContext(), TruncatedCase));
3264+
}
3265+
return replaceOperand(SI, 0, Op0);
3266+
}
3267+
}
3268+
32503269
KnownBits Known = computeKnownBits(Cond, 0, &SI);
32513270
unsigned LeadingKnownZeros = Known.countMinLeadingZeros();
32523271
unsigned LeadingKnownOnes = Known.countMinLeadingOnes();

llvm/test/Transforms/InstCombine/phi.ll

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -996,10 +996,9 @@ done:
996996
define i1 @PR24766(i8 %x1, i8 %x2, i8 %condition) {
997997
; CHECK-LABEL: @PR24766(
998998
; CHECK-NEXT: entry:
999-
; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
1000-
; CHECK-NEXT: switch i32 [[CONV]], label [[EPILOG:%.*]] [
1001-
; CHECK-NEXT: i32 0, label [[SW1:%.*]]
1002-
; CHECK-NEXT: i32 1, label [[SW2:%.*]]
999+
; CHECK-NEXT: switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
1000+
; CHECK-NEXT: i8 0, label [[SW1:%.*]]
1001+
; CHECK-NEXT: i8 1, label [[SW2:%.*]]
10031002
; CHECK-NEXT: ]
10041003
; CHECK: sw1:
10051004
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1040,10 +1039,9 @@ epilog:
10401039
define i1 @PR24766_no_constants(i8 %x1, i8 %x2, i8 %condition, i1 %another_condition) {
10411040
; CHECK-LABEL: @PR24766_no_constants(
10421041
; CHECK-NEXT: entry:
1043-
; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
1044-
; CHECK-NEXT: switch i32 [[CONV]], label [[EPILOG:%.*]] [
1045-
; CHECK-NEXT: i32 0, label [[SW1:%.*]]
1046-
; CHECK-NEXT: i32 1, label [[SW2:%.*]]
1042+
; CHECK-NEXT: switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
1043+
; CHECK-NEXT: i8 0, label [[SW1:%.*]]
1044+
; CHECK-NEXT: i8 1, label [[SW2:%.*]]
10471045
; CHECK-NEXT: ]
10481046
; CHECK: sw1:
10491047
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1085,10 +1083,9 @@ epilog:
10851083
define i1 @PR24766_two_constants(i8 %x1, i8 %x2, i8 %condition) {
10861084
; CHECK-LABEL: @PR24766_two_constants(
10871085
; CHECK-NEXT: entry:
1088-
; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
1089-
; CHECK-NEXT: switch i32 [[CONV]], label [[EPILOG:%.*]] [
1090-
; CHECK-NEXT: i32 0, label [[SW1:%.*]]
1091-
; CHECK-NEXT: i32 1, label [[SW2:%.*]]
1086+
; CHECK-NEXT: switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
1087+
; CHECK-NEXT: i8 0, label [[SW1:%.*]]
1088+
; CHECK-NEXT: i8 1, label [[SW2:%.*]]
10921089
; CHECK-NEXT: ]
10931090
; CHECK: sw1:
10941091
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
@@ -1128,11 +1125,10 @@ epilog:
11281125
define i1 @PR24766_two_constants_two_var(i8 %x1, i8 %x2, i8 %condition) {
11291126
; CHECK-LABEL: @PR24766_two_constants_two_var(
11301127
; CHECK-NEXT: entry:
1131-
; CHECK-NEXT: [[CONV:%.*]] = sext i8 [[CONDITION:%.*]] to i32
1132-
; CHECK-NEXT: switch i32 [[CONV]], label [[EPILOG:%.*]] [
1133-
; CHECK-NEXT: i32 0, label [[SW1:%.*]]
1134-
; CHECK-NEXT: i32 1, label [[SW2:%.*]]
1135-
; CHECK-NEXT: i32 2, label [[SW3:%.*]]
1128+
; CHECK-NEXT: switch i8 [[CONDITION:%.*]], label [[EPILOG:%.*]] [
1129+
; CHECK-NEXT: i8 0, label [[SW1:%.*]]
1130+
; CHECK-NEXT: i8 1, label [[SW2:%.*]]
1131+
; CHECK-NEXT: i8 2, label [[SW3:%.*]]
11361132
; CHECK-NEXT: ]
11371133
; CHECK: sw1:
11381134
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[X1:%.*]], [[X2:%.*]]
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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_zext(i16 %a, i1 %b, i1 %c) {
5+
; CHECK-LABEL: define i1 @test_switch_with_zext(
6+
; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
9+
; CHECK-NEXT: i16 37, label [[SW_BB:%.*]]
10+
; CHECK-NEXT: i16 38, label [[SW_BB]]
11+
; CHECK-NEXT: i16 39, label [[SW_BB]]
12+
; CHECK-NEXT: ]
13+
; CHECK: sw.bb:
14+
; CHECK-NEXT: ret i1 [[B]]
15+
; CHECK: sw.default:
16+
; CHECK-NEXT: ret i1 [[C]]
17+
;
18+
entry:
19+
%a.ext = zext i16 %a to i32
20+
switch i32 %a.ext, label %sw.default [
21+
i32 37, label %sw.bb
22+
i32 38, label %sw.bb
23+
i32 39, label %sw.bb
24+
]
25+
26+
sw.bb:
27+
ret i1 %b
28+
sw.default:
29+
ret i1 %c
30+
}
31+
32+
define i1 @test_switch_with_sext(i16 %a, i1 %b, i1 %c) {
33+
; CHECK-LABEL: define i1 @test_switch_with_sext(
34+
; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
35+
; CHECK-NEXT: entry:
36+
; CHECK-NEXT: switch i16 [[A]], label [[SW_DEFAULT:%.*]] [
37+
; CHECK-NEXT: i16 37, label [[SW_BB:%.*]]
38+
; CHECK-NEXT: i16 38, label [[SW_BB]]
39+
; CHECK-NEXT: i16 39, label [[SW_BB]]
40+
; CHECK-NEXT: ]
41+
; CHECK: sw.bb:
42+
; CHECK-NEXT: ret i1 [[B]]
43+
; CHECK: sw.default:
44+
; CHECK-NEXT: ret i1 [[C]]
45+
;
46+
entry:
47+
%a.ext = sext i16 %a to i32
48+
switch i32 %a.ext, label %sw.default [
49+
i32 37, label %sw.bb
50+
i32 38, label %sw.bb
51+
i32 39, label %sw.bb
52+
]
53+
54+
sw.bb:
55+
ret i1 %b
56+
sw.default:
57+
ret i1 %c
58+
}
59+
60+
; Negative tests
61+
62+
define i1 @test_switch_with_zext_unreachable_case(i16 %a, i1 %b, i1 %c) {
63+
; CHECK-LABEL: define i1 @test_switch_with_zext_unreachable_case(
64+
; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
65+
; CHECK-NEXT: entry:
66+
; CHECK-NEXT: [[A_EXT:%.*]] = zext i16 [[A]] to i32
67+
; CHECK-NEXT: switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
68+
; CHECK-NEXT: i32 37, label [[SW_BB:%.*]]
69+
; CHECK-NEXT: i32 38, label [[SW_BB]]
70+
; CHECK-NEXT: i32 39, label [[SW_BB]]
71+
; CHECK-NEXT: i32 65537, label [[SW_BB]]
72+
; CHECK-NEXT: ]
73+
; CHECK: sw.bb:
74+
; CHECK-NEXT: ret i1 [[B]]
75+
; CHECK: sw.default:
76+
; CHECK-NEXT: ret i1 [[C]]
77+
;
78+
entry:
79+
%a.ext = zext i16 %a to i32
80+
switch i32 %a.ext, label %sw.default [
81+
i32 37, label %sw.bb
82+
i32 38, label %sw.bb
83+
i32 39, label %sw.bb
84+
i32 65537, label %sw.bb
85+
]
86+
87+
sw.bb:
88+
ret i1 %b
89+
sw.default:
90+
ret i1 %c
91+
}
92+
93+
define i1 @test_switch_with_sext_unreachable_case(i16 %a, i1 %b, i1 %c) {
94+
; CHECK-LABEL: define i1 @test_switch_with_sext_unreachable_case(
95+
; CHECK-SAME: i16 [[A:%.*]], i1 [[B:%.*]], i1 [[C:%.*]]) {
96+
; CHECK-NEXT: entry:
97+
; CHECK-NEXT: [[A_EXT:%.*]] = sext i16 [[A]] to i32
98+
; CHECK-NEXT: switch i32 [[A_EXT]], label [[SW_DEFAULT:%.*]] [
99+
; CHECK-NEXT: i32 37, label [[SW_BB:%.*]]
100+
; CHECK-NEXT: i32 38, label [[SW_BB]]
101+
; CHECK-NEXT: i32 39, label [[SW_BB]]
102+
; CHECK-NEXT: i32 -65537, label [[SW_BB]]
103+
; CHECK-NEXT: ]
104+
; CHECK: sw.bb:
105+
; CHECK-NEXT: ret i1 [[B]]
106+
; CHECK: sw.default:
107+
; CHECK-NEXT: ret i1 [[C]]
108+
;
109+
entry:
110+
%a.ext = sext i16 %a to i32
111+
switch i32 %a.ext, label %sw.default [
112+
i32 37, label %sw.bb
113+
i32 38, label %sw.bb
114+
i32 39, label %sw.bb
115+
i32 -65537, label %sw.bb
116+
]
117+
118+
sw.bb:
119+
ret i1 %b
120+
sw.default:
121+
ret i1 %c
122+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt -S -passes='default<O3>' < %s | FileCheck %s
3+
4+
define i8 @test_switch_with_sext_phi(i8 %code) {
5+
; CHECK-LABEL: define noundef i8 @test_switch_with_sext_phi(
6+
; CHECK-SAME: i8 [[CODE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
7+
; CHECK-NEXT: entry:
8+
; CHECK-NEXT: switch i8 [[CODE]], label [[SW_EPILOG:%.*]] [
9+
; CHECK-NEXT: i8 76, label [[SW_BB3:%.*]]
10+
; CHECK-NEXT: i8 108, label [[SW_BB2:%.*]]
11+
; CHECK-NEXT: ]
12+
; CHECK: sw.bb2:
13+
; CHECK-NEXT: br label [[SW_EPILOG]]
14+
; CHECK: sw.bb3:
15+
; CHECK-NEXT: br label [[SW_EPILOG]]
16+
; CHECK: sw.epilog:
17+
; CHECK-NEXT: [[PEP_CODE:%.*]] = phi i8 [ 81, [[SW_BB3]] ], [ 113, [[SW_BB2]] ], [ [[CODE]], [[ENTRY:%.*]] ]
18+
; CHECK-NEXT: ret i8 [[PEP_CODE]]
19+
;
20+
entry:
21+
%conv = sext i8 %code to i32
22+
switch i32 %conv, label %sw.default [
23+
i32 105, label %sw.epilog
24+
i32 73, label %sw.bb1
25+
i32 108, label %sw.bb2
26+
i32 76, label %sw.bb3
27+
i32 63, label %sw.bb4
28+
]
29+
30+
sw.bb1:
31+
br label %sw.epilog
32+
33+
sw.bb2:
34+
br label %sw.epilog
35+
36+
sw.bb3:
37+
br label %sw.epilog
38+
39+
sw.bb4:
40+
br label %sw.epilog
41+
42+
sw.default:
43+
br label %sw.epilog
44+
45+
sw.epilog:
46+
%pep_code = phi i8 [ %code, %sw.default ], [ 63, %sw.bb4 ], [ 81, %sw.bb3 ], [ 113, %sw.bb2 ], [ 73, %sw.bb1 ], [ 105, %entry ]
47+
ret i8 %pep_code
48+
}

0 commit comments

Comments
 (0)