[AMDGPU] Set SGPR spill stack IDs when creating spill slots (#197628)

### Summary
This PR resolves https://github.com/llvm/llvm-project/issues/195092. 

Teach register classes to describe the StackID to use for their spill
slots, and use it when creating spill frame objects. This lets AMDGPU
SGPR spill slots be marked as TargetStackID::SGPRSpill at construction
time instead of patching the frame object later while emitting spill
instructions.

Also handle SGPR callee-saved spill slots created directly by
SILowerSGPRSpills, which bypass the generic PEI spill slot creation
path. Without this, late SGPR scavenging can see an SGPR spill pseudo
using a default-stack frame index and trip the SGPRSpill assertion.
This commit is contained in:
woruyu 2026-06-09 23:42:03 +08:00 committed by GitHub
parent 44714f90d8
commit eac7bb4573
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 351 additions and 20 deletions

View file

@ -833,7 +833,9 @@ public:
/// Create a new statically sized stack object that represents a spill slot,
/// returning a nonnegative identifier to represent it.
LLVM_ABI int CreateSpillStackObject(uint64_t Size, Align Alignment);
LLVM_ABI int
CreateSpillStackObject(uint64_t Size, Align Alignment,
TargetStackID::Value StackID = TargetStackID::Default);
/// Remove or mark dead a statically sized stack object.
void RemoveStackObject(int ObjectIdx) {

View file

@ -21,6 +21,7 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/RegisterBank.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/MC/LaneBitmask.h"
#include "llvm/MC/MCRegisterInfo.h"
@ -61,6 +62,7 @@ public:
/// Configurable target specific flags.
const uint8_t TSFlags;
const uint8_t SpillStackID;
/// Whether the class supports two (or more) disjunct subregister indices.
const bool HasDisjunctSubRegs;
/// Whether a combination of subregisters can cover every register in the
@ -319,6 +321,12 @@ public:
return Align(getRegClassInfo(RC).SpillAlignment / 8);
}
/// Return the stack ID for spill slots holding a spilled copy of a register
/// from this class.
TargetStackID::Value getSpillStackID(const TargetRegisterClass &RC) const {
return static_cast<TargetStackID::Value>(RC.SpillStackID);
}
/// Return true if the given TargetRegisterClass has the ValueType T.
bool isTypeLegalForClass(const TargetRegisterClass &RC, MVT T) const {
for (auto I = legalclasstypes_begin(RC); *I != MVT::Other; ++I)

View file

@ -398,6 +398,9 @@ class RegisterClass<string namespace, list<ValueType> regTypes, int alignment,
// classes will be resolved in the order defined by this value, with lower values
// taking precedence over higher ones. Ties are resolved by enumeration order.
int BaseClassOrder = ?;
// Stack ID to use for spill slots of this register class.
int SpillStackID = 0;
}
// The memberList in a RegisterClass is a dag of set operations. TableGen

View file

@ -63,9 +63,10 @@ int MachineFrameInfo::CreateStackObject(uint64_t Size, Align Alignment,
return Index;
}
int MachineFrameInfo::CreateSpillStackObject(uint64_t Size, Align Alignment) {
int MachineFrameInfo::CreateSpillStackObject(uint64_t Size, Align Alignment,
TargetStackID::Value StackID) {
Alignment = clampStackAlignment(!StackRealignable, Alignment, StackAlignment);
CreateStackObject(Size, Alignment, true);
CreateStackObject(Size, Alignment, true, nullptr, StackID);
int Index = (int)Objects.size() - NumFixedObjects - 1;
ensureMaxAlignment(Alignment);
return Index;

View file

@ -523,7 +523,8 @@ static void assignCalleeSavedSpillSlots(MachineFunction &F,
// the TargetRegisterClass if the stack alignment is smaller. Use the
// min.
Alignment = std::min(Alignment, TFI->getStackAlign());
FrameIdx = MFI.CreateStackObject(Size, Alignment, true);
FrameIdx = MFI.CreateStackObject(Size, Alignment, true, nullptr,
RegInfo->getSpillStackID(*RC));
MFI.setIsCalleeSavedObjectIndex(FrameIdx, true);
} else {
// Spill it to the stack where we must.

View file

@ -494,7 +494,8 @@ int RegAllocFastImpl::getStackSpaceFor(Register VirtReg) {
if (Alignment > CurrentAlign && !TRI->canRealignStack(MF))
Alignment = CurrentAlign;
int FrameIdx = MFI->CreateSpillStackObject(Size, Alignment);
int FrameIdx =
MFI->CreateSpillStackObject(Size, Alignment, TRI->getSpillStackID(RC));
// Assign the slot.
StackSlotForVirtReg[VirtReg] = FrameIdx;

View file

@ -102,7 +102,8 @@ unsigned VirtRegMap::createSpillSlot(const TargetRegisterClass *RC) {
if (Alignment > CurrentAlign && !TRI->canRealignStack(*MF)) {
Alignment = CurrentAlign;
}
int SS = MF->getFrameInfo().CreateSpillStackObject(Size, Alignment);
int SS = MF->getFrameInfo().CreateSpillStackObject(Size, Alignment,
TRI->getSpillStackID(*RC));
++NumSpillSlots;
return SS;
}

View file

@ -1737,7 +1737,8 @@ void SIInstrInfo::storeRegToStackSlotImpl(
MachineRegisterInfo &MRI = MF->getRegInfo();
if (RI.isSGPRClass(RC)) {
MFI->setHasSpilledSGPRs();
if (FrameInfo.getStackID(FrameIndex) == TargetStackID::SGPRSpill)
MFI->setHasSpilledSGPRs();
assert(SrcReg != AMDGPU::M0 && "m0 should not be spilled");
assert(SrcReg != AMDGPU::EXEC_LO && SrcReg != AMDGPU::EXEC_HI &&
SrcReg != AMDGPU::EXEC && "exec should not be spilled");
@ -1759,8 +1760,6 @@ void SIInstrInfo::storeRegToStackSlotImpl(
.addMemOperand(MMO)
.addReg(MFI->getStackPtrOffsetReg(), RegState::Implicit);
if (RI.spillSGPRToVGPR())
FrameInfo.setStackID(FrameIndex, TargetStackID::SGPRSpill);
return;
}
@ -1949,7 +1948,8 @@ void SIInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
FrameInfo.getObjectAlign(FrameIndex));
if (RI.isSGPRClass(RC)) {
MFI->setHasSpilledSGPRs();
if (FrameInfo.getStackID(FrameIndex) == TargetStackID::SGPRSpill)
MFI->setHasSpilledSGPRs();
assert(DestReg != AMDGPU::M0 && "m0 should not be reloaded into");
assert(DestReg != AMDGPU::EXEC_LO && DestReg != AMDGPU::EXEC_HI &&
DestReg != AMDGPU::EXEC && "exec should not be spilled");
@ -1962,8 +1962,6 @@ void SIInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
MRI.constrainRegClass(DestReg, &AMDGPU::SReg_32_XM0_XEXECRegClass);
}
if (RI.spillSGPRToVGPR())
FrameInfo.setStackID(FrameIndex, TargetStackID::SGPRSpill);
BuildMI(MBB, MI, DL, OpDesc, DestReg)
.addFrameIndex(FrameIndex) // addr
.addMemOperand(MMO)

View file

@ -265,7 +265,8 @@ bool SILowerSGPRSpills::spillCalleeSavedRegs(
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
int JunkFI = MFI.CreateStackObject(TRI->getSpillSize(*RC),
TRI->getSpillAlign(*RC), true);
TRI->getSpillAlign(*RC), true,
nullptr, TRI->getSpillStackID(*RC));
CSI.emplace_back(Reg, JunkFI);
CalleeSavedFIs.push_back(JunkFI);
@ -277,8 +278,9 @@ bool SILowerSGPRSpills::spillCalleeSavedRegs(
// can be emitted appropriately.
if (SpillRetAddrReg) {
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(RetAddrReg);
int JunkFI = MFI.CreateStackObject(TRI->getSpillSize(*RC),
TRI->getSpillAlign(*RC), true);
int JunkFI =
MFI.CreateStackObject(TRI->getSpillSize(*RC), TRI->getSpillAlign(*RC),
true, nullptr, TRI->getSpillStackID(*RC));
CSI.push_back(CalleeSavedInfo(RetAddrReg, JunkFI));
CalleeSavedFIs.push_back(JunkFI);
}

View file

@ -2569,7 +2569,9 @@ bool SIRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MI,
case AMDGPU::SI_SPILL_S96_SAVE:
case AMDGPU::SI_SPILL_S64_SAVE:
case AMDGPU::SI_SPILL_S32_SAVE: {
return spillSGPR(MI, Index, RS, nullptr, nullptr, false, false, NeedsCFI);
return spillSGPR(MI, Index, RS, nullptr, nullptr,
FrameInfo.getStackID(Index) == TargetStackID::SGPRSpill,
false, NeedsCFI);
}
// SGPR register restore
@ -2587,7 +2589,9 @@ bool SIRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MI,
case AMDGPU::SI_SPILL_S96_RESTORE:
case AMDGPU::SI_SPILL_S64_RESTORE:
case AMDGPU::SI_SPILL_S32_RESTORE: {
return restoreSGPR(MI, Index, RS);
return restoreSGPR(MI, Index, RS, nullptr, nullptr,
FrameInfo.getStackID(Index) ==
TargetStackID::SGPRSpill);
}
// VGPR register spill

View file

@ -116,6 +116,7 @@ class SIRegisterClass <string n, list<ValueType> rTypes, int Align, dag rList>
let TSFlags{2} = HasVGPR;
let TSFlags{3} = HasAGPR;
let TSFlags{4} = HasSGPR;
let SpillStackID = !if(HasSGPR, 1, 0); // TargetStackID::SGPRSpill
// RA will use RegisterClass AllocationPriority amongst other info (e.g. ordering in the basic block)
// to decide which registers to try to assign first. Usually, this RegisterClass priority is given

View file

@ -0,0 +1,294 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -O1 -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a < %s | FileCheck %s
define void @sgpr_scavenge_fi_stack_id(double %input, i1 %enter_fma_path, i1 %repeat_outer_loop, i1 %enter_sgpr_loop, i1 %enter_inner_loop, i1 %exit_inner_loop, i1 %zero_fma_result) {
; CHECK-LABEL: sgpr_scavenge_fi_stack_id:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; CHECK-NEXT: s_xor_saveexec_b64 s[4:5], -1
; CHECK-NEXT: buffer_store_dword v10, off, s[0:3], s32 offset:4 ; 4-byte Folded Spill
; CHECK-NEXT: s_mov_b64 exec, s[4:5]
; CHECK-NEXT: v_writelane_b32 v10, s30, 0
; CHECK-NEXT: v_writelane_b32 v10, s31, 1
; CHECK-NEXT: v_and_b32_e32 v2, 1, v2
; CHECK-NEXT: v_cmp_eq_u32_e64 s[10:11], 1, v2
; CHECK-NEXT: v_and_b32_e32 v2, 1, v4
; CHECK-NEXT: v_cmp_eq_u32_e64 s[12:13], 1, v2
; CHECK-NEXT: v_and_b32_e32 v2, 1, v3
; CHECK-NEXT: v_and_b32_e32 v7, 1, v7
; CHECK-NEXT: v_and_b32_e32 v6, 1, v6
; CHECK-NEXT: v_and_b32_e32 v5, 1, v5
; CHECK-NEXT: v_cmp_eq_u32_e64 s[14:15], 1, v2
; CHECK-NEXT: v_cmp_eq_u32_e64 s[4:5], 1, v7
; CHECK-NEXT: v_cmp_eq_u32_e64 s[6:7], 1, v6
; CHECK-NEXT: v_cmp_eq_u32_e64 s[8:9], 1, v5
; CHECK-NEXT: s_xor_b64 s[16:17], s[14:15], -1
; CHECK-NEXT: s_xor_b64 s[18:19], s[12:13], -1
; CHECK-NEXT: s_xor_b64 s[22:23], s[10:11], -1
; CHECK-NEXT: s_mov_b64 s[20:21], 0
; CHECK-NEXT: s_mov_b32 s25, 0x3fe62e42
; CHECK-NEXT: s_mov_b32 s24, 0xfefa39ef
; CHECK-NEXT: s_mov_b32 s27, 0x3c7abc9e
; CHECK-NEXT: s_mov_b32 s26, 0x3b39803f
; CHECK-NEXT: v_mov_b32_e32 v6, 0
; CHECK-NEXT: s_mov_b32 s28, 0
; CHECK-NEXT: s_mov_b32 s29, 0x7ff00000
; CHECK-NEXT: s_mov_b32 s41, 0x40417e50
; CHECK-NEXT: s_mov_b32 s40, 0xa9dc6553
; CHECK-NEXT: s_mov_b32 s42, 0
; CHECK-NEXT: s_mov_b32 s43, 0x7ff80000
; CHECK-NEXT: s_branch .LBB0_2
; CHECK-NEXT: .LBB0_1: ; %loop.exit.guard
; CHECK-NEXT: ; in Loop: Header=BB0_2 Depth=1
; CHECK-NEXT: s_or_b64 exec, exec, s[46:47]
; CHECK-NEXT: s_and_b64 s[44:45], exec, s[44:45]
; CHECK-NEXT: s_or_b64 s[20:21], s[44:45], s[20:21]
; CHECK-NEXT: s_andn2_b64 exec, exec, s[20:21]
; CHECK-NEXT: s_cbranch_execz .LBB0_21
; CHECK-NEXT: .LBB0_2: ; %outer_latch
; CHECK-NEXT: ; =>This Loop Header: Depth=1
; CHECK-NEXT: ; Child Loop BB0_4 Depth 2
; CHECK-NEXT: ; Child Loop BB0_8 Depth 3
; CHECK-NEXT: ; Child Loop BB0_11 Depth 4
; CHECK-NEXT: ; Child Loop BB0_13 Depth 5
; CHECK-NEXT: s_mov_b64 s[46:47], 0
; CHECK-NEXT: ; implicit-def: $sgpr44_sgpr45
; CHECK-NEXT: ; implicit-def: $sgpr56_sgpr57
; CHECK-NEXT: s_branch .LBB0_4
; CHECK-NEXT: .LBB0_3: ; %Flow11
; CHECK-NEXT: ; in Loop: Header=BB0_4 Depth=2
; CHECK-NEXT: s_or_b64 exec, exec, s[60:61]
; CHECK-NEXT: s_and_b64 s[58:59], exec, s[58:59]
; CHECK-NEXT: s_or_b64 s[46:47], s[58:59], s[46:47]
; CHECK-NEXT: s_andn2_b64 s[44:45], s[44:45], exec
; CHECK-NEXT: s_and_b64 s[58:59], s[56:57], exec
; CHECK-NEXT: s_or_b64 s[44:45], s[44:45], s[58:59]
; CHECK-NEXT: s_andn2_b64 exec, exec, s[46:47]
; CHECK-NEXT: s_cbranch_execz .LBB0_1
; CHECK-NEXT: .LBB0_4: ; %outer_header
; CHECK-NEXT: ; Parent Loop BB0_2 Depth=1
; CHECK-NEXT: ; => This Loop Header: Depth=2
; CHECK-NEXT: ; Child Loop BB0_8 Depth 3
; CHECK-NEXT: ; Child Loop BB0_11 Depth 4
; CHECK-NEXT: ; Child Loop BB0_13 Depth 5
; CHECK-NEXT: s_mov_b64 s[58:59], -1
; CHECK-NEXT: s_mov_b64 s[62:63], -1
; CHECK-NEXT: s_and_saveexec_b64 s[60:61], s[10:11]
; CHECK-NEXT: s_cbranch_execz .LBB0_19
; CHECK-NEXT: ; %bb.5: ; %fma_setup
; CHECK-NEXT: ; in Loop: Header=BB0_4 Depth=2
; CHECK-NEXT: v_fma_f64 v[2:3], v[0:1], s[24:25], 0
; CHECK-NEXT: v_fmac_f64_e64 v[2:3], 0, s[26:27]
; CHECK-NEXT: s_mov_b64 s[62:63], 0
; CHECK-NEXT: ; implicit-def: $sgpr72_sgpr73
; CHECK-NEXT: ; implicit-def: $sgpr74_sgpr75
; CHECK-NEXT: ; implicit-def: $sgpr76_sgpr77
; CHECK-NEXT: s_branch .LBB0_8
; CHECK-NEXT: .LBB0_6: ; %Flow9
; CHECK-NEXT: ; in Loop: Header=BB0_8 Depth=3
; CHECK-NEXT: s_or_b64 exec, exec, s[88:89]
; CHECK-NEXT: s_andn2_b64 s[76:77], s[76:77], exec
; CHECK-NEXT: s_and_b64 s[88:89], s[92:93], exec
; CHECK-NEXT: s_or_b64 s[76:77], s[76:77], s[88:89]
; CHECK-NEXT: s_andn2_b64 s[74:75], s[74:75], exec
; CHECK-NEXT: s_and_b64 s[88:89], s[90:91], exec
; CHECK-NEXT: s_or_b64 s[74:75], s[74:75], s[88:89]
; CHECK-NEXT: .LBB0_7: ; %Flow8
; CHECK-NEXT: ; in Loop: Header=BB0_8 Depth=3
; CHECK-NEXT: s_or_b64 exec, exec, s[78:79]
; CHECK-NEXT: s_and_b64 s[78:79], exec, s[74:75]
; CHECK-NEXT: s_or_b64 s[62:63], s[78:79], s[62:63]
; CHECK-NEXT: s_andn2_b64 s[72:73], s[72:73], exec
; CHECK-NEXT: s_and_b64 s[78:79], s[76:77], exec
; CHECK-NEXT: s_or_b64 s[72:73], s[72:73], s[78:79]
; CHECK-NEXT: s_andn2_b64 exec, exec, s[62:63]
; CHECK-NEXT: s_cbranch_execz .LBB0_18
; CHECK-NEXT: .LBB0_8: ; %sgpr_loop_header
; CHECK-NEXT: ; Parent Loop BB0_2 Depth=1
; CHECK-NEXT: ; Parent Loop BB0_4 Depth=2
; CHECK-NEXT: ; => This Loop Header: Depth=3
; CHECK-NEXT: ; Child Loop BB0_11 Depth 4
; CHECK-NEXT: ; Child Loop BB0_13 Depth 5
; CHECK-NEXT: s_or_b64 s[76:77], s[76:77], exec
; CHECK-NEXT: s_or_b64 s[74:75], s[74:75], exec
; CHECK-NEXT: s_and_saveexec_b64 s[78:79], s[12:13]
; CHECK-NEXT: s_cbranch_execz .LBB0_7
; CHECK-NEXT: ; %bb.9: ; %sgpr_pressure_loop.preheader
; CHECK-NEXT: ; in Loop: Header=BB0_8 Depth=3
; CHECK-NEXT: s_mov_b64 s[90:91], 0
; CHECK-NEXT: ; implicit-def: $sgpr88_sgpr89
; CHECK-NEXT: s_branch .LBB0_11
; CHECK-NEXT: .LBB0_10: ; %Flow7
; CHECK-NEXT: ; in Loop: Header=BB0_11 Depth=4
; CHECK-NEXT: s_xor_b64 s[94:95], s[94:95], -1
; CHECK-NEXT: s_and_b64 s[92:93], exec, s[92:93]
; CHECK-NEXT: s_or_b64 s[90:91], s[92:93], s[90:91]
; CHECK-NEXT: s_andn2_b64 s[88:89], s[88:89], exec
; CHECK-NEXT: s_and_b64 s[92:93], s[94:95], exec
; CHECK-NEXT: s_or_b64 s[88:89], s[88:89], s[92:93]
; CHECK-NEXT: s_andn2_b64 exec, exec, s[90:91]
; CHECK-NEXT: s_cbranch_execz .LBB0_16
; CHECK-NEXT: .LBB0_11: ; %sgpr_pressure_loop
; CHECK-NEXT: ; Parent Loop BB0_2 Depth=1
; CHECK-NEXT: ; Parent Loop BB0_4 Depth=2
; CHECK-NEXT: ; Parent Loop BB0_8 Depth=3
; CHECK-NEXT: ; => This Loop Header: Depth=4
; CHECK-NEXT: ; Child Loop BB0_13 Depth 5
; CHECK-NEXT: s_and_saveexec_b64 s[92:93], s[8:9]
; CHECK-NEXT: s_cbranch_execz .LBB0_14
; CHECK-NEXT: ; %bb.12: ; %inner_stack_loop.preheader
; CHECK-NEXT: ; in Loop: Header=BB0_11 Depth=4
; CHECK-NEXT: v_cndmask_b32_e64 v5, v3, 0, s[4:5]
; CHECK-NEXT: v_cndmask_b32_e64 v4, v2, 0, s[4:5]
; CHECK-NEXT: v_cndmask_b32_e64 v5, v5, 0, s[14:15]
; CHECK-NEXT: v_cndmask_b32_e64 v4, v4, 0, s[14:15]
; CHECK-NEXT: v_mul_f64 v[4:5], v[4:5], s[28:29]
; CHECK-NEXT: v_mul_f64 v[4:5], v[4:5], s[40:41]
; CHECK-NEXT: s_mov_b32 vcc_lo, 0
; CHECK-NEXT: s_mov_b64 s[94:95], 0
; CHECK-NEXT: v_mul_f64 v[4:5], v[4:5], s[42:43]
; CHECK-NEXT: .LBB0_13: ; %inner_stack_loop
; CHECK-NEXT: ; Parent Loop BB0_2 Depth=1
; CHECK-NEXT: ; Parent Loop BB0_4 Depth=2
; CHECK-NEXT: ; Parent Loop BB0_8 Depth=3
; CHECK-NEXT: ; Parent Loop BB0_11 Depth=4
; CHECK-NEXT: ; => This Inner Loop Header: Depth=5
; CHECK-NEXT: s_and_b64 s[30:31], exec, s[6:7]
; CHECK-NEXT: s_or_b64 s[94:95], s[30:31], s[94:95]
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: buffer_store_dword v7, off, s[0:3], s32 offset:8
; CHECK-NEXT: v_writelane_b32 v7, s4, 0
; CHECK-NEXT: buffer_store_dword v7, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: buffer_store_dword v7, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: buffer_load_dword v7, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_waitcnt vmcnt(0)
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: s_lshr_b32 s4, s32, 6
; CHECK-NEXT: s_lshl3_add_u32 vcc_hi, vcc_lo, s4
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: buffer_store_dword v8, off, s[0:3], s32 offset:8
; CHECK-NEXT: buffer_load_dword v8, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: buffer_load_dword v8, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: s_waitcnt vmcnt(0)
; CHECK-NEXT: v_readlane_b32 s4, v8, 0
; CHECK-NEXT: buffer_load_dword v8, off, s[0:3], s32 offset:8
; CHECK-NEXT: s_waitcnt vmcnt(0)
; CHECK-NEXT: s_not_b64 exec, exec
; CHECK-NEXT: v_mov_b32_e32 v7, vcc_hi
; CHECK-NEXT: buffer_load_dword v8, v7, s[0:3], 0 offen
; CHECK-NEXT: buffer_load_dword v9, v7, s[0:3], 0 offen offset:4
; CHECK-NEXT: v_mov_b32_e32 v7, vcc_lo
; CHECK-NEXT: s_mov_b32 vcc_lo, 1
; CHECK-NEXT: s_waitcnt vmcnt(0)
; CHECK-NEXT: v_mul_f64 v[8:9], v[4:5], v[8:9]
; CHECK-NEXT: buffer_store_dword v9, off, s[0:3], 0 offset:4
; CHECK-NEXT: buffer_store_dword v8, off, s[0:3], 0
; CHECK-NEXT: buffer_store_dword v6, v7, s[0:3], 0 offen
; CHECK-NEXT: buffer_store_dword v6, v7, s[0:3], 0 offen offset:4
; CHECK-NEXT: s_andn2_b64 exec, exec, s[94:95]
; CHECK-NEXT: s_cbranch_execnz .LBB0_13
; CHECK-NEXT: .LBB0_14: ; %Flow
; CHECK-NEXT: ; in Loop: Header=BB0_11 Depth=4
; CHECK-NEXT: s_or_b64 exec, exec, s[92:93]
; CHECK-NEXT: s_mov_b64 s[92:93], -1
; CHECK-NEXT: s_mov_b64 s[94:95], -1
; CHECK-NEXT: s_cbranch_execz .LBB0_10
; CHECK-NEXT: ; %bb.15: ; %sgpr_loop_merge
; CHECK-NEXT: ; in Loop: Header=BB0_11 Depth=4
; CHECK-NEXT: s_mov_b64 s[94:95], 0
; CHECK-NEXT: s_orn2_b64 s[92:93], s[22:23], exec
; CHECK-NEXT: s_branch .LBB0_10
; CHECK-NEXT: .LBB0_16: ; %loop.exit.guard6
; CHECK-NEXT: ; in Loop: Header=BB0_8 Depth=3
; CHECK-NEXT: s_or_b64 exec, exec, s[90:91]
; CHECK-NEXT: s_mov_b64 s[90:91], -1
; CHECK-NEXT: s_mov_b64 s[92:93], 0
; CHECK-NEXT: s_and_saveexec_b64 s[94:95], s[88:89]
; CHECK-NEXT: s_xor_b64 s[88:89], exec, s[94:95]
; CHECK-NEXT: s_cbranch_execz .LBB0_6
; CHECK-NEXT: ; %bb.17: ; %after_sgpr_loop
; CHECK-NEXT: ; in Loop: Header=BB0_8 Depth=3
; CHECK-NEXT: s_mov_b64 s[92:93], exec
; CHECK-NEXT: s_orn2_b64 s[90:91], s[18:19], exec
; CHECK-NEXT: s_branch .LBB0_6
; CHECK-NEXT: .LBB0_18: ; %loop.exit.guard5
; CHECK-NEXT: ; in Loop: Header=BB0_4 Depth=2
; CHECK-NEXT: s_or_b64 exec, exec, s[62:63]
; CHECK-NEXT: s_orn2_b64 s[62:63], s[72:73], exec
; CHECK-NEXT: .LBB0_19: ; %Flow10
; CHECK-NEXT: ; in Loop: Header=BB0_4 Depth=2
; CHECK-NEXT: s_or_b64 exec, exec, s[60:61]
; CHECK-NEXT: s_or_b64 s[56:57], s[56:57], exec
; CHECK-NEXT: s_and_saveexec_b64 s[60:61], s[62:63]
; CHECK-NEXT: s_cbranch_execz .LBB0_3
; CHECK-NEXT: ; %bb.20: ; %outer_exit
; CHECK-NEXT: ; in Loop: Header=BB0_4 Depth=2
; CHECK-NEXT: s_andn2_b64 s[56:57], s[56:57], exec
; CHECK-NEXT: s_orn2_b64 s[58:59], s[16:17], exec
; CHECK-NEXT: s_branch .LBB0_3
; CHECK-NEXT: .LBB0_21: ; %DummyReturnBlock
; CHECK-NEXT: s_or_b64 exec, exec, s[20:21]
; CHECK-NEXT: v_readlane_b32 s30, v10, 0
; CHECK-NEXT: v_readlane_b32 s31, v10, 1
; CHECK-NEXT: s_xor_saveexec_b64 s[4:5], -1
; CHECK-NEXT: buffer_load_dword v10, off, s[0:3], s32 offset:4 ; 4-byte Folded Reload
; CHECK-NEXT: s_mov_b64 exec, s[4:5]
; CHECK-NEXT: s_waitcnt vmcnt(0)
; CHECK-NEXT: s_setpc_b64 s[30:31]
entry:
%private_slot = alloca i8, align 1, addrspace(5)
br label %outer_latch
outer_latch: ; preds = %outer_exit, %entry
br label %outer_header
outer_header: ; preds = %outer_exit, %outer_latch
br i1 %enter_fma_path, label %fma_setup, label %outer_exit
fma_setup: ; preds = %outer_header
%first_fma = call double @llvm.fma.f64(double %input, double f0x3FE62E42FEFA39EF, double 0.000000e+00)
%second_fma = call double @llvm.fma.f64(double 0.000000e+00, double f0x3C7ABC9E3B39803F, double %first_fma)
br label %sgpr_loop_header
sgpr_loop_header: ; preds = %after_sgpr_loop, %fma_setup
br i1 %enter_sgpr_loop, label %sgpr_pressure_loop, label %outer_exit
sgpr_pressure_loop: ; preds = %sgpr_loop_merge, %sgpr_loop_header
%carried_sgpr_vec = phi <2 x i32> [ %next_sgpr_vec, %sgpr_loop_merge ], [ zeroinitializer, %sgpr_loop_header ]
%selected_fma = select i1 %zero_fma_result, double 0.000000e+00, double %second_fma
%new_sgpr_vec = insertelement <2 x i32> zeroinitializer, i32 0, i32 0
br i1 %enter_inner_loop, label %inner_stack_loop, label %sgpr_loop_merge
inner_stack_loop: ; preds = %inner_stack_loop, %sgpr_pressure_loop
%stack_offset = phi i32 [ 0, %sgpr_pressure_loop ], [ 1, %inner_stack_loop ]
%selected_fp = select i1 %repeat_outer_loop, double 0.000000e+00, double %selected_fma
%inf_mul = fmul double +inf, %selected_fp
%scaled_mul = fmul double %inf_mul, f0x40417E50A9DC6553
%nan_mul = fmul double %scaled_mul, +qnan
%private_gep = getelementptr [8 x i8], ptr addrspace(5) %private_slot, i32 %stack_offset
%private_load = load double, ptr addrspace(5) %private_gep, align 1
%spill_value = fmul double %nan_mul, %private_load
store double %spill_value, ptr addrspace(5) null, align 1
%indexed_null = getelementptr i8, ptr addrspace(5) null, i32 %stack_offset
store double 0.000000e+00, ptr addrspace(5) %indexed_null, align 1
br i1 %exit_inner_loop, label %inner_done, label %inner_stack_loop
inner_done: ; preds = %inner_stack_loop
br label %sgpr_loop_merge
sgpr_loop_merge: ; preds = %inner_done, %sgpr_pressure_loop
%next_sgpr_vec = phi <2 x i32> [ %new_sgpr_vec, %inner_done ], [ %carried_sgpr_vec, %sgpr_pressure_loop ]
br i1 %enter_fma_path, label %sgpr_pressure_loop, label %after_sgpr_loop
after_sgpr_loop: ; preds = %sgpr_loop_merge
br i1 %enter_sgpr_loop, label %sgpr_loop_header, label %outer_exit
outer_exit: ; preds = %after_sgpr_loop, %sgpr_loop_header, %outer_header
br i1 %repeat_outer_loop, label %outer_header, label %outer_latch
}
declare double @llvm.fma.f64(double, double, double)

View file

@ -4,6 +4,7 @@
// RUN: not llvm-tblgen -gen-register-info -I %p/../../include %s -DERROR3 2>&1 | FileCheck %s --check-prefix=ERROR3
// RUN: not llvm-tblgen -gen-register-info -I %p/../../include %s -DERROR4 2>&1 | FileCheck %s --check-prefix=ERROR4
// RUN: not llvm-tblgen -gen-register-bank -I %p/../../include %s -DERROR5 2>&1 | FileCheck %s --check-prefix=ERROR5
// RUN: not llvm-tblgen -gen-register-info -I %p/../../include %s -DERROR6 2>&1 | FileCheck %s --check-prefix=ERROR6
include "llvm/Target/Target.td"
@ -13,7 +14,11 @@ foreach Index = 0-15 in {
def R#Index : Register <"r"#Index>;
}
def GPR32 : RegisterClass<"MyTarget", [i32], 32, (sequence "R%u", 0, 15)>;
def GPR32 : RegisterClass<"MyTarget", [i32], 32, (sequence "R%u", 0, 15)> {
#ifdef ERROR6
let SpillStackID = 256;
#endif
}
def sub0 : SubRegIndex<32, 0>;
def sub1 : SubRegIndex<32, 32>;
@ -55,3 +60,6 @@ def R0_R1 : RegisterWithSubRegs<"r0_r1", [R0]> {
// ERROR5: error: No register banks defined
#endif
#ifdef ERROR6
// ERROR6: error: SpillStackID out of range [0,255]
#endif

View file

@ -593,7 +593,7 @@ TEST(MachineInstrTest, SpliceOperands) {
// test tied operands
MCRegisterClass MRC{
0, 0, 0, 0, 0, 0, 0, 0, /*Allocatable=*/true, /*BaseClass=*/true};
TargetRegisterClass RC{&MRC, 0, 0, {}, 0, 0, 0, 0, 0, 0, 0, 0};
TargetRegisterClass RC{&MRC, 0, 0, {}, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// MachineRegisterInfo will be very upset if these registers aren't
// allocatable.
assert(RC.isAllocatable() && "unusable TargetRegisterClass");

View file

@ -730,6 +730,10 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank,
int CopyCostParsed = R->getValueAsInt("CopyCost");
Allocatable = R->getValueAsBit("isAllocatable");
AltOrderSelect = R->getValueAsString("AltOrderSelect");
int SpillStackIDParsed = R->getValueAsInt("SpillStackID");
if (!isUInt<8>(SpillStackIDParsed))
PrintFatalError(R->getLoc(), "SpillStackID out of range [0,255]");
SpillStackID = SpillStackIDParsed;
int AllocationPriority = R->getValueAsInt("AllocationPriority");
if (!isUInt<5>(AllocationPriority))
PrintFatalError(R->getLoc(), "AllocationPriority out of range [0,31]");
@ -757,7 +761,7 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank,
: Members(*Props.Members), TheDef(nullptr), Name(Name.str()),
RegsWithSuperRegsTopoSigs(RegBank.getNumTopoSigs()), EnumValue(-1),
RSI(Props.RSI), CopyCost(0), Allocatable(true), AllocationPriority(0),
GlobalPriority(false), TSFlags(0) {
GlobalPriority(false), TSFlags(0), SpillStackID(0) {
MemberBV.resize(RegBank.getRegisters().size());
Artificial = true;
GeneratePressureSet = false;
@ -794,6 +798,7 @@ void CodeGenRegisterClass::inheritProperties(CodeGenRegBank &RegBank) {
AllocationPriority = Super.AllocationPriority;
GlobalPriority = Super.GlobalPriority;
TSFlags = Super.TSFlags;
SpillStackID = Super.SpillStackID;
GeneratePressureSet |= Super.GeneratePressureSet;
// Copy all allocation orders, filter out foreign registers from the larger

View file

@ -374,6 +374,7 @@ public:
uint8_t AllocationPriority;
bool GlobalPriority;
uint8_t TSFlags;
uint8_t SpillStackID;
/// Contains the combination of the lane masks of all subregisters.
LaneBitmask LaneMask;
/// True if there are at least 2 subregisters which do not interfere.

View file

@ -1514,6 +1514,7 @@ void RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, raw_ostream &MainOS,
OS << ",\n " << (unsigned)RC.AllocationPriority << ",\n "
<< (RC.GlobalPriority ? "true" : "false") << ",\n "
<< format("0x%02x", RC.TSFlags) << ", /* TSFlags */\n "
<< (unsigned)RC.SpillStackID << ", /* SpillStackID */\n "
<< (RC.HasDisjunctSubRegs ? "true" : "false")
<< ", /* HasDisjunctSubRegs */\n "
<< (RC.CoveredBySubRegs ? "true" : "false")