[llvm] [AMDGPU] Use the reserved EXEC copy register for emergency SGPR spills (PR #221972)

Pankaj Dwivedi via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 01:22:10 PDT 2026


https://github.com/PankajDwivedi-25 updated https://github.com/llvm/llvm-project/pull/221972

>From e502af4ce701661305d78cb15c81bf2bae4234ea Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Tue, 8 Sep 2026 17:35:52 +0530
Subject: [PATCH 1/2] [AMDGPU] Use the reserved EXEC copy register for
 emergency SGPR spills

---
 llvm/lib/Target/AMDGPU/SIFrameLowering.cpp    | 73 ++++++++++++++-
 llvm/lib/Target/AMDGPU/SIFrameLowering.h      |  5 +
 llvm/lib/Target/AMDGPU/SILowerSGPRSpills.cpp  | 10 +-
 llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp     | 18 +++-
 .../test/CodeGen/AMDGPU/branch-relax-spill.ll |  5 +-
 .../CodeGen/AMDGPU/bug-undef-spilled-agpr.mir | 18 ++--
 .../AMDGPU/eliminate-frame-index-select.ll    |  6 +-
 ...to-vmem-scc-clobber-reserved-exec-copy.mir | 92 +++++++++++++++++++
 8 files changed, 206 insertions(+), 21 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir

diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index 92fb77b1e6436..e9de8d013eee3 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1859,9 +1859,18 @@ void SIFrameLowering::determinePrologEpilogSGPRSaves(
   const TargetRegisterClass &RC = *TRI->getWaveMaskRegClass();
 
   Register ReservedRegForExecCopy = MFI->getSGPRForEXECCopy();
-  if (NeedExecCopyReservedReg ||
+  bool NeedExecCopy =
+      NeedExecCopyReservedReg ||
       (ReservedRegForExecCopy &&
-       MRI.isPhysRegUsed(ReservedRegForExecCopy, /*SkipRegMaskTest=*/true))) {
+       MRI.isPhysRegUsed(ReservedRegForExecCopy, /*SkipRegMaskTest=*/true));
+
+  // An SGPR spill during frame index elimination needs the register too. The
+  // code below still hands it back if a scratch SGPR turns out to be free.
+  if (!NeedExecCopy && ReservedRegForExecCopy &&
+      mayNeedExecCopyForScalarFrameIndex(MF))
+    NeedExecCopy = true;
+
+  if (NeedExecCopy) {
     MRI.reserveReg(ReservedRegForExecCopy, TRI);
     Register UnusedScratchReg = findUnusedRegister(MRI, LiveUnits, RC);
     if (UnusedScratchReg) {
@@ -1924,6 +1933,66 @@ void SIFrameLowering::determinePrologEpilogSGPRSaves(
   }
 }
 
+// Match an instruction whose frame index eliminateFrameIndex has to materialize
+// into a scavenged SGPR while SCC is live.
+static bool hasScalarFrameIndexWithLiveSCC(const SIInstrInfo &TII,
+                                           const SIRegisterInfo &TRI,
+                                           const MachineInstr &MI) {
+  if (TII.isMUBUF(MI))
+    return false;
+
+  // A move reuses its own destination instead of scavenging. Keep in sync with
+  // the IsCopy handling in SIRegisterInfo::eliminateFrameIndex.
+  unsigned Opcode = MI.getOpcode();
+  if (Opcode == AMDGPU::S_MOV_B32 || Opcode == AMDGPU::V_MOV_B32_e32 ||
+      Opcode == AMDGPU::V_MOV_B32_e64)
+    return false;
+
+  bool HasScalarFrameIndex = false;
+  for (unsigned OpNo = 0, E = MI.getNumExplicitOperands(); OpNo != E; ++OpNo) {
+    if (!MI.getOperand(OpNo).isFI())
+      continue;
+    const TargetRegisterClass *RC = TII.getRegClass(MI.getDesc(), OpNo);
+    if (RC && SIRegisterInfo::isSGPRClass(RC)) {
+      HasScalarFrameIndex = true;
+      break;
+    }
+  }
+
+  if (!HasScalarFrameIndex)
+    return false;
+
+  // The spill and the reload land on either side of MI. An indefinite answer is
+  // not worth a register: the spill still reports rather than miscompiles.
+  const MachineBasicBlock &MBB = *MI.getParent();
+  MachineBasicBlock::const_iterator I(MI);
+  return MBB.computeRegisterLiveness(&TRI, AMDGPU::SCC, I) ==
+             MachineBasicBlock::LQR_Live ||
+         MBB.computeRegisterLiveness(&TRI, AMDGPU::SCC, std::next(I)) ==
+             MachineBasicBlock::LQR_Live;
+}
+
+bool SIFrameLowering::mayNeedExecCopyForScalarFrameIndex(
+    const MachineFunction &MF) const {
+  const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
+  // Frame indices need no scalar temporary at the bottom of the stack, nor with
+  // flat scratch.
+  if (ST.hasFlatScratchEnabled() ||
+      MF.getInfo<SIMachineFunctionInfo>()->isBottomOfStack())
+    return false;
+
+  const SIInstrInfo &TII = *ST.getInstrInfo();
+  const SIRegisterInfo &TRI = *ST.getRegisterInfo();
+  for (const MachineBasicBlock &MBB : MF) {
+    for (const MachineInstr &MI : MBB) {
+      if (hasScalarFrameIndexWithLiveSCC(TII, TRI, MI))
+        return true;
+    }
+  }
+
+  return false;
+}
+
 // Only report VGPRs to generic code.
 void SIFrameLowering::determineCalleeSaves(MachineFunction &MF,
                                            BitVector &SavedVGPRs,
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.h b/llvm/lib/Target/AMDGPU/SIFrameLowering.h
index 38bf3fee0cf7b..b6af267309212 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.h
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.h
@@ -48,6 +48,11 @@ class SIFrameLowering final : public AMDGPUFrameLowering {
                             const DebugLoc &DL, LiveRegUnits &LiveUnits,
                             Register FrameReg,
                             Register FramePtrRegScratchCopy) const;
+
+  /// Return true if frame index elimination may have to spill an SGPR to
+  /// memory while SCC is live. Such a spill needs a register to hold EXEC.
+  bool mayNeedExecCopyForScalarFrameIndex(const MachineFunction &MF) const;
+
   bool
   assignCalleeSavedSpillSlots(MachineFunction &MF,
                               const TargetRegisterInfo *TRI,
diff --git a/llvm/lib/Target/AMDGPU/SILowerSGPRSpills.cpp b/llvm/lib/Target/AMDGPU/SILowerSGPRSpills.cpp
index d0a57e163d29a..dd730a1019995 100644
--- a/llvm/lib/Target/AMDGPU/SILowerSGPRSpills.cpp
+++ b/llvm/lib/Target/AMDGPU/SILowerSGPRSpills.cpp
@@ -449,6 +449,7 @@ bool SILowerSGPRSpills::run(MachineFunction &MF) {
   const GCNSubtarget &ST = MF.getSubtarget<GCNSubtarget>();
   TII = ST.getInstrInfo();
   TRI = &TII->getRegisterInfo();
+  const SIFrameLowering *TFI = ST.getFrameLowering();
 
   assert(SaveBlocks.empty() && RestoreBlocks.empty());
 
@@ -610,14 +611,17 @@ bool SILowerSGPRSpills::run(MachineFunction &MF) {
     MadeChange = true;
   }
 
-  if (SpilledToVirtVGPRLanes) {
+  // Frame index elimination may need the register to keep SCC alive across an
+  // SGPR spill to memory, and nothing can free one up after this point.
+  if (SpilledToVirtVGPRLanes || TFI->mayNeedExecCopyForScalarFrameIndex(MF)) {
     const TargetRegisterClass *RC = TRI->getWaveMaskRegClass();
     // Shift back the reserved SGPR for EXEC copy into the lowest range.
     // This SGPR is reserved to handle the whole-wave spill/copy operations
     // that might get inserted during vgpr regalloc.
+    Register ExecCopyReg = FuncInfo->getSGPRForEXECCopy();
     Register UnusedLowSGPR = TRI->findUnusedRegister(MRI, RC, MF);
-    if (UnusedLowSGPR && TRI->getHWRegIndex(UnusedLowSGPR) <
-                             TRI->getHWRegIndex(FuncInfo->getSGPRForEXECCopy()))
+    if (UnusedLowSGPR && (!ExecCopyReg || TRI->getHWRegIndex(UnusedLowSGPR) <
+                                              TRI->getHWRegIndex(ExecCopyReg)))
       FuncInfo->setSGPRForEXECCopy(UnusedLowSGPR);
   } else {
     // No SGPR spills to virtual VGPR lanes and hence there won't be any WWM
diff --git a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
index f570aa4e2f6fb..4acdc5a9f939f 100644
--- a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
@@ -222,6 +222,18 @@ struct SGPRSpillBuilder {
     RS->setRegUsed(SuperReg);
     SavedExecReg = RS->scavengeRegisterBackwards(RC, MI, false, 0, false);
 
+    // With no SGPR left to scavenge, fall back to the register reserved for
+    // EXEC copies; the S_NOT below would clobber SCC instead. Being reserved it
+    // is invisible to the scavenger, so ask the block whether it is free:
+    // inline asm can still name it.
+    if (!SavedExecReg) {
+      MCRegister ExecCopyReg = MFI.getSGPRForEXECCopy();
+      if (ExecCopyReg &&
+          MBB->computeRegisterLiveness(&TRI, ExecCopyReg, MI, UINT_MAX) ==
+              MachineBasicBlock::LQR_Dead)
+        SavedExecReg = ExecCopyReg;
+    }
+
     int64_t VGPRLanes = getPerVGPRData().VGPRLanes;
 
     if (SavedExecReg) {
@@ -236,8 +248,7 @@ struct SGPRSpillBuilder {
       TRI.buildVGPRSpillLoadStore(*this, TmpVGPRIndex, 0, /*IsLoad*/ false);
     } else {
       // The modify and restore of exec clobber SCC, which we would have to save
-      // and restore. FIXME: We probably would need to reserve a register for
-      // this.
+      // and restore.
       if (RS->isRegUsed(AMDGPU::SCC))
         emitUnsupportedError(MF.getFunction(), *MI,
                              "unhandled SGPR spill to memory");
@@ -312,8 +323,7 @@ struct SGPRSpillBuilder {
       TRI.buildVGPRSpillLoadStore(*this, Index, Offset, IsLoad);
     } else {
       // The modify and restore of exec clobber SCC, which we would have to save
-      // and restore. FIXME: We probably would need to reserve a register for
-      // this.
+      // and restore.
       if (RS->isRegUsed(AMDGPU::SCC))
         emitUnsupportedError(MF.getFunction(), *MI,
                              "unhandled SGPR spill to memory");
diff --git a/llvm/test/CodeGen/AMDGPU/branch-relax-spill.ll b/llvm/test/CodeGen/AMDGPU/branch-relax-spill.ll
index 305b44fa39b2c..01669d9f15dbb 100644
--- a/llvm/test/CodeGen/AMDGPU/branch-relax-spill.ll
+++ b/llvm/test/CodeGen/AMDGPU/branch-relax-spill.ll
@@ -325,7 +325,8 @@ define amdgpu_kernel void @spill(ptr addrspace(1) %arg, i32 %cnd) #0 {
 ; CHECK-NEXT:    ;;#ASMEND
 ; CHECK-NEXT:    s_cbranch_scc0 .LBB0_1
 ; CHECK-NEXT:  ; %bb.3: ; %entry
-; CHECK-NEXT:    s_not_b64 exec, exec
+; CHECK-NEXT:    s_mov_b64 s[100:101], exec
+; CHECK-NEXT:    s_mov_b64 exec, 3
 ; CHECK-NEXT:    buffer_store_dword v0, off, s[96:99], 0
 ; CHECK-NEXT:    v_writelane_b32 v0, s0, 0
 ; CHECK-NEXT:    v_writelane_b32 v0, s1, 1
@@ -350,7 +351,7 @@ define amdgpu_kernel void @spill(ptr addrspace(1) %arg, i32 %cnd) #0 {
 ; CHECK-NEXT:    v_readlane_b32 s0, v0, 0
 ; CHECK-NEXT:    v_readlane_b32 s1, v0, 1
 ; CHECK-NEXT:    buffer_load_dword v0, off, s[96:99], 0
-; CHECK-NEXT:    s_not_b64 exec, exec
+; CHECK-NEXT:    s_mov_b64 exec, s[100:101]
 ; CHECK-NEXT:  .LBB0_2: ; %bb3
 ; CHECK-NEXT:    ;;#ASMSTART
 ; CHECK-NEXT:    ; reg use s0
diff --git a/llvm/test/CodeGen/AMDGPU/bug-undef-spilled-agpr.mir b/llvm/test/CodeGen/AMDGPU/bug-undef-spilled-agpr.mir
index 5c564decd1e7d..7510909eb1442 100644
--- a/llvm/test/CodeGen/AMDGPU/bug-undef-spilled-agpr.mir
+++ b/llvm/test/CodeGen/AMDGPU/bug-undef-spilled-agpr.mir
@@ -22,20 +22,22 @@ body:             |
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION llvm_def_aspace_cfa $sgpr32, 0, 6
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION llvm_register_pair $pc_reg, $sgpr30, 32, $sgpr31, 32
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION undefined $agpr0
+  ; GCN-NEXT:   frame-setup CFI_INSTRUCTION undefined $sgpr6
+  ; GCN-NEXT:   frame-setup CFI_INSTRUCTION undefined $sgpr7
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION undefined $sgpr14
-  ; GCN-NEXT:   $sgpr4_sgpr5 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; GCN-NEXT:   $sgpr6_sgpr7 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
   ; GCN-NEXT:   $vgpr63 = V_ACCVGPR_READ_B32_e64 $agpr0, implicit $exec
   ; GCN-NEXT:   BUFFER_STORE_DWORD_OFFSET $vgpr63, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 0, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.2, addrspace 5)
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION offset $agpr0, 0
   ; GCN-NEXT:   $exec = S_MOV_B64 -1
   ; GCN-NEXT:   BUFFER_STORE_DWORD_OFFSET killed $vgpr62, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 4, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.3, addrspace 5)
   ; GCN-NEXT:   frame-setup CFI_INSTRUCTION offset $vgpr62, 256
-  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr4_sgpr5
+  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr6_sgpr7
   ; GCN-NEXT:   renamable $vgpr62 = IMPLICIT_DEF
   ; GCN-NEXT:   $vgpr62 = SI_SPILL_S32_TO_VGPR $sgpr15, 0, killed $vgpr62
-  ; GCN-NEXT:   $noreg = S_OR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; GCN-NEXT:   $sgpr6_sgpr7 = S_OR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
   ; GCN-NEXT:   renamable $agpr0 = lr-split COPY killed renamable $vgpr62
-  ; GCN-NEXT:   $exec = S_MOV_B64 killed $noreg
+  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr6_sgpr7
   ; GCN-NEXT:   renamable $vgpr62 = IMPLICIT_DEF
   ; GCN-NEXT:   dead renamable $vgpr62 = V_AND_B32_e32 1, killed $vgpr62, implicit $exec
   ; GCN-NEXT: {{  $}}
@@ -57,19 +59,19 @@ body:             |
   ; GCN-NEXT:   successors: %bb.2(0x80000000)
   ; GCN-NEXT:   liveins: $agpr0
   ; GCN-NEXT: {{  $}}
-  ; GCN-NEXT:   $noreg = S_OR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; GCN-NEXT:   $sgpr6_sgpr7 = S_OR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
   ; GCN-NEXT:   renamable $vgpr62 = lr-split COPY renamable $agpr0
-  ; GCN-NEXT:   $exec = S_MOV_B64 killed $noreg
+  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr6_sgpr7
   ; GCN-NEXT:   $sgpr14 = SI_RESTORE_S32_FROM_VGPR killed $vgpr62, 1
   ; GCN-NEXT:   S_BRANCH %bb.2
   ; GCN-NEXT: {{  $}}
   ; GCN-NEXT: bb.4:
-  ; GCN-NEXT:   $sgpr4_sgpr5 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; GCN-NEXT:   $sgpr6_sgpr7 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
   ; GCN-NEXT:   $vgpr63 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 0, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.2, addrspace 5)
   ; GCN-NEXT:   $agpr0 = V_ACCVGPR_WRITE_B32_e64 killed $vgpr63, implicit $exec
   ; GCN-NEXT:   $exec = S_MOV_B64 -1
   ; GCN-NEXT:   $vgpr62 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 4, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.3, addrspace 5)
-  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr4_sgpr5
+  ; GCN-NEXT:   $exec = S_MOV_B64 killed $sgpr6_sgpr7
   ; GCN-NEXT:   SI_RETURN
   bb.0:
     liveins: $sgpr12, $sgpr13, $sgpr14, $sgpr15
diff --git a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-select.ll b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-select.ll
index d7caf2293d560..6350cc89cf91e 100644
--- a/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-select.ll
+++ b/llvm/test/CodeGen/AMDGPU/eliminate-frame-index-select.ll
@@ -17,11 +17,12 @@ define void @wobble() #0 {
 ; CHECK-NEXT:    s_or_saveexec_b32 s17, -1
 ; CHECK-NEXT:    buffer_store_dword v43, off, s[0:3], s33 offset:24 ; 4-byte Folded Spill
 ; CHECK-NEXT:    s_mov_b32 exec_lo, s17
-; CHECK-NEXT:    v_writelane_b32 v43, s16, 15
+; CHECK-NEXT:    v_writelane_b32 v43, s16, 16
 ; CHECK-NEXT:    s_addk_i32 s32, 0x400
 ; CHECK-NEXT:    buffer_store_dword v40, off, s[0:3], s33 offset:8 ; 4-byte Folded Spill
 ; CHECK-NEXT:    buffer_store_dword v41, off, s[0:3], s33 offset:4 ; 4-byte Folded Spill
 ; CHECK-NEXT:    buffer_store_dword v42, off, s[0:3], s33 ; 4-byte Folded Spill
+; CHECK-NEXT:    v_writelane_b32 v43, s55, 15
 ; CHECK-NEXT:    v_writelane_b32 v43, s34, 0
 ; CHECK-NEXT:    v_writelane_b32 v43, s35, 1
 ; CHECK-NEXT:    v_writelane_b32 v43, s36, 2
@@ -107,7 +108,8 @@ define void @wobble() #0 {
 ; CHECK-NEXT:    v_readlane_b32 s35, v43, 1
 ; CHECK-NEXT:    v_readlane_b32 s34, v43, 0
 ; CHECK-NEXT:    s_mov_b32 s32, s33
-; CHECK-NEXT:    v_readlane_b32 s4, v43, 15
+; CHECK-NEXT:    v_readlane_b32 s4, v43, 16
+; CHECK-NEXT:    v_readlane_b32 s55, v43, 15
 ; CHECK-NEXT:    s_or_saveexec_b32 s5, -1
 ; CHECK-NEXT:    buffer_load_dword v43, off, s[0:3], s33 offset:24 ; 4-byte Folded Reload
 ; CHECK-NEXT:    s_mov_b32 exec_lo, s5
diff --git a/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir b/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir
new file mode 100644
index 0000000000000..df6a9e2842db8
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir
@@ -0,0 +1,92 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -run-pass=prolog-epilog -o - %s 2>&1 | FileCheck %s
+
+# The frame index operand of the S_SUB_U32 has to be materialized into a scalar
+# register, and with every other SGPR live the scavenger frees one by spilling
+# it to memory. That spill flips EXEC, so it may only run if it can save EXEC
+# somewhere first: the S_SUBB_U32 below consumes the carry that S_SUB_U32
+# produces, and S_NOT_B64 would destroy it. The register held back for EXEC
+# copies covers this, so the sequence uses S_MOV_B64 and SCC survives.
+
+---
+name:            scc_live_across_scalar_frame_index
+alignment:       4
+tracksRegLiveness: true
+frameInfo:
+  maxAlignment:    16
+stack:
+  - { id: 0, size: 64, alignment: 16 }
+machineFunctionInfo:
+  isEntryFunction: false
+  scratchRSrcReg:  '$sgpr0_sgpr1_sgpr2_sgpr3'
+  frameOffsetReg:  '$sgpr33'
+  stackPtrOffsetReg: '$sgpr32'
+  sgprForEXECCopy: '$sgpr100_sgpr101'
+  occupancy:       2
+body:             |
+  ; CHECK-LABEL: name: scc_live_across_scalar_frame_index
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT:   liveins: $vgpr0, $vgpr0
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION llvm_def_aspace_cfa $sgpr32, 0, 6
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION llvm_register_pair $pc_reg, $sgpr30, 32, $sgpr31, 32
+  ; CHECK-NEXT:   $sgpr4_sgpr5 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; CHECK-NEXT:   BUFFER_STORE_DWORD_OFFSET $vgpr0, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 64, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.2, addrspace 5)
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION offset $vgpr0, 4096
+  ; CHECK-NEXT:   $exec = S_MOV_B64 killed $sgpr4_sgpr5
+  ; CHECK-NEXT:   $vgpr0 = SI_SPILL_S32_TO_VGPR $sgpr100, 0, undef $vgpr0
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION llvm_vector_registers $sgpr100, $vgpr0, 0, 32
+  ; CHECK-NEXT:   $vgpr0 = SI_SPILL_S32_TO_VGPR $sgpr101, 1, undef $vgpr0
+  ; CHECK-NEXT:   frame-setup CFI_INSTRUCTION llvm_vector_registers $sgpr101, $vgpr0, 1, 32
+  ; CHECK-NEXT:   S_BRANCH %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT:   liveins: $vcc, $vgpr0, $sgpr96_sgpr97_sgpr98_sgpr99, $sgpr84_sgpr85_sgpr86_sgpr87_sgpr88_sgpr89_sgpr90_sgpr91_sgpr92_sgpr93_sgpr94_sgpr95, $sgpr4_sgpr5_sgpr6_sgpr7_sgpr8_sgpr9_sgpr10_sgpr11_sgpr12_sgpr13_sgpr14_sgpr15_sgpr16_sgpr17_sgpr18_sgpr19, $sgpr20_sgpr21_sgpr22_sgpr23_sgpr24_sgpr25_sgpr26_sgpr27_sgpr28_sgpr29_sgpr30_sgpr31_sgpr32_sgpr33_sgpr34_sgpr35, $sgpr36_sgpr37_sgpr38_sgpr39_sgpr40_sgpr41_sgpr42_sgpr43_sgpr44_sgpr45_sgpr46_sgpr47_sgpr48_sgpr49_sgpr50_sgpr51, $sgpr52_sgpr53_sgpr54_sgpr55_sgpr56_sgpr57_sgpr58_sgpr59_sgpr60_sgpr61_sgpr62_sgpr63_sgpr64_sgpr65_sgpr66_sgpr67, $sgpr68_sgpr69_sgpr70_sgpr71_sgpr72_sgpr73_sgpr74_sgpr75_sgpr76_sgpr77_sgpr78_sgpr79_sgpr80_sgpr81_sgpr82_sgpr83
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $sgpr100_sgpr101 = S_MOV_B64 $exec
+  ; CHECK-NEXT:   $exec = S_MOV_B64 1, implicit-def $vgpr1
+  ; CHECK-NEXT:   BUFFER_STORE_DWORD_OFFSET killed $vgpr1, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $vgpr1 = SI_SPILL_S32_TO_VGPR killed $sgpr4, 0, undef $vgpr1
+  ; CHECK-NEXT:   BUFFER_STORE_DWORD_OFFSET killed $vgpr1, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $vgpr1 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $exec = S_MOV_B64 killed $sgpr100_sgpr101, implicit killed $vgpr1
+  ; CHECK-NEXT:   $sgpr4 = S_LSHR_B32 $sgpr32, 6, implicit-def dead $scc
+  ; CHECK-NEXT:   renamable $vcc_lo = S_SUB_U32 killed renamable $vcc_lo, killed $sgpr4, implicit-def $scc
+  ; CHECK-NEXT:   $sgpr100_sgpr101 = S_MOV_B64 $exec
+  ; CHECK-NEXT:   $exec = S_MOV_B64 1, implicit-def $vgpr2
+  ; CHECK-NEXT:   BUFFER_STORE_DWORD_OFFSET killed $vgpr2, $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" store (s32) into %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $vgpr2 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $sgpr4 = SI_RESTORE_S32_FROM_VGPR killed $vgpr2, 0
+  ; CHECK-NEXT:   $vgpr2 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 68, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.3, addrspace 5)
+  ; CHECK-NEXT:   $exec = S_MOV_B64 killed $sgpr100_sgpr101, implicit killed $vgpr2
+  ; CHECK-NEXT:   renamable $vcc_hi = S_SUBB_U32 killed renamable $vcc_hi, renamable $sgpr97, implicit-def dead $scc, implicit $scc
+  ; CHECK-NEXT:   S_NOP 0, implicit killed $sgpr4_sgpr5_sgpr6_sgpr7_sgpr8_sgpr9_sgpr10_sgpr11_sgpr12_sgpr13_sgpr14_sgpr15_sgpr16_sgpr17_sgpr18_sgpr19, implicit killed $sgpr20_sgpr21_sgpr22_sgpr23_sgpr24_sgpr25_sgpr26_sgpr27_sgpr28_sgpr29_sgpr30_sgpr31_sgpr32_sgpr33_sgpr34_sgpr35, implicit killed $sgpr36_sgpr37_sgpr38_sgpr39_sgpr40_sgpr41_sgpr42_sgpr43_sgpr44_sgpr45_sgpr46_sgpr47_sgpr48_sgpr49_sgpr50_sgpr51, implicit killed $sgpr52_sgpr53_sgpr54_sgpr55_sgpr56_sgpr57_sgpr58_sgpr59_sgpr60_sgpr61_sgpr62_sgpr63_sgpr64_sgpr65_sgpr66_sgpr67, implicit killed $sgpr68_sgpr69_sgpr70_sgpr71_sgpr72_sgpr73_sgpr74_sgpr75_sgpr76_sgpr77_sgpr78_sgpr79_sgpr80_sgpr81_sgpr82_sgpr83, implicit killed $sgpr84_sgpr85_sgpr86_sgpr87_sgpr88_sgpr89_sgpr90_sgpr91_sgpr92_sgpr93_sgpr94_sgpr95, implicit killed $sgpr96_sgpr97_sgpr98_sgpr99
+  ; CHECK-NEXT:   S_BRANCH %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   liveins: $vcc, $vgpr0
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   $sgpr100 = SI_RESTORE_S32_FROM_VGPR $vgpr0, 0
+  ; CHECK-NEXT:   $sgpr101 = SI_RESTORE_S32_FROM_VGPR $vgpr0, 1
+  ; CHECK-NEXT:   $sgpr4_sgpr5 = S_XOR_SAVEEXEC_B64 -1, implicit-def $exec, implicit-def dead $scc, implicit $exec
+  ; CHECK-NEXT:   $vgpr0 = BUFFER_LOAD_DWORD_OFFSET $sgpr0_sgpr1_sgpr2_sgpr3, $sgpr32, 64, 0, 0, implicit $exec :: ("amdgpu-thread-private" load (s32) from %stack.2, addrspace 5)
+  ; CHECK-NEXT:   $exec = S_MOV_B64 killed $sgpr4_sgpr5
+  ; CHECK-NEXT:   S_ENDPGM 0, implicit $vcc
+  bb.0:
+    S_BRANCH %bb.1
+
+  bb.1:
+    liveins: $sgpr4_sgpr5_sgpr6_sgpr7_sgpr8_sgpr9_sgpr10_sgpr11_sgpr12_sgpr13_sgpr14_sgpr15_sgpr16_sgpr17_sgpr18_sgpr19, $sgpr20_sgpr21_sgpr22_sgpr23_sgpr24_sgpr25_sgpr26_sgpr27_sgpr28_sgpr29_sgpr30_sgpr31_sgpr32_sgpr33_sgpr34_sgpr35, $sgpr36_sgpr37_sgpr38_sgpr39_sgpr40_sgpr41_sgpr42_sgpr43_sgpr44_sgpr45_sgpr46_sgpr47_sgpr48_sgpr49_sgpr50_sgpr51, $sgpr52_sgpr53_sgpr54_sgpr55_sgpr56_sgpr57_sgpr58_sgpr59_sgpr60_sgpr61_sgpr62_sgpr63_sgpr64_sgpr65_sgpr66_sgpr67, $sgpr68_sgpr69_sgpr70_sgpr71_sgpr72_sgpr73_sgpr74_sgpr75_sgpr76_sgpr77_sgpr78_sgpr79_sgpr80_sgpr81_sgpr82_sgpr83, $sgpr84_sgpr85_sgpr86_sgpr87_sgpr88_sgpr89_sgpr90_sgpr91_sgpr92_sgpr93_sgpr94_sgpr95, $sgpr96_sgpr97_sgpr98_sgpr99, $vcc
+
+    renamable $vcc_lo = S_SUB_U32 killed renamable $vcc_lo, %stack.0, implicit-def $scc
+    renamable $vcc_hi = S_SUBB_U32 killed renamable $vcc_hi, renamable $sgpr97, implicit-def dead $scc, implicit $scc
+    S_NOP 0, implicit killed $sgpr4_sgpr5_sgpr6_sgpr7_sgpr8_sgpr9_sgpr10_sgpr11_sgpr12_sgpr13_sgpr14_sgpr15_sgpr16_sgpr17_sgpr18_sgpr19, implicit killed $sgpr20_sgpr21_sgpr22_sgpr23_sgpr24_sgpr25_sgpr26_sgpr27_sgpr28_sgpr29_sgpr30_sgpr31_sgpr32_sgpr33_sgpr34_sgpr35, implicit killed $sgpr36_sgpr37_sgpr38_sgpr39_sgpr40_sgpr41_sgpr42_sgpr43_sgpr44_sgpr45_sgpr46_sgpr47_sgpr48_sgpr49_sgpr50_sgpr51, implicit killed $sgpr52_sgpr53_sgpr54_sgpr55_sgpr56_sgpr57_sgpr58_sgpr59_sgpr60_sgpr61_sgpr62_sgpr63_sgpr64_sgpr65_sgpr66_sgpr67, implicit killed $sgpr68_sgpr69_sgpr70_sgpr71_sgpr72_sgpr73_sgpr74_sgpr75_sgpr76_sgpr77_sgpr78_sgpr79_sgpr80_sgpr81_sgpr82_sgpr83, implicit killed $sgpr84_sgpr85_sgpr86_sgpr87_sgpr88_sgpr89_sgpr90_sgpr91_sgpr92_sgpr93_sgpr94_sgpr95, implicit killed $sgpr96_sgpr97_sgpr98_sgpr99
+    S_BRANCH %bb.2
+
+  bb.2:
+    liveins: $vcc
+
+    S_ENDPGM 0, implicit $vcc
+...

>From 5201d623ce1ec5fac7e70c8333d6e2b6ade51928 Mon Sep 17 00:00:00 2001
From: Pankaj Dwivedi <pankajkumar.divedi at amd.com>
Date: Wed, 9 Sep 2026 13:52:00 +0530
Subject: [PATCH 2/2] Update
 llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir

Co-authored-by: Matt Arsenault <Matthew.Arsenault at amd.com>
---
 .../sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir b/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir
index df6a9e2842db8..7c391c2bd44c7 100644
--- a/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir
+++ b/llvm/test/CodeGen/AMDGPU/sgpr-spill-to-vmem-scc-clobber-reserved-exec-copy.mir
@@ -1,5 +1,5 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -run-pass=prolog-epilog -o - %s 2>&1 | FileCheck %s
+# RUN: llc -mtriple=amdgpu9.0a-amd-amdhsa -run-pass=prolog-epilog -o - %s 2>&1 | FileCheck %s
 
 # The frame index operand of the S_SUB_U32 has to be materialized into a scalar
 # register, and with every other SGPR live the scavenger frees one by spilling



More information about the llvm-commits mailing list