[llvm] [InstCombine] Preserve range information for `Op0 -nsw umin(X, Op0) --> usub.sat(Op0, X)` (PR #170989)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 6 09:17:50 PST 2025


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/170989

This implements the first part discussed in https://github.com/llvm/llvm-project/issues/168880.
Alive2: https://alive2.llvm.org/ce/z/CXAcff

For `umin(X, Op0) - Op0` we can also preserve the information implied by the nsw flag. But it is a bit more complicated and doesn't benefit real-world cases (See https://github.com/dtcxzyw/llvm-opt-benchmark/issues/3123 and https://github.com/dtcxzyw/llvm-opt-benchmark/issues/3122).


>From e03de242e7b8b29e2e2d48cf3e5b102d34f995ab Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 7 Dec 2025 01:07:51 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 .../test/Transforms/InstCombine/sub-minmax.ll | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/sub-minmax.ll b/llvm/test/Transforms/InstCombine/sub-minmax.ll
index c5af57449bf71..394c96f602417 100644
--- a/llvm/test/Transforms/InstCombine/sub-minmax.ll
+++ b/llvm/test/Transforms/InstCombine/sub-minmax.ll
@@ -663,6 +663,28 @@ define i8 @umin_sub_op0_use(i8 %x, i8 %y) {
   ret i8 %r
 }
 
+define i8 @umin_nsw_sub_op1_rhs_nneg_constant(i8 %y) {
+; CHECK-LABEL: define {{[^@]+}}@umin_nsw_sub_op1_rhs_nneg_constant
+; CHECK-SAME: (i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 13)
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %u = call i8 @llvm.umin.i8(i8 %y, i8 13)
+  %r = sub nsw i8 %y, %u
+  ret i8 %r
+}
+
+define i8 @umin_nsw_sub_op1_rhs_neg_constant(i8 %y) {
+; CHECK-LABEL: define {{[^@]+}}@umin_nsw_sub_op1_rhs_neg_constant
+; CHECK-SAME: (i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 -13)
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %u = call i8 @llvm.umin.i8(i8 %y, i8 -13)
+  %r = sub nsw i8 %y, %u
+  ret i8 %r
+}
+
 ;
 ; sub(add(X,Y), s/umin(X,Y)) --> s/umax(X,Y)
 ; sub(add(X,Y), s/umax(X,Y)) --> s/umin(X,Y)

>From 84e67d34ad0bc752154583d41bc762a514fecda4 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 7 Dec 2025 01:12:13 +0800
Subject: [PATCH 2/2] [InstCombine] Preserve range information for `Op0 -
 umin(X, Op0) --> usub.sat(Op0, X)`

---
 .../InstCombine/InstCombineAddSub.cpp           | 17 ++++++++++++++---
 llvm/test/Transforms/InstCombine/sub-minmax.ll  |  2 +-
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 9bee523c7b7e5..e98702cff66a9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2857,9 +2857,20 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
         I, Builder.CreateIntrinsic(Intrinsic::usub_sat, {Ty}, {X, Op1}));
 
   // Op0 - umin(X, Op0) --> usub.sat(Op0, X)
-  if (match(Op1, m_OneUse(m_c_UMin(m_Value(X), m_Specific(Op0)))))
-    return replaceInstUsesWith(
-        I, Builder.CreateIntrinsic(Intrinsic::usub_sat, {Ty}, {Op0, X}));
+  if (match(Op1, m_OneUse(m_c_UMin(m_Value(X), m_Specific(Op0))))) {
+    Value *USub = Builder.CreateIntrinsic(Intrinsic::usub_sat, {Ty}, {Op0, X});
+    if (auto *USubCall = dyn_cast<CallInst>(USub)) {
+      // Preserve range information implied by the nsw flag.
+      const APInt *C;
+      if (I.hasNoSignedWrap() && match(X, m_NonNegative(C))) {
+        ConstantRange CR = ConstantRange::makeExactNoWrapRegion(
+            Instruction::Sub, *C, OverflowingBinaryOperator::NoSignedWrap);
+        USubCall->addParamAttr(
+            0, Attribute::get(I.getContext(), Attribute::Range, CR));
+      }
+    }
+    return replaceInstUsesWith(I, USub);
+  }
 
   // Op0 - umax(X, Op0) --> 0 - usub.sat(X, Op0)
   if (match(Op1, m_OneUse(m_c_UMax(m_Value(X), m_Specific(Op0))))) {
diff --git a/llvm/test/Transforms/InstCombine/sub-minmax.ll b/llvm/test/Transforms/InstCombine/sub-minmax.ll
index 394c96f602417..36b487f5570f3 100644
--- a/llvm/test/Transforms/InstCombine/sub-minmax.ll
+++ b/llvm/test/Transforms/InstCombine/sub-minmax.ll
@@ -666,7 +666,7 @@ define i8 @umin_sub_op0_use(i8 %x, i8 %y) {
 define i8 @umin_nsw_sub_op1_rhs_nneg_constant(i8 %y) {
 ; CHECK-LABEL: define {{[^@]+}}@umin_nsw_sub_op1_rhs_nneg_constant
 ; CHECK-SAME: (i8 [[Y:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y]], i8 13)
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.usub.sat.i8(i8 range(i8 -115, -128) [[Y]], i8 13)
 ; CHECK-NEXT:    ret i8 [[R]]
 ;
   %u = call i8 @llvm.umin.i8(i8 %y, i8 13)



More information about the llvm-commits mailing list