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

via llvm-commits llvm-commits at lists.llvm.org
Sat Dec 6 09:18:22 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

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).


---
Full diff: https://github.com/llvm/llvm-project/pull/170989.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp (+14-3) 
- (modified) llvm/test/Transforms/InstCombine/sub-minmax.ll (+22) 


``````````diff
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 c5af57449bf71..36b487f5570f3 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 range(i8 -115, -128) [[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)

``````````

</details>


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


More information about the llvm-commits mailing list