[llvm] [VectorCombine] Fold vector sign-bit checks (PR #175194)

Valeriy Savchenko via llvm-commits llvm-commits at lists.llvm.org
Sun Jan 11 08:34:30 PST 2026


================
@@ -3806,6 +3807,215 @@ bool VectorCombine::foldCastFromReductions(Instruction &I) {
   return true;
 }
 
+/// Fold:
+///   icmp pred (reduce.{add,or,and,umax,umin}(signbit_extract(x))), C
+/// into:
+///   icmp sgt/slt (reduce.{or,umax,and,umin}(x)), -1/0
+///
+/// Sign-bit reductions produce values with known semantics:
+///   - reduce.{or,umax}: 0 if no element is negative, 1 if any is
+///   - reduce.{and,umin}: 1 if all elements are negative, 0 if any isn't
+///   - reduce.add: count of negative elements (0 to NumElts)
+///
+/// We transform to a direct sign check on reduce.{or,umax} or
+/// reduce.{and,umin} without explicit sign-bit extraction.
+///
+/// In spirit, it's similar to foldSignBitCheck in InstCombine.
+bool VectorCombine::foldSignBitReductionCmp(Instruction &I) {
+  CmpPredicate Pred;
+  Value *ReduceOp;
+  const APInt *CmpVal;
+  if (!match(&I, m_ICmp(Pred, m_Value(ReduceOp), m_APInt(CmpVal))))
+    return false;
+
+  auto *II = dyn_cast<IntrinsicInst>(ReduceOp);
+  if (!II || !II->hasOneUse())
+    return false;
+
+  Intrinsic::ID OrigIID = II->getIntrinsicID();
+  switch (OrigIID) {
+  case Intrinsic::vector_reduce_or:
+  case Intrinsic::vector_reduce_umax:
+  case Intrinsic::vector_reduce_and:
+  case Intrinsic::vector_reduce_umin:
+  case Intrinsic::vector_reduce_add:
+    break;
+  default:
+    return false;
+  }
+
+  Value *ReductionSrc = II->getArgOperand(0);
+  if (!ReductionSrc->hasOneUse())
+    return false;
+
+  auto *VecTy = dyn_cast<FixedVectorType>(ReductionSrc->getType());
+  if (!VecTy)
+    return false;
+
+  unsigned BitWidth = VecTy->getScalarSizeInBits();
+  unsigned NumElts = VecTy->getNumElements();
+
+  // Match sign-bit extraction: shr X, (bitwidth-1)
+  Value *X;
+  Constant *C;
+  if (!match(ReductionSrc, m_Shr(m_Value(X), m_Constant(C))) ||
+      !match(C, m_SpecificInt(APInt(BitWidth, BitWidth - 1))))
+    return false;
+
+  // MaxVal: 1 for or/and/umax/umin, NumElts for add
+  unsigned MaxVal = OrigIID == Intrinsic::vector_reduce_add ? NumElts : 1;
+
+  // In addition to direct comparisons EQ 0, NE 0, EQ 1, NE 1, etc. we support
+  // inequalities that can be interpreted as either EQ or NE considering a
+  // rather narrow range of possible value of sign-bit reductions.
+  bool IsEq;
+  uint64_t NormalizedCmpVal;
+  if (Pred == ICmpInst::ICMP_EQ) {
+    IsEq = true;
+    NormalizedCmpVal = CmpVal->getZExtValue();
+  } else if (Pred == ICmpInst::ICMP_NE) {
+    IsEq = false;
+    NormalizedCmpVal = CmpVal->getZExtValue();
+  } else if (Pred == ICmpInst::ICMP_SLT && CmpVal->isOne()) {
+    IsEq = true;
+    NormalizedCmpVal = 0; // slt 1 → eq 0
----------------
SavchenkoValeriy wrote:

Good catch! Yes, know `add` case is also canonicalized for 0 checks. However, it's not enough to prove the exact upper bound and we still need checks for upper values. I removed unnecessary checks and proofs.

https://github.com/llvm/llvm-project/pull/175194


More information about the llvm-commits mailing list