[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 14:12:42 PDT 2026


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

>From 834d52e6e4610405a1442ef4800636c5ffa167e5 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 | 413 ++++++++++++++++++++++++++++
 1 file changed, 413 insertions(+)

diff --git a/llvm/test/CodeGen/AMDGPU/saddsat.ll b/llvm/test/CodeGen/AMDGPU/saddsat.ll
index 0dbcd70bcad2c..d7804af84af70 100644
--- a/llvm/test/CodeGen/AMDGPU/saddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/saddsat.ll
@@ -116,6 +116,245 @@ define i16 @v_saddsat_i16(i16 %lhs, i16 %rhs) {
   ret i16 %result
 }
 
+define i16 @saddsat_i16_neg32(i16 %lhs) {
+; GFX6-LABEL: saddsat_i16_neg32:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_bfe_i32 v0, v0, 0, 16
+; GFX6-NEXT:    v_subrev_i32_e32 v0, vcc, 32, v0
+; GFX6-NEXT:    s_movk_i32 s4, 0x8000
+; GFX6-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX6-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_i16_neg32:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u16_e32 v1, 32, v0
+; GFX8-NEXT:    v_mov_b32_e32 v2, 0xffff8000
+; GFX8-NEXT:    v_cmp_lt_i16_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_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: saddsat_i16_neg32:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffe0 clamp
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-TRUE16-LABEL: saddsat_i16_neg32:
+; GFX11-TRUE16:       ; %bb.0:
+; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffe0 clamp
+; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: saddsat_i16_neg32:
+; GFX11-FAKE16:       ; %bb.0:
+; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffe0 clamp
+; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+  %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -32)
+  ret i16 %result
+}
+
+define i16 @saddsat_i16_neg64(i16 %lhs) {
+; GFX6-LABEL: saddsat_i16_neg64:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_bfe_i32 v0, v0, 0, 16
+; GFX6-NEXT:    v_subrev_i32_e32 v0, vcc, 64, v0
+; GFX6-NEXT:    s_movk_i32 s4, 0x8000
+; GFX6-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX6-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_i16_neg64:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u16_e32 v1, 64, v0
+; GFX8-NEXT:    v_mov_b32_e32 v2, 0xffff8000
+; GFX8-NEXT:    v_cmp_lt_i16_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_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: saddsat_i16_neg64:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffc0 clamp
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-TRUE16-LABEL: saddsat_i16_neg64:
+; GFX11-TRUE16:       ; %bb.0:
+; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffc0 clamp
+; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: saddsat_i16_neg64:
+; GFX11-FAKE16:       ; %bb.0:
+; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffc0 clamp
+; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+  %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -64)
+  ret i16 %result
+}
+
+define i16 @saddsat_i16_neg17(i16 %lhs) {
+; GFX6-LABEL: saddsat_i16_neg17:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_bfe_i32 v0, v0, 0, 16
+; GFX6-NEXT:    v_subrev_i32_e32 v0, vcc, 17, v0
+; GFX6-NEXT:    s_movk_i32 s4, 0x8000
+; GFX6-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX6-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_i16_neg17:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_subrev_u16_e32 v1, 17, v0
+; GFX8-NEXT:    v_mov_b32_e32 v2, 0xffff8000
+; GFX8-NEXT:    v_cmp_lt_i16_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_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: saddsat_i16_neg17:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffef clamp
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-TRUE16-LABEL: saddsat_i16_neg17:
+; GFX11-TRUE16:       ; %bb.0:
+; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffef clamp
+; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: saddsat_i16_neg17:
+; GFX11-FAKE16:       ; %bb.0:
+; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffef clamp
+; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+  %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -17)
+  ret i16 %result
+}
+
+define i16 @saddsat_i16_neg16(i16 %lhs) {
+; GFX6-LABEL: saddsat_i16_neg16:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_bfe_i32 v0, v0, 0, 16
+; GFX6-NEXT:    v_add_i32_e32 v0, vcc, -16, v0
+; GFX6-NEXT:    s_movk_i32 s4, 0x8000
+; GFX6-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX6-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_i16_neg16:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_add_u16_e32 v1, -16, v0
+; GFX8-NEXT:    v_mov_b32_e32 v2, 0xffff8000
+; GFX8-NEXT:    v_cmp_lt_i16_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_i16_neg16:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_add_i16 v0, v0, -16 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: saddsat_i16_neg16:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    v_add_nc_i16 v0, v0, -16 clamp
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-TRUE16-LABEL: saddsat_i16_neg16:
+; GFX11-TRUE16:       ; %bb.0:
+; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, -16 clamp
+; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: saddsat_i16_neg16:
+; GFX11-FAKE16:       ; %bb.0:
+; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, -16 clamp
+; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+  %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -16)
+  ret i16 %result
+}
+
+define i16 @saddsat_i16_neg65(i16 %lhs) {
+; GFX6-LABEL: saddsat_i16_neg65:
+; GFX6:       ; %bb.0:
+; GFX6-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX6-NEXT:    v_bfe_i32 v0, v0, 0, 16
+; GFX6-NEXT:    v_add_i32_e32 v0, vcc, 0xffffffbf, v0
+; GFX6-NEXT:    s_movk_i32 s4, 0x8000
+; GFX6-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX6-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX6-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX8-LABEL: saddsat_i16_neg65:
+; GFX8:       ; %bb.0:
+; GFX8-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX8-NEXT:    v_add_u16_e32 v1, 0xffbf, v0
+; GFX8-NEXT:    v_mov_b32_e32 v2, 0xffff8000
+; GFX8-NEXT:    v_cmp_lt_i16_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_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX10-LABEL: saddsat_i16_neg65:
+; GFX10:       ; %bb.0:
+; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffbf clamp
+; GFX10-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-TRUE16-LABEL: saddsat_i16_neg65:
+; GFX11-TRUE16:       ; %bb.0:
+; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffbf clamp
+; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-FAKE16-LABEL: saddsat_i16_neg65:
+; GFX11-FAKE16:       ; %bb.0:
+; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffbf clamp
+; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
+  %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -65)
+  ret i16 %result
+}
+
 define i32 @v_saddsat_i32(i32 %lhs, i32 %rhs) {
 ; GFX6-LABEL: v_saddsat_i32:
 ; GFX6:       ; %bb.0:
@@ -154,6 +393,180 @@ define i32 @v_saddsat_i32(i32 %lhs, i32 %rhs) {
   ret i32 %result
 }
 
+define i32 @saddsat_i32_neg32(i32 %x) {
+; GFX6-LABEL: saddsat_i32_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_i32_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_i32_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_i32_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_i32_neg64(i32 %x) {
+; GFX6-LABEL: saddsat_i32_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_i32_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_i32_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_i32_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_i32_neg17(i32 %x) {
+; GFX6-LABEL: saddsat_i32_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_i32_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_i32_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_i32_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_i32_neg16(i32 %x) {
+; GFX6-LABEL: saddsat_i32_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_i32_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_i32_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_i32_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_i32_neg65(i32 %x) {
+; GFX6-LABEL: saddsat_i32_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_i32_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_i32_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_i32_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 <2 x i16> @v_saddsat_v2i16(<2 x i16> %lhs, <2 x i16> %rhs) {
 ; GFX6-LABEL: v_saddsat_v2i16:
 ; GFX6:       ; %bb.0:

>From 2d34eba1ef7962e611800c3261eeb6c859aec1c8 Mon Sep 17 00:00:00 2001
From: Madhur Kumar <madhurkumar004 at gmail.com>
Date: Mon, 17 Aug 2026 02:42:21 +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 | 25 +++++++++++++
 llvm/lib/Target/AMDGPU/VOPInstructions.td  |  1 -
 llvm/test/CodeGen/AMDGPU/saddsat.ll        | 42 ++++++++++------------
 3 files changed, 43 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/VOP3Instructions.td b/llvm/lib/Target/AMDGPU/VOP3Instructions.td
index 7fc252a65d065..eea542cf5eac3 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)),
@@ -1029,14 +1033,35 @@ class OpSelBinOpClampPat<SDPatternOperator node,
 let SubtargetPredicate = isGFX9Plus, True16Predicate = NotHasTrue16BitInsts in {
   def : OpSelBinOpClampPat<saddsat, V_ADD_I16_e64>;
   def : OpSelBinOpClampPat<ssubsat, V_SUB_I16_e64>;
+
+  let AddedComplexity = 15 in {
+  def : GCNPat<
+    (saddsat i16:$src0, (i16 NegSubInlineIntConst16:$src1)),
+    (V_SUB_I16_e64 (i32 0), VSrc_b16:$src0, (i32 0),
+                    NegSubInlineIntConst16:$src1, DSTCLAMP.ENABLE, (i32 0))>;
+  }
 } // End SubtargetPredicate = isGFX9Plus, True16Predicate = NotHasTrue16BitInsts
 let True16Predicate = UseRealTrue16Insts in {
   def : OpSelBinOpClampPat<saddsat, V_ADD_I16_t16_e64>;
   def : OpSelBinOpClampPat<ssubsat, V_SUB_I16_t16_e64>;
+
+  let AddedComplexity = 15 in {
+  def : GCNPat<
+    (saddsat i16:$src0, (i16 NegSubInlineIntConst16:$src1)),
+    (V_SUB_I16_t16_e64 (i32 0), VSrcT_b16:$src0, (i32 0),
+                        NegSubInlineIntConst16:$src1, DSTCLAMP.ENABLE, (i32 0))>;
+  }
 } // End True16Predicate = UseRealTrue16Insts
 let True16Predicate = UseFakeTrue16Insts in {
   def : OpSelBinOpClampPat<saddsat, V_ADD_I16_fake16_e64>;
   def : OpSelBinOpClampPat<ssubsat, V_SUB_I16_fake16_e64>;
+
+  let AddedComplexity = 15 in {
+  def : GCNPat<
+    (saddsat i16:$src0, (i16 NegSubInlineIntConst16:$src1)),
+    (V_SUB_I16_fake16_e64 (i32 0), VSrc_b16:$src0, (i32 0),
+                          NegSubInlineIntConst16:$src1, DSTCLAMP.ENABLE, (i32 0))>;
+  }
 } // End True16Predicate = UseFakeTrue16Insts
 
 multiclass IMAD32_Pats <VOP3_Pseudo inst> {
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 d7804af84af70..5823fbaa8ce9c 100644
--- a/llvm/test/CodeGen/AMDGPU/saddsat.ll
+++ b/llvm/test/CodeGen/AMDGPU/saddsat.ll
@@ -139,26 +139,25 @@ define i16 @saddsat_i16_neg32(i16 %lhs) {
 ; GFX9-LABEL: saddsat_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i16 v0, v0, 32 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: saddsat_i16_neg32:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffe0 clamp
+; GFX10-NEXT:    v_sub_nc_i16 v0, v0, 32 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-TRUE16-LABEL: saddsat_i16_neg32:
 ; GFX11-TRUE16:       ; %bb.0:
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffe0 clamp
+; GFX11-TRUE16-NEXT:    v_sub_nc_i16 v0.l, v0.l, 32 clamp
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-FAKE16-LABEL: saddsat_i16_neg32:
 ; GFX11-FAKE16:       ; %bb.0:
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffe0 clamp
+; GFX11-FAKE16-NEXT:    v_sub_nc_i16 v0, v0, 32 clamp
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
   %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -32)
   ret i16 %result
@@ -187,26 +186,25 @@ define i16 @saddsat_i16_neg64(i16 %lhs) {
 ; GFX9-LABEL: saddsat_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i16 v0, v0, 64 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: saddsat_i16_neg64:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffc0 clamp
+; GFX10-NEXT:    v_sub_nc_i16 v0, v0, 64 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-TRUE16-LABEL: saddsat_i16_neg64:
 ; GFX11-TRUE16:       ; %bb.0:
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffc0 clamp
+; GFX11-TRUE16-NEXT:    v_sub_nc_i16 v0.l, v0.l, 64 clamp
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-FAKE16-LABEL: saddsat_i16_neg64:
 ; GFX11-FAKE16:       ; %bb.0:
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffc0 clamp
+; GFX11-FAKE16-NEXT:    v_sub_nc_i16 v0, v0, 64 clamp
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
   %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -64)
   ret i16 %result
@@ -235,26 +233,25 @@ define i16 @saddsat_i16_neg17(i16 %lhs) {
 ; GFX9-LABEL: saddsat_i16_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_i16 v0, v0, s4 clamp
+; GFX9-NEXT:    v_sub_i16 v0, v0, 17 clamp
 ; GFX9-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX10-LABEL: saddsat_i16_neg17:
 ; GFX10:       ; %bb.0:
 ; GFX10-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX10-NEXT:    v_add_nc_i16 v0, v0, 0xffef clamp
+; GFX10-NEXT:    v_sub_nc_i16 v0, v0, 17 clamp
 ; GFX10-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-TRUE16-LABEL: saddsat_i16_neg17:
 ; GFX11-TRUE16:       ; %bb.0:
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_add_nc_i16 v0.l, v0.l, 0xffef clamp
+; GFX11-TRUE16-NEXT:    v_sub_nc_i16 v0.l, v0.l, 17 clamp
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-FAKE16-LABEL: saddsat_i16_neg17:
 ; GFX11-FAKE16:       ; %bb.0:
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_add_nc_i16 v0, v0, 0xffef clamp
+; GFX11-FAKE16-NEXT:    v_sub_nc_i16 v0, v0, 17 clamp
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
   %result = call i16 @llvm.sadd.sat.i16(i16 %lhs, i16 -17)
   ret i16 %result
@@ -415,14 +412,13 @@ define i32 @saddsat_i32_neg32(i32 %x) {
 ; GFX9-LABEL: saddsat_i32_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_i32_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
@@ -450,14 +446,13 @@ define i32 @saddsat_i32_neg64(i32 %x) {
 ; GFX9-LABEL: saddsat_i32_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_i32_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
@@ -485,14 +480,13 @@ define i32 @saddsat_i32_neg17(i32 %x) {
 ; GFX9-LABEL: saddsat_i32_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_i32_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