Skip to content

Commit a43c49f

Browse files
authored
[InstCombine] Fold (x == A) || (x & -Pow2) == A + 1 into range check (#153842)
Extends `foldAndOrOfICmpsUsingRanges` to recognize and fold idioms of the form `(x == A) || (x & -Pow2) == A + 1`, where A + 1 is aligned to the mask and A > |mask|. These patterns represent a special case of contiguous range checks and can be optimized into an addition and comparison. ```llvm define i1 @src(i32 %x) { %icmp1 = icmp eq i32 %x, 127 %and = and i32 %x, -32 %icmp2 = icmp eq i32 %and, 128 %ret = or i1 %icmp1, %icmp2 ret i1 %ret } define i1 @tgt(i32 %x) { %1 = add i32 %x, -127 %2 = icmp ult i32 %1, 33 ret i1 %2 } ``` The same can be done for a conjunction of inequalities, of the form `(x != A) && (x & -Pow2) != A + 1`. ```llvm define i1 @src(i32 %x) { %icmp1 = icmp ne i32 %x, 127 %and = and i32 %x, -32 %icmp2 = icmp ne i32 %and, 128 %result = and i1 %icmp1, %icmp2 ret i1 %result } define i1 @tgt(i32 %x) { %1 = add i32 %x, -160 %2 = icmp ult i32 %1, -33 ret i1 %2 } ``` Alive2 Proof: - Equality: https://alive2.llvm.org/ce/z/gwELqs - Inequality: https://alive2.llvm.org/ce/z/ZPdSDt Closes #152948.
1 parent 085d0b0 commit a43c49f

File tree

2 files changed

+234
-4
lines changed

2 files changed

+234
-4
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,16 +1338,51 @@ Value *InstCombinerImpl::foldAndOrOfICmpsUsingRanges(ICmpInst *ICmp1,
13381338
V2 = X;
13391339
}
13401340

1341+
// Look through and with a negative power of 2 mask on V1 or V2. This
1342+
// detects idioms of the form `(x == A) || ((x & Mask) == A + 1)` where A + 1
1343+
// is aligned to the mask and A + 1 >= |Mask|. This pattern corresponds to a
1344+
// contiguous range check, which can be folded into an addition and compare.
1345+
// The same applies for `(x != A) && ((x & Mask) != A + 1)`.
1346+
auto AreContiguousRangePredicates = [](CmpPredicate Pred1, CmpPredicate Pred2,
1347+
bool IsAnd) {
1348+
if (IsAnd)
1349+
return Pred1 == ICmpInst::ICMP_NE && Pred2 == ICmpInst::ICMP_NE;
1350+
return Pred1 == ICmpInst::ICMP_EQ && Pred2 == ICmpInst::ICMP_EQ;
1351+
};
1352+
const APInt *Mask1 = nullptr, *Mask2 = nullptr;
1353+
bool MatchedAnd1 = false, MatchedAnd2 = false;
1354+
if (V1 != V2 && AreContiguousRangePredicates(Pred1, Pred2, IsAnd)) {
1355+
Value *X;
1356+
if (match(V1, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask1)))) &&
1357+
C1->getBitWidth() == C2->getBitWidth() && *C1 == *C2 + 1 &&
1358+
C1->uge(Mask1->abs()) && C1->isPowerOf2()) {
1359+
MatchedAnd1 = true;
1360+
V1 = X;
1361+
}
1362+
if (match(V2, m_OneUse(m_And(m_Value(X), m_NegatedPower2(Mask2)))) &&
1363+
C1->getBitWidth() == C2->getBitWidth() && *C2 == *C1 + 1 &&
1364+
C2->uge(Mask2->abs()) && C2->isPowerOf2()) {
1365+
MatchedAnd2 = true;
1366+
V2 = X;
1367+
}
1368+
}
1369+
13411370
if (V1 != V2)
13421371
return nullptr;
13431372

1344-
ConstantRange CR1 = ConstantRange::makeExactICmpRegion(
1345-
IsAnd ? ICmpInst::getInverseCmpPredicate(Pred1) : Pred1, *C1);
1373+
ConstantRange CR1 =
1374+
MatchedAnd1
1375+
? ConstantRange(*C1, *C1 - *Mask1)
1376+
: ConstantRange::makeExactICmpRegion(
1377+
IsAnd ? ICmpInst::getInverseCmpPredicate(Pred1) : Pred1, *C1);
13461378
if (Offset1)
13471379
CR1 = CR1.subtract(*Offset1);
13481380

1349-
ConstantRange CR2 = ConstantRange::makeExactICmpRegion(
1350-
IsAnd ? ICmpInst::getInverseCmpPredicate(Pred2) : Pred2, *C2);
1381+
ConstantRange CR2 =
1382+
MatchedAnd2
1383+
? ConstantRange(*C2, *C2 - *Mask2)
1384+
: ConstantRange::makeExactICmpRegion(
1385+
IsAnd ? ICmpInst::getInverseCmpPredicate(Pred2) : Pred2, *C2);
13511386
if (Offset2)
13521387
CR2 = CR2.subtract(*Offset2);
13531388

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

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,3 +3491,198 @@ define i1 @and_icmp_eq_with_binary_range_operands(i8 range(i8 0, 2) %x, i8 range
34913491
%ret = and i1 %icmp1, %icmp2
34923492
ret i1 %ret
34933493
}
3494+
3495+
define i1 @or_icmp_eq_and_pow2(i32 %x) {
3496+
; CHECK-LABEL: @or_icmp_eq_and_pow2(
3497+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127
3498+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 33
3499+
; CHECK-NEXT: ret i1 [[RET]]
3500+
;
3501+
%icmp1 = icmp eq i32 %x, 127
3502+
%and = and i32 %x, -32
3503+
%icmp2 = icmp eq i32 %and, 128
3504+
%ret = or i1 %icmp1, %icmp2
3505+
ret i1 %ret
3506+
}
3507+
3508+
define i1 @or_icmp_eq_and_pow2_mask_equal_to_icmp(i32 %x) {
3509+
; CHECK-LABEL: @or_icmp_eq_and_pow2_mask_equal_to_icmp(
3510+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -63
3511+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 65
3512+
; CHECK-NEXT: ret i1 [[RET]]
3513+
;
3514+
%icmp1 = icmp eq i32 %x, 63
3515+
%and = and i32 %x, -64
3516+
%icmp2 = icmp eq i32 %and, 64
3517+
%ret = or i1 %icmp1, %icmp2
3518+
ret i1 %ret
3519+
}
3520+
3521+
define i1 @and_icmp_ne_and_pow2(i32 %x) {
3522+
; CHECK-LABEL: @and_icmp_ne_and_pow2(
3523+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160
3524+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33
3525+
; CHECK-NEXT: ret i1 [[RET]]
3526+
;
3527+
%icmp1 = icmp ne i32 %x, 127
3528+
%and = and i32 %x, -32
3529+
%icmp2 = icmp ne i32 %and, 128
3530+
%ret = and i1 %icmp1, %icmp2
3531+
ret i1 %ret
3532+
}
3533+
3534+
define i1 @and_icmp_ne_and_pow2_mask_equal_to_icmp(i32 %x) {
3535+
; CHECK-LABEL: @and_icmp_ne_and_pow2_mask_equal_to_icmp(
3536+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -128
3537+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -65
3538+
; CHECK-NEXT: ret i1 [[RET]]
3539+
;
3540+
%icmp1 = icmp ne i32 %x, 63
3541+
%and = and i32 %x, -64
3542+
%icmp2 = icmp ne i32 %and, 64
3543+
%ret = and i1 %icmp1, %icmp2
3544+
ret i1 %ret
3545+
}
3546+
3547+
define i1 @or_icmp_eq_and_pow2_commute(i32 %x) {
3548+
; CHECK-LABEL: @or_icmp_eq_and_pow2_commute(
3549+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127
3550+
; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i32 [[TMP1]], 33
3551+
; CHECK-NEXT: ret i1 [[TMP4]]
3552+
;
3553+
%and = and i32 %x, -32
3554+
%icmp1 = icmp eq i32 %and, 128
3555+
%icmp2 = icmp eq i32 %x, 127
3556+
%ret = or i1 %icmp1, %icmp2
3557+
ret i1 %ret
3558+
}
3559+
3560+
define i1 @and_icmp_ne_and_pow2_commute(i32 %x) {
3561+
; CHECK-LABEL: @and_icmp_ne_and_pow2_commute(
3562+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160
3563+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33
3564+
; CHECK-NEXT: ret i1 [[RET]]
3565+
;
3566+
%and = and i32 %x, -32
3567+
%icmp1 = icmp ne i32 %and, 128
3568+
%icmp2 = icmp ne i32 %x, 127
3569+
%ret = and i1 %icmp1, %icmp2
3570+
ret i1 %ret
3571+
}
3572+
3573+
define i1 @neg_or_icmp_eq_and_pow2_multi_use(i32 %x) {
3574+
; CHECK-LABEL: @neg_or_icmp_eq_and_pow2_multi_use(
3575+
; CHECK-NEXT: [[ICMP1:%.*]] = icmp eq i32 [[X:%.*]], 127
3576+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32
3577+
; CHECK-NEXT: call void @use32(i32 [[AND]])
3578+
; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 128
3579+
; CHECK-NEXT: [[RET:%.*]] = or i1 [[ICMP1]], [[ICMP2]]
3580+
; CHECK-NEXT: ret i1 [[RET]]
3581+
;
3582+
%icmp1 = icmp eq i32 %x, 127
3583+
%and = and i32 %x, -32
3584+
call void @use32(i32 %and)
3585+
%icmp2 = icmp eq i32 %and, 128
3586+
%ret = or i1 %icmp1, %icmp2
3587+
ret i1 %ret
3588+
}
3589+
3590+
define i1 @neg_and_icmp_eq_and_pow2(i32 %x) {
3591+
; CHECK-LABEL: @neg_and_icmp_eq_and_pow2(
3592+
; CHECK-NEXT: ret i1 false
3593+
;
3594+
%icmp1 = icmp eq i32 %x, 127
3595+
%and = and i32 %x, -32
3596+
%icmp2 = icmp eq i32 %and, 128
3597+
%ret = and i1 %icmp1, %icmp2
3598+
ret i1 %ret
3599+
}
3600+
3601+
define i1 @neg_or_icmp_eq_and_non_pow2_mask(i32 %x) {
3602+
; CHECK-LABEL: @neg_or_icmp_eq_and_non_pow2_mask(
3603+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 127
3604+
; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[X]], -33
3605+
; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 128
3606+
; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]]
3607+
; CHECK-NEXT: ret i1 [[TMP4]]
3608+
;
3609+
%icmp1 = icmp eq i32 %x, 127
3610+
%and = and i32 %x, -33
3611+
%icmp2 = icmp eq i32 %and, 128
3612+
%ret = or i1 %icmp1, %icmp2
3613+
ret i1 %ret
3614+
}
3615+
3616+
define i1 @neg_and_icmp_ne_and_non_pow2_icmp(i32 %x) {
3617+
; CHECK-LABEL: @neg_and_icmp_ne_and_non_pow2_icmp(
3618+
; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 127
3619+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -33
3620+
; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 128
3621+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3622+
; CHECK-NEXT: ret i1 [[RET]]
3623+
;
3624+
%icmp1 = icmp ne i32 %x, 127
3625+
%and = and i32 %x, -33
3626+
%icmp2 = icmp ne i32 %and, 128
3627+
%ret = and i1 %icmp1, %icmp2
3628+
ret i1 %ret
3629+
}
3630+
3631+
define i1 @neg_or_icmp_eq_and_const_less_than_mask(i32 %x) {
3632+
; CHECK-LABEL: @neg_or_icmp_eq_and_const_less_than_mask(
3633+
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X:%.*]], 15
3634+
; CHECK-NEXT: ret i1 [[TMP1]]
3635+
;
3636+
%icmp1 = icmp eq i32 %x, 15
3637+
%and = and i32 %x, -32
3638+
%icmp2 = icmp eq i32 %and, 16
3639+
%ret = or i1 %icmp1, %icmp2
3640+
ret i1 %ret
3641+
}
3642+
3643+
define i1 @neg_and_icmp_ne_and_pow2_disjoint(i32 %x) {
3644+
; CHECK-LABEL: @neg_and_icmp_ne_and_pow2_disjoint(
3645+
; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 126
3646+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32
3647+
; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 128
3648+
; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3649+
; CHECK-NEXT: ret i1 [[RET]]
3650+
;
3651+
%icmp1 = icmp ne i32 %x, 126
3652+
%and = and i32 %x, -32
3653+
%icmp2 = icmp ne i32 %and, 128
3654+
%ret = and i1 %icmp1, %icmp2
3655+
ret i1 %ret
3656+
}
3657+
3658+
define i1 @neg_or_icmp_eq_double_and_pow2(i32 %x) {
3659+
; CHECK-LABEL: @neg_or_icmp_eq_double_and_pow2(
3660+
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[X:%.*]], -16
3661+
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 64
3662+
; CHECK-NEXT: [[AND2:%.*]] = and i32 [[X]], -32
3663+
; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i32 [[AND2]], 128
3664+
; CHECK-NEXT: [[TMP5:%.*]] = or i1 [[TMP2]], [[TMP4]]
3665+
; CHECK-NEXT: ret i1 [[TMP5]]
3666+
;
3667+
%and1 = and i32 %x, -16
3668+
%icmp1 = icmp eq i32 %and1, 64
3669+
%and2 = and i32 %x, -32
3670+
%icmp2 = icmp eq i32 %and2, 128
3671+
%ret = or i1 %icmp1, %icmp2
3672+
ret i1 %ret
3673+
}
3674+
3675+
define i1 @neg_select_icmp_eq_and_pow2(i32 %x) {
3676+
; CHECK-LABEL: @neg_select_icmp_eq_and_pow2(
3677+
; CHECK-NEXT: [[ICMP1:%.*]] = icmp sgt i32 [[X:%.*]], 127
3678+
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32
3679+
; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 128
3680+
; CHECK-NEXT: [[TMP1:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3681+
; CHECK-NEXT: ret i1 [[TMP1]]
3682+
;
3683+
%icmp1 = icmp sgt i32 %x, 127
3684+
%and = and i32 %x, -32
3685+
%icmp2 = icmp eq i32 %and, 128
3686+
%1 = select i1 %icmp1, i1 %icmp2, i1 false
3687+
ret i1 %1
3688+
}

0 commit comments

Comments
 (0)