Skip to content

Commit daddf40

Browse files
authored
[InstCombine] Fold xored one-complemented operand comparisons (#69882)
- [InstCombine] Add test coverage for comparisons of operands including one-complemented oparands(NFC). - [InstCombine] Fold xored one-complemented operand comparisons. Alive2: https://alive2.llvm.org/ce/z/PZMJeB Fixes #69803.
1 parent da843aa commit daddf40

File tree

2 files changed

+296
-1
lines changed

2 files changed

+296
-1
lines changed

llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7009,7 +7009,7 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
70097009
return Res;
70107010

70117011
{
7012-
Value *X, *Y;
7012+
Value *X, *Y, *Z;
70137013
// Transform (X & ~Y) == 0 --> (X & Y) != 0
70147014
// and (X & ~Y) != 0 --> (X & Y) == 0
70157015
// if A is a power of 2.
@@ -7019,6 +7019,22 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
70197019
return new ICmpInst(I.getInversePredicate(), Builder.CreateAnd(X, Y),
70207020
Op1);
70217021

7022+
// Transform (~X ^ Y) s< ~Z --> (X ^ Y) s> Z,
7023+
// (~X ^ Y) s> ~Z --> (X ^ Y) s< Z,
7024+
// (~X ^ Y) s<= ~Z --> (X ^ Y) s>= Z,
7025+
// (~X ^ Y) s>= ~Z --> (X ^ Y) s<= Z,
7026+
// (~X ^ Y) u< ~Z --> (X ^ Y) u< Z,
7027+
// (~X ^ Y) u> ~Z --> (X ^ Y) u< Z,
7028+
// (~X ^ Y) u<= ~Z --> (X ^ Y) u>= Z,
7029+
// (~X ^ Y) u>= ~Z --> (X ^ Y) u<= Z,
7030+
// (~X ^ Y) == ~Z --> (X ^ Y) == Z,
7031+
// and (~X ^ Y) != ~Z --> (X ^ Y) != Z,
7032+
if (match(&I, m_c_ICmp(Pred, m_c_Xor(m_Not(m_Value(X)), m_Value(Y)),
7033+
m_Not(m_Value(Z)))) &&
7034+
(I.getOperand(0)->hasOneUse() || I.getOperand(1)->hasOneUse()))
7035+
return new ICmpInst(I.getSwappedPredicate(Pred), Builder.CreateXor(X, Y),
7036+
Z);
7037+
70227038
// ~X < ~Y --> Y < X
70237039
// ~X < C --> X > ~C
70247040
if (match(Op0, m_Not(m_Value(X)))) {

llvm/test/Transforms/InstCombine/icmp-of-xor-x.ll

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,285 @@
33

44
declare void @llvm.assume(i1)
55
declare void @barrier()
6+
declare void @use.i8(i8)
7+
8+
; test for (~x ^ y) < ~z
9+
define i1 @test_xor1(i8 %x, i8 %y, i8 %z) {
10+
; CHECK-LABEL: @test_xor1(
11+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
12+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
13+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
14+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[TMP1]], [[Z:%.*]]
15+
; CHECK-NEXT: ret i1 [[R]]
16+
;
17+
%xor = xor i8 %x, -1
18+
call void @use.i8(i8 %xor)
19+
%xor2 = xor i8 %xor, %y
20+
%nz = xor i8 %z, -1
21+
%r = icmp slt i8 %xor2, %nz
22+
ret i1 %r
23+
}
24+
25+
; test for ~z <= (x ^ ~y)
26+
define i1 @test_xor2(i8 %x, i8 %y, i8 %z) {
27+
; CHECK-LABEL: @test_xor2(
28+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[Y:%.*]], -1
29+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
30+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y]], [[X:%.*]]
31+
; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[TMP1]], [[Z:%.*]]
32+
; CHECK-NEXT: ret i1 [[R]]
33+
;
34+
%nz = xor i8 %z, -1
35+
%xor = xor i8 %y, -1
36+
call void @use.i8(i8 %xor)
37+
%xor2 = xor i8 %xor, %x
38+
%r = icmp sle i8 %nz, %xor2
39+
ret i1 %r
40+
}
41+
42+
; test for ~z > (~x ^ y)
43+
define i1 @test_xor3(i8 %x, i8 %y, i8 %z) {
44+
; CHECK-LABEL: @test_xor3(
45+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
46+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
47+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
48+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[TMP1]], [[Z:%.*]]
49+
; CHECK-NEXT: ret i1 [[R]]
50+
;
51+
%nz = xor i8 %z, -1
52+
%xor = xor i8 %x, -1
53+
call void @use.i8(i8 %xor)
54+
%xor2 = xor i8 %xor, %y
55+
%r = icmp sgt i8 %nz, %xor2
56+
ret i1 %r
57+
}
58+
59+
; tests for equality
60+
define i1 @test_xor_ne(i8 %x, i8 %y, i8 %z) {
61+
; CHECK-LABEL: @test_xor_ne(
62+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]]
63+
; CHECK-NEXT: [[R:%.*]] = icmp ne i8 [[TMP1]], [[Z:%.*]]
64+
; CHECK-NEXT: ret i1 [[R]]
65+
;
66+
%nz = xor i8 %z, -1
67+
%xor = xor i8 %y, -1
68+
%xor2 = xor i8 %xor, %x
69+
%r = icmp ne i8 %nz, %xor2
70+
ret i1 %r
71+
}
72+
73+
define i1 @test_xor_eq(i8 %x, i8 %y, i8 %z) {
74+
; CHECK-LABEL: @test_xor_eq(
75+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[Y:%.*]], [[X:%.*]]
76+
; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[TMP1]], [[Z:%.*]]
77+
; CHECK-NEXT: ret i1 [[R]]
78+
;
79+
%nz = xor i8 %z, -1
80+
%xor = xor i8 %y, -1
81+
%xor2 = xor i8 %xor, %x
82+
%r = icmp eq i8 %nz, %xor2
83+
ret i1 %r
84+
}
85+
86+
; other tests
87+
define i1 @test_xor4(i8 %x, i8 %y, i8 %z) {
88+
; CHECK-LABEL: @test_xor4(
89+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
90+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
91+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
92+
; CHECK-NEXT: [[R:%.*]] = icmp sle i8 [[TMP1]], [[Z:%.*]]
93+
; CHECK-NEXT: ret i1 [[R]]
94+
;
95+
%nz = xor i8 %z, -1
96+
%xor = xor i8 %x, -1
97+
call void @use.i8(i8 %xor)
98+
%xor2 = xor i8 %xor, %y
99+
%r = icmp sge i8 %xor2, %nz
100+
ret i1 %r
101+
}
102+
103+
define i1 @test_xor5(i8 %x, i8 %y, i8 %z) {
104+
; CHECK-LABEL: @test_xor5(
105+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
106+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
107+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
108+
; CHECK-NEXT: [[R:%.*]] = icmp ugt i8 [[TMP1]], [[Z:%.*]]
109+
; CHECK-NEXT: ret i1 [[R]]
110+
;
111+
%nz = xor i8 %z, -1
112+
%xor = xor i8 %x, -1
113+
call void @use.i8(i8 %xor)
114+
%xor2 = xor i8 %xor, %y
115+
%r = icmp ult i8 %xor2, %nz
116+
ret i1 %r
117+
}
118+
119+
define i1 @test_xor6(i8 %x, i8 %y, i8 %z) {
120+
; CHECK-LABEL: @test_xor6(
121+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
122+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
123+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
124+
; CHECK-NEXT: [[R:%.*]] = icmp uge i8 [[TMP1]], [[Z:%.*]]
125+
; CHECK-NEXT: ret i1 [[R]]
126+
;
127+
%nz = xor i8 %z, -1
128+
%xor = xor i8 %x, -1
129+
call void @use.i8(i8 %xor)
130+
%xor2 = xor i8 %xor, %y
131+
%r = icmp ule i8 %xor2, %nz
132+
ret i1 %r
133+
}
134+
135+
define i1 @test_xor7(i8 %x, i8 %y, i8 %z) {
136+
; CHECK-LABEL: @test_xor7(
137+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
138+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
139+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
140+
; CHECK-NEXT: [[R:%.*]] = icmp ult i8 [[TMP1]], [[Z:%.*]]
141+
; CHECK-NEXT: ret i1 [[R]]
142+
;
143+
%nz = xor i8 %z, -1
144+
%xor = xor i8 %x, -1
145+
call void @use.i8(i8 %xor)
146+
%xor2 = xor i8 %xor, %y
147+
%r = icmp ugt i8 %xor2, %nz
148+
ret i1 %r
149+
}
150+
151+
define i1 @test_xor8(i8 %x, i8 %y, i8 %z) {
152+
; CHECK-LABEL: @test_xor8(
153+
; CHECK-NEXT: [[XOR:%.*]] = xor i8 [[X:%.*]], -1
154+
; CHECK-NEXT: call void @use.i8(i8 [[XOR]])
155+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y:%.*]]
156+
; CHECK-NEXT: [[R:%.*]] = icmp ule i8 [[TMP1]], [[Z:%.*]]
157+
; CHECK-NEXT: ret i1 [[R]]
158+
;
159+
%nz = xor i8 %z, -1
160+
%xor = xor i8 %x, -1
161+
call void @use.i8(i8 %xor)
162+
%xor2 = xor i8 %xor, %y
163+
%r = icmp uge i8 %xor2, %nz
164+
ret i1 %r
165+
}
166+
167+
; test (~a ^ b) < ~a
168+
define i1 @test_slt_xor(i32 %x, i32 %y) {
169+
; CHECK-LABEL: @test_slt_xor(
170+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
171+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i32 [[TMP1]], [[X]]
172+
; CHECK-NEXT: ret i1 [[R]]
173+
;
174+
%xor1 = xor i32 %x, -1
175+
%xor2 = xor i32 %xor1, %y
176+
%r = icmp slt i32 %xor2, %xor1
177+
ret i1 %r
178+
}
179+
180+
; test (a ^ ~b) <= ~b
181+
define i1 @test_sle_xor(i32 %x, i32 %y) {
182+
; CHECK-LABEL: @test_sle_xor(
183+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[Y:%.*]], [[X:%.*]]
184+
; CHECK-NEXT: [[R:%.*]] = icmp sge i32 [[TMP1]], [[Y]]
185+
; CHECK-NEXT: ret i1 [[R]]
186+
;
187+
%xor1 = xor i32 %y, -1
188+
%xor2 = xor i32 %xor1, %x
189+
%r = icmp sle i32 %xor2, %xor1
190+
ret i1 %r
191+
}
192+
193+
; test ~a > (~a ^ b)
194+
define i1 @test_sgt_xor(i32 %x, i32 %y) {
195+
; CHECK-LABEL: @test_sgt_xor(
196+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
197+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP1]], [[X]]
198+
; CHECK-NEXT: ret i1 [[CMP]]
199+
;
200+
%xor1 = xor i32 %x, -1
201+
%xor2 = xor i32 %xor1, %y
202+
%cmp = icmp sgt i32 %xor2, %xor1
203+
ret i1 %cmp
204+
}
205+
206+
define i1 @test_sge_xor(i32 %x, i32 %y) {
207+
; CHECK-LABEL: @test_sge_xor(
208+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
209+
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[TMP1]], [[X]]
210+
; CHECK-NEXT: ret i1 [[CMP]]
211+
;
212+
%xor1 = xor i32 %x, -1
213+
%xor2 = xor i32 %xor1, %y
214+
%cmp = icmp sge i32 %xor2, %xor1
215+
ret i1 %cmp
216+
}
217+
218+
define i1 @test_ult_xor(i32 %x, i32 %y) {
219+
; CHECK-LABEL: @test_ult_xor(
220+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
221+
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[TMP1]], [[X]]
222+
; CHECK-NEXT: ret i1 [[CMP]]
223+
;
224+
%xor1 = xor i32 %x, -1
225+
%xor2 = xor i32 %xor1, %y
226+
%cmp = icmp ult i32 %xor2, %xor1
227+
ret i1 %cmp
228+
}
229+
230+
define i1 @test_ule_xor(i32 %x, i32 %y) {
231+
; CHECK-LABEL: @test_ule_xor(
232+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
233+
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[TMP1]], [[X]]
234+
; CHECK-NEXT: ret i1 [[CMP]]
235+
;
236+
%xor1 = xor i32 %x, -1
237+
%xor2 = xor i32 %xor1, %y
238+
%cmp = icmp ule i32 %xor2, %xor1
239+
ret i1 %cmp
240+
}
241+
242+
define i1 @test_ugt_xor(i32 %x, i32 %y) {
243+
; CHECK-LABEL: @test_ugt_xor(
244+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
245+
; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], [[X]]
246+
; CHECK-NEXT: ret i1 [[CMP]]
247+
;
248+
%xor1 = xor i32 %x, -1
249+
%xor2 = xor i32 %xor1, %y
250+
%cmp = icmp ugt i32 %xor2, %xor1
251+
ret i1 %cmp
252+
}
253+
254+
define i1 @test_uge_xor(i32 %x, i32 %y) {
255+
; CHECK-LABEL: @test_uge_xor(
256+
; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
257+
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[TMP1]], [[X]]
258+
; CHECK-NEXT: ret i1 [[CMP]]
259+
;
260+
%xor1 = xor i32 %x, -1
261+
%xor2 = xor i32 %xor1, %y
262+
%cmp = icmp uge i32 %xor2, %xor1
263+
ret i1 %cmp
264+
}
265+
266+
; Negative tests
267+
define i1 @test_xor1_nofold_multi_use(i8 %x, i8 %y, i8 %z) {
268+
; CHECK-LABEL: @test_xor1_nofold_multi_use(
269+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X:%.*]], [[Y:%.*]]
270+
; CHECK-NEXT: [[XOR2:%.*]] = xor i8 [[TMP1]], -1
271+
; CHECK-NEXT: call void @use.i8(i8 [[XOR2]])
272+
; CHECK-NEXT: [[NZ:%.*]] = xor i8 [[Z:%.*]], -1
273+
; CHECK-NEXT: call void @use.i8(i8 [[NZ]])
274+
; CHECK-NEXT: [[R:%.*]] = icmp sgt i8 [[TMP1]], [[Z]]
275+
; CHECK-NEXT: ret i1 [[R]]
276+
;
277+
%xor = xor i8 %x, -1
278+
%xor2 = xor i8 %xor, %y
279+
call void @use.i8(i8 %xor2)
280+
%nz = xor i8 %z, -1
281+
call void @use.i8(i8 %nz)
282+
%r = icmp slt i8 %xor2, %nz
283+
ret i1 %r
284+
}
6285

7286
define i1 @xor_uge(i8 %x, i8 %y) {
8287
; CHECK-LABEL: @xor_uge(

0 commit comments

Comments
 (0)