Skip to content

Commit 6ad1dd3

Browse files
authored
[InstCombine] Fold (sext(a) & c1) == c2 to (a & c3) == trunc(c2) (#112646)
Fixes #85830. Updated Alive proof: https://alive2.llvm.org/ce/z/KnvoP5
1 parent 6dc23b7 commit 6ad1dd3

File tree

3 files changed

+259
-3
lines changed

3 files changed

+259
-3
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7715,6 +7715,32 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
77157715
if (Instruction *Res = foldReductionIdiom(I, Builder, DL))
77167716
return Res;
77177717

7718+
{
7719+
Value *A;
7720+
const APInt *C1, *C2;
7721+
ICmpInst::Predicate Pred = I.getPredicate();
7722+
if (ICmpInst::isEquality(Pred)) {
7723+
// sext(a) & c1 == c2 --> a & c3 == trunc(c2)
7724+
// sext(a) & c1 != c2 --> a & c3 != trunc(c2)
7725+
if (match(Op0, m_And(m_SExt(m_Value(A)), m_APInt(C1))) &&
7726+
match(Op1, m_APInt(C2))) {
7727+
Type *InputTy = A->getType();
7728+
unsigned InputBitWidth = InputTy->getScalarSizeInBits();
7729+
// c2 must be non-negative at the bitwidth of a.
7730+
if (C2->getActiveBits() < InputBitWidth) {
7731+
APInt TruncC1 = C1->trunc(InputBitWidth);
7732+
// Check if there are 1s in C1 high bits of size InputBitWidth.
7733+
if (C1->uge(APInt::getOneBitSet(C1->getBitWidth(), InputBitWidth)))
7734+
TruncC1.setBit(InputBitWidth - 1);
7735+
Value *AndInst = Builder.CreateAnd(A, TruncC1);
7736+
return new ICmpInst(
7737+
Pred, AndInst,
7738+
ConstantInt::get(InputTy, C2->trunc(InputBitWidth)));
7739+
}
7740+
}
7741+
}
7742+
}
7743+
77187744
return Changed ? &I : nullptr;
77197745
}
77207746

llvm/test/Transforms/InstCombine/load-cmp.ll

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,9 +312,7 @@ define i1 @test10_struct_arr_i64(i64 %x) {
312312

313313
define i1 @test10_struct_arr_noinbounds_i16(i16 %x) {
314314
; CHECK-LABEL: @test10_struct_arr_noinbounds_i16(
315-
; CHECK-NEXT: [[TMP1:%.*]] = sext i16 [[X:%.*]] to i32
316-
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[TMP1]], 268435455
317-
; CHECK-NEXT: [[R:%.*]] = icmp ne i32 [[TMP2]], 1
315+
; CHECK-NEXT: [[R:%.*]] = icmp ne i16 [[X:%.*]], 1
318316
; CHECK-NEXT: ret i1 [[R]]
319317
;
320318
%p = getelementptr [4 x %Foo], ptr @GStructArr, i32 0, i16 %x, i32 2
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
3+
4+
declare void @use(i8)
5+
6+
define i1 @fold_sext_to_and(i8 %x) {
7+
; CHECK-LABEL: define i1 @fold_sext_to_and(
8+
; CHECK-SAME: i8 [[X:%.*]]) {
9+
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -127
10+
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 1
11+
; CHECK-NEXT: ret i1 [[TMP3]]
12+
;
13+
%1 = sext i8 %x to i32
14+
%2 = and i32 %1, -2147483647
15+
%3 = icmp eq i32 %2, 1
16+
ret i1 %3
17+
}
18+
19+
define i1 @fold_sext_to_and1(i8 %x) {
20+
; CHECK-LABEL: define i1 @fold_sext_to_and1(
21+
; CHECK-SAME: i8 [[X:%.*]]) {
22+
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -127
23+
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 1
24+
; CHECK-NEXT: ret i1 [[TMP3]]
25+
;
26+
%1 = sext i8 %x to i32
27+
%2 = and i32 %1, -2147483647
28+
%3 = icmp ne i32 %2, 1
29+
ret i1 %3
30+
}
31+
32+
define i1 @fold_sext_to_and2(i8 %x) {
33+
; CHECK-LABEL: define i1 @fold_sext_to_and2(
34+
; CHECK-SAME: i8 [[X:%.*]]) {
35+
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -126
36+
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 2
37+
; CHECK-NEXT: ret i1 [[TMP3]]
38+
;
39+
%1 = sext i8 %x to i32
40+
%2 = and i32 %1, 1073741826
41+
%3 = icmp eq i32 %2, 2
42+
ret i1 %3
43+
}
44+
45+
define i1 @fold_sext_to_and3(i8 %x) {
46+
; CHECK-LABEL: define i1 @fold_sext_to_and3(
47+
; CHECK-SAME: i8 [[X:%.*]]) {
48+
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X]], -126
49+
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 2
50+
; CHECK-NEXT: ret i1 [[TMP3]]
51+
;
52+
%1 = sext i8 %x to i32
53+
%2 = and i32 %1, 1073741826
54+
%3 = icmp ne i32 %2, 2
55+
ret i1 %3
56+
}
57+
58+
define i1 @fold_sext_to_and_multi_use(i8 %x) {
59+
; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use(
60+
; CHECK-SAME: i8 [[X:%.*]]) {
61+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
62+
; CHECK-NEXT: call void @use(i32 [[TMP1]])
63+
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -127
64+
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], 1
65+
; CHECK-NEXT: ret i1 [[TMP3]]
66+
;
67+
%1 = sext i8 %x to i32
68+
call void @use(i32 %1)
69+
%2 = and i32 %1, -2147483647
70+
%3 = icmp eq i32 %2, 1
71+
ret i1 %3
72+
}
73+
74+
define i1 @fold_sext_to_and_multi_use1(i8 %x) {
75+
; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use1(
76+
; CHECK-SAME: i8 [[X:%.*]]) {
77+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
78+
; CHECK-NEXT: call void @use(i32 [[TMP1]])
79+
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -127
80+
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], 1
81+
; CHECK-NEXT: ret i1 [[TMP3]]
82+
;
83+
%1 = sext i8 %x to i32
84+
call void @use(i32 %1)
85+
%2 = and i32 %1, -2147483647
86+
%3 = icmp ne i32 %2, 1
87+
ret i1 %3
88+
}
89+
90+
define i1 @fold_sext_to_and_multi_use2(i8 %x) {
91+
; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use2(
92+
; CHECK-SAME: i8 [[X:%.*]]) {
93+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
94+
; CHECK-NEXT: call void @use(i32 [[TMP1]])
95+
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -126
96+
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i8 [[TMP2]], 2
97+
; CHECK-NEXT: ret i1 [[TMP3]]
98+
;
99+
%1 = sext i8 %x to i32
100+
call void @use(i32 %1)
101+
%2 = and i32 %1, 1073741826
102+
%3 = icmp eq i32 %2, 2
103+
ret i1 %3
104+
}
105+
106+
define i1 @fold_sext_to_and_multi_use3(i8 %x) {
107+
; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use3(
108+
; CHECK-SAME: i8 [[X:%.*]]) {
109+
; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[X]] to i32
110+
; CHECK-NEXT: call void @use(i32 [[TMP1]])
111+
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[X]], -126
112+
; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i8 [[TMP2]], 2
113+
; CHECK-NEXT: ret i1 [[TMP3]]
114+
;
115+
%1 = sext i8 %x to i32
116+
call void @use(i32 %1)
117+
%2 = and i32 %1, 1073741826
118+
%3 = icmp ne i32 %2, 2
119+
ret i1 %3
120+
}
121+
122+
; Negative tests
123+
124+
define i1 @fold_sext_to_and_wrong(i8 %x) {
125+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong(
126+
; CHECK-SAME: i8 [[X:%.*]]) {
127+
; CHECK-NEXT: ret i1 false
128+
;
129+
%1 = sext i8 %x to i32
130+
%2 = and i32 %1, -2147483647
131+
%3 = icmp eq i32 %2, -1
132+
ret i1 %3
133+
}
134+
135+
define i1 @fold_sext_to_and_wrong2(i8 %x) {
136+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong2(
137+
; CHECK-SAME: i8 [[X:%.*]]) {
138+
; CHECK-NEXT: ret i1 false
139+
;
140+
%1 = sext i8 %x to i32
141+
%2 = and i32 %1, -2147483647
142+
%3 = icmp eq i32 %2, 128
143+
ret i1 %3
144+
}
145+
146+
define i1 @fold_sext_to_and_wrong3(i8 %x) {
147+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong3(
148+
; CHECK-SAME: i8 [[X:%.*]]) {
149+
; CHECK-NEXT: ret i1 false
150+
;
151+
%1 = sext i8 %x to i32
152+
%2 = and i32 %1, 128
153+
%3 = icmp eq i32 %2, -2147483648
154+
ret i1 %3
155+
}
156+
157+
define i1 @fold_sext_to_and_wrong4(i8 %x) {
158+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong4(
159+
; CHECK-SAME: i8 [[X:%.*]]) {
160+
; CHECK-NEXT: ret i1 false
161+
;
162+
%1 = sext i8 %x to i32
163+
%2 = and i32 %1, 128
164+
%3 = icmp eq i32 %2, 1
165+
ret i1 %3
166+
}
167+
168+
define i1 @fold_sext_to_and_wrong5(i8 %x) {
169+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong5(
170+
; CHECK-SAME: i8 [[X:%.*]]) {
171+
; CHECK-NEXT: ret i1 false
172+
;
173+
%1 = sext i8 %x to i32
174+
%2 = and i32 %1, -256
175+
%3 = icmp eq i32 %2, 1
176+
ret i1 %3
177+
}
178+
179+
define i1 @fold_sext_to_and_wrong6(i8 %x) {
180+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong6(
181+
; CHECK-SAME: i8 [[X:%.*]]) {
182+
; CHECK-NEXT: ret i1 true
183+
;
184+
%1 = sext i8 %x to i32
185+
%2 = and i32 %1, -2147483647
186+
%3 = icmp ne i32 %2, -1
187+
ret i1 %3
188+
}
189+
190+
define i1 @fold_sext_to_and_wrong7(i8 %x) {
191+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong7(
192+
; CHECK-SAME: i8 [[X:%.*]]) {
193+
; CHECK-NEXT: ret i1 true
194+
;
195+
%1 = sext i8 %x to i32
196+
%2 = and i32 %1, -2147483647
197+
%3 = icmp ne i32 %2, 128
198+
ret i1 %3
199+
}
200+
201+
define i1 @fold_sext_to_and_wrong8(i8 %x) {
202+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong8(
203+
; CHECK-SAME: i8 [[X:%.*]]) {
204+
; CHECK-NEXT: ret i1 true
205+
;
206+
%1 = sext i8 %x to i32
207+
%2 = and i32 %1, 128
208+
%3 = icmp ne i32 %2, -2147483648
209+
ret i1 %3
210+
}
211+
212+
define i1 @fold_sext_to_and_wrong9(i8 %x) {
213+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong9(
214+
; CHECK-SAME: i8 [[X:%.*]]) {
215+
; CHECK-NEXT: ret i1 true
216+
;
217+
%1 = sext i8 %x to i32
218+
%2 = and i32 %1, 128
219+
%3 = icmp ne i32 %2, 1
220+
ret i1 %3
221+
}
222+
223+
define i1 @fold_sext_to_and_wrong10(i8 %x) {
224+
; CHECK-LABEL: define i1 @fold_sext_to_and_wrong10(
225+
; CHECK-SAME: i8 [[X:%.*]]) {
226+
; CHECK-NEXT: ret i1 true
227+
;
228+
%1 = sext i8 %x to i32
229+
%2 = and i32 %1, -256
230+
%3 = icmp ne i32 %2, 1
231+
ret i1 %3
232+
}

0 commit comments

Comments
 (0)