[llvm] [InstCombine] Fold an unsigned comparison of `add nsw X, C` with a constant into a signed comparison (PR #103480)

Volodymyr Vasylkun via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 13 15:10:00 PDT 2024


https://github.com/Poseydon42 created https://github.com/llvm/llvm-project/pull/103480

Given an unsigned integer comparison of `add nsw X, C1` with some constant `C2` we can fold it into a signed comparison of `X` and `C2 - C1` under the following conditions:
  * There's a `nsw` flag on the addition
  * `C2` is non-negative
  * `X + C1` is non-negative
  * `C2 - C1` is non-negative

Proofs: https://alive2.llvm.org/ce/z/EhCw3E

This would help us with a regression that is currently blocking #101049 from being merged.

>From c3b4ae6531eaeb9841759a9c15fd3d63b0f17782 Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Tue, 13 Aug 2024 23:01:11 +0100
Subject: [PATCH 1/2] Precommit tests

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

diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index baa6f3d51a40ef..88ccda417763ee 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3102,3 +3102,74 @@ define i1 @uge_add_C2_pow2_C_neg(i8 %x) {
 }
 
 declare void @llvm.assume(i1)
+
+; Change an unsigned predicate to signed in icmp (add x, C1), C2
+define i1 @icmp_add_constant_with_constant_ult_to_slt(i32 range(i32 -4, 10) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt(
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 5
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 13
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add nsw i32 %x, 5
+  %cmp = icmp ult i32 %add, 13
+  ret i1 %cmp
+}
+
+define i1 @icmp_add_constant_with_constant_ugt_to_sgt(i32 range(i32 -4, 10) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ugt_to_sgt(
+; CHECK-NEXT:    [[TMP1:%.*]] = add nsw i32 [[X:%.*]], -3
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[TMP1]], -13
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add nsw i32 %x, 10
+  %cmp = icmp ugt i32 %add, 12
+  ret i1 %cmp
+}
+
+; Negative test: x + C1 may be negative
+define i1 @icmp_add_constant_with_constant_ult_to_slt_neg1(i32 range(i32 -5, 10) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt_neg1(
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 20
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add nsw i32 %x, 4
+  %cmp = icmp ult i32 %add, 20
+  ret i1 %cmp
+}
+
+; Negative test: missing nsw flag
+define i1 @icmp_add_constant_with_constant_ult_to_slt_neg2(i8 range(i8 -4, 120) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt_neg2(
+; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X:%.*]], 15
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[ADD]], 20
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add i8 %x, 15
+  %cmp = icmp ult i8 %add, 20
+  ret i1 %cmp
+}
+
+; Negative test: C2 is negative
+define i1 @icmp_add_constant_with_constant_ult_to_slt_neg3(i32 range(i32 -4, 10) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt_neg3(
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], -6
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add nsw i32 %x, 4
+  %cmp = icmp ult i32 %add, -6
+  ret i1 %cmp
+}
+
+; Negative test: C2 - C1 is negative
+define i1 @icmp_add_constant_with_constant_ult_to_slt_neg4(i32 range(i32 -4, 10) %x) {
+; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt_neg4(
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 5
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %add = add nsw i32 %x, 5
+  %cmp = icmp ult i32 %add, 2
+  ret i1 %cmp
+}

>From 12f5eff910e56ab59e0285131bf0b92a2b3eb883 Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Tue, 13 Aug 2024 23:03:46 +0100
Subject: [PATCH 2/2] Implement fold & update tests

---
 llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp    | 7 +++++++
 llvm/test/Transforms/InstCombine/icmp-add.ll               | 6 ++----
 llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll | 2 +-
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 10a89b47e07537..b7eac02dd21745 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3077,6 +3077,13 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp,
       return new ICmpInst(Pred, X, ConstantInt::get(Ty, NewC));
   }
 
+  ConstantRange LHS_CR = computeConstantRange(X, true).add(*C2);
+  if (ICmpInst::isUnsigned(Pred) && Add->hasNoSignedWrap() &&
+      C.isNonNegative() && LHS_CR.isAllNonNegative() &&
+      (C - *C2).isNonNegative())
+    return new ICmpInst(ICmpInst::getSignedPredicate(Pred), X,
+                        ConstantInt::get(Ty, C - *C2));
+
   auto CR = ConstantRange::makeExactICmpRegion(Pred, C).subtract(*C2);
   const APInt &Upper = CR.getUpper();
   const APInt &Lower = CR.getLower();
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 88ccda417763ee..07e7756915b459 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -3106,8 +3106,7 @@ declare void @llvm.assume(i1)
 ; Change an unsigned predicate to signed in icmp (add x, C1), C2
 define i1 @icmp_add_constant_with_constant_ult_to_slt(i32 range(i32 -4, 10) %x) {
 ; CHECK-LABEL: @icmp_add_constant_with_constant_ult_to_slt(
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 5
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[ADD]], 13
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 8
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %add = add nsw i32 %x, 5
@@ -3117,8 +3116,7 @@ define i1 @icmp_add_constant_with_constant_ult_to_slt(i32 range(i32 -4, 10) %x)
 
 define i1 @icmp_add_constant_with_constant_ugt_to_sgt(i32 range(i32 -4, 10) %x) {
 ; CHECK-LABEL: @icmp_add_constant_with_constant_ugt_to_sgt(
-; CHECK-NEXT:    [[TMP1:%.*]] = add nsw i32 [[X:%.*]], -3
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[TMP1]], -13
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], 2
 ; CHECK-NEXT:    ret i1 [[CMP]]
 ;
   %add = add nsw i32 %x, 10
diff --git a/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll b/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
index 61ed955ea13e4e..6fc52ab3f26e03 100644
--- a/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
+++ b/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
@@ -1359,7 +1359,7 @@ define void @PR27626_5(ptr %a, i32 %x, i32 %y, i32 %z, i64 %n) {
 ; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[SMAX]], -4
 ; CHECK-NEXT:    [[TMP1:%.*]] = lshr i64 [[TMP0]], 1
 ; CHECK-NEXT:    [[TMP2:%.*]] = add nuw nsw i64 [[TMP1]], 1
-; CHECK-NEXT:    [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[TMP0]], 6
+; CHECK-NEXT:    [[MIN_ITERS_CHECK:%.*]] = icmp slt i64 [[N]], 10
 ; CHECK-NEXT:    br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
 ; CHECK:       vector.ph:
 ; CHECK-NEXT:    [[N_VEC:%.*]] = and i64 [[TMP2]], 9223372036854775804



More information about the llvm-commits mailing list