[llvm] [InstCombine] Fold `icmp samesign u{gt/lt} (X +nsw C2), C` -> `icmp s{gt/lt} X, (C - C2)` (PR #169960)

Tirthankar Mazumder via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 4 09:01:55 PST 2025


https://github.com/wermos updated https://github.com/llvm/llvm-project/pull/169960

>From b7ac032f438af6bfe54677398be67d6854d98216 Mon Sep 17 00:00:00 2001
From: wermos <63574588+wermos at users.noreply.github.com>
Date: Sat, 29 Nov 2025 01:55:58 +0530
Subject: [PATCH 1/2] Pre-commit tests

---
 llvm/test/Transforms/InstCombine/icmp-add.ll | 64 ++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 8449c7c5ea935..a9f7d36602dfb 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3440,3 +3440,67 @@ define i1 @val_is_aligend_pred_mismatch(i32 %num) {
   %_0 = icmp sge i32 %num.masked, %num
   ret i1 %_0
 }
+
+define i1 @icmp_samesign_with_nsw_add(i32 %arg0) {
+; CHECK-LABEL: @icmp_samesign_with_nsw_add(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[ARG0:%.*]], -26
+; CHECK-NEXT:    [[V1:%.*]] = icmp ult i32 [[TMP0]], -8
+; CHECK-NEXT:    ret i1 [[V1]]
+;
+entry:
+  %v0 = add nsw i32 %arg0, -18
+  %v1 = icmp samesign ugt i32 %v0, 7
+  ret i1 %v1
+}
+
+; Shouldn't fire since -124 - 12 causes signed overflow
+define i1 @icmp_samesign_with_nsw_add_no_fire(i8 %arg0) {
+; CHECK-LABEL: @icmp_samesign_with_nsw_add_no_fire(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = add i8 [[ARG0:%.*]], -121
+; CHECK-NEXT:    [[V1:%.*]] = icmp ult i8 [[TMP0]], 123
+; CHECK-NEXT:    ret i1 [[V1]]
+;
+entry:
+  %v0 = add nsw i8 %arg0, 12
+  %v1 = icmp samesign ugt i8 %v0, -124
+  ret i1 %v1
+}
+
+define i1 @icmp_with_nuw_add(i32 %arg0) {
+; CHECK-LABEL: @icmp_with_nuw_add(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[V1:%.*]] = icmp ugt i32 [[ARG0:%.*]], 11
+; CHECK-NEXT:    ret i1 [[V1]]
+;
+entry:
+  %v0 = add nuw i32 %arg0, 7
+  %v1 = icmp ugt i32 %v0, 18
+  ret i1 %v1
+}
+
+define i1 @icmp_partial_negative_samesign_ult_to_slt(i8 range(i8 -1, 5) %x) {
+; CHECK-LABEL: @icmp_partial_negative_samesign_ult_to_slt(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X:%.*]], -5
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ult i8 [[ADD]], -3
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %add = add nsw i8 %x, -5
+  %cmp = icmp samesign ult i8 %add, -3
+  ret i1 %cmp
+}
+
+define i1 @icmp_pos_samesign_slt_to_ult(i8 range(i8 1, 5) %x) {
+; CHECK-LABEL: @icmp_pos_samesign_slt_to_ult(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ult i8 [[X:%.*]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %add = add nsw i8 %x, 1
+  %cmp = icmp samesign slt i8 %add, 3
+  ret i1 %cmp
+}

>From 0f78f473685f70c7bfecc6427764a1888134ab83 Mon Sep 17 00:00:00 2001
From: wermos <63574588+wermos at users.noreply.github.com>
Date: Sat, 29 Nov 2025 02:02:43 +0530
Subject: [PATCH 2/2] Fold `icmp samesign u{gt/lt} (add nsw X, C2), C` -> `icmp
 s{gt/lt} X, (C - C2)` whenever applicable.

---
 .../InstCombine/InstCombineCompares.cpp       | 51 ++++++++++++++-----
 llvm/test/Transforms/InstCombine/icmp-add.ll  | 16 +++---
 .../remove-loop-phi-multiply-by-zero.ll       |  8 +--
 3 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 33eee8e059486..0bfa8db174dfd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3132,7 +3132,7 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
 
   Value *Op0, *Op1;
   Instruction *Ext0, *Ext1;
-  const CmpInst::Predicate Pred = Cmp.getPredicate();
+  const CmpPredicate Pred = Cmp.getCmpPredicate();
   if (match(Add,
             m_Add(m_CombineAnd(m_Instruction(Ext0), m_ZExtOrSExt(m_Value(Op0))),
                   m_CombineAnd(m_Instruction(Ext1),
@@ -3167,24 +3167,49 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
 
   // If the add does not wrap, we can always adjust the compare by subtracting
   // the constants. Equality comparisons are handled elsewhere. SGE/SLE/UGE/ULE
-  // are canonicalized to SGT/SLT/UGT/ULT.
-  if ((Add->hasNoSignedWrap() &&
-       (Pred == ICmpInst::ICMP_SGT || Pred == ICmpInst::ICMP_SLT)) ||
-      (Add->hasNoUnsignedWrap() &&
-       (Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_ULT))) {
-    bool Overflow;
+  // has been canonicalized to SGT/SLT/UGT/ULT.
+  CmpInst::Predicate ChosenPred = Pred.getPreferredSignedPredicate();
+  bool AddIsNSW = Add->hasNoSignedWrap();
+  bool AddIsNUW = Add->hasNoUnsignedWrap();
+
+  auto ComputeNewConstant = [&](bool UseSignedMath) -> std::optional<APInt> {
+    bool Overflow = false;
     APInt NewC =
-        Cmp.isSigned() ? C.ssub_ov(*C2, Overflow) : C.usub_ov(*C2, Overflow);
+        UseSignedMath ? C.ssub_ov(*C2, Overflow) : C.usub_ov(*C2, Overflow);
     // If there is overflow, the result must be true or false.
     // TODO: Can we assert there is no overflow because InstSimplify always
     // handles those cases?
-    if (!Overflow)
-      // icmp Pred (add nsw X, C2), C --> icmp Pred X, (C - C2)
-      return new ICmpInst(Pred, X, ConstantInt::get(Ty, NewC));
+    if (Overflow)
+      return std::nullopt;
+    return NewC;
+  };
+
+  bool IsSignedCmp =
+      ChosenPred == ICmpInst::ICMP_SGT || ChosenPred == ICmpInst::ICMP_SLT;
+  bool IsUnsignedCmp =
+      ChosenPred == ICmpInst::ICMP_UGT || ChosenPred == ICmpInst::ICMP_ULT;
+
+  if (AddIsNSW && AddIsNUW) {
+    // in this case, we will try both flags, but with a preference for nuw and
+    // an unsigned predicate
+    if (std::optional<APInt> NewC =
+            ComputeNewConstant(/*UseSignedMath=*/false)) {
+      return new ICmpInst(ChosenPred, X, ConstantInt::get(Ty, *NewC));
+    }
+    if (std::optional<APInt> NewC =
+            ComputeNewConstant(/*UseSignedMath=*/true)) {
+      return new ICmpInst(ChosenPred, X, ConstantInt::get(Ty, *NewC));
+    }
+  } else if (AddIsNSW && IsSignedCmp) {
+    if (std::optional<APInt> NewC = ComputeNewConstant(/*UseSignedMath=*/true))
+      return new ICmpInst(ChosenPred, X, ConstantInt::get(Ty, *NewC));
+  } else if (AddIsNUW && IsUnsignedCmp) {
+    if (std::optional<APInt> NewC = ComputeNewConstant(/*UseSignedMath=*/false))
+      return new ICmpInst(ChosenPred, X, ConstantInt::get(Ty, *NewC));
   }
 
-  if (ICmpInst::isUnsigned(Pred) && Add->hasNoSignedWrap() &&
-      C.isNonNegative() && (C - *C2).isNonNegative() &&
+  if (ICmpInst::isUnsigned(Pred) && AddIsNSW && C.isNonNegative() &&
+      (C - *C2).isNonNegative() &&
       computeConstantRange(X, /*ForSigned=*/true).add(*C2).isAllNonNegative())
     return new ICmpInst(ICmpInst::getSignedPredicate(Pred), X,
                         ConstantInt::get(Ty, C - *C2));
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index a9f7d36602dfb..e5b977fc60e6e 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3444,8 +3444,7 @@ define i1 @val_is_aligend_pred_mismatch(i32 %num) {
 define i1 @icmp_samesign_with_nsw_add(i32 %arg0) {
 ; CHECK-LABEL: @icmp_samesign_with_nsw_add(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[ARG0:%.*]], -26
-; CHECK-NEXT:    [[V1:%.*]] = icmp ult i32 [[TMP0]], -8
+; CHECK-NEXT:    [[V1:%.*]] = icmp sgt i32 [[ARG0:%.*]], 25
 ; CHECK-NEXT:    ret i1 [[V1]]
 ;
 entry:
@@ -3454,9 +3453,9 @@ entry:
   ret i1 %v1
 }
 
-; Shouldn't fire since -124 - 12 causes signed overflow
-define i1 @icmp_samesign_with_nsw_add_no_fire(i8 %arg0) {
-; CHECK-LABEL: @icmp_samesign_with_nsw_add_no_fire(
+; Negative test; Fold shouldn't fire since -124 - 12 causes signed overflow
+define i1 @icmp_samesign_with_nsw_add_neg(i8 %arg0) {
+; CHECK-LABEL: @icmp_samesign_with_nsw_add_neg(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[TMP0:%.*]] = add i8 [[ARG0:%.*]], -121
 ; CHECK-NEXT:    [[V1:%.*]] = icmp ult i8 [[TMP0]], 123
@@ -3471,20 +3470,19 @@ entry:
 define i1 @icmp_with_nuw_add(i32 %arg0) {
 ; CHECK-LABEL: @icmp_with_nuw_add(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[V1:%.*]] = icmp ugt i32 [[ARG0:%.*]], 11
+; CHECK-NEXT:    [[V1:%.*]] = icmp ult i32 [[ARG0:%.*]], 11
 ; CHECK-NEXT:    ret i1 [[V1]]
 ;
 entry:
   %v0 = add nuw i32 %arg0, 7
-  %v1 = icmp ugt i32 %v0, 18
+  %v1 = icmp ult i32 %v0, 18
   ret i1 %v1
 }
 
 define i1 @icmp_partial_negative_samesign_ult_to_slt(i8 range(i8 -1, 5) %x) {
 ; CHECK-LABEL: @icmp_partial_negative_samesign_ult_to_slt(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X:%.*]], -5
-; CHECK-NEXT:    [[CMP:%.*]] = icmp samesign ult i8 [[ADD]], -3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[X:%.*]], 2
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
 entry:
diff --git a/llvm/test/Transforms/InstCombine/remove-loop-phi-multiply-by-zero.ll b/llvm/test/Transforms/InstCombine/remove-loop-phi-multiply-by-zero.ll
index a4a9de7fd1285..8eccdee2b5a4d 100644
--- a/llvm/test/Transforms/InstCombine/remove-loop-phi-multiply-by-zero.ll
+++ b/llvm/test/Transforms/InstCombine/remove-loop-phi-multiply-by-zero.ll
@@ -209,7 +209,7 @@ define double @test_multiple_phi_operands(ptr %arr_d, i1 %entry_cond) {
 ; CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr [[ARRAYIDX]], align 8
 ; CHECK-NEXT:    [[MUL]] = fmul fast double [[F_PROD_01]], [[TMP0]]
 ; CHECK-NEXT:    [[INC]] = add nuw nsw i64 [[I_02]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[I_02]], 999
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[I_02]], 999
 ; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[END:%.*]]
 ; CHECK:       end:
 ; CHECK-NEXT:    ret double [[MUL]]
@@ -248,7 +248,7 @@ define double @test_multiple_phi_operands_with_non_zero(ptr %arr_d, i1 %entry_co
 ; CHECK-NEXT:    [[TMP0:%.*]] = load double, ptr [[ARRAYIDX]], align 8
 ; CHECK-NEXT:    [[MUL]] = fmul fast double [[F_PROD_01]], [[TMP0]]
 ; CHECK-NEXT:    [[INC]] = add nuw nsw i64 [[I_02]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[I_02]], 999
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[I_02]], 999
 ; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[END:%.*]]
 ; CHECK:       end:
 ; CHECK-NEXT:    ret double [[MUL]]
@@ -355,7 +355,7 @@ define i32 @test_multiple_int_phi_operands(ptr %arr_d, i1 %entry_cond) {
 ; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
 ; CHECK-NEXT:    [[MUL]] = mul i32 [[F_PROD_01]], [[TMP0]]
 ; CHECK-NEXT:    [[INC]] = add nuw nsw i64 [[I_02]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[I_02]], 999
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[I_02]], 999
 ; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[END:%.*]]
 ; CHECK:       end:
 ; CHECK-NEXT:    ret i32 [[MUL]]
@@ -394,7 +394,7 @@ define i32 @test_multiple_int_phi_operands_initalise_to_non_zero(ptr %arr_d, i1
 ; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
 ; CHECK-NEXT:    [[MUL]] = mul i32 [[F_PROD_01]], [[TMP0]]
 ; CHECK-NEXT:    [[INC]] = add nuw nsw i64 [[I_02]], 1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[I_02]], 999
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[I_02]], 999
 ; CHECK-NEXT:    br i1 [[CMP]], label [[FOR_BODY]], label [[END:%.*]]
 ; CHECK:       end:
 ; CHECK-NEXT:    ret i32 [[MUL]]



More information about the llvm-commits mailing list