[llvm] [AMDGPU] Legalize SGPR hi16 copies into S_PACK_HH_B32_B16 (PR #218371)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 06:20:56 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/218371

>From 5c54e5555d6674c2e8e020d1f1bd71c034572a5c Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 24 Aug 2026 12:47:24 +0200
Subject: [PATCH 1/2] [AMDGPU] Legalize SGPR hi16 copies into S_PACK_HH_B32_B16

SGPR hi16 is an artificial subregister with no register class and
cannot be allocated, so a copy reading it must become a pack instead
---
 llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp    |  50 ++++++++
 llvm/lib/Target/AMDGPU/SIFoldOperands.cpp     |   3 +-
 llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll     | 115 ++++++++++++++++++
 .../AMDGPU/fix-sgpr-copies-f16-true16.mir     |  30 +++++
 4 files changed, 197 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp b/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
index 3bbc62412de9b..bdf01a6da56ae 100644
--- a/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
@@ -626,6 +626,53 @@ static bool hoistAndMergeSGPRInits(unsigned Reg,
   return Changed;
 }
 
+static bool tryLegalizeHi16SGPRCopy(MachineInstr &MI, const SIRegisterInfo *TRI,
+                                    const SIInstrInfo *TII,
+                                    const GCNSubtarget &ST,
+                                    const TargetRegisterClass *SrcRC,
+                                    const TargetRegisterClass *DstRC) {
+  MachineOperand &Src = MI.getOperand(1);
+  unsigned SubReg = Src.getSubReg();
+  if (SubReg == AMDGPU::NoSubRegister || !ST.hasScalarPackInsts() ||
+      MI.getOperand(0).getSubReg() != AMDGPU::NoSubRegister ||
+      !TRI->isSGPRClass(SrcRC) || !TRI->isSGPRClass(DstRC) ||
+      TRI->getRegSizeInBits(*DstRC) != 32 ||
+      TRI->getSubRegIdxSize(SubReg) != 16)
+    return false;
+
+  unsigned SubRegOffset = TRI->getSubRegIdxOffset(SubReg);
+  if (SubRegOffset % 32 != 16)
+    return false;
+
+  MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
+  bool IsKill = Src.isKill();
+  bool IsUndef = Src.isUndef();
+  // Copy out the 32-bit channel first: movePackToVALU drops subreg sources
+  // if the pack is later forced to VALU.
+  if (TRI->getRegSizeInBits(*SrcRC) != 32) {
+    unsigned Channel = SubRegOffset / 32;
+    Register Chan32 = MRI.createVirtualRegister(&AMDGPU::SReg_32RegClass);
+    BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(AMDGPU::COPY),
+            Chan32)
+        .addReg(Src.getReg(),
+                getKillRegState(IsKill) | getUndefRegState(IsUndef),
+                TRI->getSubRegFromChannel(Channel));
+    Src.setReg(Chan32);
+    IsKill = true;
+    IsUndef = false;
+  }
+  // Cache the register: addReg below may reallocate MI's operands and
+  // invalidate Src.
+  Register SrcReg = Src.getReg();
+  MI.setDesc(TII->get(AMDGPU::S_PACK_HH_B32_B16));
+  Src.setSubReg(AMDGPU::NoSubRegister);
+  Src.setIsKill(false);
+  Src.setIsUndef(IsUndef);
+  MachineInstrBuilder(*MI.getMF(), MI)
+      .addReg(SrcReg, getKillRegState(IsKill) | getUndefRegState(IsUndef));
+  return true;
+}
+
 bool SIFixSGPRCopies::run(MachineFunction &MF) {
   // Only need to run this in SelectionDAG path.
   if (MF.getProperties().hasSelected())
@@ -656,6 +703,9 @@ bool SIFixSGPRCopies::run(MachineFunction &MF) {
         const TargetRegisterClass *SrcRC, *DstRC;
         std::tie(SrcRC, DstRC) = getCopyRegClasses(MI, *TRI, *MRI);
 
+        if (tryLegalizeHi16SGPRCopy(MI, TRI, TII, ST, SrcRC, DstRC))
+          continue;
+
         if (isSGPRToVGPRCopy(SrcRC, DstRC, *TRI)) {
           // Since VGPR to SGPR copies affect VGPR to SGPR copy
           // score and, hence the lowering decision, let's try to get rid of
diff --git a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
index 584efa001f2a5..6280bd19b6949 100644
--- a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
@@ -2217,7 +2217,8 @@ bool SIFoldOperandsImpl::tryFoldFoldableCopy(
       OpToFold.getSubReg()) {
     if (DstRC == &AMDGPU::SReg_32RegClass &&
         DstRC == MRI->getRegClass(OpToFold.getReg())) {
-      assert(OpToFold.getSubReg() == AMDGPU::lo16);
+      if (OpToFold.getSubReg() != AMDGPU::lo16)
+        return false;
       OpToFold.setSubReg(0);
     }
   }
diff --git a/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll b/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
index 9db4987fc8210..334b4e3b436dc 100644
--- a/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
@@ -6767,6 +6767,121 @@ define half @v_copysign_f16_0_f32(float %sign) {
   ret half %op
 }
 
+; A VALU user of a uniform copysign result selects the true16 pattern
+; reading an SGPR hi16.
+define amdgpu_kernel void @s_copysign_f16_0_f32_valu_user(float %sign, i1 %cond) {
+; SI-LABEL: s_copysign_f16_0_f32_valu_user:
+; SI:       ; %bb.0: ; %entry
+; SI-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
+; SI-NEXT:    s_waitcnt lgkmcnt(0)
+; SI-NEXT:    s_bitcmp1_b32 s1, 0
+; SI-NEXT:    s_cselect_b64 s[2:3], -1, 0
+; SI-NEXT:    s_and_b64 vcc, exec, s[2:3]
+; SI-NEXT:    s_cbranch_vccnz .LBB76_2
+; SI-NEXT:  ; %bb.1: ; %if.end
+; SI-NEXT:    s_and_b32 s0, s0, 0x80000000
+; SI-NEXT:    s_lshr_b32 s0, s0, 16
+; SI-NEXT:    v_cvt_f32_f16_e32 v0, s0
+; SI-NEXT:    s_mov_b64 s[0:1], 0
+; SI-NEXT:    s_mov_b32 s3, 0xf000
+; SI-NEXT:    s_mov_b32 s2, -1
+; SI-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; SI-NEXT:    buffer_store_dword v0, off, s[0:3], 0
+; SI-NEXT:  .LBB76_2: ; %exit
+; SI-NEXT:    s_endpgm
+;
+; VI-LABEL: s_copysign_f16_0_f32_valu_user:
+; VI:       ; %bb.0: ; %entry
+; VI-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x24
+; VI-NEXT:    s_waitcnt lgkmcnt(0)
+; VI-NEXT:    s_bitcmp1_b32 s1, 0
+; VI-NEXT:    s_cselect_b64 s[2:3], -1, 0
+; VI-NEXT:    s_and_b64 vcc, exec, s[2:3]
+; VI-NEXT:    s_cbranch_vccnz .LBB76_2
+; VI-NEXT:  ; %bb.1: ; %if.end
+; VI-NEXT:    s_lshr_b32 s0, s0, 16
+; VI-NEXT:    s_and_b32 s0, 0x8000, s0
+; VI-NEXT:    v_cvt_f32_f16_e32 v0, s0
+; VI-NEXT:    v_mov_b32_e32 v1, 0
+; VI-NEXT:    v_cvt_i32_f32_e32 v2, v0
+; VI-NEXT:    v_mov_b32_e32 v0, 0
+; VI-NEXT:    flat_store_dword v[0:1], v2
+; VI-NEXT:  .LBB76_2: ; %exit
+; VI-NEXT:    s_endpgm
+;
+; GFX9-LABEL: s_copysign_f16_0_f32_valu_user:
+; GFX9:       ; %bb.0: ; %entry
+; GFX9-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x24
+; GFX9-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX9-NEXT:    s_bitcmp1_b32 s1, 0
+; GFX9-NEXT:    s_cselect_b64 s[2:3], -1, 0
+; GFX9-NEXT:    s_and_b64 vcc, exec, s[2:3]
+; GFX9-NEXT:    s_cbranch_vccnz .LBB76_2
+; GFX9-NEXT:  ; %bb.1: ; %if.end
+; GFX9-NEXT:    s_lshr_b32 s0, s0, 16
+; GFX9-NEXT:    s_and_b32 s0, 0x8000, s0
+; GFX9-NEXT:    v_cvt_f32_f16_e32 v0, s0
+; GFX9-NEXT:    v_mov_b32_e32 v1, 0
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v2, v0
+; GFX9-NEXT:    v_mov_b32_e32 v0, 0
+; GFX9-NEXT:    global_store_dword v[0:1], v2, off
+; GFX9-NEXT:  .LBB76_2: ; %exit
+; GFX9-NEXT:    s_endpgm
+;
+; GFX11-TRUE16-LABEL: s_copysign_f16_0_f32_valu_user:
+; GFX11-TRUE16:       ; %bb.0: ; %entry
+; GFX11-TRUE16-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
+; GFX11-TRUE16-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-TRUE16-NEXT:    s_bitcmp1_b32 s1, 0
+; GFX11-TRUE16-NEXT:    s_cselect_b32 s1, -1, 0
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-TRUE16-NEXT:    s_and_b32 vcc_lo, exec_lo, s1
+; GFX11-TRUE16-NEXT:    s_cbranch_vccnz .LBB76_2
+; GFX11-TRUE16-NEXT:  ; %bb.1: ; %if.end
+; GFX11-TRUE16-NEXT:    s_pack_hh_b32_b16 s0, s0, s0
+; GFX11-TRUE16-NEXT:    v_mov_b32_e32 v1, 0
+; GFX11-TRUE16-NEXT:    v_and_b16 v0.l, 0x8000, s0
+; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_2)
+; GFX11-TRUE16-NEXT:    v_cvt_f32_f16_e32 v2, v0.l
+; GFX11-TRUE16-NEXT:    v_mov_b32_e32 v0, 0
+; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX11-TRUE16-NEXT:    global_store_b32 v[0:1], v2, off
+; GFX11-TRUE16-NEXT:  .LBB76_2: ; %exit
+; GFX11-TRUE16-NEXT:    s_endpgm
+;
+; GFX11-FAKE16-LABEL: s_copysign_f16_0_f32_valu_user:
+; GFX11-FAKE16:       ; %bb.0: ; %entry
+; GFX11-FAKE16-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
+; GFX11-FAKE16-NEXT:    s_waitcnt lgkmcnt(0)
+; GFX11-FAKE16-NEXT:    s_bitcmp1_b32 s1, 0
+; GFX11-FAKE16-NEXT:    s_cselect_b32 s1, -1, 0
+; GFX11-FAKE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
+; GFX11-FAKE16-NEXT:    s_and_b32 vcc_lo, exec_lo, s1
+; GFX11-FAKE16-NEXT:    s_cbranch_vccnz .LBB76_2
+; GFX11-FAKE16-NEXT:  ; %bb.1: ; %if.end
+; GFX11-FAKE16-NEXT:    s_lshr_b32 s0, s0, 16
+; GFX11-FAKE16-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, 0
+; GFX11-FAKE16-NEXT:    s_and_b32 s0, 0x8000, s0
+; GFX11-FAKE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-FAKE16-NEXT:    v_cvt_f32_f16_e32 v2, s0
+; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
+; GFX11-FAKE16-NEXT:    global_store_b32 v[0:1], v2, off
+; GFX11-FAKE16-NEXT:  .LBB76_2: ; %exit
+; GFX11-FAKE16-NEXT:    s_endpgm
+entry:
+  br i1 %cond, label %exit, label %if.end
+
+if.end:
+  %result = call float @llvm.copysign.f32(float 0.0, float %sign)
+  %trunc = fptrunc float %result to half
+  %cvt = fptosi half %trunc to i32
+  store i32 %cvt, ptr addrspace(1) null, align 4
+  br label %exit
+
+exit:
+  ret void
+}
+
 define amdgpu_ps i32 @s_copysign_f16_0_f64(double inreg %sign) {
 ; SI-LABEL: s_copysign_f16_0_f64:
 ; SI:       ; %bb.0:
diff --git a/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir b/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
index 6c96be0860b43..d6d28e7df9af4 100644
--- a/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
+++ b/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
@@ -464,3 +464,33 @@ body:             |
     %2:sreg_32 = COPY %1.hi16
     %3:vgpr_16 = V_OR_B16_t16_e64 0, %0.lo16, 0, killed %2, 0, implicit $exec
 ...
+
+# SGPR hi16 is an artificial register that cannot be allocated; must become
+# S_PACK_HH.
+
+---
+name: sgpr32_hi16_to_sgpr32
+body: |
+  bb.0:
+    ; GCN-LABEL: name: sgpr32_hi16_to_sgpr32
+    ; GCN: [[DEF:%[0-9]+]]:sreg_32 = IMPLICIT_DEF
+    ; GCN-NEXT: [[S_PACK_HH_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_HH_B32_B16 [[DEF]], [[DEF]]
+    ; GCN-NEXT: [[S_PACK_LL_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_LL_B32_B16 [[S_PACK_HH_B32_B16_]], [[S_PACK_HH_B32_B16_]], implicit-def dead $scc
+    %0:sreg_32 = IMPLICIT_DEF
+    %1:sreg_32 = COPY %0.hi16
+    %2:sreg_32 = S_PACK_LL_B32_B16 %1:sreg_32, %1:sreg_32, implicit-def dead $scc
+...
+
+---
+name: sgpr64_sub1_hi16_to_sgpr32
+body: |
+  bb.0:
+    ; GCN-LABEL: name: sgpr64_sub1_hi16_to_sgpr32
+    ; GCN: [[DEF:%[0-9]+]]:sreg_64 = IMPLICIT_DEF
+    ; GCN-NEXT: [[COPY:%[0-9]+]]:sreg_32 = COPY [[DEF]].sub1
+    ; GCN-NEXT: [[S_PACK_HH_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_HH_B32_B16 [[COPY]], killed [[COPY]]
+    ; GCN-NEXT: [[S_PACK_LL_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_LL_B32_B16 [[S_PACK_HH_B32_B16_]], [[S_PACK_HH_B32_B16_]], implicit-def dead $scc
+    %0:sreg_64 = IMPLICIT_DEF
+    %1:sreg_32 = COPY %0.sub1_hi16
+    %2:sreg_32 = S_PACK_LL_B32_B16 %1:sreg_32, %1:sreg_32, implicit-def dead $scc
+...

>From b1c7904bc95d101db12b82b6941f643422db1ec4 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 3 Sep 2026 14:21:51 +0200
Subject: [PATCH 2/2] Address comments

---
 llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp    |  50 --------
 llvm/lib/Target/AMDGPU/SIInstructions.td      |   7 +-
 llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll     | 121 ++++--------------
 .../AMDGPU/fix-sgpr-copies-f16-true16.mir     |  30 -----
 4 files changed, 32 insertions(+), 176 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp b/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
index bdf01a6da56ae..3bbc62412de9b 100644
--- a/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFixSGPRCopies.cpp
@@ -626,53 +626,6 @@ static bool hoistAndMergeSGPRInits(unsigned Reg,
   return Changed;
 }
 
-static bool tryLegalizeHi16SGPRCopy(MachineInstr &MI, const SIRegisterInfo *TRI,
-                                    const SIInstrInfo *TII,
-                                    const GCNSubtarget &ST,
-                                    const TargetRegisterClass *SrcRC,
-                                    const TargetRegisterClass *DstRC) {
-  MachineOperand &Src = MI.getOperand(1);
-  unsigned SubReg = Src.getSubReg();
-  if (SubReg == AMDGPU::NoSubRegister || !ST.hasScalarPackInsts() ||
-      MI.getOperand(0).getSubReg() != AMDGPU::NoSubRegister ||
-      !TRI->isSGPRClass(SrcRC) || !TRI->isSGPRClass(DstRC) ||
-      TRI->getRegSizeInBits(*DstRC) != 32 ||
-      TRI->getSubRegIdxSize(SubReg) != 16)
-    return false;
-
-  unsigned SubRegOffset = TRI->getSubRegIdxOffset(SubReg);
-  if (SubRegOffset % 32 != 16)
-    return false;
-
-  MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
-  bool IsKill = Src.isKill();
-  bool IsUndef = Src.isUndef();
-  // Copy out the 32-bit channel first: movePackToVALU drops subreg sources
-  // if the pack is later forced to VALU.
-  if (TRI->getRegSizeInBits(*SrcRC) != 32) {
-    unsigned Channel = SubRegOffset / 32;
-    Register Chan32 = MRI.createVirtualRegister(&AMDGPU::SReg_32RegClass);
-    BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(AMDGPU::COPY),
-            Chan32)
-        .addReg(Src.getReg(),
-                getKillRegState(IsKill) | getUndefRegState(IsUndef),
-                TRI->getSubRegFromChannel(Channel));
-    Src.setReg(Chan32);
-    IsKill = true;
-    IsUndef = false;
-  }
-  // Cache the register: addReg below may reallocate MI's operands and
-  // invalidate Src.
-  Register SrcReg = Src.getReg();
-  MI.setDesc(TII->get(AMDGPU::S_PACK_HH_B32_B16));
-  Src.setSubReg(AMDGPU::NoSubRegister);
-  Src.setIsKill(false);
-  Src.setIsUndef(IsUndef);
-  MachineInstrBuilder(*MI.getMF(), MI)
-      .addReg(SrcReg, getKillRegState(IsKill) | getUndefRegState(IsUndef));
-  return true;
-}
-
 bool SIFixSGPRCopies::run(MachineFunction &MF) {
   // Only need to run this in SelectionDAG path.
   if (MF.getProperties().hasSelected())
@@ -703,9 +656,6 @@ bool SIFixSGPRCopies::run(MachineFunction &MF) {
         const TargetRegisterClass *SrcRC, *DstRC;
         std::tie(SrcRC, DstRC) = getCopyRegClasses(MI, *TRI, *MRI);
 
-        if (tryLegalizeHi16SGPRCopy(MI, TRI, TII, ST, SrcRC, DstRC))
-          continue;
-
         if (isSGPRToVGPRCopy(SrcRC, DstRC, *TRI)) {
           // Since VGPR to SGPR copies affect VGPR to SGPR copy
           // score and, hence the lowering decision, let's try to get rid of
diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td
index 441098168f450..fff8b1763d565 100644
--- a/llvm/lib/Target/AMDGPU/SIInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SIInstructions.td
@@ -2519,7 +2519,12 @@ def : GCNPat <
              (V_LSHRREV_B32_e64 (i32 16), $src1)), lo16)
 >;
 
-// TODO: Scalar case for 0 magnitude special case
+def : GCNPat <
+  (UniformBinFrag<fcopysign> (fp16vt fpimm_pos_zero), f32:$src1),
+  (S_AND_B32 (S_MOV_B32 (i32 0x00008000)),
+             (S_LSHR_B32 SReg_32:$src1, (i32 16)))
+>;
+
 def : GCNPat <
   (fcopysign (fp16vt fpimm_pos_zero), f32:$src1),
   (V_AND_B16_t16_e64 0, (S_MOV_B32 (i32 0x00008000)),
diff --git a/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll b/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
index 334b4e3b436dc..9cd67976be791 100644
--- a/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
+++ b/llvm/test/CodeGen/AMDGPU/fcopysign.f16.ll
@@ -6767,119 +6767,50 @@ define half @v_copysign_f16_0_f32(float %sign) {
   ret half %op
 }
 
-; A VALU user of a uniform copysign result selects the true16 pattern
-; reading an SGPR hi16.
-define amdgpu_kernel void @s_copysign_f16_0_f32_valu_user(float %sign, i1 %cond) {
+; Uniform copysign with a VALU user must not select the true16 pattern that
+; reads an SGPR hi16.
+define amdgpu_ps i32 @s_copysign_f16_0_f32_valu_user(float inreg %sign) {
 ; SI-LABEL: s_copysign_f16_0_f32_valu_user:
-; SI:       ; %bb.0: ; %entry
-; SI-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
-; SI-NEXT:    s_waitcnt lgkmcnt(0)
-; SI-NEXT:    s_bitcmp1_b32 s1, 0
-; SI-NEXT:    s_cselect_b64 s[2:3], -1, 0
-; SI-NEXT:    s_and_b64 vcc, exec, s[2:3]
-; SI-NEXT:    s_cbranch_vccnz .LBB76_2
-; SI-NEXT:  ; %bb.1: ; %if.end
+; SI:       ; %bb.0:
 ; SI-NEXT:    s_and_b32 s0, s0, 0x80000000
 ; SI-NEXT:    s_lshr_b32 s0, s0, 16
 ; SI-NEXT:    v_cvt_f32_f16_e32 v0, s0
-; SI-NEXT:    s_mov_b64 s[0:1], 0
-; SI-NEXT:    s_mov_b32 s3, 0xf000
-; SI-NEXT:    s_mov_b32 s2, -1
 ; SI-NEXT:    v_cvt_i32_f32_e32 v0, v0
-; SI-NEXT:    buffer_store_dword v0, off, s[0:3], 0
-; SI-NEXT:  .LBB76_2: ; %exit
-; SI-NEXT:    s_endpgm
+; SI-NEXT:    v_readfirstlane_b32 s0, v0
+; SI-NEXT:    ; return to shader part epilog
 ;
 ; VI-LABEL: s_copysign_f16_0_f32_valu_user:
-; VI:       ; %bb.0: ; %entry
-; VI-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x24
-; VI-NEXT:    s_waitcnt lgkmcnt(0)
-; VI-NEXT:    s_bitcmp1_b32 s1, 0
-; VI-NEXT:    s_cselect_b64 s[2:3], -1, 0
-; VI-NEXT:    s_and_b64 vcc, exec, s[2:3]
-; VI-NEXT:    s_cbranch_vccnz .LBB76_2
-; VI-NEXT:  ; %bb.1: ; %if.end
+; VI:       ; %bb.0:
 ; VI-NEXT:    s_lshr_b32 s0, s0, 16
 ; VI-NEXT:    s_and_b32 s0, 0x8000, s0
 ; VI-NEXT:    v_cvt_f32_f16_e32 v0, s0
-; VI-NEXT:    v_mov_b32_e32 v1, 0
-; VI-NEXT:    v_cvt_i32_f32_e32 v2, v0
-; VI-NEXT:    v_mov_b32_e32 v0, 0
-; VI-NEXT:    flat_store_dword v[0:1], v2
-; VI-NEXT:  .LBB76_2: ; %exit
-; VI-NEXT:    s_endpgm
+; VI-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; VI-NEXT:    v_readfirstlane_b32 s0, v0
+; VI-NEXT:    ; return to shader part epilog
 ;
 ; GFX9-LABEL: s_copysign_f16_0_f32_valu_user:
-; GFX9:       ; %bb.0: ; %entry
-; GFX9-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x24
-; GFX9-NEXT:    s_waitcnt lgkmcnt(0)
-; GFX9-NEXT:    s_bitcmp1_b32 s1, 0
-; GFX9-NEXT:    s_cselect_b64 s[2:3], -1, 0
-; GFX9-NEXT:    s_and_b64 vcc, exec, s[2:3]
-; GFX9-NEXT:    s_cbranch_vccnz .LBB76_2
-; GFX9-NEXT:  ; %bb.1: ; %if.end
+; GFX9:       ; %bb.0:
 ; GFX9-NEXT:    s_lshr_b32 s0, s0, 16
 ; GFX9-NEXT:    s_and_b32 s0, 0x8000, s0
 ; GFX9-NEXT:    v_cvt_f32_f16_e32 v0, s0
-; GFX9-NEXT:    v_mov_b32_e32 v1, 0
-; GFX9-NEXT:    v_cvt_i32_f32_e32 v2, v0
-; GFX9-NEXT:    v_mov_b32_e32 v0, 0
-; GFX9-NEXT:    global_store_dword v[0:1], v2, off
-; GFX9-NEXT:  .LBB76_2: ; %exit
-; GFX9-NEXT:    s_endpgm
-;
-; GFX11-TRUE16-LABEL: s_copysign_f16_0_f32_valu_user:
-; GFX11-TRUE16:       ; %bb.0: ; %entry
-; GFX11-TRUE16-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
-; GFX11-TRUE16-NEXT:    s_waitcnt lgkmcnt(0)
-; GFX11-TRUE16-NEXT:    s_bitcmp1_b32 s1, 0
-; GFX11-TRUE16-NEXT:    s_cselect_b32 s1, -1, 0
-; GFX11-TRUE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
-; GFX11-TRUE16-NEXT:    s_and_b32 vcc_lo, exec_lo, s1
-; GFX11-TRUE16-NEXT:    s_cbranch_vccnz .LBB76_2
-; GFX11-TRUE16-NEXT:  ; %bb.1: ; %if.end
-; GFX11-TRUE16-NEXT:    s_pack_hh_b32_b16 s0, s0, s0
-; GFX11-TRUE16-NEXT:    v_mov_b32_e32 v1, 0
-; GFX11-TRUE16-NEXT:    v_and_b16 v0.l, 0x8000, s0
-; GFX11-TRUE16-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_2)
-; GFX11-TRUE16-NEXT:    v_cvt_f32_f16_e32 v2, v0.l
-; GFX11-TRUE16-NEXT:    v_mov_b32_e32 v0, 0
-; GFX11-TRUE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
-; GFX11-TRUE16-NEXT:    global_store_b32 v[0:1], v2, off
-; GFX11-TRUE16-NEXT:  .LBB76_2: ; %exit
-; GFX11-TRUE16-NEXT:    s_endpgm
-;
-; GFX11-FAKE16-LABEL: s_copysign_f16_0_f32_valu_user:
-; GFX11-FAKE16:       ; %bb.0: ; %entry
-; GFX11-FAKE16-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
-; GFX11-FAKE16-NEXT:    s_waitcnt lgkmcnt(0)
-; GFX11-FAKE16-NEXT:    s_bitcmp1_b32 s1, 0
-; GFX11-FAKE16-NEXT:    s_cselect_b32 s1, -1, 0
-; GFX11-FAKE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
-; GFX11-FAKE16-NEXT:    s_and_b32 vcc_lo, exec_lo, s1
-; GFX11-FAKE16-NEXT:    s_cbranch_vccnz .LBB76_2
-; GFX11-FAKE16-NEXT:  ; %bb.1: ; %if.end
-; GFX11-FAKE16-NEXT:    s_lshr_b32 s0, s0, 16
-; GFX11-FAKE16-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, 0
-; GFX11-FAKE16-NEXT:    s_and_b32 s0, 0x8000, s0
-; GFX11-FAKE16-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX11-FAKE16-NEXT:    v_cvt_f32_f16_e32 v2, s0
-; GFX11-FAKE16-NEXT:    v_cvt_i32_f32_e32 v2, v2
-; GFX11-FAKE16-NEXT:    global_store_b32 v[0:1], v2, off
-; GFX11-FAKE16-NEXT:  .LBB76_2: ; %exit
-; GFX11-FAKE16-NEXT:    s_endpgm
-entry:
-  br i1 %cond, label %exit, label %if.end
-
-if.end:
+; GFX9-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; GFX9-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX9-NEXT:    ; return to shader part epilog
+;
+; GFX11-LABEL: s_copysign_f16_0_f32_valu_user:
+; GFX11:       ; %bb.0:
+; GFX11-NEXT:    s_lshr_b32 s0, s0, 16
+; GFX11-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(SALU_CYCLE_1)
+; GFX11-NEXT:    s_and_b32 s0, 0x8000, s0
+; GFX11-NEXT:    v_cvt_f32_f16_e32 v0, s0
+; GFX11-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX11-NEXT:    v_cvt_i32_f32_e32 v0, v0
+; GFX11-NEXT:    v_readfirstlane_b32 s0, v0
+; GFX11-NEXT:    ; return to shader part epilog
   %result = call float @llvm.copysign.f32(float 0.0, float %sign)
   %trunc = fptrunc float %result to half
   %cvt = fptosi half %trunc to i32
-  store i32 %cvt, ptr addrspace(1) null, align 4
-  br label %exit
-
-exit:
-  ret void
+  ret i32 %cvt
 }
 
 define amdgpu_ps i32 @s_copysign_f16_0_f64(double inreg %sign) {
diff --git a/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir b/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
index d6d28e7df9af4..6c96be0860b43 100644
--- a/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
+++ b/llvm/test/CodeGen/AMDGPU/fix-sgpr-copies-f16-true16.mir
@@ -464,33 +464,3 @@ body:             |
     %2:sreg_32 = COPY %1.hi16
     %3:vgpr_16 = V_OR_B16_t16_e64 0, %0.lo16, 0, killed %2, 0, implicit $exec
 ...
-
-# SGPR hi16 is an artificial register that cannot be allocated; must become
-# S_PACK_HH.
-
----
-name: sgpr32_hi16_to_sgpr32
-body: |
-  bb.0:
-    ; GCN-LABEL: name: sgpr32_hi16_to_sgpr32
-    ; GCN: [[DEF:%[0-9]+]]:sreg_32 = IMPLICIT_DEF
-    ; GCN-NEXT: [[S_PACK_HH_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_HH_B32_B16 [[DEF]], [[DEF]]
-    ; GCN-NEXT: [[S_PACK_LL_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_LL_B32_B16 [[S_PACK_HH_B32_B16_]], [[S_PACK_HH_B32_B16_]], implicit-def dead $scc
-    %0:sreg_32 = IMPLICIT_DEF
-    %1:sreg_32 = COPY %0.hi16
-    %2:sreg_32 = S_PACK_LL_B32_B16 %1:sreg_32, %1:sreg_32, implicit-def dead $scc
-...
-
----
-name: sgpr64_sub1_hi16_to_sgpr32
-body: |
-  bb.0:
-    ; GCN-LABEL: name: sgpr64_sub1_hi16_to_sgpr32
-    ; GCN: [[DEF:%[0-9]+]]:sreg_64 = IMPLICIT_DEF
-    ; GCN-NEXT: [[COPY:%[0-9]+]]:sreg_32 = COPY [[DEF]].sub1
-    ; GCN-NEXT: [[S_PACK_HH_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_HH_B32_B16 [[COPY]], killed [[COPY]]
-    ; GCN-NEXT: [[S_PACK_LL_B32_B16_:%[0-9]+]]:sreg_32 = S_PACK_LL_B32_B16 [[S_PACK_HH_B32_B16_]], [[S_PACK_HH_B32_B16_]], implicit-def dead $scc
-    %0:sreg_64 = IMPLICIT_DEF
-    %1:sreg_32 = COPY %0.sub1_hi16
-    %2:sreg_32 = S_PACK_LL_B32_B16 %1:sreg_32, %1:sreg_32, implicit-def dead $scc
-...



More information about the llvm-commits mailing list