[llvm] [InstCombine] Canonicalize signed saturated additions (PR #153053)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 9 02:22:03 PST 2025


================
@@ -1115,6 +1113,107 @@ static Value *canonicalizeSaturatedAdd(ICmpInst *Cmp, Value *TVal, Value *FVal,
   return nullptr;
 }
 
+static Value *canonicalizeSaturatedAddSigned(ICmpInst *Cmp, Value *TVal,
+                                             Value *FVal,
+                                             InstCombiner::BuilderTy &Builder) {
+  // Match saturated add with constant.
+  Value *Cmp0 = Cmp->getOperand(0);
+  Value *Cmp1 = Cmp->getOperand(1);
+  ICmpInst::Predicate Pred = Cmp->getPredicate();
+  Value *X, *Y;
+  const APInt *C;
+
+  // Canonicalize INT_MAX to true value of the select.
+  if (match(FVal, m_MaxSignedValue())) {
+    std::swap(TVal, FVal);
+    Pred = CmpInst::getInversePredicate(Pred);
+  }
+  if (!match(TVal, m_MaxSignedValue()))
+    return nullptr;
+
+  // sge maximum signed value is canonicalized to eq minimum signed value and
+  // requires special handling (a == INT_MAX) ? INT_MAX : a + 1 -> sadd.sat(a,
+  // 1)
+  if (Pred == ICmpInst::ICMP_EQ) {
+    if (match(FVal, m_Add(m_Specific(Cmp0), m_One())) && Cmp1 == TVal) {
+      return Builder.CreateBinaryIntrinsic(
+          Intrinsic::sadd_sat, Cmp0, ConstantInt::get(Cmp0->getType(), 1));
+    }
+    return nullptr;
+  }
+
+  if ((Pred == ICmpInst::ICMP_SGE || Pred == ICmpInst::ICMP_SGT) &&
+      match(FVal, m_Add(m_Specific(Cmp0), m_APIntAllowPoison(C))) &&
+      match(Cmp1, m_SpecificIntAllowPoison(
+                      APInt::getSignedMaxValue(
+                          Cmp1->getType()->getScalarSizeInBits()) -
+                      *C)) &&
+      !C->isNegative()) {
+    // (X > INT_MAX - C) ? INT_MAX : (X + C) --> sadd.sat(X, C)
+    // (X >= INT_MAX - C) ? INT_MAX : (X + C) --> sadd.sat(X, C)
+    return Builder.CreateBinaryIntrinsic(Intrinsic::sadd_sat, Cmp0,
+                                         ConstantInt::get(Cmp0->getType(), *C));
+  }
+
+  if (Pred == ICmpInst::ICMP_SGT &&
+      match(FVal, m_Add(m_Specific(Cmp0), m_APIntAllowPoison(C))) &&
+      match(Cmp1, m_SpecificIntAllowPoison(
+                      APInt::getSignedMaxValue(
+                          Cmp1->getType()->getScalarSizeInBits()) -
+                      *C - 1)) &&
+      !C->isNegative()) {
+    // (X > INT_MAX - C - 1) ? INT_MAX : (X + C) --> sadd.sat(X, C)
+    return Builder.CreateBinaryIntrinsic(Intrinsic::sadd_sat, Cmp0,
+                                         ConstantInt::get(Cmp0->getType(), *C));
+  }
+
+  // This does not work with 0, or negative numbers as
+  // (X >= INT_MIN + 0 + 1) ? INT_MAX : (X + 0) is not a saturated add.
+  if (Pred == ICmpInst::ICMP_SGE &&
+      match(FVal, m_Add(m_Specific(Cmp0), m_APIntAllowPoison(C))) &&
+      match(Cmp1, m_SpecificIntAllowPoison(
+                      APInt::getSignedMinValue(
+                          Cmp1->getType()->getScalarSizeInBits()) -
+                      *C + 1)) &&
+      C->isStrictlyPositive()) {
+    // (X >= INT_MAX - C + 1) ? INT_MAX : (X + C) --> sadd.sat(X, C)
+    return Builder.CreateBinaryIntrinsic(Intrinsic::sadd_sat, Cmp0,
+                                         ConstantInt::get(Cmp0->getType(), *C));
+  }
+
+  // Canonicalize predicate to less-than or less-or-equal-than.
+  if (Pred == ICmpInst::ICMP_SGT || Pred == ICmpInst::ICMP_SGE) {
+    std::swap(Cmp0, Cmp1);
+    Pred = CmpInst::getSwappedPredicate(Pred);
+  }
+
+  if (Pred != ICmpInst::ICMP_SLT && Pred != ICmpInst::ICMP_SLE)
+    return nullptr;
+
+  if (match(Cmp0, m_NSWSub(m_MaxSignedValue(), m_Value(X))) &&
+      match(FVal, m_c_Add(m_Specific(X), m_Value(Y))) && Y == Cmp1) {
----------------
dtcxzyw wrote:

```suggestion
      match(FVal, m_c_Add(m_Specific(X), m_Specific(Cmp1)))) {
```

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


More information about the llvm-commits mailing list