[llvm] [InstCombine] Canonicalize (a + 1 == 0) ? -1 : a + 1 -> uadd.sat(a, 1) (PR #144566)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 17 13:25:07 PDT 2025


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/144566

>From eed6e69e3c6b83ae0f9c5510c52db318d3373ec8 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 17 Jun 2025 12:40:21 -0400
Subject: [PATCH 1/4] [InstCombine] Pre-commit test

---
 .../Transforms/InstCombine/saturating-add-sub.ll    | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
index cfd679c0cc592..b3bad2f88288a 100644
--- a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
@@ -2350,4 +2350,17 @@ define i8 @fold_add_umax_to_usub_multiuse(i8 %a) {
   ret i8 %sel
 }
 
+define i32 @add_check_zero(i32 %num) {
+; CHECK-LABEL: @add_check_zero(
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[NUM:%.*]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ADD]], 0
+; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP]], i32 -1, i32 [[ADD]]
+; CHECK-NEXT:    ret i32 [[COND]]
+;
+  %add = add i32 %num, 1
+  %cmp = icmp eq i32 %add, 0
+  %cond = select i1 %cmp, i32 -1, i32 %add
+  ret i32 %cond
+}
+
 declare void @usei8(i8)

>From 978106ee6b5a0e8077f09c93062013d7ceb4b127 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 17 Jun 2025 13:01:36 -0400
Subject: [PATCH 2/4] [InstCombine] Canonicalize (a + 1 == 0) ? -1 : a + 1 ->
 uadd.sat(a, 1)

This is because this actually is just:

// ((X + Y) u< X) ? -1 : (X + Y) --> uadd.sat(X, Y)
// ((X + Y) u< Y) ? -1 : (X + Y) --> uadd.sat(X, Y)

However, ult 1 is canonicalized to eq 0.

Alive2: https://alive2.llvm.org/ce/z/Y4eHav
---
 llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp | 11 +++++++++--
 .../test/Transforms/InstCombine/saturating-add-sub.ll |  4 +---
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 73ba0f78e8053..d733a37efd68e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1013,12 +1013,19 @@ static Value *canonicalizeSaturatedAdd(ICmpInst *Cmp, Value *TVal, Value *FVal,
 
   // uge -1 is canonicalized to eq -1 and requires special handling
   // (a == -1) ? -1 : a + 1 -> uadd.sat(a, 1)
+  // ult 1 is canonicalized to eq 0
+  // (a + 1 == 0) ? -1 : a + 1 -> uadd.sat(a, 1)
   if (Pred == ICmpInst::ICMP_EQ) {
     if (match(FVal, m_Add(m_Specific(Cmp0), m_One())) &&
-        match(Cmp1, m_AllOnes())) {
+        match(Cmp1, m_AllOnes()))
       return Builder.CreateBinaryIntrinsic(
           Intrinsic::uadd_sat, Cmp0, ConstantInt::get(Cmp0->getType(), 1));
-    }
+
+    if (match(Cmp1, m_Zero()) && match(Cmp0, m_Add(m_Value(X), m_One())) &&
+        match(FVal, m_Add(m_Specific(X), m_One())))
+      return Builder.CreateBinaryIntrinsic(Intrinsic::uadd_sat, X,
+                                           ConstantInt::get(X->getType(), 1));
+
     return nullptr;
   }
 
diff --git a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
index b3bad2f88288a..cc8ed078d4229 100644
--- a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
@@ -2352,9 +2352,7 @@ define i8 @fold_add_umax_to_usub_multiuse(i8 %a) {
 
 define i32 @add_check_zero(i32 %num) {
 ; CHECK-LABEL: @add_check_zero(
-; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[NUM:%.*]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[ADD]], 0
-; CHECK-NEXT:    [[COND:%.*]] = select i1 [[CMP]], i32 -1, i32 [[ADD]]
+; CHECK-NEXT:    [[COND:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[NUM:%.*]], i32 1)
 ; CHECK-NEXT:    ret i32 [[COND]]
 ;
   %add = add i32 %num, 1

>From 499c0167da524000ecd2182578cf9e9f32a4b0fc Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 17 Jun 2025 15:50:44 -0400
Subject: [PATCH 3/4] Deal with the root problem; simplify icmp

---
 llvm/include/llvm/IR/PatternMatch.h                | 14 --------------
 .../Transforms/InstCombine/InstCombineCompares.cpp |  4 +---
 .../Transforms/InstCombine/InstCombineSelect.cpp   |  7 -------
 3 files changed, 1 insertion(+), 24 deletions(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 2eaa7d0faabc1..e2e662011e7a4 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -2562,20 +2562,6 @@ struct UAddWithOverflow_match {
         return L.match(Op1) && R.match(ICmpLHS) && S.match(ICmpRHS);
     }
 
-    // Match special-case for increment-by-1.
-    if (Pred == ICmpInst::ICMP_EQ) {
-      // (a + 1) == 0
-      // (1 + a) == 0
-      if (AddExpr.match(ICmpLHS) && m_ZeroInt().match(ICmpRHS) &&
-          (m_One().match(AddLHS) || m_One().match(AddRHS)))
-        return L.match(AddLHS) && R.match(AddRHS) && S.match(ICmpLHS);
-      // 0 == (a + 1)
-      // 0 == (1 + a)
-      if (m_ZeroInt().match(ICmpLHS) && AddExpr.match(ICmpRHS) &&
-          (m_One().match(AddLHS) || m_One().match(AddRHS)))
-        return L.match(AddLHS) && R.match(AddRHS) && S.match(ICmpRHS);
-    }
-
     return false;
   }
 };
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 084e7fbaa268a..a4dbbf925a5c8 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3621,10 +3621,8 @@ Instruction *InstCombinerImpl::foldICmpBinOpEqualityWithConstant(
   case Instruction::Add: {
     // (A + C2) == C --> A == (C - C2)
     // (A + C2) != C --> A != (C - C2)
-    // TODO: Remove the one-use limitation? See discussion in D58633.
     if (Constant *C2 = dyn_cast<Constant>(BOp1)) {
-      if (BO->hasOneUse())
-        return new ICmpInst(Pred, BOp0, ConstantExpr::getSub(RHS, C2));
+      return new ICmpInst(Pred, BOp0, ConstantExpr::getSub(RHS, C2));
     } else if (C.isZero()) {
       // Replace ((add A, B) != 0) with (A != -B) if A or B is
       // efficiently invertible, or if the add has just this one use.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index d733a37efd68e..1460176407041 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1013,19 +1013,12 @@ static Value *canonicalizeSaturatedAdd(ICmpInst *Cmp, Value *TVal, Value *FVal,
 
   // uge -1 is canonicalized to eq -1 and requires special handling
   // (a == -1) ? -1 : a + 1 -> uadd.sat(a, 1)
-  // ult 1 is canonicalized to eq 0
-  // (a + 1 == 0) ? -1 : a + 1 -> uadd.sat(a, 1)
   if (Pred == ICmpInst::ICMP_EQ) {
     if (match(FVal, m_Add(m_Specific(Cmp0), m_One())) &&
         match(Cmp1, m_AllOnes()))
       return Builder.CreateBinaryIntrinsic(
           Intrinsic::uadd_sat, Cmp0, ConstantInt::get(Cmp0->getType(), 1));
 
-    if (match(Cmp1, m_Zero()) && match(Cmp0, m_Add(m_Value(X), m_One())) &&
-        match(FVal, m_Add(m_Specific(X), m_One())))
-      return Builder.CreateBinaryIntrinsic(Intrinsic::uadd_sat, X,
-                                           ConstantInt::get(X->getType(), 1));
-
     return nullptr;
   }
 

>From c39cd187d16de8907f576321e9059bf56801ae32 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Tue, 17 Jun 2025 16:24:56 -0400
Subject: [PATCH 4/4] fix

---
 llvm/include/llvm/IR/PatternMatch.h                | 14 ++++++++++++++
 llvm/test/Analysis/ValueTracking/phi-known-bits.ll |  2 +-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index e2e662011e7a4..2eaa7d0faabc1 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -2562,6 +2562,20 @@ struct UAddWithOverflow_match {
         return L.match(Op1) && R.match(ICmpLHS) && S.match(ICmpRHS);
     }
 
+    // Match special-case for increment-by-1.
+    if (Pred == ICmpInst::ICMP_EQ) {
+      // (a + 1) == 0
+      // (1 + a) == 0
+      if (AddExpr.match(ICmpLHS) && m_ZeroInt().match(ICmpRHS) &&
+          (m_One().match(AddLHS) || m_One().match(AddRHS)))
+        return L.match(AddLHS) && R.match(AddRHS) && S.match(ICmpLHS);
+      // 0 == (a + 1)
+      // 0 == (1 + a)
+      if (m_ZeroInt().match(ICmpLHS) && AddExpr.match(ICmpRHS) &&
+          (m_One().match(AddLHS) || m_One().match(AddRHS)))
+        return L.match(AddLHS) && R.match(AddRHS) && S.match(ICmpRHS);
+    }
+
     return false;
   }
 };
diff --git a/llvm/test/Analysis/ValueTracking/phi-known-bits.ll b/llvm/test/Analysis/ValueTracking/phi-known-bits.ll
index 436aadbc25de6..d2fa99237a4ac 100644
--- a/llvm/test/Analysis/ValueTracking/phi-known-bits.ll
+++ b/llvm/test/Analysis/ValueTracking/phi-known-bits.ll
@@ -1119,7 +1119,7 @@ define i32 @issue_124275_wrong_br_direction(i32 noundef %inp) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[TMP0:%.*]] = xor i32 [[INP:%.*]], -2
 ; CHECK-NEXT:    [[XOR_INP_NEG:%.*]] = add i32 [[TMP0]], 1
-; CHECK-NEXT:    [[CMP_NE_NOT:%.*]] = icmp eq i32 [[XOR_INP_NEG]], 0
+; CHECK-NEXT:    [[CMP_NE_NOT:%.*]] = icmp eq i32 [[INP]], 1
 ; CHECK-NEXT:    br i1 [[CMP_NE_NOT]], label [[B1:%.*]], label [[B0:%.*]]
 ; CHECK:       B0:
 ; CHECK-NEXT:    [[PHI_B0:%.*]] = phi i32 [ [[PHI_B1:%.*]], [[B1]] ], [ [[XOR_INP_NEG]], [[ENTRY:%.*]] ]



More information about the llvm-commits mailing list