[llvm] [InstCombine] Strip off sign bit preserving or flipping operations (PR #141974)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Fri May 30 01:08:53 PDT 2025


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/141974

>From 203ca20171c66c2cbc20ffb102dcf047b90fda54 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 30 May 2025 00:21:59 +0800
Subject: [PATCH 1/2] [InstCombine] Strip off sign bit preserving or flipping
 operations

---
 .../InstCombine/InstCombineCompares.cpp       | 97 ++++++++++++-------
 .../InstCombine/InstCombineInternal.h         |  6 ++
 2 files changed, 67 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index bdc7a49700cfc..c585b095b1ede 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -72,32 +72,6 @@ static bool hasBranchUse(ICmpInst &I) {
   return false;
 }
 
-/// Returns true if the exploded icmp can be expressed as a signed comparison
-/// to zero and updates the predicate accordingly.
-/// The signedness of the comparison is preserved.
-/// TODO: Refactor with decomposeBitTestICmp()?
-static bool isSignTest(ICmpInst::Predicate &Pred, const APInt &C) {
-  if (!ICmpInst::isSigned(Pred))
-    return false;
-
-  if (C.isZero())
-    return ICmpInst::isRelational(Pred);
-
-  if (C.isOne()) {
-    if (Pred == ICmpInst::ICMP_SLT) {
-      Pred = ICmpInst::ICMP_SLE;
-      return true;
-    }
-  } else if (C.isAllOnes()) {
-    if (Pred == ICmpInst::ICMP_SGT) {
-      Pred = ICmpInst::ICMP_SGE;
-      return true;
-    }
-  }
-
-  return false;
-}
-
 /// This is called when we see this pattern:
 ///   cmp pred (load (gep GV, ...)), cmpcst
 /// where GV is a global variable with a constant initializer. Try to simplify
@@ -2188,16 +2162,6 @@ Instruction *InstCombinerImpl::foldICmpMulConstant(ICmpInst &Cmp,
   if (!match(Mul->getOperand(1), m_APInt(MulC)))
     return nullptr;
 
-  // If this is a test of the sign bit and the multiply is sign-preserving with
-  // a constant operand, use the multiply LHS operand instead:
-  // (X * +MulC) < 0 --> X < 0
-  // (X * -MulC) < 0 --> X > 0
-  if (isSignTest(Pred, C) && Mul->hasNoSignedWrap()) {
-    if (MulC->isNegative())
-      Pred = ICmpInst::getSwappedPredicate(Pred);
-    return new ICmpInst(Pred, X, ConstantInt::getNullValue(MulTy));
-  }
-
   if (MulC->isZero())
     return nullptr;
 
@@ -3545,12 +3509,73 @@ Instruction *InstCombinerImpl::foldICmpBitCast(ICmpInst &Cmp) {
   return nullptr;
 }
 
+std::pair<Value *, bool>
+InstCombinerImpl::stripSignBitPreservingOrFlippingOperations(Value *X,
+                                                             unsigned Depth) {
+  if (!X->hasOneUse() || Depth++ >= MaxAnalysisRecursionDepth)
+    return {X, false};
+
+  auto FlipSign = [](std::pair<Value *, bool> P, bool Flip) {
+    return std::make_pair(P.first, P.second ^ Flip);
+  };
+
+  Value *V;
+  const APInt *C;
+  if (match(X, m_NSWTrunc(m_Value(V))) &&
+      shouldChangeType(X->getType(), V->getType()))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  if (match(X, m_SExt(m_Value(V))) &&
+      shouldChangeType(X->getType(), V->getType()))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  if (match(X, m_Xor(m_Value(V), m_APInt(C))))
+    return FlipSign(stripSignBitPreservingOrFlippingOperations(V, Depth),
+                    C->isNegative());
+
+  if (match(X, m_AShr(m_Value(V), m_Value())))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  if (match(X, m_NSWSub(m_Zero(), m_Value(V))))
+    return FlipSign(stripSignBitPreservingOrFlippingOperations(V, Depth),
+                    /*Flip=*/true);
+
+  if (match(X, m_NSWShl(m_Value(V), m_Value())))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  if (match(X, m_NSWMul(m_Value(V), m_APInt(C))) && !C->isZero())
+    return FlipSign(stripSignBitPreservingOrFlippingOperations(V, Depth),
+                    C->isNegative());
+
+  if (match(X, m_SMax(m_Value(V), m_Negative())))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  if (match(X, m_SMin(m_Value(V), m_StrictlyPositive())))
+    return stripSignBitPreservingOrFlippingOperations(V, Depth);
+
+  return {X, false};
+}
+
 /// Try to fold integer comparisons with a constant operand: icmp Pred X, C
 /// where X is some kind of instruction.
 Instruction *InstCombinerImpl::foldICmpInstWithConstant(ICmpInst &Cmp) {
   const APInt *C;
 
   if (match(Cmp.getOperand(1), m_APInt(C))) {
+    bool TrueIfSigned = false;
+    // Strip off sign bit preserving/flipping operations.
+    if (isSignBitCheck(Cmp.getPredicate(), *C, TrueIfSigned)) {
+      auto [X, FlipSign] = stripSignBitPreservingOrFlippingOperations(
+          Cmp.getOperand(0), /*Depth=*/0);
+      if (X != Cmp.getOperand(0)) {
+        if (TrueIfSigned == FlipSign)
+          return new ICmpInst(ICmpInst::ICMP_SGT, X,
+                              ConstantInt::getAllOnesValue(X->getType()));
+        return new ICmpInst(ICmpInst::ICMP_SLT, X,
+                            ConstantInt::getNullValue(X->getType()));
+      }
+    }
+
     if (auto *BO = dyn_cast<BinaryOperator>(Cmp.getOperand(0)))
       if (Instruction *I = foldICmpBinOpWithConstant(Cmp, BO, *C))
         return I;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 5e0cd17fb1924..12a33e89bf040 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -812,6 +812,12 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
       return takeLog2(Op, /*Depth=*/0, AssumeNonZero, /*DoFold=*/true);
     return nullptr;
   }
+
+  /// Try to strip off sign bit preserving/flipping operations.
+  /// The second return value indicates whether we should flip the sign bit
+  /// first.
+  std::pair<Value *, bool>
+  stripSignBitPreservingOrFlippingOperations(Value *X, unsigned Depth);
 };
 
 class Negator final {

>From 9611274e1089c8a02ff35fa1e1a899c0cf951af1 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 30 May 2025 16:08:33 +0800
Subject: [PATCH 2/2] [InstCombine] Remove one-use constraint

---
 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 c585b095b1ede..03db8fc5e1d1c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3512,7 +3512,7 @@ Instruction *InstCombinerImpl::foldICmpBitCast(ICmpInst &Cmp) {
 std::pair<Value *, bool>
 InstCombinerImpl::stripSignBitPreservingOrFlippingOperations(Value *X,
                                                              unsigned Depth) {
-  if (!X->hasOneUse() || Depth++ >= MaxAnalysisRecursionDepth)
+  if (Depth++ >= MaxAnalysisRecursionDepth)
     return {X, false};
 
   auto FlipSign = [](std::pair<Value *, bool> P, bool Flip) {



More information about the llvm-commits mailing list