[llvm] [llvm] [InstCombine] fold "icmp eq (X + (V - 1)) & -V, X" to "icmp eq (and X, V - 1), 0" (PR #152851)
Pavel Skripkin via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 13 14:00:08 PDT 2025
https://github.com/pskrgag updated https://github.com/llvm/llvm-project/pull/152851
>From d8b5d15f61d601be1381cb9fd38df6a1885f0de2 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sat, 9 Aug 2025 00:28:26 +0300
Subject: [PATCH 01/13] fold "icmp eq (num + (val - 1)) & -val, num" to "icmp
eq 0, (and num, val - 1)"
---
.../InstCombine/InstCombineCompares.cpp | 64 +++++++++++++
.../InstCombine/InstCombineInternal.h | 1 +
llvm/test/Transforms/InstCombine/icmp-add.ll | 90 +++++++++++++++++++
3 files changed, 155 insertions(+)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index cf94d28100488..722b03eb53f06 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1320,6 +1320,67 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
return nullptr;
}
+// Fold icmp eq (num + (val - 1)) & -val, num
+// to
+// icmp eq 0, (and num, val - 1)
+// For value being power of two
+Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
+ Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1);
+ Value *Neg, *Add, *Num, *Mask, *Value;
+ CmpInst::Predicate Pred = Cmp.getPredicate();
+ const APInt *NegConst, *MaskConst, *NumCost;
+
+ if (Pred != ICmpInst::ICMP_EQ)
+ return nullptr;
+
+ // Match num + neg
+ if (!match(Op0, m_And(m_Value(Add), m_Value(Neg))))
+ return nullptr;
+
+ // Match num & mask
+ if (!match(Add, m_Add(m_Value(Num), m_Value(Mask))))
+ return nullptr;
+
+ // Check the constant case
+ if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) {
+ // Mask + 1 should be a power-of-two
+ if (!(*MaskConst + 1).isPowerOf2())
+ return nullptr;
+
+ // Neg = -(Mask + 1)
+ if (*NegConst != -(*MaskConst + 1))
+ return nullptr;
+ } else {
+ // Match neg = sub 0, val
+ if (!match(Neg, m_Sub(m_Zero(), m_Value(Value))))
+ return nullptr;
+
+ // mask = %val - 1, which can be represented as sub %val, 1 or add %val, -1
+ if (!match(Mask, m_Add(m_Value(Value), m_AllOnes())) &&
+ !match(Mask, m_Sub(m_Value(Value), m_One())))
+ return nullptr;
+
+ // Value should be a known power-of-two.
+ if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp))
+ return nullptr;
+ }
+
+ // Guard against weird special-case where Op1 gets optimized to constant. Leave it constant
+ // fonder.
+ if (match(Op1, m_APInt(NumCost)))
+ return nullptr;
+
+ if (!match(Op1, m_Value(Num)))
+ return nullptr;
+
+ // Create new icmp eq (num & (val - 1)), 0
+ auto NewAnd = Builder.CreateAnd(Num, Mask);
+ auto Zero = llvm::Constant::getNullValue(Num->getType());
+ auto ICmp = Builder.CreateICmp(CmpInst::ICMP_EQ, NewAnd, Zero);
+
+ return replaceInstUsesWith(Cmp, ICmp);
+}
+
/// Fold icmp Pred X, C.
/// TODO: This code structure does not make sense. The saturating add fold
/// should be moved to some other helper and extended as noted below (it is also
@@ -7644,6 +7705,9 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
if (Instruction *Res = foldICmpUsingKnownBits(I))
return Res;
+ if (Instruction *Res = foldNextMultiply(I))
+ return Res;
+
// Test if the ICmpInst instruction is used exclusively by a select as
// part of a minimum or maximum operation. If so, refrain from doing
// any other folding. This helps out other analyses which understand
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index c67e27e5b3e7c..5f83cb1b9ae28 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -721,6 +721,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp);
Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp);
Instruction *foldICmpWithConstant(ICmpInst &Cmp);
+ Instruction *foldNextMultiply(ICmpInst &Cmp);
Instruction *foldICmpUsingBoolRange(ICmpInst &I);
Instruction *foldICmpInstWithConstant(ICmpInst &Cmp);
Instruction *foldICmpInstWithConstantNotInt(ICmpInst &Cmp);
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 1a41c1f3e1045..698619ab8aad1 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3300,3 +3300,93 @@ entry:
%cmp = icmp ult i32 %add, 253
ret i1 %cmp
}
+
+define i1 @val_is_aligend_sub(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_sub(
+; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
+; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
+; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
+ %pow = icmp eq i32 %1, 1
+ call void @llvm.assume(i1 %pow)
+
+ %mask = sub i32 %val, 1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %num, %mask
+ %_2.sroa.0.0 = and i32 %num.biased, %neg
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_add(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_add(
+; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
+; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
+; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
+ %pow = icmp eq i32 %1, 1
+ call void @llvm.assume(i1 %pow)
+
+ %mask = add i32 %val, -1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %num, %mask
+ %_2.sroa.0.0 = and i32 %num.biased, %neg
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %_2.sroa.0.0 = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+; Should not work for non-power-of-two cases
+define i1 @val_is_aligend_const_non-pow2(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_non-pow2(
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 6
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -7
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 6
+ %_2.sroa.0.0 = and i32 %num.biased, -7
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_non_pow(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_non_pow(
+; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL:%.*]], -1
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %mask = add i32 %val, -1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %num, %mask
+ %_2.sroa.0.0 = and i32 %num.biased, %neg
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
>From 93fd0c6df54831cd55bf2fd76e72c1f33bc9b882 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sat, 9 Aug 2025 15:09:47 +0300
Subject: [PATCH 02/13] coding style...
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 722b03eb53f06..d30a67c5b0738 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1365,8 +1365,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
return nullptr;
}
- // Guard against weird special-case where Op1 gets optimized to constant. Leave it constant
- // fonder.
+ // Guard against weird special-case where Op1 gets optimized to constant.
+ // Leave it constant fonder.
if (match(Op1, m_APInt(NumCost)))
return nullptr;
>From 914dd1f90e2a069afc52b66db6f597bde9117e43 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sat, 9 Aug 2025 22:07:01 +0300
Subject: [PATCH 03/13] address review comments
---
.../InstCombine/InstCombineCompares.cpp | 30 +++++++----
llvm/test/Transforms/InstCombine/icmp-add.ll | 54 +++++++++++++++++--
2 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index d30a67c5b0738..6adfbcd11ff91 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1330,16 +1330,21 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
CmpInst::Predicate Pred = Cmp.getPredicate();
const APInt *NegConst, *MaskConst, *NumCost;
- if (Pred != ICmpInst::ICMP_EQ)
+ if (!ICmpInst::isEquality(Pred))
return nullptr;
// Match num + neg
- if (!match(Op0, m_And(m_Value(Add), m_Value(Neg))))
+ if (!match(Op0, m_c_And(m_Value(Add), m_Value(Neg))))
return nullptr;
- // Match num & mask
- if (!match(Add, m_Add(m_Value(Num), m_Value(Mask))))
- return nullptr;
+ // Match num & mask and handle commutative care
+ if (!match(Add, m_c_Add(m_Value(Num), m_Value(Mask)))) {
+ if (match(Neg, m_c_Add(m_Value(Num), m_Value(Mask)))) {
+ std::swap(Add, Neg);
+ } else {
+ return nullptr;
+ }
+ }
// Check the constant case
if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) {
@@ -1355,10 +1360,14 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
if (!match(Neg, m_Sub(m_Zero(), m_Value(Value))))
return nullptr;
- // mask = %val - 1, which can be represented as sub %val, 1 or add %val, -1
- if (!match(Mask, m_Add(m_Value(Value), m_AllOnes())) &&
- !match(Mask, m_Sub(m_Value(Value), m_One())))
- return nullptr;
+ // mask = add %val, -1. No commutative here, since it's canonical representation for sub %val, -1
+ if (!match(Mask, m_Add(m_Value(Value), m_AllOnes()))) {
+ if (match(Num, m_Add(m_Value(Value), m_AllOnes()))) {
+ std::swap(Mask, Num);
+ } else {
+ return nullptr;
+ }
+ }
// Value should be a known power-of-two.
if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp))
@@ -1376,9 +1385,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
// Create new icmp eq (num & (val - 1)), 0
auto NewAnd = Builder.CreateAnd(Num, Mask);
auto Zero = llvm::Constant::getNullValue(Num->getType());
- auto ICmp = Builder.CreateICmp(CmpInst::ICMP_EQ, NewAnd, Zero);
- return replaceInstUsesWith(Cmp, ICmp);
+ return new ICmpInst(Pred, NewAnd, Zero);
}
/// Fold icmp Pred X, C.
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 698619ab8aad1..6b0cc07538755 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3307,8 +3307,8 @@ define i1 @val_is_aligend_sub(i32 %num, i32 %val) {
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
@@ -3330,8 +3330,8 @@ define i1 @val_is_aligend_add(i32 %num, i32 %val) {
; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED:%.*]], [[NEG]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
; CHECK-NEXT: ret i1 [[_0]]
;
%1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
@@ -3347,6 +3347,52 @@ define i1 @val_is_aligend_add(i32 %num, i32 %val) {
ret i1 %_0
}
+define i1 @val_is_aligend_add_commute_add(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_add_commute_add(
+; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
+; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
+; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
+ %pow = icmp eq i32 %1, 1
+ call void @llvm.assume(i1 %pow)
+
+ %mask = add i32 %val, -1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %mask, %num
+ %_2.sroa.0.0 = and i32 %num.biased, %neg
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_add_commute_and(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_add_commute_and(
+; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
+; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
+; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
+ %pow = icmp eq i32 %1, 1
+ call void @llvm.assume(i1 %pow)
+
+ %mask = add i32 %val, -1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %mask, %num
+ %_2.sroa.0.0 = and i32 %neg, %num.biased
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
define i1 @val_is_aligend_const_pow2(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
>From fa89f888d571d2811546e32094633dad2401b472 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sat, 9 Aug 2025 22:17:18 +0300
Subject: [PATCH 04/13] coding style....
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 6adfbcd11ff91..569c4dd513d6d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1360,7 +1360,8 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
if (!match(Neg, m_Sub(m_Zero(), m_Value(Value))))
return nullptr;
- // mask = add %val, -1. No commutative here, since it's canonical representation for sub %val, -1
+ // mask = add %val, -1. No commutative here, since it's canonical
+ // representation for sub %val, -1
if (!match(Mask, m_Add(m_Value(Value), m_AllOnes()))) {
if (match(Num, m_Add(m_Value(Value), m_AllOnes()))) {
std::swap(Mask, Num);
>From 25aec6bffd7bd4d873a70509ff7a21441e957030 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sun, 10 Aug 2025 16:38:12 +0300
Subject: [PATCH 05/13] review
---
.../InstCombine/InstCombineCompares.cpp | 27 +++-----
llvm/test/Transforms/InstCombine/icmp-add.ll | 65 ++++++++++++++++++-
2 files changed, 72 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 569c4dd513d6d..ff4334739b520 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1325,26 +1325,18 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// icmp eq 0, (and num, val - 1)
// For value being power of two
Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
- Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1);
+ Value *Op0 = Cmp.getOperand(0);
Value *Neg, *Add, *Num, *Mask, *Value;
- CmpInst::Predicate Pred = Cmp.getPredicate();
- const APInt *NegConst, *MaskConst, *NumCost;
-
- if (!ICmpInst::isEquality(Pred))
- return nullptr;
+ CmpPredicate Pred;
+ const APInt *NegConst, *MaskConst;
// Match num + neg
- if (!match(Op0, m_c_And(m_Value(Add), m_Value(Neg))))
+ if (!match(Op0, m_OneUse(m_c_And(m_Value(Add), m_Value(Neg)))))
return nullptr;
// Match num & mask and handle commutative care
- if (!match(Add, m_c_Add(m_Value(Num), m_Value(Mask)))) {
- if (match(Neg, m_c_Add(m_Value(Num), m_Value(Mask)))) {
- std::swap(Add, Neg);
- } else {
- return nullptr;
- }
- }
+ if (!match(Op0, m_c_And(m_c_Add(m_Value(Num), m_Value(Mask)), m_Value(Neg))))
+ return nullptr;
// Check the constant case
if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) {
@@ -1375,12 +1367,11 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
return nullptr;
}
- // Guard against weird special-case where Op1 gets optimized to constant.
- // Leave it constant fonder.
- if (match(Op1, m_APInt(NumCost)))
+ // Verify that Add and Num are connected by ICmp.
+ if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Add), m_Specific(Num))))
return nullptr;
- if (!match(Op1, m_Value(Num)))
+ if (!ICmpInst::isEquality(Pred))
return nullptr;
// Create new icmp eq (num & (val - 1)), 0
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 6b0cc07538755..b8dacd957167f 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3301,6 +3301,8 @@ entry:
ret i1 %cmp
}
+; PR 152851
+
define i1 @val_is_aligend_sub(i32 %num, i32 %val) {
; CHECK-LABEL: @val_is_aligend_sub(
; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
@@ -3406,8 +3408,8 @@ define i1 @val_is_aligend_const_pow2(i32 %num) {
}
; Should not work for non-power-of-two cases
-define i1 @val_is_aligend_const_non-pow2(i32 %num) {
-; CHECK-LABEL: @val_is_aligend_const_non-pow2(
+define i1 @val_is_aligend_const_non_pow2(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_non_pow2(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 6
; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -7
; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
@@ -3436,3 +3438,62 @@ define i1 @val_is_aligend_non_pow(i32 %num, i32 %val) {
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}
+
+define i1 @val_is_aligend_const_pow2_multiuse(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse(
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
+; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %_2.sroa.0.0 = and i32 %num.biased, -4096
+ call void @use(i32 %_2.sroa.0.0)
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse1(
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: call void @use(i32 [[NUM_BIASED]])
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ call void @use(i32 %num.biased)
+ %_2.sroa.0.0 = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_add_multiuse(i32 %num, i32 %val) {
+; CHECK-LABEL: @val_is_aligend_add_multiuse(
+; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
+; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
+; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
+; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
+ %pow = icmp eq i32 %1, 1
+ call void @llvm.assume(i1 %pow)
+
+ %mask = add i32 %val, -1
+ %neg = sub nsw i32 0, %val
+
+ %num.biased = add i32 %num, %mask
+ %_2.sroa.0.0 = and i32 %num.biased, %neg
+
+ call void @use(i32 %_2.sroa.0.0)
+
+ %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ ret i1 %_0
+}
>From b66f1280c958a6cc161760f45a86e0e25c0974bc Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sun, 10 Aug 2025 20:02:13 +0300
Subject: [PATCH 06/13] learning llvm...
---
.../InstCombine/InstCombineCompares.cpp | 29 +++++++------------
.../InstCombine/InstCombineInternal.h | 2 +-
llvm/test/Transforms/InstCombine/icmp-add.ll | 5 ++--
3 files changed, 15 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index ff4334739b520..c15c3b4c0d2ee 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1324,26 +1324,23 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// to
// icmp eq 0, (and num, val - 1)
// For value being power of two
-Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
- Value *Op0 = Cmp.getOperand(0);
- Value *Neg, *Add, *Num, *Mask, *Value;
+Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
+ Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1);
+ Value *Neg, *Num, *Mask, *Value;
CmpPredicate Pred;
const APInt *NegConst, *MaskConst;
- // Match num + neg
- if (!match(Op0, m_OneUse(m_c_And(m_Value(Add), m_Value(Neg)))))
+ if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Num),
+ m_OneUse(m_c_And(
+ m_OneUse(m_c_Add(m_Value(Num), m_Value(Mask))),
+ m_Value(Neg))))))
return nullptr;
- // Match num & mask and handle commutative care
- if (!match(Op0, m_c_And(m_c_Add(m_Value(Num), m_Value(Mask)), m_Value(Neg))))
+ if (!ICmpInst::isEquality(Pred))
return nullptr;
// Check the constant case
- if (match(Neg, m_APInt(NegConst)) && match(Mask, m_APInt(MaskConst))) {
- // Mask + 1 should be a power-of-two
- if (!(*MaskConst + 1).isPowerOf2())
- return nullptr;
-
+ if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) {
// Neg = -(Mask + 1)
if (*NegConst != -(*MaskConst + 1))
return nullptr;
@@ -1367,11 +1364,7 @@ Instruction *InstCombinerImpl::foldNextMultiply(ICmpInst &Cmp) {
return nullptr;
}
- // Verify that Add and Num are connected by ICmp.
- if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Add), m_Specific(Num))))
- return nullptr;
-
- if (!ICmpInst::isEquality(Pred))
+ if (!match(Op0, m_Specific(Num)) && !match(Op1, m_Specific(Num)))
return nullptr;
// Create new icmp eq (num & (val - 1)), 0
@@ -7705,7 +7698,7 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
if (Instruction *Res = foldICmpUsingKnownBits(I))
return Res;
- if (Instruction *Res = foldNextMultiply(I))
+ if (Instruction *Res = foldIsMultipleOfAPowerOfTwo(I))
return Res;
// Test if the ICmpInst instruction is used exclusively by a select as
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 5f83cb1b9ae28..2340028ce93dc 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -721,7 +721,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp);
Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp);
Instruction *foldICmpWithConstant(ICmpInst &Cmp);
- Instruction *foldNextMultiply(ICmpInst &Cmp);
+ Instruction *foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp);
Instruction *foldICmpUsingBoolRange(ICmpInst &I);
Instruction *foldICmpInstWithConstant(ICmpInst &Cmp);
Instruction *foldICmpInstWithConstantNotInt(ICmpInst &Cmp);
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index b8dacd957167f..0b597ebd1c16f 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3454,12 +3454,13 @@ define i1 @val_is_aligend_const_pow2_multiuse(i32 %num) {
ret i1 %_0
}
+; Applies since number of instructions do not change
define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse1(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
; CHECK-NEXT: call void @use(i32 [[NUM_BIASED]])
-; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM]], 4095
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
>From db1316ba0f75b504176327d943cbf4f5dce3be1d Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Sun, 10 Aug 2025 23:13:04 +0300
Subject: [PATCH 07/13] use m_Defereded instead of m_Specific
---
.../lib/Transforms/InstCombine/InstCombineCompares.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index c15c3b4c0d2ee..d0c5d89c0380d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1325,15 +1325,14 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// icmp eq 0, (and num, val - 1)
// For value being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
- Value *Op0 = Cmp.getOperand(0), *Op1 = Cmp.getOperand(1);
Value *Neg, *Num, *Mask, *Value;
CmpPredicate Pred;
const APInt *NegConst, *MaskConst;
if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Num),
- m_OneUse(m_c_And(
- m_OneUse(m_c_Add(m_Value(Num), m_Value(Mask))),
- m_Value(Neg))))))
+ m_OneUse(m_c_And(m_OneUse(m_c_Add(m_Deferred(Num),
+ m_Value(Mask))),
+ m_Value(Neg))))))
return nullptr;
if (!ICmpInst::isEquality(Pred))
@@ -1364,9 +1363,6 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
return nullptr;
}
- if (!match(Op0, m_Specific(Num)) && !match(Op1, m_Specific(Num)))
- return nullptr;
-
// Create new icmp eq (num & (val - 1)), 0
auto NewAnd = Builder.CreateAnd(Num, Mask);
auto Zero = llvm::Constant::getNullValue(Num->getType());
>From c72f7bbfa891cb5820aa81416ad97b1815af9cb1 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Mon, 11 Aug 2025 20:29:48 +0300
Subject: [PATCH 08/13] don't open code bitwise not
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index d0c5d89c0380d..732fb61f56309 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1341,7 +1341,7 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
// Check the constant case
if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) {
// Neg = -(Mask + 1)
- if (*NegConst != -(*MaskConst + 1))
+ if (*NegConst != ~*MaskConst)
return nullptr;
} else {
// Match neg = sub 0, val
>From 0e9e8e7141ea7a1255f13f23fd0ae6664973c813 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Mon, 11 Aug 2025 22:00:12 +0300
Subject: [PATCH 09/13] drop non-constant path as it happens to be
non-profitable
---
.../InstCombine/InstCombineCompares.cpp | 32 +---
llvm/test/Transforms/InstCombine/icmp-add.ll | 139 ------------------
2 files changed, 8 insertions(+), 163 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 732fb61f56309..6274417365f26 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1325,7 +1325,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// icmp eq 0, (and num, val - 1)
// For value being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
- Value *Neg, *Num, *Mask, *Value;
+ Value *Neg, *Num, *Mask;
CmpPredicate Pred;
const APInt *NegConst, *MaskConst;
@@ -1338,30 +1338,14 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
if (!ICmpInst::isEquality(Pred))
return nullptr;
- // Check the constant case
- if (match(Neg, m_APInt(NegConst)) && match(Mask, m_LowBitMask(MaskConst))) {
- // Neg = -(Mask + 1)
- if (*NegConst != ~*MaskConst)
- return nullptr;
- } else {
- // Match neg = sub 0, val
- if (!match(Neg, m_Sub(m_Zero(), m_Value(Value))))
- return nullptr;
-
- // mask = add %val, -1. No commutative here, since it's canonical
- // representation for sub %val, -1
- if (!match(Mask, m_Add(m_Value(Value), m_AllOnes()))) {
- if (match(Num, m_Add(m_Value(Value), m_AllOnes()))) {
- std::swap(Mask, Num);
- } else {
- return nullptr;
- }
- }
+ // Check only constant case, since it's the only profitable.
+ // See https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2657/
+ if (!match(Neg, m_APInt(NegConst)) || !match(Mask, m_LowBitMask(MaskConst)))
+ return nullptr;
- // Value should be a known power-of-two.
- if (!isKnownToBeAPowerOfTwo(Value, false, &Cmp))
- return nullptr;
- }
+ // Neg = -(Mask + 1)
+ if (*NegConst != ~*MaskConst)
+ return nullptr;
// Create new icmp eq (num & (val - 1)), 0
auto NewAnd = Builder.CreateAnd(Num, Mask);
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 0b597ebd1c16f..7b4650ec3e1b8 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3303,98 +3303,6 @@ entry:
; PR 152851
-define i1 @val_is_aligend_sub(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_sub(
-; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
-; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
-; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
- %pow = icmp eq i32 %1, 1
- call void @llvm.assume(i1 %pow)
-
- %mask = sub i32 %val, 1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %num, %mask
- %_2.sroa.0.0 = and i32 %num.biased, %neg
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
-
-define i1 @val_is_aligend_add(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_add(
-; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[NUM:%.*]])
-; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
-; CHECK-NEXT: [[NEG:%.*]] = add i32 [[NUM]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM1:%.*]], [[NEG]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
- %pow = icmp eq i32 %1, 1
- call void @llvm.assume(i1 %pow)
-
- %mask = add i32 %val, -1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %num, %mask
- %_2.sroa.0.0 = and i32 %num.biased, %neg
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
-
-define i1 @val_is_aligend_add_commute_add(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_add_commute_add(
-; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
-; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
-; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
- %pow = icmp eq i32 %1, 1
- call void @llvm.assume(i1 %pow)
-
- %mask = add i32 %val, -1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %mask, %num
- %_2.sroa.0.0 = and i32 %num.biased, %neg
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
-
-define i1 @val_is_aligend_add_commute_and(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_add_commute_and(
-; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
-; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
-; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = and i32 [[NUM:%.*]], [[MASK]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP2]], 0
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
- %pow = icmp eq i32 %1, 1
- call void @llvm.assume(i1 %pow)
-
- %mask = add i32 %val, -1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %mask, %num
- %_2.sroa.0.0 = and i32 %neg, %num.biased
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
-
define i1 @val_is_aligend_const_pow2(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2(
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
@@ -3421,24 +3329,6 @@ define i1 @val_is_aligend_const_non_pow2(i32 %num) {
ret i1 %_0
}
-define i1 @val_is_aligend_non_pow(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_non_pow(
-; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL:%.*]], -1
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
-; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %mask = add i32 %val, -1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %num, %mask
- %_2.sroa.0.0 = and i32 %num.biased, %neg
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
-
define i1 @val_is_aligend_const_pow2_multiuse(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
@@ -3469,32 +3359,3 @@ define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) {
%_0 = icmp eq i32 %_2.sroa.0.0, %num
ret i1 %_0
}
-
-define i1 @val_is_aligend_add_multiuse(i32 %num, i32 %val) {
-; CHECK-LABEL: @val_is_aligend_add_multiuse(
-; CHECK-NEXT: [[TMP1:%.*]] = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 [[VAL:%.*]])
-; CHECK-NEXT: [[POW:%.*]] = icmp eq i32 [[TMP1]], 1
-; CHECK-NEXT: call void @llvm.assume(i1 [[POW]])
-; CHECK-NEXT: [[MASK:%.*]] = add i32 [[VAL]], -1
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[VAL]]
-; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], [[MASK]]
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], [[NEG]]
-; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
-; CHECK-NEXT: ret i1 [[_0]]
-;
- %1 = tail call range(i32 1, 33) i32 @llvm.ctpop.i32(i32 %val)
- %pow = icmp eq i32 %1, 1
- call void @llvm.assume(i1 %pow)
-
- %mask = add i32 %val, -1
- %neg = sub nsw i32 0, %val
-
- %num.biased = add i32 %num, %mask
- %_2.sroa.0.0 = and i32 %num.biased, %neg
-
- call void @use(i32 %_2.sroa.0.0)
-
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
- ret i1 %_0
-}
>From 327aaed2436c97b9bcbc345650efa80cfb1f7727 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Tue, 12 Aug 2025 23:22:29 +0300
Subject: [PATCH 10/13] review
---
.../InstCombine/InstCombineCompares.cpp | 29 ++---
llvm/test/Transforms/InstCombine/icmp-add.ll | 119 +++++++++++++++---
2 files changed, 115 insertions(+), 33 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 6274417365f26..43ca2b5cd7b25 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1323,33 +1323,28 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
// Fold icmp eq (num + (val - 1)) & -val, num
// to
// icmp eq 0, (and num, val - 1)
-// For value being power of two
+// For val being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
- Value *Neg, *Num, *Mask;
+ Value *Num;
CmpPredicate Pred;
- const APInt *NegConst, *MaskConst;
+ const APInt *Mask, *Neg;
- if (!match(&Cmp, m_c_ICmp(Pred, m_Value(Num),
- m_OneUse(m_c_And(m_OneUse(m_c_Add(m_Deferred(Num),
- m_Value(Mask))),
- m_Value(Neg))))))
+ if (!match(&Cmp,
+ m_c_ICmp(Pred, m_Value(Num),
+ m_OneUse(m_c_And(m_OneUse(m_c_Add(m_Deferred(Num),
+ m_LowBitMask(Mask))),
+ m_APInt(Neg))))))
return nullptr;
- if (!ICmpInst::isEquality(Pred))
- return nullptr;
-
- // Check only constant case, since it's the only profitable.
- // See https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2657/
- if (!match(Neg, m_APInt(NegConst)) || !match(Mask, m_LowBitMask(MaskConst)))
+ if (*Neg != ~*Mask)
return nullptr;
- // Neg = -(Mask + 1)
- if (*NegConst != ~*MaskConst)
+ if (!ICmpInst::isEquality(Pred))
return nullptr;
// Create new icmp eq (num & (val - 1)), 0
- auto NewAnd = Builder.CreateAnd(Num, Mask);
- auto Zero = llvm::Constant::getNullValue(Num->getType());
+ auto NewAnd = Builder.CreateAnd(Num, *Mask);
+ auto Zero = Constant::getNullValue(Num->getType());
return new ICmpInst(Pred, NewAnd, Zero);
}
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 7b4650ec3e1b8..cb428097f2ae1 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3310,8 +3310,44 @@ define i1 @val_is_aligend_const_pow2(i32 %num) {
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
- %_2.sroa.0.0 = and i32 %num.biased, -4096
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2_add_commute(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_add_commute(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 4095, %num
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2_and_commute(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_and_commute(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %num.masked = and i32 -4096, %num.biased
+ %_0 = icmp eq i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2_icm_commute(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_icm_commute(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %num, %num.masked
ret i1 %_0
}
@@ -3319,28 +3355,28 @@ define i1 @val_is_aligend_const_pow2(i32 %num) {
define i1 @val_is_aligend_const_non_pow2(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_non_pow2(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 6
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -7
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -7
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 6
- %_2.sroa.0.0 = and i32 %num.biased, -7
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ %num.masked = and i32 %num.biased, -7
+ %_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}
define i1 @val_is_aligend_const_pow2_multiuse(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
-; CHECK-NEXT: call void @use(i32 [[_2_SROA_0_0]])
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4096
+; CHECK-NEXT: call void @use(i32 [[NUM_MASKED]])
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
- %_2.sroa.0.0 = and i32 %num.biased, -4096
- call void @use(i32 %_2.sroa.0.0)
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ %num.masked = and i32 %num.biased, -4096
+ call void @use(i32 %num.masked)
+ %_0 = icmp eq i32 %num.masked, %num
ret i1 %_0
}
@@ -3349,13 +3385,64 @@ define i1 @val_is_aligend_const_pow2_multiuse1(i32 %num) {
; CHECK-LABEL: @val_is_aligend_const_pow2_multiuse1(
; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
; CHECK-NEXT: call void @use(i32 [[NUM_BIASED]])
-; CHECK-NEXT: [[_2_SROA_0_0:%.*]] = and i32 [[NUM_BIASED]], -4096
-; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[_2_SROA_0_0]], [[NUM]]
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4096
+; CHECK-NEXT: [[_0:%.*]] = icmp eq i32 [[NUM_MASKED]], [[NUM]]
; CHECK-NEXT: ret i1 [[_0]]
;
%num.biased = add i32 %num, 4095
call void @use(i32 %num.biased)
- %_2.sroa.0.0 = and i32 %num.biased, -4096
- %_0 = icmp eq i32 %_2.sroa.0.0, %num
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp eq i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_pow2_ne(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_pow2_ne(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[TMP1]], 0
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp ne i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_mismatch(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_mismatch(
+; CHECK-NEXT: [[NUM_BIASED:%.*]] = add i32 [[NUM:%.*]], 4095
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = and i32 [[NUM_BIASED]], -4095
+; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[NUM_MASKED]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4095
+ %num.masked = and i32 %num.biased, -4095
+ %_0 = icmp ne i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_const_mismatch1(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_const_mismatch1(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], -4096
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = add i32 [[TMP1]], 4096
+; CHECK-NEXT: [[_0:%.*]] = icmp ne i32 [[NUM_MASKED]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4096
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp ne i32 %num.masked, %num
+ ret i1 %_0
+}
+
+define i1 @val_is_aligend_pred_mismatch(i32 %num) {
+; CHECK-LABEL: @val_is_aligend_pred_mismatch(
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[NUM:%.*]], -4096
+; CHECK-NEXT: [[NUM_MASKED:%.*]] = add i32 [[TMP1]], 4096
+; CHECK-NEXT: [[_0:%.*]] = icmp sge i32 [[NUM_MASKED]], [[NUM]]
+; CHECK-NEXT: ret i1 [[_0]]
+;
+ %num.biased = add i32 %num, 4096
+ %num.masked = and i32 %num.biased, -4096
+ %_0 = icmp sge i32 %num.masked, %num
ret i1 %_0
}
>From ee4c1a20522d356824c2ee421673ea5f473cf961 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 13 Aug 2025 21:38:27 +0300
Subject: [PATCH 11/13] style fixes
---
.../Transforms/InstCombine/InstCombineCompares.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 43ca2b5cd7b25..3a9c0922080ca 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1320,10 +1320,10 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
return nullptr;
}
-// Fold icmp eq (num + (val - 1)) & -val, num
-// to
-// icmp eq 0, (and num, val - 1)
-// For val being power of two
+/// Fold icmp eq (num + (val - 1)) & -val, num
+/// to
+/// icmp eq 0, (and num, val - 1)
+/// For val being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
Value *Num;
CmpPredicate Pred;
@@ -1343,8 +1343,8 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
return nullptr;
// Create new icmp eq (num & (val - 1)), 0
- auto NewAnd = Builder.CreateAnd(Num, *Mask);
- auto Zero = Constant::getNullValue(Num->getType());
+ auto *NewAnd = Builder.CreateAnd(Num, *Mask);
+ auto *Zero = Constant::getNullValue(Num->getType());
return new ICmpInst(Pred, NewAnd, Zero);
}
>From b58e38810f7efe1c2bd5d3b62cad1591bd8b0560 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 13 Aug 2025 22:41:50 +0300
Subject: [PATCH 12/13] swap operands in comment
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 3a9c0922080ca..8aa2693da039e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1322,7 +1322,7 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
/// Fold icmp eq (num + (val - 1)) & -val, num
/// to
-/// icmp eq 0, (and num, val - 1)
+/// icmp eq (and num, val - 1), 0
/// For val being power of two
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
Value *Num;
>From 52ef6a676cd488d11fed3ec1bfd9671fbd4a15e6 Mon Sep 17 00:00:00 2001
From: Pavel Skripkin <paskripkin at gmail.com>
Date: Wed, 13 Aug 2025 23:59:46 +0300
Subject: [PATCH 13/13] comment fixes
---
llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 8aa2693da039e..a64f422c3eede 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -1320,10 +1320,10 @@ Instruction *InstCombinerImpl::foldICmpWithZero(ICmpInst &Cmp) {
return nullptr;
}
-/// Fold icmp eq (num + (val - 1)) & -val, num
+/// Fold icmp eq (num + mask) & ~mask, num
/// to
-/// icmp eq (and num, val - 1), 0
-/// For val being power of two
+/// icmp eq (and num, mask), 0
+/// Where mask is a low bit mask.
Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
Value *Num;
CmpPredicate Pred;
@@ -1342,7 +1342,7 @@ Instruction *InstCombinerImpl::foldIsMultipleOfAPowerOfTwo(ICmpInst &Cmp) {
if (!ICmpInst::isEquality(Pred))
return nullptr;
- // Create new icmp eq (num & (val - 1)), 0
+ // Create new icmp eq (num & mask), 0
auto *NewAnd = Builder.CreateAnd(Num, *Mask);
auto *Zero = Constant::getNullValue(Num->getType());
More information about the llvm-commits
mailing list