23#define DEBUG_TYPE "amdgpu-wait-sgpr-hazards"
27class AMDGPUWaitSGPRHazards {
35 bool CullSGPRHazardsOnFunctionBoundary;
36 bool CullSGPRHazardsAtMemWait;
37 unsigned CullSGPRHazardsMemWaitThreshold;
39 AMDGPUWaitSGPRHazards() =
default;
42 static std::optional<unsigned> sgprNumber(
Register Reg,
49 case AMDGPU::SGPR_NULL:
50 case AMDGPU::SGPR_NULL64:
56 unsigned RegN =
TRI.getHWRegIndex(
Reg);
63 return Reg == AMDGPU::VCC ||
Reg == AMDGPU::VCC_LO ||
Reg == AMDGPU::VCC_HI;
74 while (
I->isBundledWithPred())
80 if (
I->getOpcode() != AMDGPU::S_GETPC_B64)
84 const unsigned NewBytes = 4;
86 "Unexpected instruction insertion in bundle");
89 while (NextMI !=
End && NextMI->isBundledWithPred()) {
90 for (
auto &Operand : NextMI->operands()) {
91 if (Operand.isGlobal())
92 Operand.setOffset(Operand.getOffset() + NewBytes);
99 static constexpr unsigned None = 0;
100 static constexpr unsigned SALU = (1 << 0);
101 static constexpr unsigned VALU = (1 << 1);
103 std::bitset<64> Tracked;
104 std::bitset<128> SALUHazards;
105 std::bitset<128> VALUHazards;
106 unsigned VCCHazard =
None;
107 bool ActiveFlat =
false;
109 bool merge(
const HazardState &
RHS) {
110 HazardState Orig(*
this);
112 return (*
this != Orig);
116 return Tracked ==
RHS.Tracked && SALUHazards ==
RHS.SALUHazards &&
117 VALUHazards ==
RHS.VALUHazards && VCCHazard ==
RHS.VCCHazard &&
118 ActiveFlat ==
RHS.ActiveFlat;
124 Tracked |=
RHS.Tracked;
125 SALUHazards |=
RHS.SALUHazards;
126 VALUHazards |=
RHS.VALUHazards;
127 VCCHazard |=
RHS.VCCHazard;
128 ActiveFlat |=
RHS.ActiveFlat;
132 struct BlockHazardState {
139 static constexpr unsigned WAVE32_NOPS = 4;
140 static constexpr unsigned WAVE64_NOPS = 8;
145 unsigned Count = DsNopCount;
150 unsigned mergeMasks(
unsigned Mask1,
unsigned Mask2) {
181 auto MBB =
MI->getParent();
182 if (
MI ==
MBB->instr_begin())
186 if (It->getOpcode() != AMDGPU::S_WAITCNT_DEPCTR)
189 It->getOperand(0).setImm(
190 mergeMasks(Mask,
static_cast<unsigned>(It->getOperand(0).getImm())));
195 enum { WA_VALU = 0x1, WA_SALU = 0x2, WA_VCC = 0x4 };
197 HazardState State = BlockState[&
MBB].In;
199 bool Emitted =
false;
205 if (
MI->isMetaInstruction())
209 if (
MI->getOpcode() == AMDGPU::DS_NOP) {
210 if (++DsNops >= DsNopCount)
211 State.Tracked.reset();
219 State.ActiveFlat =
true;
225 State.VCCHazard = HazardState::None;
226 State.SALUHazards.reset();
227 State.VALUHazards.reset();
232 if (
MI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR) {
233 unsigned int Mask =
static_cast<unsigned>(
MI->getOperand(0).
getImm());
235 State.VCCHazard &= ~HazardState::VALU;
237 State.SALUHazards.reset();
238 State.VCCHazard &= ~HazardState::SALU;
241 State.VALUHazards.reset();
246 if (CullSGPRHazardsAtMemWait &&
247 (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT ||
248 MI->getOpcode() == AMDGPU::S_WAIT_SAMPLECNT ||
249 MI->getOpcode() == AMDGPU::S_WAIT_BVHCNT) &&
250 (
MI->getOperand(0).isImm() &&
MI->getOperand(0).getImm() == 0) &&
251 (State.Tracked.count() >= CullSGPRHazardsMemWaitThreshold)) {
252 if (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT && State.ActiveFlat) {
253 State.ActiveFlat =
false;
255 State.Tracked.reset();
257 insertHazardCull(
MBB,
MI);
265 if (!IsVALU && !IsSALU)
282 auto RegNumber = sgprNumber(
Reg, *
TRI);
288 unsigned RegN = *RegNumber;
289 unsigned PairN = (RegN >> 1) & 0x3f;
293 if (!State.Tracked[PairN]) {
295 State.Tracked.set(PairN);
306 if (State.VCCHazard & HazardState::VALU)
307 State.VCCHazard = HazardState::None;
309 State.VALUHazards.reset();
313 for (
unsigned RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
314 Wait |= State.SALUHazards[RegN + RegIdx] ? WA_SALU : 0;
315 Wait |= IsVALU && State.VALUHazards[RegN + RegIdx] ? WA_VALU : 0;
317 if (isVCC(
Reg) && State.VCCHazard) {
320 if (State.VCCHazard & HazardState::SALU)
322 if (State.VCCHazard & HazardState::VALU)
328 State.VCCHazard = IsSALU ? HazardState::SALU : HazardState::VALU;
330 for (
unsigned RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
332 State.SALUHazards.set(RegN + RegIdx);
334 State.VALUHazards.set(RegN + RegIdx);
341 (
MI->isCall() ||
MI->isReturn() ||
MI->isIndirectBranch()) &&
342 MI->getOpcode() != AMDGPU::S_ENDPGM &&
343 MI->getOpcode() != AMDGPU::S_ENDPGM_SAVED;
346 const bool HasImplicitVCC =
353 if (State.VCCHazard & HazardState::VALU)
355 if (State.SALUHazards.any() || (State.VCCHazard & HazardState::SALU))
357 if (State.VALUHazards.any())
359 if (CullSGPRHazardsOnFunctionBoundary && State.Tracked.any()) {
360 State.Tracked.reset();
362 insertHazardCull(
MBB,
MI);
368 if (
Op.isImplicit() &&
369 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
371 processOperand(
Op,
true);
379 State.VCCHazard &= ~HazardState::VALU;
382 if (
Wait & WA_SALU) {
383 State.SALUHazards.reset();
384 State.VCCHazard &= ~HazardState::SALU;
387 if (
Wait & WA_VALU) {
388 State.VALUHazards.reset();
392 if (!mergeConsecutiveWaitAlus(
MI, Mask)) {
394 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
403 if (
MI->isCall() && !CullSGPRHazardsOnFunctionBoundary)
409 if (
Op.isImplicit() &&
410 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
412 processOperand(
Op,
false);
416 BlockHazardState &BS = BlockState[&
MBB];
417 bool Changed = State != BS.Out;
419 assert(!
Changed &&
"Hazard state should not change on emit pass");
437 const unsigned VccLoIdx = *sgprNumber(AMDGPU::VCC_LO, *
TRI);
438 const unsigned VccHiIdx = *sgprNumber(AMDGPU::VCC_HI, *
TRI);
442 PendingVALUWriteSet(128);
445 auto CommitWrites = [&](
unsigned Mask) {
447 WriteSet |= PendingSALUWriteSet;
448 bool VccLoBit = WriteSet[VccLoIdx];
449 bool VccHiBit = WriteSet[VccHiIdx];
452 WriteSet |= PendingVALUWriteSet;
453 WriteSet[VccLoIdx] = VccLoBit;
454 WriteSet[VccHiIdx] = VccHiBit;
457 WriteSet[VccLoIdx] = VccLoBit || PendingVALUWriteSet[VccLoIdx];
458 WriteSet[VccHiIdx] = VccHiBit || PendingVALUWriteSet[VccHiIdx];
461 PendingSALUWriteSet.reset();
462 PendingVALUWriteSet.
reset();
466 if (
MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
467 (
MI.getOperand(0).getImm() & ConstantMaskBits) ==
474 static_cast<unsigned>(MaskOp.
getImm())));
481 CommitWrites(
static_cast<unsigned>(
MI.getOperand(0).getImm()));
487 if (PrevWait && (
MI.isCall() ||
MI.isReturn() ||
MI.isBranch() ||
488 MI.isTerminator())) {
493 if (
MI.isTerminator())
498 if (
MI.isMetaInstruction())
503 if (!IsVALU && !IsSALU)
513 std::optional<unsigned> RegNumber = sgprNumber(
Reg, *
TRI);
516 unsigned RegN = *RegNumber;
522 PendingSALUWriteSet.set(RegN, RegN + SGPRCount);
524 PendingVALUWriteSet.
set(RegN, RegN + SGPRCount);
529 WriteSet.find_prev(RegN + SGPRCount) >= (
signed)RegN) {
543 if (!ST->hasVALUReadSGPRHazard() && !ST->hasVALUMaskWriteHazard())
547 const AMDGPUOptions &CLOpts = ST->getCLOpts();
548 CullSGPRHazardsOnFunctionBoundary =
valueOr(
549 CLOpts.sgpr_hazard_boundary_cull,
551 CullSGPRHazardsAtMemWait =
valueOr(
552 CLOpts.sgpr_hazard_mem_wait_cull,
554 CullSGPRHazardsMemWaitThreshold =
555 CLOpts.sgpr_hazard_mem_wait_cull_threshold.value_or(
557 "amdgpu-sgpr-hazard-mem-wait-cull-threshold", 8));
559 TII = ST->getInstrInfo();
560 TRI = ST->getRegisterInfo();
562 DsNopCount = ST->isWave64() ? WAVE64_NOPS : WAVE32_NOPS;
566 if (ST->hasVALUMaskWriteHazard())
567 return ST->isWave64() ? runWaitMerging(MF) :
false;
571 !CullSGPRHazardsOnFunctionBoundary) {
575 BlockState[&EntryBlock].In.Tracked.set();
589 while (!Worklist.
empty()) {
594 HazardState NewState = BlockState[&
MBB].Out;
596 for (
auto Succ :
MBB.successors()) {
598 auto &SuccState = BlockState[Succ];
599 if (Succ->getSinglePredecessor() && !Succ->isEntryBlock()) {
600 if (SuccState.In != NewState) {
601 SuccState.In = NewState;
604 }
else if (SuccState.In.merge(NewState)) {
630 return AMDGPUWaitSGPRHazards().run(MF);
641char AMDGPUWaitSGPRHazardsLegacy::ID = 0;
646 "AMDGPU Insert waits for SGPR read hazards",
false,
false)
651 if (AMDGPUWaitSGPRHazards().run(MF))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static void updateGetPCBundle(MachineInstr *NewMI)
AMD GCN specific subclass of TargetSubtarget.
const HexagonInstrInfo * TII
static LoopDeletionResult merge(LoopDeletionResult A, LoopDeletionResult B)
Register const TargetRegisterInfo * TRI
static bool runOnMachineBasicBlock(MachineBasicBlock &MBB, MachineInstr *FirstNextMBBInstr, function_ref< MachineBranchProbabilityInfo &()> GetMBPI)
runOnMachineBasicBlock - Fill in delay slots for the given basic block.
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
MachineRegisterInfo & MRI
Interface definition for SIInstrInfo.
This file implements the SmallBitVector class.
Represent the analysis usage information of a pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
uint64_t getFnAttributeAsParsedInteger(StringRef Kind, uint64_t Default=0) const
For a string attribute Kind, parse attribute as an integer.
CallingConv::ID getCallingConv() const
getCallingConv()/setCallingConv(CC) - These method get and set the calling convention of this functio...
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Instructions::iterator instr_iterator
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineBasicBlock & front() const
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
const MachineOperand & getOperand(unsigned i) const
LLVM_ABI MachineInstrBundleIterator< MachineInstr > eraseFromParent()
Unlink 'this' from the containing basic block and delete it.
LLVM_ABI void moveBefore(MachineInstr *MovePos)
Move the instruction before MovePos.
bool isBundled() const
Return true if this instruction part of a bundle.
MachineOperand class - Representation of each machine instruction operand.
void setImm(int64_t immVal)
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Wrapper class representing virtual and physical registers.
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
static bool isSALU(const MachineInstr &MI)
static bool isFLATGlobal(const MachineInstr &MI)
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isFLAT(const MachineInstr &MI)
A vector that has set insertion semantics.
bool empty() const
Determine if the SetVector is empty or not.
bool insert(const value_type &X)
Insert a new element into the SetVector.
value_type pop_back_val()
This is a 'bitvector' (really, a variable-sized bit array), optimized for the case when the array is ...
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
self_iterator getIterator()
unsigned decodeFieldVaVcc(unsigned Encoded)
unsigned encodeFieldVaVcc(unsigned Encoded, unsigned VaVcc)
unsigned decodeFieldHoldCnt(unsigned Encoded, const IsaVersion &Version)
unsigned encodeFieldHoldCnt(unsigned Encoded, unsigned HoldCnt, const IsaVersion &Version)
unsigned encodeFieldVaSsrc(unsigned Encoded, unsigned VaSsrc)
unsigned encodeFieldVaVdst(unsigned Encoded, unsigned VaVdst)
unsigned decodeFieldSaSdst(unsigned Encoded)
unsigned decodeFieldVaSdst(unsigned Encoded)
unsigned encodeFieldVmVsrc(unsigned Encoded, unsigned VmVsrc)
unsigned decodeFieldVaSsrc(unsigned Encoded)
unsigned encodeFieldSaSdst(unsigned Encoded, unsigned SaSdst)
unsigned decodeFieldVaVdst(unsigned Encoded)
int getDefaultDepCtrEncoding(const MCSubtargetInfo &STI)
unsigned decodeFieldVmVsrc(unsigned Encoded)
unsigned encodeFieldVaSdst(unsigned Encoded, unsigned VaSdst)
LLVM_ABI IsaVersion getIsaVersion(StringRef GPU)
unsigned getRegBitWidth(unsigned RCID)
Get the size in bits of a register from the register class RC.
LLVM_READNONE constexpr bool isEntryFunctionCC(CallingConv::ID CC)
CallingConv Namespace - This namespace contains an enum with a value for the well-known calling conve...
This is an optimization pass for GlobalISel generic memory operations.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
bool operator!=(uint64_t V1, const APInt &V2)
char & AMDGPUWaitSGPRHazardsLegacyID
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
bool operator==(const AddressRangeValuePair &LHS, const AddressRangeValuePair &RHS)
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
auto reverse(ContainerTy &&C)
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
DWARFExpression::Operation Op
constexpr bool valueOr(BoolOrDefault X, bool Default)
bool operator|=(SparseBitVector< ElementSize > &LHS, const SparseBitVector< ElementSize > *RHS)
@ End
Run the machine verifier once, at the end of the pipeline.
IterT prev_nodbg(IterT It, IterT Begin, bool SkipPseudoOp=true)
Decrement It, then continue decrementing it while it points to a debug instruction.
Instruction set architecture version.