[llvm] [AMDGPU] Fold saddsat(x, -C) to ssubsat(x, C) when C is an inline constant (PR #216592)

Madhur Kumar via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 16 12:01:29 PDT 2026


https://github.com/MadhurKumar004 created https://github.com/llvm/llvm-project/pull/216592

None

>From 884bf7a59f6fefb199b8d7bc6c956a3ef3bab542 Mon Sep 17 00:00:00 2001
From: Madhur Kumar <madhurkumar004 at gmail.com>
Date: Mon, 17 Aug 2026 00:18:40 +0530
Subject: [PATCH 1/2] Pre-commit test

---
 llvm/test/CodeGen/AMDGPU/saddsat.ll | 212 ++++++++++++++++++++++++++++
 1 file changed, 212 insertions(+)

diff --git a/llvm/test/CodeGen/AMDGPU/saddsat.ll b/llvm/test/CodeGen/AMDGPU/saddsat.ll
index 0dbcd70bcad2c..5e7452eac9a52 100644
--- a/llvm/test/CodeGen/AMDGPU/saddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/saddsat.ll
@@ -492,6 +492,218 @@ define i64 @v_saddsat_i64(i64 %lhs, i64 %rhs) {
   ret i64 %result
 }
 
+define i32 @saddsat_neg32(i32 %x) {
+; GFX6-LABEL: saddsat_neg32:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_subrev_i32_e32 v1, vcc, 32, v0
+; GFX6-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX6-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_neg32:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u32_e32 v1, vcc, 32, v0
+; GFX8-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX8-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_neg32:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_movk_i32 s4, 0xffe0
+; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_neg32:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffe0, v0 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -32)
+  ret i32 %r
+}
+
+define i32 @saddsat_neg64(i32 %x) {
+; GFX6-LABEL: saddsat_neg64:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_subrev_i32_e32 v1, vcc, 64, v0
+; GFX6-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX6-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_neg64:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u32_e32 v1, vcc, 64, v0
+; GFX8-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX8-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_neg64:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_movk_i32 s4, 0xffc0
+; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_neg64:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffc0, v0 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -64)
+  ret i32 %r
+}
+
+define i32 @saddsat_neg17(i32 %x) {
+; GFX6-LABEL: saddsat_neg17:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_subrev_i32_e32 v1, vcc, 17, v0
+; GFX6-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX6-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_neg17:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u32_e32 v1, vcc, 17, v0
+; GFX8-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX8-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_neg17:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_movk_i32 s4, 0xffef
+; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_neg17:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffef, v0 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -17)
+  ret i32 %r
+}
+
+define i32 @saddsat_neg16(i32 %x) {
+; GFX6-LABEL: saddsat_neg16:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_add_i32_e32 v1, vcc, -16, v0
+; GFX6-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX6-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_neg16:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_add_u32_e32 v1, vcc, -16, v0
+; GFX8-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX8-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_neg16:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_add_i32 v0, v0, -16 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_neg16:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, v0, -16 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -16)
+  ret i32 %r
+}
+
+define i32 @saddsat_neg65(i32 %x) {
+; GFX6-LABEL: saddsat_neg65:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_add_i32_e32 v1, vcc, 0xffffffbf, v0
+; GFX6-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX6-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX6-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_neg65:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_add_u32_e32 v1, vcc, 0xffffffbf, v0
+; GFX8-NEXT:    v_bfrev_b32_e32 v2, 1
+; GFX8-NEXT:    v_cmp_lt_i32_e32 vcc, v1, v0
+; GFX8-NEXT:    v_cndmask_b32_e32 v0, v2, v1, vcc
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_neg65:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    s_movk_i32 s4, 0xffbf
+; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_neg65:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffbf, v0 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -65)
+  ret i32 %r
+}
+
+define i32 @saddsat_reg(i32 %x, i32 %y) {
+; GFX6-LABEL: saddsat_reg:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_cmp_gt_i32_e32 vcc, 0, v1
+; GFX6-NEXT:    v_add_i32_e64 v1, s[4:5], v0, v1
+; GFX6-NEXT:    v_cmp_lt_i32_e64 s[4:5], v1, v0
+; GFX6-NEXT:    v_ashrrev_i32_e32 v0, 31, v1
+; GFX6-NEXT:    s_xor_b64 s[4:5], vcc, s[4:5]
+; GFX6-NEXT:    v_cndmask_b32_e64 v0, v1, -v0, s[4:5]
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_reg:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_cmp_gt_i32_e32 vcc, 0, v1
+; GFX8-NEXT:    v_add_u32_e64 v1, s[4:5], v0, v1
+; GFX8-NEXT:    v_cmp_lt_i32_e64 s[4:5], v1, v0
+; GFX8-NEXT:    v_ashrrev_i32_e32 v0, 31, v1
+; GFX8-NEXT:    s_xor_b64 s[4:5], vcc, s[4:5]
+; GFX8-NEXT:    v_cndmask_b32_e64 v0, v1, -v0, s[4:5]
+; GFX8-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: saddsat_reg:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_add_i32 v0, v0, v1 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10PLUS-LABEL: saddsat_reg:
+; GFX10PLUS:       ; %bb.0:
+; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10PLUS-NEXT:    v_add_nc_i32 v0, v0, v1 clamp
+; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
+  %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 %y)
+  ret i32 %r
+}
+
 declare i8 @llvm.sadd.sat.i8(i8, i8) #0
 declare i16 @llvm.sadd.sat.i16(i16, i16) #0
 declare <2 x i16> @llvm.sadd.sat.v2i16(<2 x i16>, <2 x i16>) #0

>From 58cf98b3a3460971218b6efca35deb9ad71ed8fb Mon Sep 17 00:00:00 2001
From: Madhur Kumar <madhurkumar004 at gmail.com>
Date: Mon, 17 Aug 2026 00:26:36 +0530
Subject: [PATCH 2/2] [AMDGPU] Fold saddsat(x, -C) to ssubsat(x, C) when C is
 an inline constant

---
 llvm/lib/Target/AMDGPU/VOP3Instructions.td |  4 ++++
 llvm/lib/Target/AMDGPU/VOPInstructions.td  |  1 -
 llvm/test/CodeGen/AMDGPU/saddsat.ll        | 15 ++++++---------
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/VOP3Instructions.td b/llvm/lib/Target/AMDGPU/VOP3Instructions.td
index 7fc252a65d065..afc47c8bd50ba 100644
--- a/llvm/lib/Target/AMDGPU/VOP3Instructions.td
+++ b/llvm/lib/Target/AMDGPU/VOP3Instructions.td
@@ -1002,6 +1002,10 @@ def : ThreeOp_i32_Pats<uaddsat, umin, V_ADD_MIN_U32_e64>;
 def : VOPBinOpClampPat<saddsat, V_ADD_I32_e64, i32>;
 def : VOPBinOpClampPat<ssubsat, V_SUB_I32_e64, i32>;
 
+def : GCNPat<
+  (saddsat i32:$src0, (i32 NegSubInlineConst32:$src1)),
+  (V_SUB_I32_e64 VSrc_b32:$src0, NegSubInlineConst32:$src1, DSTCLAMP.ENABLE)>;
+
 def : GCNPat<(DivergentBinFrag<or> (or_oneuse i64:$src0, i64:$src1), i64:$src2),
              (REG_SEQUENCE VReg_64,
                (V_OR3_B32_e64 (i32 (EXTRACT_SUBREG $src0, sub0)),
diff --git a/llvm/lib/Target/AMDGPU/VOPInstructions.td b/llvm/lib/Target/AMDGPU/VOPInstructions.td
index a379785616c6c..b64165748eb98 100644
--- a/llvm/lib/Target/AMDGPU/VOPInstructions.td
+++ b/llvm/lib/Target/AMDGPU/VOPInstructions.td
@@ -1365,7 +1365,6 @@ class getVSrcOp<ValueType vt> {
 }
 
 // Class for binary integer operations with the clamp bit set for saturation
-// TODO: Add sub with negated inline constant pattern.
 class VOPBinOpClampPat<SDPatternOperator node, Instruction inst, ValueType vt> :
   GCNPat<(node vt:$src0, vt:$src1),
          (inst getVSrcOp<vt>.ret:$src0, getVSrcOp<vt>.ret:$src1,
diff --git a/llvm/test/CodeGen/AMDGPU/saddsat.ll b/llvm/test/CodeGen/AMDGPU/saddsat.ll
index 5e7452eac9a52..e5636a84aa5da 100644
--- a/llvm/test/CodeGen/AMDGPU/saddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/saddsat.ll
@@ -514,14 +514,13 @@ define i32 @saddsat_neg32(i32 %x) {
 ; GFX9-LABEL: saddsat_neg32:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    s_movk_i32 s4, 0xffe0
-; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i32 v0, v0, 32 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: saddsat_neg32:
 ; GFX10PLUS:       ; %bb.0:
 ; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffe0, v0 clamp
+; GFX10PLUS-NEXT:    v_sub_nc_i32 v0, v0, 32 clamp
 ; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
   %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -32)
   ret i32 %r
@@ -549,14 +548,13 @@ define i32 @saddsat_neg64(i32 %x) {
 ; GFX9-LABEL: saddsat_neg64:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    s_movk_i32 s4, 0xffc0
-; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i32 v0, v0, 64 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: saddsat_neg64:
 ; GFX10PLUS:       ; %bb.0:
 ; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffc0, v0 clamp
+; GFX10PLUS-NEXT:    v_sub_nc_i32 v0, v0, 64 clamp
 ; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
   %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -64)
   ret i32 %r
@@ -584,14 +582,13 @@ define i32 @saddsat_neg17(i32 %x) {
 ; GFX9-LABEL: saddsat_neg17:
 ; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX9-NEXT:    s_movk_i32 s4, 0xffef
-; GFX9-NEXT:    v_add_i32 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i32 v0, v0, 17 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10PLUS-LABEL: saddsat_neg17:
 ; GFX10PLUS:       ; %bb.0:
 ; GFX10PLUS-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10PLUS-NEXT:    v_add_nc_i32 v0, 0xffffffef, v0 clamp
+; GFX10PLUS-NEXT:    v_sub_nc_i32 v0, v0, 17 clamp
 ; GFX10PLUS-NEXT:    s_setpc_b64 s[30:31]
   %r = call i32 @llvm.sadd.sat.i32(i32 %x, i32 -17)
   ret i32 %r



More information about the llvm-commits mailing list