[llvm] [InstCombine] Fold uadd.sat(X, C) - C to umin(X, ~C) (PR #215130)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 9 12:14:31 PDT 2026
https://github.com/Bagodiya created https://github.com/llvm/llvm-project/pull/215130
`uadd.sat(X, C) - C --> umin(X, ~C)` for nonzero `C`.
The saturating add gives `X + C` or `UMAX`, so subtracting `C` leaves `X` or
`UMAX - C`, which is the unsigned minimum. `UMAX - C == ~C`, so the constant
is just the inverted `C`.
There was already a test documenting this miss in saturating-add-sub.ll
(`test_scalar_uadd_sub_const`) - it folds now.
Alive2: https://alive2.llvm.org/ce/z/3MdJCo
Fixes #215103
>From bc47d0684fd77c6fcf5ce8719101c396db05edd6 Mon Sep 17 00:00:00 2001
From: Sofiya Bagodiya <sofiyabagodiya at gmail.com>
Date: Sun, 9 Aug 2026 15:10:38 -0400
Subject: [PATCH] [InstCombine] Fold uadd.sat(X, C) - C to umin(X, ~C)
Fixes #215103
---
.../InstCombine/InstCombineAddSub.cpp | 12 ++++-
.../InstCombine/saturating-add-sub.ll | 50 +++++++++++++++++++
2 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 52a57e153bb4b..1d74547b5c6d0 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1003,7 +1003,17 @@ Instruction *InstCombinerImpl::foldAddWithConstant(BinaryOperator &Add) {
return replaceInstUsesWith(
Add, Builder.CreateBinaryIntrinsic(
Intrinsic::usub_sat, X, ConstantInt::get(Add.getType(), -*C)));
-
+ // uadd.sat(X, C) + -C --> umin(X, ~C)
+ // The saturating add gives X + C or UMAX, so subtracting C leaves X or
+ // UMAX - C. Note UMAX - C == ~C.
+ {
+ APInt SatC = -*C;
+ if (!SatC.isZero() && match(Op0, m_OneUse(m_Intrinsic<Intrinsic::uadd_sat>(
+ m_Value(X), m_SpecificInt(SatC)))))
+ return replaceInstUsesWith(
+ Add, Builder.CreateBinaryIntrinsic(Intrinsic::umin, X,
+ ConstantInt::get(Ty, ~SatC)));
+ }
// Fold (add (zext (add X, -C)), C) -> (zext X) if X u>= C.
// Truncate C to the narrow type to avoid mismatched width comparisons.
{
diff --git a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
index f1cc997a41ba1..341459ed94c9d 100644
--- a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
@@ -1367,15 +1367,65 @@ define i8 @test_scalar_uadd_sub_commuted_wrong(i8 %a, i8 %b) {
define i8 @test_scalar_uadd_sub_const(i8 %a) {
; CHECK-LABEL: @test_scalar_uadd_sub_const(
+; CHECK-NEXT: [[RES:%.*]] = call i8 @llvm.umin.i8(i8 [[A:%.*]], i8 -43)
+; CHECK-NEXT: ret i8 [[RES]]
+;
+ %sat = call i8 @llvm.uadd.sat.i8(i8 %a, i8 42)
+ %res = sub i8 %sat, 42
+ ret i8 %res
+}
+
+define <2 x i8> @test_vector_uadd_sub_const(<2 x i8> %a) {
+; CHECK-LABEL: @test_vector_uadd_sub_const(
+; CHECK-NEXT: [[RES:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[A:%.*]], <2 x i8> splat (i8 -43))
+; CHECK-NEXT: ret <2 x i8> [[RES]]
+;
+ %sat = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> splat (i8 42))
+ %res = sub <2 x i8> %sat, splat (i8 42)
+ ret <2 x i8> %res
+}
+
+; negative test - the constants do not cancel
+
+define i8 @test_scalar_uadd_sub_const_mismatch(i8 %a) {
+; CHECK-LABEL: @test_scalar_uadd_sub_const_mismatch(
+; CHECK-NEXT: [[SAT:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[A:%.*]], i8 42)
+; CHECK-NEXT: [[RES:%.*]] = add i8 [[SAT]], -43
+; CHECK-NEXT: ret i8 [[RES]]
+;
+ %sat = call i8 @llvm.uadd.sat.i8(i8 %a, i8 42)
+ %res = sub i8 %sat, 43
+ ret i8 %res
+}
+
+; negative test - extra use of the saturating add
+
+define i8 @test_scalar_uadd_sub_const_multiuse(i8 %a) {
+; CHECK-LABEL: @test_scalar_uadd_sub_const_multiuse(
; CHECK-NEXT: [[SAT:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[A:%.*]], i8 42)
; CHECK-NEXT: [[RES:%.*]] = add i8 [[SAT]], -42
+; CHECK-NEXT: call void @usei8(i8 [[SAT]])
; CHECK-NEXT: ret i8 [[RES]]
;
%sat = call i8 @llvm.uadd.sat.i8(i8 %a, i8 42)
%res = sub i8 %sat, 42
+ call void @usei8(i8 %sat)
ret i8 %res
}
+; negative test - non-splat vector
+
+define <2 x i8> @test_vector_uadd_sub_const_nonsplat(<2 x i8> %a) {
+; CHECK-LABEL: @test_vector_uadd_sub_const_nonsplat(
+; CHECK-NEXT: [[SAT:%.*]] = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> [[A:%.*]], <2 x i8> <i8 42, i8 3>)
+; CHECK-NEXT: [[RES:%.*]] = add <2 x i8> [[SAT]], <i8 -42, i8 -3>
+; CHECK-NEXT: ret <2 x i8> [[RES]]
+;
+ %sat = call <2 x i8> @llvm.uadd.sat.v2i8(<2 x i8> %a, <2 x i8> <i8 42, i8 3>)
+ %res = sub <2 x i8> %sat, <i8 42, i8 3>
+ ret <2 x i8> %res
+}
+
define i1 @scalar_uadd_eq_zero(i8 %a, i8 %b) {
; CHECK-LABEL: @scalar_uadd_eq_zero(
; CHECK-NEXT: [[TMP1:%.*]] = or i8 [[A:%.*]], [[B:%.*]]
More information about the llvm-commits
mailing list