Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 36 additions & 29 deletions llvm/docs/AMDGPUUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6027,8 +6027,13 @@ Frame Pointer

If the kernel needs a frame pointer for the reasons defined in
``SIFrameLowering`` then SGPR33 is used and is always set to ``0`` in the
kernel prolog. If a frame pointer is not required then all uses of the frame
pointer are replaced with immediate ``0`` offsets.
kernel prolog. On GFX12+, when dynamic VGPRs are enabled, the prologue will
check if the kernel is running on a compute queue, and if so it will reserve
some scratch space for any dynamic VGPRs that might need to be saved by the
CWSR trap handler. In this case, the frame pointer will be initialized to
a suitably aligned offset above this reserved area. If a frame pointer is not
required then all uses of the frame pointer are replaced with immediate ``0``
offsets.

.. _amdgpu-amdhsa-kernel-prolog-flat-scratch:

Expand Down Expand Up @@ -17140,33 +17145,35 @@ within a map that has been added by the same *vendor-name*.
.. table:: AMDPAL Code Object Hardware Stage Metadata Map
:name: amdgpu-amdpal-code-object-hardware-stage-metadata-map-table

========================== ============== ========= ===============================================================
String Key Value Type Required? Description
========================== ============== ========= ===============================================================
".entry_point" string The ELF symbol pointing to this pipeline's stage entry point.
".scratch_memory_size" integer Scratch memory size in bytes.
".lds_size" integer Local Data Share size in bytes.
".perf_data_buffer_size" integer Performance data buffer size in bytes.
".vgpr_count" integer Number of VGPRs used.
".agpr_count" integer Number of AGPRs used.
".sgpr_count" integer Number of SGPRs used.
".vgpr_limit" integer If non-zero, indicates the shader was compiled with a
directive to instruct the compiler to limit the VGPR usage to
be less than or equal to the specified value (only set if
different from HW default).
".sgpr_limit" integer SGPR count upper limit (only set if different from HW
default).
".threadgroup_dimensions" sequence of Thread-group X/Y/Z dimensions (Compute only).
3 integers
".wavefront_size" integer Wavefront size (only set if different from HW default).
".uses_uavs" boolean The shader reads or writes UAVs.
".uses_rovs" boolean The shader reads or writes ROVs.
".writes_uavs" boolean The shader writes to one or more UAVs.
".writes_depth" boolean The shader writes out a depth value.
".uses_append_consume" boolean The shader uses append and/or consume operations, either
memory or GDS.
".uses_prim_id" boolean The shader uses PrimID.
========================== ============== ========= ===============================================================
=========================== ============== ========= ===============================================================
String Key Value Type Required? Description
=========================== ============== ========= ===============================================================
".entry_point" string The ELF symbol pointing to this pipeline's stage entry point.
".scratch_memory_size" integer Scratch memory size in bytes.
".lds_size" integer Local Data Share size in bytes.
".perf_data_buffer_size" integer Performance data buffer size in bytes.
".vgpr_count" integer Number of VGPRs used.
".agpr_count" integer Number of AGPRs used.
".sgpr_count" integer Number of SGPRs used.
".dynamic_vgpr_saved_count" integer No Number of dynamic VGPRs that can be stored in scratch by the
CWSR trap handler. Only used on GFX12+.
".vgpr_limit" integer If non-zero, indicates the shader was compiled with a
directive to instruct the compiler to limit the VGPR usage to
be less than or equal to the specified value (only set if
different from HW default).
".sgpr_limit" integer SGPR count upper limit (only set if different from HW
default).
".threadgroup_dimensions" sequence of Thread-group X/Y/Z dimensions (Compute only).
3 integers
".wavefront_size" integer Wavefront size (only set if different from HW default).
".uses_uavs" boolean The shader reads or writes UAVs.
".uses_rovs" boolean The shader reads or writes ROVs.
".writes_uavs" boolean The shader writes to one or more UAVs.
".writes_depth" boolean The shader writes out a depth value.
".uses_append_consume" boolean The shader uses append and/or consume operations, either
memory or GDS.
".uses_prim_id" boolean The shader uses PrimID.
=========================== ============== ========= ===============================================================

..

Expand Down
9 changes: 8 additions & 1 deletion llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1440,8 +1440,15 @@ void AMDGPUAsmPrinter::EmitPALMetadata(const MachineFunction &MF,
MD->setEntryPoint(CC, MF.getFunction().getName());
MD->setNumUsedVgprs(CC, CurrentProgramInfo.NumVGPRsForWavesPerEU, Ctx);

// Only set AGPRs for supported devices
// For targets that support dynamic VGPRs, set the number of saved dynamic
// VGPRs (if any) in the PAL metadata.
const GCNSubtarget &STM = MF.getSubtarget<GCNSubtarget>();
if (STM.isDynamicVGPREnabled() &&
MFI->getScratchReservedForDynamicVGPRs() > 0)
MD->setHwStage(CC, ".dynamic_vgpr_saved_count",
MFI->getScratchReservedForDynamicVGPRs() / 4);

// Only set AGPRs for supported devices
if (STM.hasMAIInsts()) {
MD->setNumUsedAgprs(CC, CurrentProgramInfo.NumAccVGPR);
}
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AMDGPU/SIDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ enum Id { // HwRegCode, (6) [5:0]

enum Offset : unsigned { // Offset, (5) [10:6]
OFFSET_MEM_VIOL = 8,
OFFSET_ME_ID = 8, // in HW_ID2
};

enum ModeRegisterMasks : uint32_t {
Expand Down
71 changes: 62 additions & 9 deletions llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,17 +691,62 @@ void SIFrameLowering::emitEntryFunctionPrologue(MachineFunction &MF,
}
assert(ScratchWaveOffsetReg || !PreloadedScratchWaveOffsetReg);

if (hasFP(MF)) {
unsigned Offset = FrameInfo.getStackSize() * getScratchScaleFactor(ST);
if (!mayReserveScratchForCWSR(MF)) {
if (hasFP(MF)) {
Register FPReg = MFI->getFrameOffsetReg();
assert(FPReg != AMDGPU::FP_REG);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_MOV_B32), FPReg).addImm(0);
}

if (requiresStackPointerReference(MF)) {
Register SPReg = MFI->getStackPtrOffsetReg();
assert(SPReg != AMDGPU::SP_REG);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_MOV_B32), SPReg).addImm(Offset);
}
} else {
// We need to check if we're on a compute queue - if we are, then the CWSR
// trap handler may need to store some VGPRs on the stack. The first VGPR
// block is saved separately, so we only need to allocate space for any
// additional VGPR blocks used. For now, we will make sure there's enough
// room for the theoretical maximum number of VGPRs that can be allocated.
// FIXME: Figure out if the shader uses fewer VGPRs in practice.
assert(hasFP(MF));
Register FPReg = MFI->getFrameOffsetReg();
assert(FPReg != AMDGPU::FP_REG);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_MOV_B32), FPReg).addImm(0);
}

if (requiresStackPointerReference(MF)) {
Register SPReg = MFI->getStackPtrOffsetReg();
assert(SPReg != AMDGPU::SP_REG);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_MOV_B32), SPReg)
.addImm(FrameInfo.getStackSize() * getScratchScaleFactor(ST));
unsigned VGPRSize =
llvm::alignTo((ST.getAddressableNumVGPRs() -
AMDGPU::IsaInfo::getVGPRAllocGranule(&ST)) *
4,
FrameInfo.getMaxAlign());
MFI->setScratchReservedForDynamicVGPRs(VGPRSize);

BuildMI(MBB, I, DL, TII->get(AMDGPU::S_GETREG_B32), FPReg)
.addImm(AMDGPU::Hwreg::HwregEncoding::encode(
AMDGPU::Hwreg::ID_HW_ID2, AMDGPU::Hwreg::OFFSET_ME_ID, 2));
// The MicroEngine ID is 0 for the graphics queue, and 1 or 2 for compute
// (3 is unused, so we ignore it). Unfortunately, S_GETREG doesn't set
// SCC, so we need to check for 0 manually.
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_CMP_LG_U32)).addImm(0).addReg(FPReg);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_CMOVK_I32), FPReg).addImm(VGPRSize);
if (requiresStackPointerReference(MF)) {
Register SPReg = MFI->getStackPtrOffsetReg();
assert(SPReg != AMDGPU::SP_REG);

// If at least one of the constants can be inlined, then we can use
// s_cselect. Otherwise, use a mov and cmovk.
if (AMDGPU::isInlinableLiteral32(Offset, ST.hasInv2PiInlineImm()) ||
AMDGPU::isInlinableLiteral32(Offset + VGPRSize,
ST.hasInv2PiInlineImm())) {
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_CSELECT_B32), SPReg)
.addImm(Offset + VGPRSize)
.addImm(Offset);
} else {
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_MOV_B32), SPReg).addImm(Offset);
BuildMI(MBB, I, DL, TII->get(AMDGPU::S_CMOVK_I32), SPReg)
.addImm(Offset + VGPRSize);
}
}
}

bool NeedsFlatScratchInit =
Expand Down Expand Up @@ -1831,9 +1876,17 @@ bool SIFrameLowering::hasFPImpl(const MachineFunction &MF) const {
return frameTriviallyRequiresSP(MFI) || MFI.isFrameAddressTaken() ||
MF.getSubtarget<GCNSubtarget>().getRegisterInfo()->hasStackRealignment(
MF) ||
mayReserveScratchForCWSR(MF) ||
MF.getTarget().Options.DisableFramePointerElim(MF);
}

bool SIFrameLowering::mayReserveScratchForCWSR(
const MachineFunction &MF) const {
return MF.getSubtarget<GCNSubtarget>().isDynamicVGPREnabled() &&
AMDGPU::isEntryFunctionCC(MF.getFunction().getCallingConv()) &&
AMDGPU::isCompute(MF.getFunction().getCallingConv());
}

// This is essentially a reduced version of hasFP for entry functions. Since the
// stack pointer is known 0 on entry to kernels, we never really need an FP
// register. We may need to initialize the stack pointer depending on the frame
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AMDGPU/SIFrameLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ class SIFrameLowering final : public AMDGPUFrameLowering {

public:
bool requiresStackPointerReference(const MachineFunction &MF) const;

// Returns true if the function may need to reserve space on the stack for the
// CWSR trap handler.
bool mayReserveScratchForCWSR(const MachineFunction &MF) const;
};

} // end namespace llvm
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,8 @@ yaml::SIMachineFunctionInfo::SIMachineFunctionInfo(
ArgInfo(convertArgumentInfo(MFI.getArgInfo(), TRI)),
PSInputAddr(MFI.getPSInputAddr()), PSInputEnable(MFI.getPSInputEnable()),
MaxMemoryClusterDWords(MFI.getMaxMemoryClusterDWords()),
Mode(MFI.getMode()), HasInitWholeWave(MFI.hasInitWholeWave()) {
Mode(MFI.getMode()), HasInitWholeWave(MFI.hasInitWholeWave()),
ScratchReservedForDynamicVGPRs(MFI.getScratchReservedForDynamicVGPRs()) {
for (Register Reg : MFI.getSGPRSpillPhysVGPRs())
SpillPhysVGPRS.push_back(regToString(Reg, TRI));

Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/AMDGPU/SIMachineFunctionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ struct SIMachineFunctionInfo final : public yaml::MachineFunctionInfo {

bool HasInitWholeWave = false;

unsigned ScratchReservedForDynamicVGPRs = 0;

SIMachineFunctionInfo() = default;
SIMachineFunctionInfo(const llvm::SIMachineFunctionInfo &,
const TargetRegisterInfo &TRI,
Expand Down Expand Up @@ -350,6 +352,8 @@ template <> struct MappingTraits<SIMachineFunctionInfo> {
YamlIO.mapOptional("longBranchReservedReg", MFI.LongBranchReservedReg,
StringValue());
YamlIO.mapOptional("hasInitWholeWave", MFI.HasInitWholeWave, false);
YamlIO.mapOptional("scratchReservedForDynamicVGPRs",
MFI.ScratchReservedForDynamicVGPRs, 0);
}
};

Expand Down Expand Up @@ -455,6 +459,10 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunction,
unsigned NumSpilledSGPRs = 0;
unsigned NumSpilledVGPRs = 0;

// The size in bytes of the scratch space reserved for the CWSR trap handler
// to spill some of the dynamic VGPRs.
unsigned ScratchReservedForDynamicVGPRs = 0;

// Tracks information about user SGPRs that will be setup by hardware which
// will apply to all wavefronts of the grid.
GCNUserSGPRUsageInfo UserSGPRInfo;
Expand Down Expand Up @@ -780,6 +788,15 @@ class SIMachineFunctionInfo final : public AMDGPUMachineFunction,
BytesInStackArgArea = Bytes;
}

// This is only used if we need to save any dynamic VGPRs in scratch.
unsigned getScratchReservedForDynamicVGPRs() const {
return ScratchReservedForDynamicVGPRs;
}

void setScratchReservedForDynamicVGPRs(unsigned SizeInBytes) {
ScratchReservedForDynamicVGPRs = SizeInBytes;
}

// Add user SGPRs.
Register addPrivateSegmentBuffer(const SIRegisterInfo &TRI);
Register addDispatchPtr(const SIRegisterInfo &TRI);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ SIRegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC,
Register SIRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
const SIFrameLowering *TFI = ST.getFrameLowering();
const SIMachineFunctionInfo *FuncInfo = MF.getInfo<SIMachineFunctionInfo>();

// During ISel lowering we always reserve the stack pointer in entry and chain
// functions, but never actually want to reference it when accessing our own
// frame. If we need a frame pointer we use it, but otherwise we can just use
Expand Down
Loading