[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