[llvm] [InstCombine] fold icmp with add/sub instructions having the same operands (PR #143241)

Acthinks Yang via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 9 03:06:19 PDT 2025


https://github.com/Acthinks updated https://github.com/llvm/llvm-project/pull/143241

>From eb0cf99f362879f9ec8c2a6dd9aac6585169a763 Mon Sep 17 00:00:00 2001
From: QiYue <yangzhh at mail.ustc.edu.cn>
Date: Sat, 7 Jun 2025 12:34:28 +0800
Subject: [PATCH 1/4] [InstrCombin] fold icmp with add/sub instructions having
 the same operands

Closes#143211
---
 .../InstCombine/InstCombineCompares.cpp       |  41 +++++++
 .../Transforms/InstCombine/icmp-subadd.ll     | 114 ++++++++++++++++++
 2 files changed, 155 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-subadd.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index c112fae351817..1054d091fd226 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7728,6 +7728,47 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
+  // In case of a comparison with add/sub instructions having the same operands,
+  // check whether cmp operands have same signed no wrap. If so, just compare
+  // the sub's second operand and zero.
+  // For example:
+  //   %tmp1 = sub nsw i8 %x, %y
+  //   %tmp2 = add nsw i8 %x, %y
+  //   %cmp = icmp sgt i8 %tmp1, %tmp2
+  // transform this into:
+  //   %cmp = icmp slt i32 %y, 0
+  // This handles similar cases to transform.
+  {
+    Value *A, *B;
+    auto *I0 = dyn_cast<OverflowingBinaryOperator>(Op0);
+    auto *I1 = dyn_cast<OverflowingBinaryOperator>(Op1);
+    bool UnsignedCmp = ICmpInst::isUnsigned(Pred);
+    bool SignedCmp = ICmpInst::isSigned(Pred);
+    bool EqualityCmp = ICmpInst::isEquality(Pred);
+
+    if (I0 && I1) {
+      bool I0NUW = I0->hasNoUnsignedWrap();
+      bool I1NUW = I1->hasNoUnsignedWrap();
+      bool I0NSW = I0->hasNoSignedWrap();
+      bool I1NSW = I1->hasNoSignedWrap();
+      bool Swaped = false;
+      if ((UnsignedCmp && I0NUW && I1NUW) || (SignedCmp && I0NSW && I1NSW) ||
+          (EqualityCmp && I0NUW && I0NSW && I1NUW && I1NSW)) {
+        if (I0->getOpcode() == Instruction::Add &&
+            I1->getOpcode() == Instruction::Sub) {
+          std::swap(I0, I1);
+          Swaped = true;
+        }
+        if (match(I0, m_Sub(m_Value(A), m_Value(B))) &&
+            (match(I1, m_Add(m_Specific(A), m_Specific(B))) ||
+             match(I1, m_Add(m_Specific(B), m_Specific(A))))) {
+          return new ICmpInst(Swaped ? Pred : I.getSwappedPredicate(), B,
+                              ConstantInt::get(Op0->getType(), 0));
+        }
+      }
+    }
+  }
+
   // Try to optimize equality comparisons against alloca-based pointers.
   if (Op0->getType()->isPointerTy() && I.isEquality()) {
     assert(Op1->getType()->isPointerTy() &&
diff --git a/llvm/test/Transforms/InstCombine/icmp-subadd.ll b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
new file mode 100644
index 0000000000000..46e5ebe57a1b0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
@@ -0,0 +1,114 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @test-same-operands-sub-add-nsw-icmp-sgt(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-sgt(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[B]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nsw i8 %a, %b
+  %add = add nsw i8 %a, %b
+  %cmp = icmp sgt i8 %sub, %add
+  ret i1 %cmp
+}
+
+define i1 @test-same-operands-sub-add-nsw-icmp-slt(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-slt(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[B]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nsw i8 %a, %b
+  %add = add nsw i8 %a, %b
+  %cmp = icmp slt i8 %sub, %add
+  ret i1 %cmp
+}
+
+define i1 @test-same-operands-sub-add-nsw-icmp-sle(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-sle(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[B]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nsw i8 %a, %b
+  %add = add nsw i8 %a, %b
+  %cmp = icmp sle i8 %sub, %add
+  ret i1 %cmp
+}
+
+define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[B]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nuw nsw i8 %a, %b
+  %add = add nuw nsw i8 %a, %b
+  %cmp = icmp eq i8 %sub, %add
+  ret i1 %cmp
+}
+
+; Check not folded
+define i1 @test-same-operands-sub-add-nsw-icmp-eq(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-eq(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SUB]], [[ADD]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nsw i8 %a, %b
+  %add = add nsw i8 %a, %b
+  %cmp = icmp eq i8 %sub, %add
+  ret i1 %cmp
+}
+
+define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-add-sub-nsw-icmp-sgt(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[B]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nsw i8 %a, %b
+  %add = add nsw i8 %a, %b
+  %cmp = icmp sgt i8 %add, %sub
+  ret i1 %cmp
+}
+
+define i1 @test-add-sub-nuw-icmp-uge(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-uge(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %sub = sub nuw i8 %a, %b
+  %add = add nuw i8 %a, %b
+  %cmp = icmp uge i8 %add, %sub
+  ret i1 %cmp
+}
+
+; Check not folded
+define i1 @test-add-sub-nuw-icmp-sge(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-sge(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sge i8 [[ADD]], [[SUB]]
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %sub = sub nuw i8 %a, %b
+  %add = add nuw i8 %a, %b
+  %cmp = icmp sge i8 %add, %sub
+  ret i1 %cmp
+}
+
+define i1 @test-add-swap-sub-nuw-icmp-uge(i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @test-add-swap-sub-nuw-icmp-uge(
+; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %sub = sub nuw i8 %a, %b
+  %add = add nuw i8 %b, %a
+  %cmp = icmp uge i8 %add, %sub
+  ret i1 %cmp
+}

>From e96e81eeb01a6df7316e71d69afeb5ff358ae1a2 Mon Sep 17 00:00:00 2001
From: Acthinks <yangzhh at mail.ustc.edu.cn>
Date: Sat, 7 Jun 2025 19:58:52 +0800
Subject: [PATCH 2/4] Relax equality no wrap constraints & remove swap handling

---
 .../InstCombine/InstCombineCompares.cpp       | 13 +++--------
 .../Transforms/InstCombine/icmp-subadd.ll     | 22 +++++++++++++------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 1054d091fd226..905ac3a71d114 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7751,18 +7751,11 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
       bool I1NUW = I1->hasNoUnsignedWrap();
       bool I0NSW = I0->hasNoSignedWrap();
       bool I1NSW = I1->hasNoSignedWrap();
-      bool Swaped = false;
       if ((UnsignedCmp && I0NUW && I1NUW) || (SignedCmp && I0NSW && I1NSW) ||
-          (EqualityCmp && I0NUW && I0NSW && I1NUW && I1NSW)) {
-        if (I0->getOpcode() == Instruction::Add &&
-            I1->getOpcode() == Instruction::Sub) {
-          std::swap(I0, I1);
-          Swaped = true;
-        }
+          (EqualityCmp && ((I0NUW && I1NUW) || (I0NSW && I1NSW)))) {
         if (match(I0, m_Sub(m_Value(A), m_Value(B))) &&
-            (match(I1, m_Add(m_Specific(A), m_Specific(B))) ||
-             match(I1, m_Add(m_Specific(B), m_Specific(A))))) {
-          return new ICmpInst(Swaped ? Pred : I.getSwappedPredicate(), B,
+            match(I1, m_Add(m_Specific(A), m_Specific(B)))) {
+          return new ICmpInst(I.getSwappedPredicate(), B,
                               ConstantInt::get(Op0->getType(), 0));
         }
       }
diff --git a/llvm/test/Transforms/InstCombine/icmp-subadd.ll b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
index 46e5ebe57a1b0..03b75097233fc 100644
--- a/llvm/test/Transforms/InstCombine/icmp-subadd.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
@@ -49,13 +49,10 @@ define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
-; Check not folded
 define i1 @test-same-operands-sub-add-nsw-icmp-eq(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-same-operands-sub-add-nsw-icmp-eq(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[SUB]], [[ADD]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[B]], 0
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub nsw i8 %a, %b
@@ -64,10 +61,13 @@ define i1 @test-same-operands-sub-add-nsw-icmp-eq(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
+; Should floded by foldICmpCommutative in the future
 define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-sub-nsw-icmp-sgt(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[B]], 0
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[ADD]], [[SUB]]
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub nsw i8 %a, %b
@@ -76,10 +76,14 @@ define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
+; Should floded by foldICmpCommutative in the future
 define i1 @test-add-sub-nuw-icmp-uge(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-uge(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[ADD]], [[SUB]]
+; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub nuw i8 %a, %b
   %add = add nuw i8 %a, %b
@@ -102,10 +106,14 @@ define i1 @test-add-sub-nuw-icmp-sge(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
+; Should floded by foldICmpCommutative in the future
 define i1 @test-add-swap-sub-nuw-icmp-uge(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-swap-sub-nuw-icmp-uge(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i8 [[A]], [[B]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[B]], [[A]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[ADD]], [[SUB]]
+; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub nuw i8 %a, %b
   %add = add nuw i8 %b, %a

>From 687c94109124e9ca55e72658bfb485306cfaede1 Mon Sep 17 00:00:00 2001
From: Acthinks <yangzhh at mail.ustc.edu.cn>
Date: Sun, 8 Jun 2025 00:43:47 +0800
Subject: [PATCH 3/4] 1. A more concise implementation of the original swap
 code

2. looser equality constraints, such as nsw/nuw combinations, can be folded
---
 .../InstCombine/InstCombineCompares.cpp       | 41 +++++++++++--------
 .../Transforms/InstCombine/icmp-subadd.ll     | 21 +++-------
 2 files changed, 28 insertions(+), 34 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 905ac3a71d114..1f2b8cb6b3764 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7728,36 +7728,41 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
-  // In case of a comparison with add/sub instructions having the same operands,
-  // check whether cmp operands have same signed no wrap. If so, just compare
-  // the sub's second operand and zero.
-  // For example:
-  //   %tmp1 = sub nsw i8 %x, %y
-  //   %tmp2 = add nsw i8 %x, %y
-  //   %cmp = icmp sgt i8 %tmp1, %tmp2
-  // transform this into:
-  //   %cmp = icmp slt i32 %y, 0
+  // When comparing results of sub and add instructions with identical operands,
+  // optimization is valid when the comparison type and overflow flags satisfy:
+  //   - Signed comparisons (slt/sgt/sle/sge) require 'nsw' flags
+  //   - Unsigned comparisons (ult/ugt/ule/uge) require 'nuw' flags
+  //   - Equality comparisons (eq/ne) accept either 'nsw' or 'nuw'
+  //
+  // If conditions are met, the comparison simplifies to a zero comparison on
+  // the second operand of the sub instruction with the swapped predicate.
+  // Example transformation for signed comparison:
+  //   %sub = sub nsw i8 %x, %y
+  //   %add = add nsw i8 %x, %y
+  //   %cmp = icmp sgt i8 %sub, %add  // (x - y) > (x + y)
+  // becomes:
+  //   %cmp = icmp slt i8 %y, 0      // y < 0
+  //
   // This handles similar cases to transform.
   {
     Value *A, *B;
     auto *I0 = dyn_cast<OverflowingBinaryOperator>(Op0);
     auto *I1 = dyn_cast<OverflowingBinaryOperator>(Op1);
-    bool UnsignedCmp = ICmpInst::isUnsigned(Pred);
-    bool SignedCmp = ICmpInst::isSigned(Pred);
-    bool EqualityCmp = ICmpInst::isEquality(Pred);
-
     if (I0 && I1) {
       bool I0NUW = I0->hasNoUnsignedWrap();
       bool I1NUW = I1->hasNoUnsignedWrap();
       bool I0NSW = I0->hasNoSignedWrap();
       bool I1NSW = I1->hasNoSignedWrap();
+      bool UnsignedCmp = ICmpInst::isUnsigned(Pred);
+      bool SignedCmp = ICmpInst::isSigned(Pred);
+      bool EqualityCmp = ICmpInst::isEquality(Pred);
+      CmpPredicate CmpPred;
       if ((UnsignedCmp && I0NUW && I1NUW) || (SignedCmp && I0NSW && I1NSW) ||
-          (EqualityCmp && ((I0NUW && I1NUW) || (I0NSW && I1NSW)))) {
-        if (match(I0, m_Sub(m_Value(A), m_Value(B))) &&
-            match(I1, m_Add(m_Specific(A), m_Specific(B)))) {
-          return new ICmpInst(I.getSwappedPredicate(), B,
+          (EqualityCmp && ((I0NUW || I0NSW) && (I1NUW || I1NSW)))) {
+        if (match(&I, m_c_ICmp(CmpPred, m_Sub(m_Value(A), m_Value(B)),
+                               m_c_Add(m_Deferred(A), m_Deferred(B)))))
+          return new ICmpInst(CmpPredicate::getSwapped(CmpPred), B,
                               ConstantInt::get(Op0->getType(), 0));
-        }
       }
     }
   }
diff --git a/llvm/test/Transforms/InstCombine/icmp-subadd.ll b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
index 03b75097233fc..fd7e1250d893f 100644
--- a/llvm/test/Transforms/InstCombine/icmp-subadd.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-subadd.ll
@@ -43,8 +43,8 @@ define i1 @test-same-operands-sub-add-nsw-nuw-icmp-eq(i8 %a, i8 %b) {
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[B]], 0
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
-  %sub = sub nuw nsw i8 %a, %b
-  %add = add nuw nsw i8 %a, %b
+  %sub = sub nsw i8 %a, %b
+  %add = add nuw i8 %a, %b
   %cmp = icmp eq i8 %sub, %add
   ret i1 %cmp
 }
@@ -61,13 +61,10 @@ define i1 @test-same-operands-sub-add-nsw-icmp-eq(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
-; Should floded by foldICmpCommutative in the future
 define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-sub-nsw-icmp-sgt(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[ADD]], [[SUB]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i8 [[B]], 0
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %sub = sub nsw i8 %a, %b
@@ -76,14 +73,10 @@ define i1 @test-add-sub-nsw-icmp-sgt(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
-; Should floded by foldICmpCommutative in the future
 define i1 @test-add-sub-nuw-icmp-uge(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-sub-nuw-icmp-uge(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[ADD]], [[SUB]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %sub = sub nuw i8 %a, %b
   %add = add nuw i8 %a, %b
@@ -106,14 +99,10 @@ define i1 @test-add-sub-nuw-icmp-sge(i8 %a, i8 %b) {
   ret i1 %cmp
 }
 
-; Should floded by foldICmpCommutative in the future
 define i1 @test-add-swap-sub-nuw-icmp-uge(i8 %a, i8 %b) {
 ; CHECK-LABEL: define i1 @test-add-swap-sub-nuw-icmp-uge(
 ; CHECK-SAME: i8 [[A:%.*]], i8 [[B:%.*]]) {
-; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i8 [[A]], [[B]]
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[B]], [[A]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[ADD]], [[SUB]]
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %sub = sub nuw i8 %a, %b
   %add = add nuw i8 %b, %a

>From 6975edbe15d25d604b31b169ba5ef2b9bc49ecb7 Mon Sep 17 00:00:00 2001
From: Acthinks <yangzhh at mail.ustc.edu.cn>
Date: Mon, 9 Jun 2025 17:27:27 +0800
Subject: [PATCH 4/4] Code Refactoring

---
 .../InstCombine/InstCombineCompares.cpp       | 50 ++++++++-----------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 1f2b8cb6b3764..67f0cbeacfd3d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7728,41 +7728,33 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
-  // When comparing results of sub and add instructions with identical operands,
-  // optimization is valid when the comparison type and overflow flags satisfy:
-  //   - Signed comparisons (slt/sgt/sle/sge) require 'nsw' flags
-  //   - Unsigned comparisons (ult/ugt/ule/uge) require 'nuw' flags
-  //   - Equality comparisons (eq/ne) accept either 'nsw' or 'nuw'
-  //
-  // If conditions are met, the comparison simplifies to a zero comparison on
-  // the second operand of the sub instruction with the swapped predicate.
-  // Example transformation for signed comparison:
-  //   %sub = sub nsw i8 %x, %y
-  //   %add = add nsw i8 %x, %y
-  //   %cmp = icmp sgt i8 %sub, %add  // (x - y) > (x + y)
-  // becomes:
-  //   %cmp = icmp slt i8 %y, 0      // y < 0
-  //
-  // This handles similar cases to transform.
+  // icmp slt (sub nsw x, y), (add nsw x, y)  -->  icmp sgt y, 0
+  // icmp sgt (sub nsw x, y), (add nsw x, y)  -->  icmp slt y, 0
+  // icmp sle (sub nsw x, y), (add nsw x, y)  -->  icmp sge y, 0
+  // icmp sge (sub nsw x, y), (add nsw x, y)  -->  icmp sle y, 0
+  // icmp ult (sub nuw x, y), (add nuw x, y)  -->  icmp ugt y, 0
+  // icmp ugt (sub nuw x, y), (add nuw x, y)  -->  icmp ult y, 0
+  // icmp ule (sub nuw x, y), (add nuw x, y)  -->  icmp uge y, 0
+  // icmp uge (sub nuw x, y), (add nuw x, y)  -->  icmp ule y, 0
+  // icmp eq (sub nsw/nuw x, y), (add nsw/nuw x, y)   -->  icmp eq y, 0
+  // icmp ne (sub nsw/nuw x, y), (add nsw/nuw x, y)   -->  icmp ne y, 0
   {
     Value *A, *B;
-    auto *I0 = dyn_cast<OverflowingBinaryOperator>(Op0);
-    auto *I1 = dyn_cast<OverflowingBinaryOperator>(Op1);
-    if (I0 && I1) {
+    CmpPredicate CmpPred;
+    if (match(&I, m_c_ICmp(CmpPred, m_Sub(m_Value(A), m_Value(B)),
+                           m_c_Add(m_Deferred(A), m_Deferred(B))))) {
+      auto *I0 = cast<OverflowingBinaryOperator>(Op0);
+      auto *I1 = cast<OverflowingBinaryOperator>(Op1);
       bool I0NUW = I0->hasNoUnsignedWrap();
       bool I1NUW = I1->hasNoUnsignedWrap();
       bool I0NSW = I0->hasNoSignedWrap();
       bool I1NSW = I1->hasNoSignedWrap();
-      bool UnsignedCmp = ICmpInst::isUnsigned(Pred);
-      bool SignedCmp = ICmpInst::isSigned(Pred);
-      bool EqualityCmp = ICmpInst::isEquality(Pred);
-      CmpPredicate CmpPred;
-      if ((UnsignedCmp && I0NUW && I1NUW) || (SignedCmp && I0NSW && I1NSW) ||
-          (EqualityCmp && ((I0NUW || I0NSW) && (I1NUW || I1NSW)))) {
-        if (match(&I, m_c_ICmp(CmpPred, m_Sub(m_Value(A), m_Value(B)),
-                               m_c_Add(m_Deferred(A), m_Deferred(B)))))
-          return new ICmpInst(CmpPredicate::getSwapped(CmpPred), B,
-                              ConstantInt::get(Op0->getType(), 0));
+      if ((ICmpInst::isUnsigned(Pred) && I0NUW && I1NUW) ||
+          (ICmpInst::isSigned(Pred) && I0NSW && I1NSW) ||
+          (ICmpInst::isEquality(Pred) &&
+           ((I0NUW || I0NSW) && (I1NUW || I1NSW)))) {
+        return new ICmpInst(CmpPredicate::getSwapped(CmpPred), B,
+                            ConstantInt::get(Op0->getType(), 0));
       }
     }
   }



More information about the llvm-commits mailing list