[llvm] [llvm] [InstCombine] fold "icmp eq (X + (V - 1)) & -V, X" to "icmp eq 0, (and X, V - 1)" (PR #152851)

Pavel Skripkin via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 10 11:13:01 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 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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



More information about the llvm-commits mailing list