Skip to content

Commit 434c092

Browse files
committed
[InstCombine] Fold (x == A) || (x & -Pow2) == A + 1 into range check
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
1 parent 3d7db85 commit 434c092

File tree

2 files changed

+51
-28
lines changed

2 files changed

+51
-28
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: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3494,10 +3494,8 @@ define i1 @and_icmp_eq_with_binary_range_operands(i8 range(i8 0, 2) %x, i8 range
34943494

34953495
define i1 @or_icmp_eq_and_pow2(i32 %x) {
34963496
; CHECK-LABEL: @or_icmp_eq_and_pow2(
3497-
; CHECK-NEXT: [[ICMP1:%.*]] = icmp eq i32 [[X:%.*]], 127
3498-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32
3499-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 128
3500-
; CHECK-NEXT: [[RET:%.*]] = or i1 [[ICMP1]], [[ICMP2]]
3497+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127
3498+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 33
35013499
; CHECK-NEXT: ret i1 [[RET]]
35023500
;
35033501
%icmp1 = icmp eq i32 %x, 127
@@ -3509,10 +3507,8 @@ define i1 @or_icmp_eq_and_pow2(i32 %x) {
35093507

35103508
define i1 @or_icmp_eq_and_pow2_mask_equal_to_icmp(i32 %x) {
35113509
; CHECK-LABEL: @or_icmp_eq_and_pow2_mask_equal_to_icmp(
3512-
; CHECK-NEXT: [[ICMP1:%.*]] = icmp eq i32 [[X:%.*]], 63
3513-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -64
3514-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[AND]], 64
3515-
; CHECK-NEXT: [[RET:%.*]] = or i1 [[ICMP1]], [[ICMP2]]
3510+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -63
3511+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], 65
35163512
; CHECK-NEXT: ret i1 [[RET]]
35173513
;
35183514
%icmp1 = icmp eq i32 %x, 63
@@ -3524,10 +3520,8 @@ define i1 @or_icmp_eq_and_pow2_mask_equal_to_icmp(i32 %x) {
35243520

35253521
define i1 @and_icmp_ne_and_pow2(i32 %x) {
35263522
; CHECK-LABEL: @and_icmp_ne_and_pow2(
3527-
; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 127
3528-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -32
3529-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 128
3530-
; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3523+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160
3524+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33
35313525
; CHECK-NEXT: ret i1 [[RET]]
35323526
;
35333527
%icmp1 = icmp ne i32 %x, 127
@@ -3539,10 +3533,8 @@ define i1 @and_icmp_ne_and_pow2(i32 %x) {
35393533

35403534
define i1 @and_icmp_ne_and_pow2_mask_equal_to_icmp(i32 %x) {
35413535
; CHECK-LABEL: @and_icmp_ne_and_pow2_mask_equal_to_icmp(
3542-
; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[X:%.*]], 63
3543-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X]], -64
3544-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[AND]], 64
3545-
; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3536+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -128
3537+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -65
35463538
; CHECK-NEXT: ret i1 [[RET]]
35473539
;
35483540
%icmp1 = icmp ne i32 %x, 63
@@ -3554,10 +3546,8 @@ define i1 @and_icmp_ne_and_pow2_mask_equal_to_icmp(i32 %x) {
35543546

35553547
define i1 @or_icmp_eq_and_pow2_commute(i32 %x) {
35563548
; CHECK-LABEL: @or_icmp_eq_and_pow2_commute(
3557-
; CHECK-NEXT: [[ICMP1:%.*]] = and i32 [[X:%.*]], -32
3558-
; CHECK-NEXT: [[AND:%.*]] = icmp eq i32 [[ICMP1]], 128
3559-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp eq i32 [[X]], 127
3560-
; CHECK-NEXT: [[TMP4:%.*]] = or i1 [[AND]], [[ICMP2]]
3549+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -127
3550+
; CHECK-NEXT: [[TMP4:%.*]] = icmp ult i32 [[TMP1]], 33
35613551
; CHECK-NEXT: ret i1 [[TMP4]]
35623552
;
35633553
%and = and i32 %x, -32
@@ -3569,10 +3559,8 @@ define i1 @or_icmp_eq_and_pow2_commute(i32 %x) {
35693559

35703560
define i1 @and_icmp_ne_and_pow2_commute(i32 %x) {
35713561
; CHECK-LABEL: @and_icmp_ne_and_pow2_commute(
3572-
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], -32
3573-
; CHECK-NEXT: [[ICMP1:%.*]] = icmp ne i32 [[AND]], 128
3574-
; CHECK-NEXT: [[ICMP2:%.*]] = icmp ne i32 [[X]], 127
3575-
; CHECK-NEXT: [[RET:%.*]] = and i1 [[ICMP1]], [[ICMP2]]
3562+
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X:%.*]], -160
3563+
; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[TMP1]], -33
35763564
; CHECK-NEXT: ret i1 [[RET]]
35773565
;
35783566
%and = and i32 %x, -32

0 commit comments

Comments
 (0)