[llvm] [InstCombine] Add optimization to combine adds through zext nneg, and add testcase (PR #157723)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 25 09:16:57 PDT 2025


https://github.com/Aethezz updated https://github.com/llvm/llvm-project/pull/157723

>From 0e8a5fd5b05517159b0df102913086d6e4bb2390 Mon Sep 17 00:00:00 2001
From: Aethezz <ellisonlao999 at gmail.com>
Date: Tue, 9 Sep 2025 13:20:22 -0400
Subject: [PATCH 1/4] Combine adds through zext nneg, and add testcase

---
 .../InstCombine/InstCombineAddSub.cpp           | 17 +++++++++++++++++
 llvm/test/Transforms/InstCombine/zext.ll        | 11 +++++++++++
 2 files changed, 28 insertions(+)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index d934638c15e75..f4a2ab5377a34 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1910,6 +1910,23 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
   if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
     return Res;
 
+  // Combine adds through zext nneg:
+  // (zext nneg (X + C1)) + C2 --> zext X if C1 + C2 == 0
+  {
+    Value *X;
+    const APInt *C1, *C2;
+    if (match(&I, m_c_Add(m_NNegZExt(m_Add(m_Value(X), m_APInt(C1))),
+                          m_APInt(C2)))) {
+      // Check if the constants cancel out (C1 + C2 == 0)
+      APInt Sum = C1->sext(C2->getBitWidth()) + *C2;
+      if (Sum.isZero()) {
+        // The add inside the zext and the outer add cancel out
+        Value *NewZExt = Builder.CreateZExt(X, I.getType());
+        return replaceInstUsesWith(I, NewZExt);
+      }
+    }
+  }
+
   // Re-enqueue users of the induction variable of add recurrence if we infer
   // new nuw/nsw flags.
   if (Changed) {
diff --git a/llvm/test/Transforms/InstCombine/zext.ll b/llvm/test/Transforms/InstCombine/zext.ll
index e4d18e9395219..15aa2aaec430a 100644
--- a/llvm/test/Transforms/InstCombine/zext.ll
+++ b/llvm/test/Transforms/InstCombine/zext.ll
@@ -976,3 +976,14 @@ entry:
   %res = zext nneg i2 %x to i32
   ret i32 %res
 }
+
+define i32 @zext_nneg_add_cancel(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_add_cancel(
+; CHECK-NEXT:    [[ADD2:%.*]] = zext i8 [[ARG:%.*]] to i32
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  %add2 = add i32 %zext, 2
+  ret i32 %add2
+}
\ No newline at end of file

>From edde23371aa6a9b94ebde072f8c09da9e244feeb Mon Sep 17 00:00:00 2001
From: Aethezz <ellisonlao999 at gmail.com>
Date: Tue, 16 Sep 2025 12:48:02 -0400
Subject: [PATCH 2/4] added preconditions and adjusted testcases

---
 .../InstCombine/InstCombineAddSub.cpp         | 26 ++++++++++++------
 llvm/test/Transforms/InstCombine/zext.ll      | 27 ++++++++++++++++++-
 2 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index f4a2ab5377a34..7058af37ab153 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1910,20 +1910,30 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
   if (Instruction *Res = foldBinOpOfSelectAndCastOfSelectCondition(I))
     return Res;
 
-  // Combine adds through zext nneg:
-  // (zext nneg (X + C1)) + C2 --> zext X if C1 + C2 == 0
   {
     Value *X;
     const APInt *C1, *C2;
     if (match(&I, m_c_Add(m_NNegZExt(m_Add(m_Value(X), m_APInt(C1))),
                           m_APInt(C2)))) {
-      // Check if the constants cancel out (C1 + C2 == 0)
+      // Check if inner constant C1 is negative C1 < 0 and outer constant C2 >=
+      // 0
+      if (!C1->isNegative() || C2->isNegative())
+        return nullptr;
+
       APInt Sum = C1->sext(C2->getBitWidth()) + *C2;
-      if (Sum.isZero()) {
-        // The add inside the zext and the outer add cancel out
-        Value *NewZExt = Builder.CreateZExt(X, I.getType());
-        return replaceInstUsesWith(I, NewZExt);
-      }
+      APInt newSum = Sum.trunc(C1->getBitWidth());
+
+      if (newSum.sext(C2->getBitWidth()) != Sum)
+        return nullptr;
+
+      // X if sum is zero, else X + newSum
+      Value *Inner =
+          Sum.isZero()
+              ? X
+              : Builder.CreateAdd(X, ConstantInt::get(X->getType(), newSum));
+
+      Value *NewZExt = Builder.CreateZExt(Inner, I.getType());
+      return replaceInstUsesWith(I, NewZExt);
     }
   }
 
diff --git a/llvm/test/Transforms/InstCombine/zext.ll b/llvm/test/Transforms/InstCombine/zext.ll
index 15aa2aaec430a..c2622aab437af 100644
--- a/llvm/test/Transforms/InstCombine/zext.ll
+++ b/llvm/test/Transforms/InstCombine/zext.ll
@@ -986,4 +986,29 @@ define i32 @zext_nneg_add_cancel(i8 %arg) {
   %zext = zext nneg i8 %add to i32
   %add2 = add i32 %zext, 2
   ret i32 %add2
-}
\ No newline at end of file
+}
+
+define i32 @zext_nneg_no_cancel(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_no_cancel(
+; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[ARG:%.*]], -1
+; CHECK-NEXT:    [[ADD2:%.*]] = zext i8 [[TMP1]] to i32
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  %add2 = add i32 %zext, 1
+  ret i32 %add2
+}
+
+define i32 @zext_nneg_overflow(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_overflow(
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
+; CHECK-NEXT:    [[ADD2:%.*]] = add nuw nsw i32 [[ZEXT]], 299
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  %add2 = add i32 %zext, 299
+  ret i32 %add2
+}

>From 93612576fa58811a72d127fb8d02ddf8f04fe56f Mon Sep 17 00:00:00 2001
From: Aethezz <ellisonlao999 at gmail.com>
Date: Wed, 24 Sep 2025 11:21:35 -0400
Subject: [PATCH 3/4] update review and added multiuse

---
 .../InstCombine/InstCombineAddSub.cpp         |  9 ++--
 llvm/test/Transforms/InstCombine/zext.ll      | 46 +++++++++++++++++++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 7058af37ab153..1a339e2620e82 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1913,8 +1913,8 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
   {
     Value *X;
     const APInt *C1, *C2;
-    if (match(&I, m_c_Add(m_NNegZExt(m_Add(m_Value(X), m_APInt(C1))),
-                          m_APInt(C2)))) {
+    if (match(&I,
+              m_Add(m_NNegZExt(m_Add(m_Value(X), m_APInt(C1))), m_APInt(C2)))) {
       // Check if inner constant C1 is negative C1 < 0 and outer constant C2 >=
       // 0
       if (!C1->isNegative() || C2->isNegative())
@@ -1923,7 +1923,7 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
       APInt Sum = C1->sext(C2->getBitWidth()) + *C2;
       APInt newSum = Sum.trunc(C1->getBitWidth());
 
-      if (newSum.sext(C2->getBitWidth()) != Sum)
+      if (!Sum.isSignedIntN(C1->getBitWidth()))
         return nullptr;
 
       // X if sum is zero, else X + newSum
@@ -1932,8 +1932,7 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
               ? X
               : Builder.CreateAdd(X, ConstantInt::get(X->getType(), newSum));
 
-      Value *NewZExt = Builder.CreateZExt(Inner, I.getType());
-      return replaceInstUsesWith(I, NewZExt);
+      return new ZExtInst(Inner, I.getType());
     }
   }
 
diff --git a/llvm/test/Transforms/InstCombine/zext.ll b/llvm/test/Transforms/InstCombine/zext.ll
index c2622aab437af..5b3e7caea3bb8 100644
--- a/llvm/test/Transforms/InstCombine/zext.ll
+++ b/llvm/test/Transforms/InstCombine/zext.ll
@@ -988,6 +988,21 @@ define i32 @zext_nneg_add_cancel(i8 %arg) {
   ret i32 %add2
 }
 
+define i32 @zext_nneg_add_cancel_multi_use(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_add_cancel_multi_use(
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
+; CHECK-NEXT:    [[ADD2:%.*]] = zext i8 [[ARG]] to i32
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  call void @use32(i32 %zext)
+  %add2 = add i32 %zext, 2
+  ret i32 %add2
+}
+
 define i32 @zext_nneg_no_cancel(i8 %arg) {
 ; CHECK-LABEL: @zext_nneg_no_cancel(
 ; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[ARG:%.*]], -1
@@ -1000,6 +1015,22 @@ define i32 @zext_nneg_no_cancel(i8 %arg) {
   ret i32 %add2
 }
 
+define i32 @zext_nneg_no_cancel_multi_use(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_no_cancel_multi_use(
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
+; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[ARG]], -1
+; CHECK-NEXT:    [[ADD2:%.*]] = zext i8 [[TMP1]] to i32
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  call void @use32(i32 %zext)
+  %add2 = add i32 %zext, 1
+  ret i32 %add2
+}
+
 define i32 @zext_nneg_overflow(i8 %arg) {
 ; CHECK-LABEL: @zext_nneg_overflow(
 ; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
@@ -1012,3 +1043,18 @@ define i32 @zext_nneg_overflow(i8 %arg) {
   %add2 = add i32 %zext, 299
   ret i32 %add2
 }
+
+define i32 @zext_nneg_overflow_multi_use(i8 %arg) {
+; CHECK-LABEL: @zext_nneg_overflow_multi_use(
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
+; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
+; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
+; CHECK-NEXT:    [[ADD2:%.*]] = add nuw nsw i32 [[ZEXT]], 299
+; CHECK-NEXT:    ret i32 [[ADD2]]
+;
+  %add = add i8 %arg, -2
+  %zext = zext nneg i8 %add to i32
+  call void @use32(i32 %zext)
+  %add2 = add i32 %zext, 299
+  ret i32 %add2
+}

>From 0f37733f316a75e44270c0ce46d85ccc8172e65a Mon Sep 17 00:00:00 2001
From: Aethezz <ellisonlao999 at gmail.com>
Date: Thu, 25 Sep 2025 12:16:26 -0400
Subject: [PATCH 4/4] fix extra case and multiuse

---
 .../InstCombine/InstCombineAddSub.cpp         | 27 +++++++++++++------
 llvm/test/Transforms/InstCombine/zext.ll      | 17 +-----------
 2 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 1a339e2620e82..8d4a2a11d235e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1915,22 +1915,33 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
     const APInt *C1, *C2;
     if (match(&I,
               m_Add(m_NNegZExt(m_Add(m_Value(X), m_APInt(C1))), m_APInt(C2)))) {
-      // Check if inner constant C1 is negative C1 < 0 and outer constant C2 >=
-      // 0
+      // Check if inner const C1 is negative C1 < 0 and outer const C2 >= 0
       if (!C1->isNegative() || C2->isNegative())
         return nullptr;
 
       APInt Sum = C1->sext(C2->getBitWidth()) + *C2;
-      APInt newSum = Sum.trunc(C1->getBitWidth());
 
       if (!Sum.isSignedIntN(C1->getBitWidth()))
         return nullptr;
 
-      // X if sum is zero, else X + newSum
-      Value *Inner =
-          Sum.isZero()
-              ? X
-              : Builder.CreateAdd(X, ConstantInt::get(X->getType(), newSum));
+      Value *Inner;
+
+      if (Sum.isZero()) {
+        Inner = X;
+      } else {
+        unsigned XWidth = X->getType()->getIntegerBitWidth();
+        APInt MinX = APInt::getSignedMinValue(XWidth);
+        if ((MinX + Sum.trunc(XWidth)).isNegative())
+          return nullptr;
+
+        auto *ZExt = cast<ZExtInst>(I.getOperand(0));
+        auto *InnerAdd = ZExt->getOperand(0);
+        if (!ZExt->hasOneUse() || !InnerAdd->hasOneUse())
+          return nullptr;
+
+        Inner = Builder.CreateAdd(
+            X, ConstantInt::get(X->getType(), Sum.trunc(C1->getBitWidth())));
+      }
 
       return new ZExtInst(Inner, I.getType());
     }
diff --git a/llvm/test/Transforms/InstCombine/zext.ll b/llvm/test/Transforms/InstCombine/zext.ll
index 5b3e7caea3bb8..2f9cbcdf00218 100644
--- a/llvm/test/Transforms/InstCombine/zext.ll
+++ b/llvm/test/Transforms/InstCombine/zext.ll
@@ -1020,8 +1020,7 @@ define i32 @zext_nneg_no_cancel_multi_use(i8 %arg) {
 ; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
 ; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
 ; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
-; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[ARG]], -1
-; CHECK-NEXT:    [[ADD2:%.*]] = zext i8 [[TMP1]] to i32
+; CHECK-NEXT:    [[ADD2:%.*]] = add nuw nsw i32 [[ZEXT]], 1
 ; CHECK-NEXT:    ret i32 [[ADD2]]
 ;
   %add = add i8 %arg, -2
@@ -1044,17 +1043,3 @@ define i32 @zext_nneg_overflow(i8 %arg) {
   ret i32 %add2
 }
 
-define i32 @zext_nneg_overflow_multi_use(i8 %arg) {
-; CHECK-LABEL: @zext_nneg_overflow_multi_use(
-; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[ARG:%.*]], -2
-; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i8 [[ADD]] to i32
-; CHECK-NEXT:    call void @use32(i32 [[ZEXT]])
-; CHECK-NEXT:    [[ADD2:%.*]] = add nuw nsw i32 [[ZEXT]], 299
-; CHECK-NEXT:    ret i32 [[ADD2]]
-;
-  %add = add i8 %arg, -2
-  %zext = zext nneg i8 %add to i32
-  call void @use32(i32 %zext)
-  %add2 = add i32 %zext, 299
-  ret i32 %add2
-}



More information about the llvm-commits mailing list