[llvm] [AMDGPU] Use `v_cvt_pk_*` instructions for saturated conversions (PR #202680)

Igor Wodiany via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 07:57:19 PDT 2026


https://github.com/IgWod created https://github.com/llvm/llvm-project/pull/202680

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.

>From a03c043fb0ee901fcac417ec9f68e37883bd79dd Mon Sep 17 00:00:00 2001
From: Igor Wodiany <igor.wodiany at amd.com>
Date: Thu, 9 Apr 2026 13:07:14 +0100
Subject: [PATCH] [AMDGPU] Use v_cvt_pk_* instructions for saturated
 conversions

---
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp |   4 +
 .../lib/Target/AMDGPU/AMDGPULegalizerInfo.cpp |   3 +-
 .../AMDGPU/AMDGPURegBankLegalizeRules.cpp     |   3 +
 llvm/lib/Target/AMDGPU/SOPInstructions.td     |  24 ++
 llvm/lib/Target/AMDGPU/VOP3PInstructions.td   |  94 +++++
 llvm/test/CodeGen/AMDGPU/fptosi-sat-scalar.ll | 126 ++----
 llvm/test/CodeGen/AMDGPU/fptosi-sat-vector.ll | 375 ++++++++++++++----
 llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll | 310 +++++++++++----
 8 files changed, 692 insertions(+), 247 deletions(-)

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)
+; GFX7-ISEL-NEXT:    v_cvt_i32_f32_e32 v0, s19
+; GFX7-ISEL-NEXT:    s_movk_i32 s4, 0x8000
+; GFX7-ISEL-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX7-ISEL-NEXT:    v_cvt_i32_f32_e32 v2, s18
+; GFX7-ISEL-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX7-ISEL-NEXT:    v_cvt_i32_f32_e32 v3, s17
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v4, 16, v0
+; GFX7-ISEL-NEXT:    v_cvt_i32_f32_e32 v0, s16
+; GFX7-ISEL-NEXT:    v_med3_i32 v2, v2, s4, v1
+; GFX7-ISEL-NEXT:    v_med3_i32 v3, v3, s4, v1
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX7-ISEL-NEXT:    v_med3_i32 v0, v0, s4, v1
+; GFX7-ISEL-NEXT:    v_and_b32_e32 v0, 0xffff, v0
+; GFX7-ISEL-NEXT:    v_and_b32_e32 v1, 0xffff, v2
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v0, v0, v3
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v1, v1, v4
+; GFX7-ISEL-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-GI-LABEL: test_s_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 v0, s16
+; GFX7-GI-NEXT:    v_cvt_i32_f32_e32 v1, s17
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s4, v0
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s5, v1
+; GFX7-GI-NEXT:    v_cvt_i32_f32_e32 v0, s18
+; GFX7-GI-NEXT:    v_cvt_i32_f32_e32 v1, s19
+; GFX7-GI-NEXT:    s_min_i32 s5, s5, 0x7fff
+; GFX7-GI-NEXT:    s_min_i32 s4, s4, 0x7fff
+; GFX7-GI-NEXT:    s_max_i32 s5, s5, 0xffff8000
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s6, v0
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s7, v1
+; GFX7-GI-NEXT:    s_max_i32 s4, s4, 0xffff8000
+; GFX7-GI-NEXT:    s_min_i32 s6, s6, 0x7fff
+; GFX7-GI-NEXT:    s_min_i32 s7, s7, 0x7fff
+; GFX7-GI-NEXT:    s_and_b32 s5, s5, 0xffff
+; GFX7-GI-NEXT:    s_max_i32 s6, s6, 0xffff8000
+; GFX7-GI-NEXT:    s_max_i32 s7, s7, 0xffff8000
+; GFX7-GI-NEXT:    s_and_b32 s4, s4, 0xffff
+; GFX7-GI-NEXT:    s_lshl_b32 s5, s5, 16
+; GFX7-GI-NEXT:    s_or_b32 s4, s4, s5
+; GFX7-GI-NEXT:    s_and_b32 s5, s6, 0xffff
+; GFX7-GI-NEXT:    s_and_b32 s6, s7, 0xffff
+; GFX7-GI-NEXT:    s_lshl_b32 s6, s6, 16
+; GFX7-GI-NEXT:    s_or_b32 s5, s5, s6
+; GFX7-GI-NEXT:    v_mov_b32_e32 v0, s4
+; GFX7-GI-NEXT:    v_mov_b32_e32 v1, s5
+; GFX7-GI-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: test_s_signed_v4f32_v4i16:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v0, s19
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v2, s18
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v3, s17
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v4, s16
+; GFX9-NEXT:    s_movk_i32 s4, 0x8000
+; GFX9-NEXT:    v_mov_b32_e32 v1, 0x7fff
+; GFX9-NEXT:    v_med3_i32 v5, v0, s4, v1
+; GFX9-NEXT:    v_med3_i32 v2, v2, s4, v1
+; GFX9-NEXT:    v_med3_i32 v0, v3, s4, v1
+; GFX9-NEXT:    v_med3_i32 v1, v4, s4, v1
+; GFX9-NEXT:    v_and_b32_e32 v1, 0xffff, v1
+; GFX9-NEXT:    v_lshl_or_b32 v0, v0, 16, v1
+; GFX9-NEXT:    v_and_b32_e32 v1, 0xffff, v2
+; GFX9-NEXT:    v_lshl_or_b32 v1, v5, 16, v1
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: test_s_signed_v4f32_v4i16:
+; GFX11:       ; %bb.0:
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_cvt_pk_i16_f32 v0, s0, s1
+; GFX11-NEXT:    v_cvt_pk_i16_f32 v1, s2, s3
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-ISEL-LABEL: test_s_signed_v4f32_v4i16:
+; 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_pk_i16_f32 v0, s0, s1
+; GFX12-ISEL-NEXT:    v_cvt_pk_i16_f32 v1, s2, s3
+; GFX12-ISEL-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-GI-LABEL: test_s_signed_v4f32_v4i16:
+; 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_cvt_i32_f32 s1, s1
+; GFX12-GI-NEXT:    s_cvt_i32_f32 s2, s2
+; GFX12-GI-NEXT:    s_cvt_i32_f32 s3, s3
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    s_min_i32 s0, s0, 0x7fff
+; GFX12-GI-NEXT:    s_min_i32 s1, s1, 0x7fff
+; GFX12-GI-NEXT:    s_min_i32 s2, s2, 0x7fff
+; GFX12-GI-NEXT:    s_min_i32 s3, s3, 0x7fff
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    s_max_i32 s0, s0, 0xffff8000
+; GFX12-GI-NEXT:    s_max_i32 s1, s1, 0xffff8000
+; GFX12-GI-NEXT:    s_max_i32 s2, s2, 0xffff8000
+; GFX12-GI-NEXT:    s_max_i32 s3, s3, 0xffff8000
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    s_pack_ll_b32_b16 s0, s0, s1
+; GFX12-GI-NEXT:    s_pack_ll_b32_b16 s1, s2, s3
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    v_dual_mov_b32 v0, s0 :: v_dual_mov_b32 v1, s1
+; GFX12-GI-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
+}
+
 ;
 ; Float to signed 8-bit
 ;
@@ -6762,50 +6971,39 @@ define <4 x i8> @test_signed_v4f32_v4i8(<4 x float> %f) {
 ; GFX11-FAKE16-LABEL: test_signed_v4f32_v4i8:
 ; GFX11-FAKE16:       ; %bb.0:
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v3, v3
-; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v4, v1
+; GFX11-FAKE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
 ; GFX11-FAKE16-NEXT:    s_movk_i32 s0, 0xff80
-; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v1, v1
-; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v0, v0
-; GFX11-FAKE16-NEXT:    v_med3_i32 v3, v3, s0, 0x7f
-; GFX11-FAKE16-NEXT:    v_med3_i32 v2, v2, s0, 0x7f
-; GFX11-FAKE16-NEXT:    v_med3_i32 v1, v1, s0, 0x7f
-; GFX11-FAKE16-NEXT:    v_med3_i32 v0, v0, s0, 0x7f
+; GFX11-FAKE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX11-FAKE16-NEXT:    v_med3_i32 v3, v4, s0, 0x7f
+; GFX11-FAKE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX11-FAKE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
 ; GFX11-FAKE16-NEXT:    v_lshlrev_b16 v3, 8, v3
-; GFX11-FAKE16-NEXT:    v_and_b32_e32 v2, 0xff, v2
-; GFX11-FAKE16-NEXT:    v_lshlrev_b16 v1, 8, v1
-; GFX11-FAKE16-NEXT:    v_and_b32_e32 v0, 0xff, v0
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v2, v2, v3
-; GFX11-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v1
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v0, v0, v1
-; GFX11-FAKE16-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v3, v3, v4
-; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v3
-; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
+; GFX11-FAKE16-NEXT:    v_pk_max_u16 v2, 0x800080, v2
+; GFX11-FAKE16-NEXT:    v_pk_max_u16 v0, 0x800080, v0
+; GFX11-FAKE16-NEXT:    v_and_b32_e32 v1, 0xffff, v3
+; GFX11-FAKE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX11-FAKE16-NEXT:    v_or_b32_e32 v1, v1, v3
+; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
+; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-TRUE16-LABEL: test_signed_v4f32_v4i8:
 ; GFX11-TRUE16:       ; %bb.0:
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v3, v3
-; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX11-TRUE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v3, v1
 ; GFX11-TRUE16-NEXT:    s_movk_i32 s0, 0xff80
-; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v1, v1
-; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v0, v0
-; GFX11-TRUE16-NEXT:    v_med3_i32 v3, v3, s0, 0x7f
-; GFX11-TRUE16-NEXT:    v_med3_i32 v4, v2, s0, 0x7f
-; GFX11-TRUE16-NEXT:    v_med3_i32 v1, v1, s0, 0x7f
-; GFX11-TRUE16-NEXT:    v_med3_i32 v0, v0, s0, 0x7f
-; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v3.l
-; GFX11-TRUE16-NEXT:    v_and_b16 v2.h, 0xff, v4.l
-; GFX11-TRUE16-NEXT:    v_and_b16 v0.l, 0xff, v0.l
-; GFX11-TRUE16-NEXT:    v_or_b16 v2.h, v2.h, v2.l
-; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v1.l
-; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v3.l, v2.h
-; GFX11-TRUE16-NEXT:    v_or_b16 v0.l, v0.l, v2.l
-; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v2
-; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v2.l, v2.h
-; GFX11-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX11-TRUE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX11-TRUE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX11-TRUE16-NEXT:    v_med3_i32 v1, v3, s0, 0x7f
+; GFX11-TRUE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
+; GFX11-TRUE16-NEXT:    v_pk_max_u16 v2, 0x800080, v2
+; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v1.l, 8, v1.l
+; GFX11-TRUE16-NEXT:    v_pk_max_u16 v0, 0x800080, v0
+; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v1.h, v2.l
+; GFX11-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
@@ -6816,27 +7014,22 @@ define <4 x i8> @test_signed_v4f32_v4i8(<4 x float> %f) {
 ; GFX12-FAKE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-FAKE16-NEXT:    v_cvt_i32_f32_e32 v3, v3
-; GFX12-FAKE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX12-FAKE16-NEXT:    v_cvt_i32_f32_e32 v4, v1
+; GFX12-FAKE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
 ; GFX12-FAKE16-NEXT:    s_movk_i32 s0, 0xff80
-; GFX12-FAKE16-NEXT:    v_cvt_i32_f32_e32 v1, v1
-; GFX12-FAKE16-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; GFX12-FAKE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
 ; GFX12-FAKE16-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-FAKE16-NEXT:    v_med3_i32 v3, v3, s0, 0x7f
-; GFX12-FAKE16-NEXT:    v_med3_i32 v2, v2, s0, 0x7f
-; GFX12-FAKE16-NEXT:    v_med3_i32 v1, v1, s0, 0x7f
-; GFX12-FAKE16-NEXT:    v_med3_i32 v0, v0, s0, 0x7f
+; GFX12-FAKE16-NEXT:    v_med3_i32 v3, v4, s0, 0x7f
+; GFX12-FAKE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX12-FAKE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
 ; GFX12-FAKE16-NEXT:    v_lshlrev_b16 v3, 8, v3
-; GFX12-FAKE16-NEXT:    v_and_b32_e32 v2, 0xff, v2
-; GFX12-FAKE16-NEXT:    v_lshlrev_b16 v1, 8, v1
-; GFX12-FAKE16-NEXT:    v_and_b32_e32 v0, 0xff, v0
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v2, v2, v3
-; GFX12-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v1
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v0, v0, v1
-; GFX12-FAKE16-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v3, v3, v4
-; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v3
-; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
+; GFX12-FAKE16-NEXT:    v_pk_max_u16 v2, 0x800080, v2
+; GFX12-FAKE16-NEXT:    v_pk_max_u16 v0, 0x800080, v0
+; GFX12-FAKE16-NEXT:    v_and_b32_e32 v1, 0xffff, v3
+; GFX12-FAKE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX12-FAKE16-NEXT:    v_or_b32_e32 v1, v1, v3
+; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
+; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX12-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-TRUE16-LABEL: test_signed_v4f32_v4i8:
@@ -6846,26 +7039,20 @@ define <4 x i8> @test_signed_v4f32_v4i8(<4 x float> %f) {
 ; GFX12-TRUE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-TRUE16-NEXT:    v_cvt_i32_f32_e32 v3, v3
-; GFX12-TRUE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX12-TRUE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX12-TRUE16-NEXT:    v_cvt_i32_f32_e32 v3, v1
 ; GFX12-TRUE16-NEXT:    s_movk_i32 s0, 0xff80
-; GFX12-TRUE16-NEXT:    v_cvt_i32_f32_e32 v1, v1
-; GFX12-TRUE16-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; GFX12-TRUE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX12-TRUE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
 ; GFX12-TRUE16-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-TRUE16-NEXT:    v_med3_i32 v3, v3, s0, 0x7f
-; GFX12-TRUE16-NEXT:    v_med3_i32 v4, v2, s0, 0x7f
-; GFX12-TRUE16-NEXT:    v_med3_i32 v1, v1, s0, 0x7f
-; GFX12-TRUE16-NEXT:    v_med3_i32 v0, v0, s0, 0x7f
-; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v3.l
-; GFX12-TRUE16-NEXT:    v_and_b16 v2.h, 0xff, v4.l
-; GFX12-TRUE16-NEXT:    v_and_b16 v0.l, 0xff, v0.l
-; GFX12-TRUE16-NEXT:    v_or_b16 v2.h, v2.h, v2.l
-; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v1.l
-; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v3.l, v2.h
-; GFX12-TRUE16-NEXT:    v_or_b16 v0.l, v0.l, v2.l
-; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v2
-; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v2.l, v2.h
-; GFX12-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX12-TRUE16-NEXT:    v_med3_i32 v1, v3, s0, 0x7f
+; GFX12-TRUE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
+; GFX12-TRUE16-NEXT:    v_pk_max_u16 v2, 0x800080, v2
+; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v1.l, 8, v1.l
+; GFX12-TRUE16-NEXT:    v_pk_max_u16 v0, 0x800080, v0
+; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v1.h, v2.l
+; GFX12-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
 ; GFX12-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
@@ -6999,27 +7186,39 @@ define <4 x i8> @test_s_signed_v4f32_v4i8(<4 x float> inreg %f) {
 ; 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 s3, s3
 ; GFX12-ISEL-NEXT:    s_cvt_i32_f32 s2, s2
 ; GFX12-ISEL-NEXT:    s_cvt_i32_f32 s1, s1
 ; 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 v1, 0xffffff80, s3, v0
-; GFX12-ISEL-NEXT:    v_lshlrev_b32_e32 v1, 8, v1
-; GFX12-ISEL-NEXT:    v_med3_i32 v2, 0xffffff80, s2, v0
-; GFX12-ISEL-NEXT:    v_med3_i32 v3, 0xffffff80, s1, v0
-; GFX12-ISEL-NEXT:    v_med3_i32 v0, 0xffffff80, s0, v0
-; GFX12-ISEL-NEXT:    v_and_b32_e32 v2, 0xff, v2
-; GFX12-ISEL-NEXT:    v_lshlrev_b32_e32 v3, 8, v3
-; GFX12-ISEL-NEXT:    v_and_b32_e32 v0, 0xff, v0
-; GFX12-ISEL-NEXT:    v_or_b32_e32 v2, v2, v1
-; GFX12-ISEL-NEXT:    v_and_b32_e32 v1, 0xff00, v3
-; GFX12-ISEL-NEXT:    v_or_b32_e32 v0, v0, v3
-; GFX12-ISEL-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
-; GFX12-ISEL-NEXT:    v_or_b32_e32 v1, v1, v4
-; GFX12-ISEL-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
-; GFX12-ISEL-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
+; GFX12-ISEL-NEXT:    s_min_i32 s3, s3, 0x7f
+; GFX12-ISEL-NEXT:    s_min_i32 s2, s2, 0x7f
+; GFX12-ISEL-NEXT:    s_min_i32 s1, s1, 0x7f
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_max_i32 s3, s3, 0xffffff80
+; GFX12-ISEL-NEXT:    s_max_i32 s2, s2, 0xffffff80
+; GFX12-ISEL-NEXT:    s_max_i32 s1, s1, 0xffffff80
+; GFX12-ISEL-NEXT:    s_min_i32 s0, s0, 0x7f
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_lshl_b32 s3, s3, 8
+; GFX12-ISEL-NEXT:    s_and_b32 s2, s2, 0xff
+; GFX12-ISEL-NEXT:    s_lshl_b32 s1, s1, 8
+; GFX12-ISEL-NEXT:    s_max_i32 s0, s0, 0xffffff80
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_or_b32 s2, s2, s3
+; GFX12-ISEL-NEXT:    s_and_b32 s0, s0, 0xff
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_lshl_b32 s3, s2, 16
+; GFX12-ISEL-NEXT:    s_and_b32 s4, s1, 0xff00
+; GFX12-ISEL-NEXT:    s_or_b32 s0, s0, s1
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_or_b32 s1, s4, s3
+; GFX12-ISEL-NEXT:    s_lshr_b32 s3, s3, 24
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_lshr_b32 s1, s1, 8
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    v_dual_mov_b32 v0, s0 :: v_dual_mov_b32 v1, s1
+; GFX12-ISEL-NEXT:    v_dual_mov_b32 v2, s2 :: v_dual_mov_b32 v3, s3
 ; GFX12-ISEL-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-GI-LABEL: test_s_signed_v4f32_v4i8:
diff --git a/llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll b/llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll
index 14094c50e2290..c6d579c604777 100644
--- a/llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll
+++ b/llvm/test/CodeGen/AMDGPU/fptoui-sat-vector.ll
@@ -6174,6 +6174,184 @@ define <8 x i64> @test_s_unsigned_v8f16_v8i64(<8 x half> inreg %f) {
     ret <8 x i64> %x
 }
 
+;
+; Float to unsigned 16-bit
+;
+
+define <4 x i16> @test_unsigned_v4f32_v4i16(<4 x float> %f) {
+; GFX7-ISEL-LABEL: test_unsigned_v4f32_v4i16:
+; GFX7-ISEL:       ; %bb.0:
+; GFX7-ISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v3, v3
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v1, v1
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v2, v2
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v0, v0
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v3, 0xffff, v3
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v1, 0xffff, v1
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v2, 0xffff, v2
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v0, 0xffff, v0
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v1, 16, v1
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v0, v0, v1
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v1, v2, v3
+; GFX7-ISEL-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-GI-LABEL: test_unsigned_v4f32_v4i16:
+; GFX7-GI:       ; %bb.0:
+; GFX7-GI-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v1, v1
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v0, v0
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v3, v3
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v2, v2
+; GFX7-GI-NEXT:    v_min_u32_e32 v1, 0xffff, v1
+; GFX7-GI-NEXT:    v_min_u32_e32 v0, 0xffff, v0
+; GFX7-GI-NEXT:    v_min_u32_e32 v3, 0xffff, v3
+; GFX7-GI-NEXT:    v_lshlrev_b32_e32 v1, 16, v1
+; GFX7-GI-NEXT:    v_min_u32_e32 v2, 0xffff, v2
+; GFX7-GI-NEXT:    v_or_b32_e32 v0, v0, v1
+; GFX7-GI-NEXT:    v_lshlrev_b32_e32 v1, 16, v3
+; GFX7-GI-NEXT:    v_or_b32_e32 v1, v2, v1
+; GFX7-GI-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: test_unsigned_v4f32_v4i16:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v2, v2
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v3, v3
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v0, v0
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v1, v1
+; GFX9-NEXT:    v_min_u32_e32 v2, 0xffff, v2
+; GFX9-NEXT:    v_min_u32_e32 v3, 0xffff, v3
+; GFX9-NEXT:    v_min_u32_e32 v0, 0xffff, v0
+; GFX9-NEXT:    v_min_u32_e32 v1, 0xffff, v1
+; 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_unsigned_v4f32_v4i16:
+; GFX11:       ; %bb.0:
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX11-NEXT:    v_cvt_pk_u16_f32 v1, v2, v3
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-LABEL: test_unsigned_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_u16_f32 v0, v0, v1
+; GFX12-NEXT:    v_cvt_pk_u16_f32 v1, v2, v3
+; GFX12-NEXT:    s_setpc_b64 s[30:31]
+    %x = call <4 x i16> @llvm.fptoui.sat.v4f32.v4i16(<4 x float> %f)
+    ret <4 x i16> %x
+}
+
+define <4 x i16> @test_s_unsigned_v4f32_v4i16(<4 x float> inreg %f) {
+; GFX7-ISEL-LABEL: test_s_unsigned_v4f32_v4i16:
+; GFX7-ISEL:       ; %bb.0:
+; GFX7-ISEL-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v0, s18
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v1, s19
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v3, s17
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v2, 0xffff, v0
+; GFX7-ISEL-NEXT:    v_cvt_u32_f32_e32 v0, s16
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v1, 0xffff, v1
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v3, 0xffff, v3
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v1, 16, v1
+; GFX7-ISEL-NEXT:    v_min_u32_e32 v0, 0xffff, v0
+; GFX7-ISEL-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v0, v0, v3
+; GFX7-ISEL-NEXT:    v_or_b32_e32 v1, v2, v1
+; GFX7-ISEL-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX7-GI-LABEL: test_s_unsigned_v4f32_v4i16:
+; GFX7-GI:       ; %bb.0:
+; GFX7-GI-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v1, s17
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v0, s16
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s5, v1
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v1, s19
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s4, v0
+; GFX7-GI-NEXT:    v_cvt_u32_f32_e32 v0, s18
+; GFX7-GI-NEXT:    s_min_u32 s5, s5, 0xffff
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s7, v1
+; GFX7-GI-NEXT:    s_min_u32 s4, s4, 0xffff
+; GFX7-GI-NEXT:    v_readfirstlane_b32 s6, v0
+; GFX7-GI-NEXT:    s_min_u32 s7, s7, 0xffff
+; GFX7-GI-NEXT:    s_lshl_b32 s5, s5, 16
+; GFX7-GI-NEXT:    s_min_u32 s6, s6, 0xffff
+; GFX7-GI-NEXT:    s_or_b32 s4, s4, s5
+; GFX7-GI-NEXT:    s_lshl_b32 s5, s7, 16
+; GFX7-GI-NEXT:    s_or_b32 s5, s6, s5
+; GFX7-GI-NEXT:    v_mov_b32_e32 v0, s4
+; GFX7-GI-NEXT:    v_mov_b32_e32 v1, s5
+; GFX7-GI-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX9-LABEL: test_s_unsigned_v4f32_v4i16:
+; GFX9:       ; %bb.0:
+; GFX9-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v0, s19
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v1, s18
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v2, s17
+; GFX9-NEXT:    v_cvt_u32_f32_e32 v3, s16
+; GFX9-NEXT:    v_min_u32_e32 v4, 0xffff, v0
+; GFX9-NEXT:    v_min_u32_e32 v1, 0xffff, v1
+; GFX9-NEXT:    v_min_u32_e32 v0, 0xffff, v2
+; GFX9-NEXT:    v_min_u32_e32 v2, 0xffff, v3
+; GFX9-NEXT:    v_and_b32_e32 v2, 0xffff, v2
+; GFX9-NEXT:    v_and_b32_e32 v1, 0xffff, v1
+; GFX9-NEXT:    v_lshl_or_b32 v0, v0, 16, v2
+; GFX9-NEXT:    v_lshl_or_b32 v1, v4, 16, v1
+; GFX9-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX11-LABEL: test_s_unsigned_v4f32_v4i16:
+; GFX11:       ; %bb.0:
+; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GFX11-NEXT:    v_cvt_pk_u16_f32 v0, s0, s1
+; GFX11-NEXT:    v_cvt_pk_u16_f32 v1, s2, s3
+; GFX11-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-ISEL-LABEL: test_s_unsigned_v4f32_v4i16:
+; 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_pk_u16_f32 v0, s0, s1
+; GFX12-ISEL-NEXT:    v_cvt_pk_u16_f32 v1, s2, s3
+; GFX12-ISEL-NEXT:    s_setpc_b64 s[30:31]
+;
+; GFX12-GI-LABEL: test_s_unsigned_v4f32_v4i16:
+; 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_u32_f32 s0, s0
+; GFX12-GI-NEXT:    s_cvt_u32_f32 s1, s1
+; GFX12-GI-NEXT:    s_cvt_u32_f32 s2, s2
+; GFX12-GI-NEXT:    s_cvt_u32_f32 s3, s3
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    s_min_u32 s0, s0, 0xffff
+; GFX12-GI-NEXT:    s_min_u32 s1, s1, 0xffff
+; GFX12-GI-NEXT:    s_min_u32 s2, s2, 0xffff
+; GFX12-GI-NEXT:    s_min_u32 s3, s3, 0xffff
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    s_pack_ll_b32_b16 s0, s0, s1
+; GFX12-GI-NEXT:    s_pack_ll_b32_b16 s1, s2, s3
+; GFX12-GI-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-GI-NEXT:    v_dual_mov_b32 v0, s0 :: v_dual_mov_b32 v1, s1
+; GFX12-GI-NEXT:    s_setpc_b64 s[30:31]
+    %x = call <4 x i16> @llvm.fptoui.sat.v4f32.v4i16(<4 x float> %f)
+    ret <4 x i16> %x
+}
+
 ;
 ; Float to unsigned 8-bit
 ;
@@ -6236,44 +6414,33 @@ define <4 x i8> @test_unsigned_v4f32_v4i8(<4 x float> %f) {
 ; GFX11-FAKE16-LABEL: test_unsigned_v4f32_v4i8:
 ; GFX11-FAKE16:       ; %bb.0:
 ; GFX11-FAKE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    v_cvt_u32_f32_e32 v3, v3
-; GFX11-FAKE16-NEXT:    v_cvt_u32_f32_e32 v1, v1
-; GFX11-FAKE16-NEXT:    v_cvt_u32_f32_e32 v2, v2
-; GFX11-FAKE16-NEXT:    v_cvt_u32_f32_e32 v0, v0
-; GFX11-FAKE16-NEXT:    v_min_u32_e32 v3, 0xff, v3
-; GFX11-FAKE16-NEXT:    v_min_u32_e32 v1, 0xff, v1
-; GFX11-FAKE16-NEXT:    v_min_u32_e32 v2, 0xff, v2
-; GFX11-FAKE16-NEXT:    v_min_u32_e32 v0, 0xff, v0
+; GFX11-FAKE16-NEXT:    v_cvt_u32_f32_e32 v4, v1
+; GFX11-FAKE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX11-FAKE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX11-FAKE16-NEXT:    v_min_u32_e32 v3, 0xff, v4
+; GFX11-FAKE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX11-FAKE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
 ; GFX11-FAKE16-NEXT:    v_lshlrev_b16 v3, 8, v3
-; GFX11-FAKE16-NEXT:    v_lshlrev_b16 v1, 8, v1
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v2, v2, v3
-; GFX11-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v1
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v0, v0, v1
 ; GFX11-FAKE16-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
-; GFX11-FAKE16-NEXT:    v_or_b32_e32 v3, v3, v4
-; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v3
+; GFX11-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v3
+; GFX11-FAKE16-NEXT:    v_or_b32_e32 v1, v3, v4
 ; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
+; GFX11-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX11-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX11-TRUE16-LABEL: test_unsigned_v4f32_v4i8:
 ; GFX11-TRUE16:       ; %bb.0:
 ; GFX11-TRUE16-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    v_cvt_u32_f32_e32 v3, v3
-; GFX11-TRUE16-NEXT:    v_cvt_u32_f32_e32 v4, v2
-; GFX11-TRUE16-NEXT:    v_cvt_u32_f32_e32 v1, v1
-; GFX11-TRUE16-NEXT:    v_cvt_u32_f32_e32 v0, v0
-; GFX11-TRUE16-NEXT:    v_min_u32_e32 v3, 0xff, v3
-; GFX11-TRUE16-NEXT:    v_min_u32_e32 v1, 0xff, v1
-; GFX11-TRUE16-NEXT:    v_min_u32_e32 v0, 0xff, v0
-; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v3.l
+; GFX11-TRUE16-NEXT:    v_cvt_u32_f32_e32 v4, v1
+; GFX11-TRUE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX11-TRUE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
 ; GFX11-TRUE16-NEXT:    v_min_u32_e32 v3, 0xff, v4
-; GFX11-TRUE16-NEXT:    v_or_b16 v2.h, v3.l, v2.l
-; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v1.l
-; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v3.l, v2.h
-; GFX11-TRUE16-NEXT:    v_or_b16 v0.l, v0.l, v2.l
-; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v2
-; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v2.l, v2.h
-; GFX11-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX11-TRUE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX11-TRUE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
+; GFX11-TRUE16-NEXT:    v_lshlrev_b16 v1.l, 8, v3.l
+; GFX11-TRUE16-NEXT:    v_mov_b16_e32 v1.h, v2.l
+; GFX11-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX11-TRUE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
 ; GFX11-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
@@ -6284,23 +6451,18 @@ define <4 x i8> @test_unsigned_v4f32_v4i8(<4 x float> %f) {
 ; GFX12-FAKE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-FAKE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-FAKE16-NEXT:    v_cvt_u32_f32_e32 v3, v3
-; GFX12-FAKE16-NEXT:    v_cvt_u32_f32_e32 v1, v1
-; GFX12-FAKE16-NEXT:    v_cvt_u32_f32_e32 v2, v2
-; GFX12-FAKE16-NEXT:    v_cvt_u32_f32_e32 v0, v0
-; GFX12-FAKE16-NEXT:    v_min_u32_e32 v3, 0xff, v3
-; GFX12-FAKE16-NEXT:    v_min_u32_e32 v1, 0xff, v1
-; GFX12-FAKE16-NEXT:    v_min_u32_e32 v2, 0xff, v2
-; GFX12-FAKE16-NEXT:    v_min_u32_e32 v0, 0xff, v0
+; GFX12-FAKE16-NEXT:    v_cvt_u32_f32_e32 v4, v1
+; GFX12-FAKE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX12-FAKE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
+; GFX12-FAKE16-NEXT:    v_min_u32_e32 v3, 0xff, v4
+; GFX12-FAKE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX12-FAKE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
 ; GFX12-FAKE16-NEXT:    v_lshlrev_b16 v3, 8, v3
-; GFX12-FAKE16-NEXT:    v_lshlrev_b16 v1, 8, v1
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v2, v2, v3
-; GFX12-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v1
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v0, v0, v1
 ; GFX12-FAKE16-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
-; GFX12-FAKE16-NEXT:    v_or_b32_e32 v3, v3, v4
-; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v3
+; GFX12-FAKE16-NEXT:    v_and_b32_e32 v3, 0xffff, v3
+; GFX12-FAKE16-NEXT:    v_or_b32_e32 v1, v3, v4
 ; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
+; GFX12-FAKE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX12-FAKE16-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-TRUE16-LABEL: test_unsigned_v4f32_v4i8:
@@ -6310,22 +6472,16 @@ define <4 x i8> @test_unsigned_v4f32_v4i8(<4 x float> %f) {
 ; GFX12-TRUE16-NEXT:    s_wait_samplecnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-TRUE16-NEXT:    s_wait_kmcnt 0x0
-; GFX12-TRUE16-NEXT:    v_cvt_u32_f32_e32 v3, v3
-; GFX12-TRUE16-NEXT:    v_cvt_u32_f32_e32 v4, v2
-; GFX12-TRUE16-NEXT:    v_cvt_u32_f32_e32 v1, v1
-; GFX12-TRUE16-NEXT:    v_cvt_u32_f32_e32 v0, v0
-; GFX12-TRUE16-NEXT:    v_min_u32_e32 v3, 0xff, v3
-; GFX12-TRUE16-NEXT:    v_min_u32_e32 v1, 0xff, v1
-; GFX12-TRUE16-NEXT:    v_min_u32_e32 v0, 0xff, v0
-; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v3.l
+; GFX12-TRUE16-NEXT:    v_cvt_u32_f32_e32 v4, v1
+; GFX12-TRUE16-NEXT:    v_cvt_pk_u16_f32 v2, v2, v3
+; GFX12-TRUE16-NEXT:    v_cvt_pk_u16_f32 v0, v0, v1
 ; GFX12-TRUE16-NEXT:    v_min_u32_e32 v3, 0xff, v4
-; GFX12-TRUE16-NEXT:    v_or_b16 v2.h, v3.l, v2.l
-; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v2.l, 8, v1.l
-; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v3.l, v2.h
-; GFX12-TRUE16-NEXT:    v_or_b16 v0.l, v0.l, v2.l
-; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v2
-; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v2.l, v2.h
-; GFX12-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v3
+; GFX12-TRUE16-NEXT:    v_pk_min_u16 v2, 0xff00ff, v2
+; GFX12-TRUE16-NEXT:    v_pk_min_u16 v0, 0xff00ff, v0
+; GFX12-TRUE16-NEXT:    v_lshlrev_b16 v1.l, 8, v3.l
+; GFX12-TRUE16-NEXT:    v_mov_b16_e32 v1.h, v2.l
+; GFX12-TRUE16-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v1, 8, v1
 ; GFX12-TRUE16-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
 ; GFX12-TRUE16-NEXT:    s_setpc_b64 s[30:31]
 ;
@@ -6417,21 +6573,22 @@ define <4 x i8> @test_s_unsigned_v4f32_v4i8(<4 x float> inreg %f) {
 ; GFX11:       ; %bb.0:
 ; GFX11-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; GFX11-NEXT:    v_cvt_u32_f32_e32 v0, s3
-; GFX11-NEXT:    v_cvt_u32_f32_e32 v1, s2
-; GFX11-NEXT:    v_cvt_u32_f32_e32 v2, s1
+; GFX11-NEXT:    v_cvt_u32_f32_e32 v1, s1
+; GFX11-NEXT:    v_cvt_u32_f32_e32 v2, s2
 ; GFX11-NEXT:    v_min_u32_e32 v0, 0xff, v0
 ; GFX11-NEXT:    v_min_u32_e32 v1, 0xff, v1
-; GFX11-NEXT:    v_min_u32_e32 v3, 0xff, v2
+; GFX11-NEXT:    v_min_u32_e32 v2, 0xff, v2
 ; GFX11-NEXT:    v_lshlrev_b32_e32 v0, 8, v0
-; GFX11-NEXT:    v_or_b32_e32 v2, v1, v0
+; GFX11-NEXT:    v_lshlrev_b32_e32 v1, 8, v1
+; GFX11-NEXT:    v_or_b32_e32 v2, v2, v0
 ; GFX11-NEXT:    v_cvt_u32_f32_e32 v0, s0
-; GFX11-NEXT:    v_lshlrev_b32_e32 v1, 8, v3
-; GFX11-NEXT:    v_lshlrev_b32_e32 v3, 16, v2
+; GFX11-NEXT:    v_and_b32_e32 v3, 0xff00, v1
+; GFX11-NEXT:    v_lshlrev_b32_e32 v4, 16, v2
 ; GFX11-NEXT:    v_min_u32_e32 v0, 0xff, v0
-; GFX11-NEXT:    v_or_b32_e32 v4, v1, v3
+; GFX11-NEXT:    v_or_b32_e32 v3, v3, v4
 ; GFX11-NEXT:    v_or_b32_e32 v0, v0, v1
-; GFX11-NEXT:    v_lshrrev_b32_e32 v3, 24, v3
-; GFX11-NEXT:    v_lshrrev_b32_e32 v1, 8, v4
+; GFX11-NEXT:    v_lshrrev_b32_e32 v1, 8, v3
+; GFX11-NEXT:    v_lshrrev_b32_e32 v3, 24, v4
 ; GFX11-NEXT:    s_setpc_b64 s[30:31]
 ;
 ; GFX12-ISEL-LABEL: test_s_unsigned_v4f32_v4i8:
@@ -6442,27 +6599,28 @@ define <4 x i8> @test_s_unsigned_v4f32_v4i8(<4 x float> inreg %f) {
 ; GFX12-ISEL-NEXT:    s_wait_bvhcnt 0x0
 ; GFX12-ISEL-NEXT:    s_wait_kmcnt 0x0
 ; GFX12-ISEL-NEXT:    s_cvt_u32_f32 s3, s3
-; GFX12-ISEL-NEXT:    s_cvt_u32_f32 s2, s2
 ; GFX12-ISEL-NEXT:    s_cvt_u32_f32 s1, s1
+; GFX12-ISEL-NEXT:    s_cvt_u32_f32 s2, s2
 ; GFX12-ISEL-NEXT:    s_cvt_u32_f32 s0, s0
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-ISEL-NEXT:    s_min_u32 s3, s3, 0xff
-; GFX12-ISEL-NEXT:    s_min_u32 s2, s2, 0xff
+; GFX12-ISEL-NEXT:    s_min_u32 s1, s1, 0xff
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-ISEL-NEXT:    s_lshl_b32 s3, s3, 8
-; GFX12-ISEL-NEXT:    s_min_u32 s1, s1, 0xff
+; GFX12-ISEL-NEXT:    s_min_u32 s2, s2, 0xff
+; GFX12-ISEL-NEXT:    s_lshl_b32 s1, s1, 8
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-ISEL-NEXT:    s_or_b32 s2, s2, s3
+; GFX12-ISEL-NEXT:    s_and_b32 s3, s1, 0xff00
+; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-ISEL-NEXT:    s_lshl_b32 s4, s2, 16
 ; GFX12-ISEL-NEXT:    s_min_u32 s0, s0, 0xff
-; GFX12-ISEL-NEXT:    s_lshl_b32 s1, s1, 8
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT:    s_lshl_b32 s3, s2, 16
+; GFX12-ISEL-NEXT:    s_or_b32 s3, s3, s4
 ; GFX12-ISEL-NEXT:    s_or_b32 s0, s0, s1
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT:    s_or_b32 s1, s1, s3
-; GFX12-ISEL-NEXT:    s_lshr_b32 s3, s3, 24
-; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-ISEL-NEXT:    s_lshr_b32 s1, s1, 8
+; GFX12-ISEL-NEXT:    s_lshr_b32 s1, s3, 8
+; GFX12-ISEL-NEXT:    s_lshr_b32 s3, s4, 24
 ; GFX12-ISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
 ; GFX12-ISEL-NEXT:    v_dual_mov_b32 v0, s0 :: v_dual_mov_b32 v1, s1
 ; GFX12-ISEL-NEXT:    v_dual_mov_b32 v2, s2 :: v_dual_mov_b32 v3, s3



More information about the llvm-commits mailing list