[llvm] [AMDGPU] Use `v_cvt_pk_*` instructions for saturated conversions (PR #202680)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 07:58:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Igor Wodiany (IgWod)
<details>
<summary>Changes</summary>
This an initial attempt on trying to use packed instructions for saturated conversions for vectors, and it is a follow up on #<!-- -->187487. However the current approach has two main limitations:
1) GISel doesn't pack f32_i16 conversion and uses scalar instructions due to bank legalization rules. The rules are needed to avoid regressing `*-scalar` SALU tests.
2) f32_i8 case has spurious `cvt` instructions due to the shape of the DAG after vector unpacking (see comment in the code) - it still results in reduction in the number of instructions, but the code is sub-optimal.
I don't see any easy solution for any of those issues, but I'm open to suggestions. Regardless the change should be a net win, so I wonder if it worth merging it and iterating over it in the future.
Assisted-by: Claude Code
---
Patch is 52.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/202680.diff
8 Files Affected:
- (modified) llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp (+4)
- (modified) llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp (+2-1)
- (modified) llvm/lib/Target/AMDGPU/AMDGPURegBankLegalizeRules.cpp (+3)
- (modified) llvm/lib/Target/AMDGPU/SOPInstructions.td (+24)
- (modified) llvm/lib/Target/AMDGPU/VOP3PInstructions.td (+94)
- (modified) llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll (+44-82)
- (modified) llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll (+287-88)
- (modified) llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll (+234-76)
``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index 78fcd0fd24b6f..a6fa8b286fafd 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -3949,6 +3949,10 @@ SDValue AMDGPUTargetLowering::LowerFP_TO_INT_SAT(const SDValue Op,
if (DstVT == MVT::i16 && SatWidth == DstWidth && SrcVT == MVT::f16)
return Op;
+ if (Subtarget->hasGFX11Insts() && DstVT == MVT::i16 && SrcVT == MVT::f32 &&
+ (SatWidth == 16 || SatWidth == 8))
+ return Op;
+
// Perform all saturation at selected width (i16 or i32) and truncate
if (SatWidth < DstWidth && SatWidth <= 32) {
// For f16 conversion with sub-i16 saturation perform saturation
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp b/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
index 678b444dac077..841ff93412c87 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp
@@ -1212,9 +1212,10 @@ AMDGPULegalizerInfo::AMDGPULegalizerInfo(const GCNSubtarget &ST_,
auto &FPToISat = getActionDefinitionsBuilder({G_FPTOSI_SAT, G_FPTOUI_SAT})
.legalFor({{S32, S32}, {S32, S64}})
.legalFor(ST.has16BitInsts(),{{S16, S16}})
+ .legalFor(ST.hasGFX11Insts(), {{S16, S32}})
.narrowScalarFor({{S64, S16}}, changeTo(0, S32));
- // If available, widen width <16 to i16, intead of i32 so v_cvt_i16/u16_f16 can be used.
+ // If available, widen width <16 to i16, so v_cvt_i16/u16_f16 can be used.
if (ST.has16BitInsts())
FPToISat.minScalarIf(typeIs(1, S16), 0, S16);
diff --git a/llvm/lib/Target/AMDGPU/AMDGPURegBankLegalizeRules.cpp b/llvm/lib/Target/AMDGPU/AMDGPURegBankLegalizeRules.cpp
index 74cd8b1b4b614..a241773962eb0 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPURegBankLegalizeRules.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPURegBankLegalizeRules.cpp
@@ -1496,6 +1496,9 @@ RegBankLegalizeRules::RegBankLegalizeRules(const GCNSubtarget &_ST,
.Any({{UniS32, S16}, {{Sgpr32}, {Sgpr16}}}, hasSALUFloat)
.Any({{UniS32, S16}, {{UniInVgprS32}, {Vgpr16}}}, !hasSALUFloat)
.Any({{DivS32, S16}, {{Vgpr32}, {Vgpr16}}})
+ .Any({{UniS16, S32}, {{Sgpr16}, {Sgpr32}}}, hasSALUFloat)
+ .Any({{UniS16, S32}, {{UniInVgprS16}, {Vgpr32}}}, !hasSALUFloat)
+ .Any({{DivS16, S32}, {{Vgpr16}, {Vgpr32}}})
.Any({{UniS32, S32}, {{Sgpr32}, {Sgpr32}}}, hasSALUFloat)
.Any({{UniS32, S32}, {{UniInVgprS32}, {Vgpr32}}}, !hasSALUFloat)
.Any({{DivS32, S32}, {{Vgpr32}, {Vgpr32}}})
diff --git a/llvm/lib/Target/AMDGPU/SOPInstructions.td b/llvm/lib/Target/AMDGPU/SOPInstructions.td
index b49bd100f9d90..cd8204e4e06f8 100644
--- a/llvm/lib/Target/AMDGPU/SOPInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SOPInstructions.td
@@ -729,6 +729,30 @@ def S_MAX_U32 : SOP2_32 <"s_max_u32",
} // End isCommutable = 1
} // End Defs = [SCC]
+// f32 -> i16/i8 saturated conversions. Required because those conversions
+// are kept legal on GFX11+ for the V_CVT_PK_[IU]16_F32 packed patterns.
+class SOP_FPToUSatPat<dag lhs, int hi> : GCNPat<
+ lhs, (S_MIN_U32 (S_CVT_U32_F32 (f32 $src0)), (i32 hi))>;
+class SOP_FPToSSatPat<dag lhs, int hi, int lo> : GCNPat<
+ lhs, (S_MAX_I32 (S_MIN_I32 (S_CVT_I32_F32 (f32 $src0)), (i32 hi)),
+ (i32 lo))>;
+
+let SubtargetPredicate = HasSALUFloatInsts, AddedComplexity = 9 in {
+ def : SOP_FPToUSatPat<(i16 (UniformBinFrag<fp_to_uint_sat> f32:$src0, i16)),
+ 0xffff>;
+ def : SOP_FPToUSatPat<(i16 (UniformBinFrag<fp_to_uint_sat> f32:$src0, i8)),
+ 0xff>;
+ def : SOP_FPToSSatPat<(i16 (UniformBinFrag<fp_to_sint_sat> f32:$src0, i16)),
+ 0x7fff, 0xffff8000>;
+ def : SOP_FPToSSatPat<(i16 (UniformBinFrag<fp_to_sint_sat> f32:$src0, i8)),
+ 0x7f, 0xffffff80>;
+
+ def : SOP_FPToUSatPat<(i16 (UniformUnaryFrag<fp_to_uint_sat_gi> f32:$src0)),
+ 0xffff>;
+ def : SOP_FPToSSatPat<(i16 (UniformUnaryFrag<fp_to_sint_sat_gi> f32:$src0)),
+ 0x7fff, 0xffff8000>;
+}
+
let SubtargetPredicate = isGFX12Plus in {
def S_ADD_U64 : SOP2_64<"s_add_u64">{
let isCommutable = 1;
diff --git a/llvm/lib/Target/AMDGPU/VOP3PInstructions.td b/llvm/lib/Target/AMDGPU/VOP3PInstructions.td
index 5371d7fce8812..263f01141a1e8 100644
--- a/llvm/lib/Target/AMDGPU/VOP3PInstructions.td
+++ b/llvm/lib/Target/AMDGPU/VOP3PInstructions.td
@@ -3139,3 +3139,97 @@ defm V_DOT8_U32_U4 : VOP3P_Real_gfx10_gfx11_gfx12_gfx13<0x19>;
defm V_DOT4_I32_I8 : VOP3P_Real_gfx10 <0x16>;
defm V_DOT8_I32_I4 : VOP3P_Real_gfx10 <0x18>;
+
+//===----------------------------------------------------------------------===//
+// GFX11+ Patterns
+//===----------------------------------------------------------------------===//
+
+// Packed f32 -> i16 saturated conversion.
+multiclass CvtPkF32ToI16Pat<SDPatternOperator dagOp, SDPatternOperator giOp,
+ Instruction inst> {
+ def : GCNPat<(v2i16 (build_vector
+ (i16 (dagOp (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i16)),
+ (i16 (dagOp (f32 (VOP3Mods f32:$src1, i32:$src1_modifiers)), i16)))),
+ (inst $src0_modifiers, $src0, $src1_modifiers, $src1)>;
+ def : GCNPat<(v2i16 (build_vector
+ (i16 (giOp (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)))),
+ (i16 (giOp (f32 (VOP3Mods f32:$src1, i32:$src1_modifiers)))))),
+ (inst $src0_modifiers, $src0, $src1_modifiers, $src1)>;
+}
+
+multiclass FPToIntSatI16Pat<dag lhs, dag rhs> {
+ let True16Predicate = UseRealTrue16Insts in
+ def : GCNPat<lhs, (EXTRACT_SUBREG rhs, lo16)>;
+ let True16Predicate = NotUseRealTrue16Insts in
+ def : GCNPat<lhs, rhs>;
+}
+
+multiclass FPToSSatI16Pat<dag lhs, int hi, int lo> {
+ defm : FPToIntSatI16Pat<lhs,
+ (V_MED3_I32_e64 (V_CVT_I32_F32_e64 $src0_modifiers, $src0),
+ (S_MOV_B32 (i32 lo)), (i32 hi))>;
+}
+multiclass FPToUSatI16Pat<dag lhs, int hi> {
+ defm : FPToIntSatI16Pat<lhs,
+ (V_MIN_U32_e64 (V_CVT_U32_F32_e64 $src0_modifiers, $src0), (i32 hi))>;
+}
+
+let SubtargetPredicate = isGFX11Plus in {
+ // f32 -> i16 saturated conversion.
+ // FIXME: GISel diverges from SelectionDAG when the v2i16 build_vector is
+ // fed by *uniform* f32 values on subtargets with HasSALUFloat. The
+ // regbank rule routes the f32 -> i16 conversion to the SALU, preventing
+ // this packed pattern from matching. SDAG always matches the packed form.
+ // At the same time we need the regbank rules for SALUFloat to keep
+ // "*-scalar.ll" tests from regressing.
+ defm : CvtPkF32ToI16Pat<fp_to_sint_sat, fp_to_sint_sat_gi, V_CVT_PK_I16_F32_e64>;
+ defm : CvtPkF32ToI16Pat<fp_to_uint_sat, fp_to_uint_sat_gi, V_CVT_PK_U16_F32_e64>;
+
+ // f32 -> i8 saturated conversion.
+ // FIXME: Only partially folds for v{4,8,...}i8 results. For example:
+ //
+ // t93: i16 = fp_to_sint_sat t4, i8 ;; <-- re-extracted
+ // t88: i16 = shl t93, 8
+ // t98: i16 = fp_to_sint_sat t2, i8
+ // t97: i16 = and t98, 255
+ // t89: i16 = or t97, t88 ;; <-- matches pattern below
+ // CopyToReg $vgpr0, t89
+ //
+ // t79: i16 = fp_to_sint_sat t6, i8
+ // t78: i16 = and t79, 255
+ // t73: i16 = fp_to_sint_sat t8, i8
+ // t68: i16 = shl t73, 8
+ // t69: i16 = or t78, t68 ;; <-- matches pattern below
+ // CopyToReg $vgpr2, t69
+ //
+ // ;; t93 is re-extracted from the packed OR instead of reusing t93 directly.
+ // t107: i32 = zero_extend t88
+ // t47: i32 = or t107, t110
+ // t50: i32 = srl t47, 8
+ // t51: i16 = trunc t50
+ // CopyToReg $vgpr1, t51
+ //
+ // The two `or` nodes (t89, t69) match the packed pattern. But each
+ // fp_to_sint_sat result is also used independently by the byte-extract
+ // chain feeding $vgpr1 and $vgpr3 (not shown here), so
+ // V_CVT_I32_F32 + V_MED3_I32 sequence is still generated.
+ def : GCNPat<(i16 (or (i16 (and (i16 (fp_to_sint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i8)), (i16 0xff))),
+ (i16 (shl (i16 (fp_to_sint_sat (f32 (VOP3Mods f32:$src1, i32:$src1_modifiers)), i8)), (i16 8))))),
+ (V_PK_MAX_U16 SRCMODS.OP_SEL_1, (V_PK_MIN_U16 SRCMODS.OP_SEL_1, (V_CVT_PK_U16_F32_e64 $src0_modifiers, $src0, $src1_modifiers, $src1),
+ SRCMODS.OP_SEL_1, (i32 0x00ff00ff), DSTCLAMP.NONE),
+ SRCMODS.OP_SEL_1, (i32 0x00800080), DSTCLAMP.NONE)>;
+ def : GCNPat<(i16 (or (i16 (fp_to_uint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i8)),
+ (i16 (shl (i16 (fp_to_uint_sat (f32 (VOP3Mods f32:$src1, i32:$src1_modifiers)), i8)), (i16 8))))),
+ (V_PK_MIN_U16 SRCMODS.OP_SEL_1, (V_CVT_PK_U16_F32_e64 $src0_modifiers, $src0, $src1_modifiers, $src1),
+ SRCMODS.OP_SEL_1, (i32 0x00ff00ff), DSTCLAMP.NONE)>;
+
+ // Fallback for f32 -> i16 / f32 -> i8 saturated conversion. Required because
+ // f32 -> i16 has to be legal so that the packed pattern above can match.
+ defm : FPToSSatI16Pat<(i16 (fp_to_sint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i16)), 0x7fff, 0x8000>;
+ defm : FPToUSatI16Pat<(i16 (fp_to_uint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i16)), 0xffff>;
+ defm : FPToSSatI16Pat<(i16 (fp_to_sint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i8)), 0x7f, -128>;
+ defm : FPToUSatI16Pat<(i16 (fp_to_uint_sat (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)), i8)), 0xff>;
+
+ defm : FPToSSatI16Pat<(i16 (fp_to_sint_sat_gi (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)))), 0x7fff, 0x8000>;
+ defm : FPToUSatI16Pat<(i16 (fp_to_uint_sat_gi (f32 (VOP3Mods f32:$src0, i32:$src0_modifiers)))), 0xffff>;
+}
diff --git a/llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll b/llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll
index 12e7211355080..b2d3ba3400af0 100644
--- a/llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll
+++ b/llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll
@@ -150,34 +150,22 @@ define i16 @test_signed_i16_f32(float %f) nounwind {
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_cvt_i32_f32_e32 v0, v0
-; GFX11-NEXT: s_movk_i32 s0, 0x8000
+; GFX11-NEXT: s_mov_b32 s0, 0x8000
; GFX11-NEXT: v_med3_i32 v0, v0, s0, 0x7fff
; GFX11-NEXT: s_setpc_b64 s[30:31]
;
-; GFX12-ISEL-LABEL: test_signed_i16_f32:
-; GFX12-ISEL: ; %bb.0:
-; GFX12-ISEL-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-ISEL-NEXT: s_wait_expcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_samplecnt 0x0
-; GFX12-ISEL-NEXT: s_wait_bvhcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_kmcnt 0x0
-; GFX12-ISEL-NEXT: v_cvt_i32_f32_e32 v0, v0
-; GFX12-ISEL-NEXT: s_movk_i32 s0, 0x8000
-; GFX12-ISEL-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT: v_med3_i32 v0, v0, s0, 0x7fff
-; GFX12-ISEL-NEXT: s_setpc_b64 s[30:31]
-;
-; GFX12-GI-LABEL: test_signed_i16_f32:
-; GFX12-GI: ; %bb.0:
-; GFX12-GI-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-GI-NEXT: s_wait_expcnt 0x0
-; GFX12-GI-NEXT: s_wait_samplecnt 0x0
-; GFX12-GI-NEXT: s_wait_bvhcnt 0x0
-; GFX12-GI-NEXT: s_wait_kmcnt 0x0
-; GFX12-GI-NEXT: v_cvt_i32_f32_e32 v0, v0
-; GFX12-GI-NEXT: v_mov_b32_e32 v1, 0xffff8000
-; GFX12-GI-NEXT: v_med3_i32 v0, v0, v1, 0x7fff
-; GFX12-GI-NEXT: s_setpc_b64 s[30:31]
+; GFX12-LABEL: test_signed_i16_f32:
+; GFX12: ; %bb.0:
+; GFX12-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT: s_wait_expcnt 0x0
+; GFX12-NEXT: s_wait_samplecnt 0x0
+; GFX12-NEXT: s_wait_bvhcnt 0x0
+; GFX12-NEXT: s_wait_kmcnt 0x0
+; GFX12-NEXT: v_cvt_i32_f32_e32 v0, v0
+; GFX12-NEXT: s_mov_b32 s0, 0x8000
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: v_med3_i32 v0, v0, s0, 0x7fff
+; GFX12-NEXT: s_setpc_b64 s[30:31]
%x = call i16 @llvm.fptosi.sat.i16.f32(float %f)
ret i16 %x
}
@@ -502,34 +490,21 @@ define i8 @test_s_signed_i8_f32(float inreg %f) nounwind {
; GFX11-NEXT: v_med3_i32 v0, v0, s0, 0x7f
; GFX11-NEXT: s_setpc_b64 s[30:31]
;
-; GFX12-ISEL-LABEL: test_s_signed_i8_f32:
-; GFX12-ISEL: ; %bb.0:
-; GFX12-ISEL-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-ISEL-NEXT: s_wait_expcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_samplecnt 0x0
-; GFX12-ISEL-NEXT: s_wait_bvhcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_kmcnt 0x0
-; GFX12-ISEL-NEXT: v_mov_b32_e32 v0, 0x7f
-; GFX12-ISEL-NEXT: s_cvt_i32_f32 s0, s0
-; GFX12-ISEL-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT: v_med3_i32 v0, 0xffffff80, s0, v0
-; GFX12-ISEL-NEXT: s_setpc_b64 s[30:31]
-;
-; GFX12-GI-LABEL: test_s_signed_i8_f32:
-; GFX12-GI: ; %bb.0:
-; GFX12-GI-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-GI-NEXT: s_wait_expcnt 0x0
-; GFX12-GI-NEXT: s_wait_samplecnt 0x0
-; GFX12-GI-NEXT: s_wait_bvhcnt 0x0
-; GFX12-GI-NEXT: s_wait_kmcnt 0x0
-; GFX12-GI-NEXT: s_cvt_i32_f32 s0, s0
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: s_min_i32 s0, s0, 0x7f
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: s_max_i32 s0, s0, 0xffffff80
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: v_mov_b32_e32 v0, s0
-; GFX12-GI-NEXT: s_setpc_b64 s[30:31]
+; GFX12-LABEL: test_s_signed_i8_f32:
+; GFX12: ; %bb.0:
+; GFX12-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT: s_wait_expcnt 0x0
+; GFX12-NEXT: s_wait_samplecnt 0x0
+; GFX12-NEXT: s_wait_bvhcnt 0x0
+; GFX12-NEXT: s_wait_kmcnt 0x0
+; GFX12-NEXT: s_cvt_i32_f32 s0, s0
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: s_min_i32 s0, s0, 0x7f
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: s_max_i32 s0, s0, 0xffffff80
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: v_mov_b32_e32 v0, s0
+; GFX12-NEXT: s_setpc_b64 s[30:31]
%x = call i8 @llvm.fptosi.sat.i8.f32(float %f)
ret i8 %x
}
@@ -567,38 +542,25 @@ define i16 @test_s_signed_i16_f32(float inreg %f) nounwind {
; GFX11: ; %bb.0:
; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX11-NEXT: v_cvt_i32_f32_e32 v0, s0
-; GFX11-NEXT: s_movk_i32 s0, 0x8000
+; GFX11-NEXT: s_mov_b32 s0, 0x8000
; GFX11-NEXT: v_med3_i32 v0, v0, s0, 0x7fff
; GFX11-NEXT: s_setpc_b64 s[30:31]
;
-; GFX12-ISEL-LABEL: test_s_signed_i16_f32:
-; GFX12-ISEL: ; %bb.0:
-; GFX12-ISEL-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-ISEL-NEXT: s_wait_expcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_samplecnt 0x0
-; GFX12-ISEL-NEXT: s_wait_bvhcnt 0x0
-; GFX12-ISEL-NEXT: s_wait_kmcnt 0x0
-; GFX12-ISEL-NEXT: v_mov_b32_e32 v0, 0x7fff
-; GFX12-ISEL-NEXT: s_cvt_i32_f32 s0, s0
-; GFX12-ISEL-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT: v_med3_i32 v0, 0xffff8000, s0, v0
-; GFX12-ISEL-NEXT: s_setpc_b64 s[30:31]
-;
-; GFX12-GI-LABEL: test_s_signed_i16_f32:
-; GFX12-GI: ; %bb.0:
-; GFX12-GI-NEXT: s_wait_loadcnt_dscnt 0x0
-; GFX12-GI-NEXT: s_wait_expcnt 0x0
-; GFX12-GI-NEXT: s_wait_samplecnt 0x0
-; GFX12-GI-NEXT: s_wait_bvhcnt 0x0
-; GFX12-GI-NEXT: s_wait_kmcnt 0x0
-; GFX12-GI-NEXT: s_cvt_i32_f32 s0, s0
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: s_min_i32 s0, s0, 0x7fff
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: s_max_i32 s0, s0, 0xffff8000
-; GFX12-GI-NEXT: s_wait_alu depctr_sa_sdst(0)
-; GFX12-GI-NEXT: v_mov_b32_e32 v0, s0
-; GFX12-GI-NEXT: s_setpc_b64 s[30:31]
+; GFX12-LABEL: test_s_signed_i16_f32:
+; GFX12: ; %bb.0:
+; GFX12-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT: s_wait_expcnt 0x0
+; GFX12-NEXT: s_wait_samplecnt 0x0
+; GFX12-NEXT: s_wait_bvhcnt 0x0
+; GFX12-NEXT: s_wait_kmcnt 0x0
+; GFX12-NEXT: s_cvt_i32_f32 s0, s0
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: s_min_i32 s0, s0, 0x7fff
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: s_max_i32 s0, s0, 0xffff8000
+; GFX12-NEXT: s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT: v_mov_b32_e32 v0, s0
+; GFX12-NEXT: s_setpc_b64 s[30:31]
%x = call i16 @llvm.fptosi.sat.i16.f32(float %f)
ret i16 %x
}
diff --git a/llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll b/llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll
index a893711ce0942..4cb86cb0c8428 100644
--- a/llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll
+++ b/llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll
@@ -6689,6 +6689,215 @@ define <8 x i64> @test_s_signed_v8f16_v8i64(<8 x half> inreg %f) {
ret <8 x i64> %x
}
+;
+; Float to signed 16-bit
+;
+
+define <4 x i16> @test_signed_v4f32_v4i16(<4 x float> %f) {
+; GFX7-ISEL-LABEL: test_signed_v4f32_v4i16:
+; GFX7-ISEL: ; %bb.0:
+; GFX7-ISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-ISEL-NEXT: v_cvt_i32_f32_e32 v1, v1
+; GFX7-ISEL-NEXT: v_cvt_i32_f32_e32 v0, v0
+; GFX7-ISEL-NEXT: v_cvt_i32_f32_e32 v3, v3
+; GFX7-ISEL-NEXT: v_cvt_i32_f32_e32 v2, v2
+; GFX7-ISEL-NEXT: s_movk_i32 s4, 0x8000
+; GFX7-ISEL-NEXT: v_mov_b32_e32 v4, 0x7fff
+; GFX7-ISEL-NEXT: v_med3_i32 v1, v1, s4, v4
+; GFX7-ISEL-NEXT: v_med3_i32 v0, v0, s4, v4
+; GFX7-ISEL-NEXT: v_med3_i32 v3, v3, s4, v4
+; GFX7-ISEL-NEXT: v_med3_i32 v2, v2, s4, v4
+; GFX7-ISEL-NEXT: v_lshlrev_b32_e32 v1, 16, v1
+; GFX7-ISEL-NEXT: v_and_b32_e32 v0, 0xffff, v0
+; GFX7-ISEL-NEXT: v_lshlrev_b32_e32 v3, 16, v3
+; GFX7-ISEL-NEXT: v_or_b32_e32 v0, v0, v1
+; GFX7-ISEL-NEXT: v_and_b32_e32 v1, 0xffff, v2
+; GFX7-ISEL-NEXT: v_or_b32_e32 v1, v1, v3
+; GFX7-ISEL-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX7-GI-LABEL: test_signed_v4f32_v4i16:
+; GFX7-GI: ; %bb.0:
+; GFX7-GI-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-GI-NEXT: v_cvt_i32_f32_e32 v1, v1
+; GFX7-GI-NEXT: v_cvt_i32_f32_e32 v0, v0
+; GFX7-GI-NEXT: v_cvt_i32_f32_e32 v2, v2
+; GFX7-GI-NEXT: v_cvt_i32_f32_e32 v3, v3
+; GFX7-GI-NEXT: v_mov_b32_e32 v4, 0x7fff
+; GFX7-GI-NEXT: v_mov_b32_e32 v5, 0xffff8000
+; GFX7-GI-NEXT: v_med3_i32 v1, v1, v5, v4
+; GFX7-GI-NEXT: v_med3_i32 v0, v0, v5, v4
+; GFX7-GI-NEXT: v_and_b32_e32 v1, 0xffff, v1
+; GFX7-GI-NEXT: v_med3_i32 v2, v2, v5, v4
+; GFX7-GI-NEXT: v_med3_i32 v3, v3, v5, v4
+; GFX7-GI-NEXT: v_and_b32_e32 v0, 0xffff, v0
+; GFX7-GI-NEXT: v_lshlrev_b32_e32 v1, 16, v1
+; GFX7-GI-NEXT: v_or_b32_e32 v0, v0, v1
+; GFX7-GI-NEXT: v_and_b32_e32 v1, 0xffff, v2
+; GFX7-GI-NEXT: v_and_b32_e32 v2, 0xffff, v3
+; GFX7-GI-NEXT: v_lshlrev_b32_e32 v2, 16, v2
+; GFX7-GI-NEXT: v_or_b32_e32 v1, v1, v2
+; GFX7-GI-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: test_signed_v4f32_v4i16:
+; GFX9: ; %bb.0:
+; GFX9-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT: v_cvt_i32_f32_e32 v2, v2
+; GFX9-NEXT: v_cvt_i32_f32_e32 v3, v3
+; GFX9-NEXT: v_cvt_i32_f32_e32 v0, v0
+; GFX9-NEXT: v_cvt_i32_f32_e32 v1, v1
+; GFX9-NEXT: s_movk_i32 s4, 0x8000
+; GFX9-NEXT: v_mov_b32_e32 v4, 0x7fff
+; GFX9-NEXT: v_med3_i32 v2, v2, s4, v4
+; GFX9-NEXT: v_med3_i32 v3, v3, s4, v4
+; GFX9-NEXT: v_med3_i32 v0, v0, s4, v4
+; GFX9-NEXT: v_med3_i32 v1, v1, s4, v4
+; GFX9-NEXT: s_mov_b32 s4, 0x5040100
+; GFX9-NEXT: v_perm_b32 v0, v1, v0, s4
+; GFX9-NEXT: v_perm_b32 v1, v3, v2, s4
+; GFX9-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: test_signed_v4f32_v4i16:
+; GFX11: ; %bb.0:
+; GFX11-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT: v_cvt_pk_i16_f32 v0, v0, v1
+; GFX11-NEXT: v_cvt_pk_i16_f32 v1, v2, v3
+; GFX11-NEXT: s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: test_signed_v4f32_v4i16:
+; GFX12: ; %bb.0:
+; GFX12-NEXT: s_wait_loadcnt_dscnt 0x0
+; GFX12-NEXT: s_wait_expcnt 0x0
+; GFX12-NEXT: s_wait_samplecnt 0x0
+; GFX12-NEXT: s_wait_bvhcnt 0x0
+; GFX12-NEXT: s_wait_kmcnt 0x0
+; GFX12-NEXT: v_cvt_pk_i16_f32 v0, v0, v1
+; GFX12-NEXT: v_cvt_pk_i16_f32 v1, v2, v3
+; GFX12-NEXT: s_setpc_b64 s[30:31]
+ %x = call <4 x i16> @llvm.fptosi.sat.v4f32.v4i16(<4 x float> %f)
+ ret <4 x i16> %x
+}
+
+define <4 x i16> @test_s_signed_v4f32_v4i16(<4 x float> inreg %f) {
+; GFX7-ISEL-LABEL: test_s_signed_v4f32_v4i16:
+; GFX7-ISEL: ; %bb.0:
+; GFX7-ISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GF...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/202680
More information about the llvm-commits
mailing list