Skip to content

Commit 0d454d6

Browse files
authored
[InstCombine] Fold xor of icmps using range information (#76334)
This patch folds xor of icmps into a single comparison using range-based reasoning as `foldAndOrOfICmpsUsingRanges` does. Fixes #70928.
1 parent 422b67a commit 0d454d6

File tree

3 files changed

+182
-21
lines changed

3 files changed

+182
-21
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3956,35 +3956,50 @@ Value *InstCombinerImpl::foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS,
39563956
const APInt *LC, *RC;
39573957
if (match(LHS1, m_APInt(LC)) && match(RHS1, m_APInt(RC)) &&
39583958
LHS0->getType() == RHS0->getType() &&
3959-
LHS0->getType()->isIntOrIntVectorTy() &&
3960-
(LHS->hasOneUse() || RHS->hasOneUse())) {
3959+
LHS0->getType()->isIntOrIntVectorTy()) {
39613960
// Convert xor of signbit tests to signbit test of xor'd values:
39623961
// (X > -1) ^ (Y > -1) --> (X ^ Y) < 0
39633962
// (X < 0) ^ (Y < 0) --> (X ^ Y) < 0
39643963
// (X > -1) ^ (Y < 0) --> (X ^ Y) > -1
39653964
// (X < 0) ^ (Y > -1) --> (X ^ Y) > -1
39663965
bool TrueIfSignedL, TrueIfSignedR;
3967-
if (isSignBitCheck(PredL, *LC, TrueIfSignedL) &&
3966+
if ((LHS->hasOneUse() || RHS->hasOneUse()) &&
3967+
isSignBitCheck(PredL, *LC, TrueIfSignedL) &&
39683968
isSignBitCheck(PredR, *RC, TrueIfSignedR)) {
39693969
Value *XorLR = Builder.CreateXor(LHS0, RHS0);
39703970
return TrueIfSignedL == TrueIfSignedR ? Builder.CreateIsNeg(XorLR) :
39713971
Builder.CreateIsNotNeg(XorLR);
39723972
}
39733973

3974-
// (X > C) ^ (X < C + 2) --> X != C + 1
3975-
// (X < C + 2) ^ (X > C) --> X != C + 1
3976-
// Considering the correctness of this pattern, we should avoid that C is
3977-
// non-negative and C + 2 is negative, although it will be matched by other
3978-
// patterns.
3979-
const APInt *C1, *C2;
3980-
if ((PredL == CmpInst::ICMP_SGT && match(LHS1, m_APInt(C1)) &&
3981-
PredR == CmpInst::ICMP_SLT && match(RHS1, m_APInt(C2))) ||
3982-
(PredL == CmpInst::ICMP_SLT && match(LHS1, m_APInt(C2)) &&
3983-
PredR == CmpInst::ICMP_SGT && match(RHS1, m_APInt(C1))))
3984-
if (LHS0 == RHS0 && *C1 + 2 == *C2 &&
3985-
(C1->isNegative() || C2->isNonNegative()))
3986-
return Builder.CreateICmpNE(LHS0,
3987-
ConstantInt::get(LHS0->getType(), *C1 + 1));
3974+
// Fold (icmp pred1 X, C1) ^ (icmp pred2 X, C2)
3975+
// into a single comparison using range-based reasoning.
3976+
if (LHS0 == RHS0) {
3977+
ConstantRange CR1 = ConstantRange::makeExactICmpRegion(PredL, *LC);
3978+
ConstantRange CR2 = ConstantRange::makeExactICmpRegion(PredR, *RC);
3979+
auto CRUnion = CR1.exactUnionWith(CR2);
3980+
auto CRIntersect = CR1.exactIntersectWith(CR2);
3981+
if (CRUnion && CRIntersect)
3982+
if (auto CR = CRUnion->exactIntersectWith(CRIntersect->inverse())) {
3983+
if (CR->isFullSet())
3984+
return ConstantInt::getTrue(I.getType());
3985+
if (CR->isEmptySet())
3986+
return ConstantInt::getFalse(I.getType());
3987+
3988+
CmpInst::Predicate NewPred;
3989+
APInt NewC, Offset;
3990+
CR->getEquivalentICmp(NewPred, NewC, Offset);
3991+
3992+
if ((Offset.isZero() && (LHS->hasOneUse() || RHS->hasOneUse())) ||
3993+
(LHS->hasOneUse() && RHS->hasOneUse())) {
3994+
Value *NewV = LHS0;
3995+
Type *Ty = LHS0->getType();
3996+
if (!Offset.isZero())
3997+
NewV = Builder.CreateAdd(NewV, ConstantInt::get(Ty, Offset));
3998+
return Builder.CreateICmp(NewPred, NewV,
3999+
ConstantInt::get(Ty, NewC));
4000+
}
4001+
}
4002+
}
39884003
}
39894004

39904005
// Instead of trying to imitate the folds for and/or, decompose this 'xor'

llvm/test/Transforms/InstCombine/and-or-icmps.ll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,10 +3015,8 @@ define i32 @icmp_x_slt_0_and_icmp_y_sgt_neg1_i32_fail(i32 %x, i32 %y) {
30153015

30163016
define i32 @icmp_slt_0_xor_icmp_sge_neg2_i32_fail(i32 %x) {
30173017
; CHECK-LABEL: @icmp_slt_0_xor_icmp_sge_neg2_i32_fail(
3018-
; CHECK-NEXT: [[A:%.*]] = icmp sgt i32 [[X:%.*]], -3
3019-
; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[X]], 0
3020-
; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[TMP1]], [[A]]
3021-
; CHECK-NEXT: [[D:%.*]] = zext i1 [[TMP2]] to i32
3018+
; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i32 [[X:%.*]], -2
3019+
; CHECK-NEXT: [[D:%.*]] = zext i1 [[TMP1]] to i32
30223020
; CHECK-NEXT: ret i32 [[D]]
30233021
;
30243022
%A = icmp sge i32 %x, -2

llvm/test/Transforms/InstCombine/xor-icmps.ll

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,151 @@ define i1 @xor_icmp_ptr(ptr %c, ptr %d) {
171171
ret i1 %xor
172172
}
173173

174+
; Tests from PR70928
175+
define i1 @xor_icmp_true_signed(i32 %a) {
176+
; CHECK-LABEL: @xor_icmp_true_signed(
177+
; CHECK-NEXT: ret i1 true
178+
;
179+
%cmp = icmp sgt i32 %a, 5
180+
%cmp1 = icmp slt i32 %a, 6
181+
%cmp3 = xor i1 %cmp, %cmp1
182+
ret i1 %cmp3
183+
}
184+
define i1 @xor_icmp_true_signed_multiuse1(i32 %a) {
185+
; CHECK-LABEL: @xor_icmp_true_signed_multiuse1(
186+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 5
187+
; CHECK-NEXT: call void @use(i1 [[CMP]])
188+
; CHECK-NEXT: ret i1 true
189+
;
190+
%cmp = icmp sgt i32 %a, 5
191+
call void @use(i1 %cmp)
192+
%cmp1 = icmp slt i32 %a, 6
193+
%cmp3 = xor i1 %cmp, %cmp1
194+
ret i1 %cmp3
195+
}
196+
define i1 @xor_icmp_true_signed_multiuse2(i32 %a) {
197+
; CHECK-LABEL: @xor_icmp_true_signed_multiuse2(
198+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 5
199+
; CHECK-NEXT: call void @use(i1 [[CMP]])
200+
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[A]], 6
201+
; CHECK-NEXT: call void @use(i1 [[CMP1]])
202+
; CHECK-NEXT: ret i1 true
203+
;
204+
%cmp = icmp sgt i32 %a, 5
205+
call void @use(i1 %cmp)
206+
%cmp1 = icmp slt i32 %a, 6
207+
call void @use(i1 %cmp1)
208+
%cmp3 = xor i1 %cmp, %cmp1
209+
ret i1 %cmp3
210+
}
211+
define i1 @xor_icmp_true_signed_commuted(i32 %a) {
212+
; CHECK-LABEL: @xor_icmp_true_signed_commuted(
213+
; CHECK-NEXT: ret i1 true
214+
;
215+
%cmp = icmp sgt i32 %a, 5
216+
%cmp1 = icmp slt i32 %a, 6
217+
%cmp3 = xor i1 %cmp1, %cmp
218+
ret i1 %cmp3
219+
}
220+
define i1 @xor_icmp_true_unsigned(i32 %a) {
221+
; CHECK-LABEL: @xor_icmp_true_unsigned(
222+
; CHECK-NEXT: ret i1 true
223+
;
224+
%cmp = icmp ugt i32 %a, 5
225+
%cmp1 = icmp ult i32 %a, 6
226+
%cmp3 = xor i1 %cmp, %cmp1
227+
ret i1 %cmp3
228+
}
229+
define i1 @xor_icmp_to_ne(i32 %a) {
230+
; CHECK-LABEL: @xor_icmp_to_ne(
231+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i32 [[A:%.*]], 5
232+
; CHECK-NEXT: ret i1 [[CMP3]]
233+
;
234+
%cmp = icmp sgt i32 %a, 4
235+
%cmp1 = icmp slt i32 %a, 6
236+
%cmp3 = xor i1 %cmp, %cmp1
237+
ret i1 %cmp3
238+
}
239+
define i1 @xor_icmp_to_ne_multiuse1(i32 %a) {
240+
; CHECK-LABEL: @xor_icmp_to_ne_multiuse1(
241+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 4
242+
; CHECK-NEXT: call void @use(i1 [[CMP]])
243+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ne i32 [[A]], 5
244+
; CHECK-NEXT: ret i1 [[CMP3]]
245+
;
246+
%cmp = icmp sgt i32 %a, 4
247+
call void @use(i1 %cmp)
248+
%cmp1 = icmp slt i32 %a, 6
249+
%cmp3 = xor i1 %cmp, %cmp1
250+
ret i1 %cmp3
251+
}
252+
define i1 @xor_icmp_to_icmp_add(i32 %a) {
253+
; CHECK-LABEL: @xor_icmp_to_icmp_add(
254+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[A:%.*]], -6
255+
; CHECK-NEXT: [[CMP3:%.*]] = icmp ult i32 [[TMP1]], -2
256+
; CHECK-NEXT: ret i1 [[CMP3]]
257+
;
258+
%cmp = icmp sgt i32 %a, 3
259+
%cmp1 = icmp slt i32 %a, 6
260+
%cmp3 = xor i1 %cmp, %cmp1
261+
ret i1 %cmp3
262+
}
263+
; Negative tests
264+
; The result of ConstantRange::difference is not exact.
265+
define i1 @xor_icmp_invalid_range(i8 %x0) {
266+
; CHECK-LABEL: @xor_icmp_invalid_range(
267+
; CHECK-NEXT: [[TMP1:%.*]] = and i8 [[X0:%.*]], -5
268+
; CHECK-NEXT: [[OR_COND:%.*]] = icmp ne i8 [[TMP1]], 0
269+
; CHECK-NEXT: ret i1 [[OR_COND]]
270+
;
271+
%cmp = icmp eq i8 %x0, 0
272+
%cmp4 = icmp ne i8 %x0, 4
273+
%or.cond = xor i1 %cmp, %cmp4
274+
ret i1 %or.cond
275+
}
276+
define i1 @xor_icmp_to_ne_multiuse2(i32 %a) {
277+
; CHECK-LABEL: @xor_icmp_to_ne_multiuse2(
278+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 4
279+
; CHECK-NEXT: call void @use(i1 [[CMP]])
280+
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[A]], 6
281+
; CHECK-NEXT: call void @use(i1 [[CMP1]])
282+
; CHECK-NEXT: [[CMP3:%.*]] = xor i1 [[CMP]], [[CMP1]]
283+
; CHECK-NEXT: ret i1 [[CMP3]]
284+
;
285+
%cmp = icmp sgt i32 %a, 4
286+
call void @use(i1 %cmp)
287+
%cmp1 = icmp slt i32 %a, 6
288+
call void @use(i1 %cmp1)
289+
%cmp3 = xor i1 %cmp, %cmp1
290+
ret i1 %cmp3
291+
}
292+
define i1 @xor_icmp_to_icmp_add_multiuse1(i32 %a) {
293+
; CHECK-LABEL: @xor_icmp_to_icmp_add_multiuse1(
294+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 3
295+
; CHECK-NEXT: call void @use(i1 [[CMP]])
296+
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[A]], 6
297+
; CHECK-NEXT: [[CMP3:%.*]] = xor i1 [[CMP]], [[CMP1]]
298+
; CHECK-NEXT: ret i1 [[CMP3]]
299+
;
300+
%cmp = icmp sgt i32 %a, 3
301+
call void @use(i1 %cmp)
302+
%cmp1 = icmp slt i32 %a, 6
303+
%cmp3 = xor i1 %cmp, %cmp1
304+
ret i1 %cmp3
305+
}
306+
define i1 @xor_icmp_to_icmp_add_multiuse2(i32 %a) {
307+
; CHECK-LABEL: @xor_icmp_to_icmp_add_multiuse2(
308+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], 3
309+
; CHECK-NEXT: call void @use(i1 [[CMP]])
310+
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[A]], 6
311+
; CHECK-NEXT: call void @use(i1 [[CMP1]])
312+
; CHECK-NEXT: [[CMP3:%.*]] = xor i1 [[CMP]], [[CMP1]]
313+
; CHECK-NEXT: ret i1 [[CMP3]]
314+
;
315+
%cmp = icmp sgt i32 %a, 3
316+
call void @use(i1 %cmp)
317+
%cmp1 = icmp slt i32 %a, 6
318+
call void @use(i1 %cmp1)
319+
%cmp3 = xor i1 %cmp, %cmp1
320+
ret i1 %cmp3
321+
}

0 commit comments

Comments
 (0)