[llvm] [AMDGPU] Lower uniform usubsat to SOP (S_MAX_U32 + S_SUB_I32) (PR #203155)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 10 20:00:54 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Anshil Gandhi (gandhi56)
<details>
<summary>Changes</summary>
Add TableGen GCNPat rules in SOPInstructions.td so uniform usubsat lowers to scalar ops: max(a, b) - b, which matches unsigned saturating subtract (result is a - b when a >= b, otherwise 0). i32 uses S_MAX_U32 and S_SUB_I32 directly; i16 zero-extends operands with S_AND_B32 / 0xffff before the same sequence. Patterns use AddedComplexity = 20 so the backend prefers these scalar (SOP) forms over VALU when the operation is uniform.
Authored by: Jeffrey Byrnes
---
Full diff: https://github.com/llvm/llvm-project/pull/203155.diff
2 Files Affected:
- (modified) llvm/lib/Target/AMDGPU/SOPInstructions.td (+21)
- (added) llvm/test/CodeGen/AMDGPU/usubsat-uniform-sdag.ll (+33)
``````````diff
diff --git a/llvm/lib/Target/AMDGPU/SOPInstructions.td b/llvm/lib/Target/AMDGPU/SOPInstructions.td
index b49bd100f9d90..1c5c331d56585 100644
--- a/llvm/lib/Target/AMDGPU/SOPInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SOPInstructions.td
@@ -2078,6 +2078,27 @@ let AddedComplexity = 20 in {
>;
}
+// Uniform saturating unsigned subtraction: usubsat(a, b) = max(a, b) - b
+// If a >= b, result is a - b; otherwise max(a, b) = b, so b - b = 0.
+// Higher complexity to prefer scalar over VALU patterns for uniform values.
+let AddedComplexity = 20 in {
+ def : GCNPat<
+ (i32 (UniformBinFrag<usubsat> i32:$src0, i32:$src1)),
+ (S_SUB_I32 (S_MAX_U32 $src0, $src1), $src1)
+ >;
+
+ // i16 uniform usubsat - zero-extend to i32, apply max+sub, result is already
+ // correct since usubsat clamps to 0 (never negative).
+ def : GCNPat<
+ (i16 (UniformBinFrag<usubsat> i16:$src0, i16:$src1)),
+ (S_SUB_I32
+ (S_MAX_U32
+ (S_AND_B32 $src0, (i32 0xffff)),
+ (S_AND_B32 $src1, (i32 0xffff))),
+ (S_AND_B32 $src1, (i32 0xffff)))
+ >;
+} // End AddedComplexity = 20
+
// V_ADD_I32_e32/S_ADD_U32 produces carry in VCC/SCC. For the vector
// case, the sgpr-copies pass will fix this to use the vector version.
def : GCNPat <
diff --git a/llvm/test/CodeGen/AMDGPU/usubsat-uniform-sdag.ll b/llvm/test/CodeGen/AMDGPU/usubsat-uniform-sdag.ll
new file mode 100644
index 0000000000000..87c5b57e10520
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/usubsat-uniform-sdag.ll
@@ -0,0 +1,33 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; NOTE: Tests SelectionDAG (default llc) lowering of uniform @llvm.usub.sat
+; to scalar SOPs (s_max_u32 + s_sub_i32). GlobalISel coverage lives in
+; GlobalISel/usubsat.ll.
+;
+; RUN: llc -mtriple=amdgcn-amd-amdpal -mcpu=gfx1201 < %s | FileCheck %s
+
+declare i32 @llvm.usub.sat.i32(i32, i32)
+declare i16 @llvm.usub.sat.i16(i16, i16)
+
+define amdgpu_ps i32 @uniform_usubsat_i32(i32 inreg %a, i32 inreg %b) {
+; CHECK-LABEL: uniform_usubsat_i32:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_max_u32 s0, s0, s1
+; CHECK-NEXT: s_delay_alu instid0(SALU_CYCLE_1)
+; CHECK-NEXT: s_sub_co_i32 s0, s0, s1
+; CHECK-NEXT: ; return to shader part epilog
+ %r = call i32 @llvm.usub.sat.i32(i32 %a, i32 %b)
+ ret i32 %r
+}
+
+define amdgpu_ps i16 @uniform_usubsat_i16(i16 inreg %a, i16 inreg %b) {
+; CHECK-LABEL: uniform_usubsat_i16:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_and_b32 s1, s1, 0xffff
+; CHECK-NEXT: s_and_b32 s0, s0, 0xffff
+; CHECK-NEXT: s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(SALU_CYCLE_1)
+; CHECK-NEXT: s_max_u32 s0, s0, s1
+; CHECK-NEXT: s_sub_co_i32 s0, s0, s1
+; CHECK-NEXT: ; return to shader part epilog
+ %r = call i16 @llvm.usub.sat.i16(i16 %a, i16 %b)
+ ret i16 %r
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/203155
More information about the llvm-commits
mailing list