23#define DEBUG_TYPE "amdgpu-wait-sgpr-hazards"
27 cl::desc(
"Cull hazards on function boundaries"));
32 cl::desc(
"Cull hazards on memory waits"));
36 cl::desc(
"Number of tracked SGPRs before initiating hazard cull on memory "
41class AMDGPUWaitSGPRHazards {
49 bool CullSGPRHazardsOnFunctionBoundary;
50 bool CullSGPRHazardsAtMemWait;
51 unsigned CullSGPRHazardsMemWaitThreshold;
53 AMDGPUWaitSGPRHazards() =
default;
56 static std::optional<unsigned> sgprNumber(
Register Reg,
63 case AMDGPU::SGPR_NULL:
64 case AMDGPU::SGPR_NULL64:
70 unsigned RegN = TRI.getHWRegIndex(
Reg);
77 return Reg == AMDGPU::VCC ||
Reg == AMDGPU::VCC_LO ||
Reg == AMDGPU::VCC_HI;
88 while (
I->isBundledWithPred())
94 if (
I->getOpcode() != AMDGPU::S_GETPC_B64)
98 const unsigned NewBytes = 4;
100 "Unexpected instruction insertion in bundle");
103 while (NextMI !=
End && NextMI->isBundledWithPred()) {
104 for (
auto &Operand : NextMI->operands()) {
105 if (Operand.isGlobal())
106 Operand.setOffset(Operand.getOffset() + NewBytes);
113 static constexpr unsigned None = 0;
114 static constexpr unsigned SALU = (1 << 0);
115 static constexpr unsigned VALU = (1 << 1);
117 std::bitset<64> Tracked;
118 std::bitset<128> SALUHazards;
119 std::bitset<128> VALUHazards;
120 unsigned VCCHazard = None;
121 bool ActiveFlat =
false;
123 bool merge(
const HazardState &
RHS) {
124 HazardState Orig(*
this);
126 return (*
this != Orig);
130 return Tracked ==
RHS.Tracked && SALUHazards ==
RHS.SALUHazards &&
131 VALUHazards ==
RHS.VALUHazards && VCCHazard ==
RHS.VCCHazard &&
132 ActiveFlat ==
RHS.ActiveFlat;
138 Tracked |=
RHS.Tracked;
139 SALUHazards |=
RHS.SALUHazards;
140 VALUHazards |=
RHS.VALUHazards;
141 VCCHazard |=
RHS.VCCHazard;
142 ActiveFlat |=
RHS.ActiveFlat;
146 struct BlockHazardState {
151 DenseMap<const MachineBasicBlock *, BlockHazardState> BlockState;
153 static constexpr unsigned WAVE32_NOPS = 4;
154 static constexpr unsigned WAVE64_NOPS = 8;
156 void insertHazardCull(MachineBasicBlock &
MBB,
159 unsigned Count = DsNopCount;
164 unsigned mergeMasks(
unsigned Mask1,
unsigned Mask2) {
200 if (It->getOpcode() != AMDGPU::S_WAITCNT_DEPCTR)
203 It->getOperand(0).setImm(
204 mergeMasks(Mask,
static_cast<unsigned>(It->getOperand(0).getImm())));
208 bool runOnMachineBasicBlock(MachineBasicBlock &
MBB,
bool Emit) {
209 enum { WA_VALU = 0x1, WA_SALU = 0x2, WA_VCC = 0x4 };
211 HazardState State = BlockState[&
MBB].In;
212 SmallSet<Register, 8> SeenRegs;
219 if (
MI->isMetaInstruction())
223 if (
MI->getOpcode() == AMDGPU::DS_NOP) {
224 if (++DsNops >= DsNopCount)
225 State.Tracked.reset();
233 State.ActiveFlat =
true;
239 State.VCCHazard = HazardState::None;
240 State.SALUHazards.reset();
241 State.VALUHazards.reset();
246 if (
MI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR) {
247 unsigned int Mask =
static_cast<unsigned>(
MI->getOperand(0).
getImm());
249 State.VCCHazard &= ~HazardState::VALU;
251 State.SALUHazards.reset();
252 State.VCCHazard &= ~HazardState::SALU;
255 State.VALUHazards.reset();
260 if (CullSGPRHazardsAtMemWait &&
261 (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT ||
262 MI->getOpcode() == AMDGPU::S_WAIT_SAMPLECNT ||
263 MI->getOpcode() == AMDGPU::S_WAIT_BVHCNT) &&
264 (
MI->getOperand(0).isImm() &&
MI->getOperand(0).getImm() == 0) &&
265 (State.Tracked.count() >= CullSGPRHazardsMemWaitThreshold)) {
266 if (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT && State.ActiveFlat) {
267 State.ActiveFlat =
false;
269 State.Tracked.reset();
271 insertHazardCull(
MBB,
MI);
279 if (!IsVALU && !IsSALU)
284 auto processOperand = [&](
const MachineOperand &
Op,
bool IsUse) {
289 if (!TRI->isSGPRReg(*MRI,
Reg))
296 auto RegNumber = sgprNumber(
Reg, *TRI);
302 unsigned RegN = *RegNumber;
303 unsigned PairN = (RegN >> 1) & 0x3f;
307 if (!State.Tracked[PairN]) {
309 State.Tracked.set(PairN);
320 if (State.VCCHazard & HazardState::VALU)
321 State.VCCHazard = HazardState::None;
323 State.VALUHazards.reset();
327 for (
unsigned RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
328 Wait |= State.SALUHazards[RegN + RegIdx] ? WA_SALU : 0;
329 Wait |= IsVALU && State.VALUHazards[RegN + RegIdx] ? WA_VALU : 0;
331 if (isVCC(
Reg) && State.VCCHazard) {
334 if (State.VCCHazard & HazardState::SALU)
336 if (State.VCCHazard & HazardState::VALU)
342 State.VCCHazard = IsSALU ? HazardState::SALU : HazardState::VALU;
344 for (
unsigned RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
346 State.SALUHazards.set(RegN + RegIdx);
348 State.VALUHazards.set(RegN + RegIdx);
355 (
MI->isCall() ||
MI->isReturn() ||
MI->isIndirectBranch()) &&
356 MI->getOpcode() != AMDGPU::S_ENDPGM &&
357 MI->getOpcode() != AMDGPU::S_ENDPGM_SAVED;
360 const bool HasImplicitVCC =
367 if (State.VCCHazard & HazardState::VALU)
369 if (State.SALUHazards.any() || (State.VCCHazard & HazardState::SALU))
371 if (State.VALUHazards.any())
373 if (CullSGPRHazardsOnFunctionBoundary && State.Tracked.any()) {
374 State.Tracked.reset();
376 insertHazardCull(
MBB,
MI);
381 for (
const MachineOperand &
Op :
MI->all_uses()) {
382 if (
Op.isImplicit() &&
383 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
385 processOperand(
Op,
true);
393 State.VCCHazard &= ~HazardState::VALU;
396 if (
Wait & WA_SALU) {
397 State.SALUHazards.reset();
398 State.VCCHazard &= ~HazardState::SALU;
401 if (
Wait & WA_VALU) {
402 State.VALUHazards.reset();
406 if (!mergeConsecutiveWaitAlus(
MI, Mask)) {
408 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
417 if (
MI->isCall() && !CullSGPRHazardsOnFunctionBoundary)
422 for (
const MachineOperand &
Op :
MI->all_defs()) {
423 if (
Op.isImplicit() &&
424 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
426 processOperand(
Op,
false);
430 BlockHazardState &BS = BlockState[&
MBB];
431 bool Changed = State != BS.Out;
433 assert(!
Changed &&
"Hazard state should not change on emit pass");
451 const unsigned VccLoIdx = *sgprNumber(AMDGPU::VCC_LO, *TRI);
452 const unsigned VccHiIdx = *sgprNumber(AMDGPU::VCC_HI, *TRI);
454 for (MachineBasicBlock &
MBB : MF) {
455 SmallBitVector WriteSet(128), PendingSALUWriteSet(128),
456 PendingVALUWriteSet(128);
457 MachineInstr *PrevWait =
nullptr;
459 auto CommitWrites = [&](
unsigned Mask) {
461 WriteSet |= PendingSALUWriteSet;
462 bool VccLoBit = WriteSet[VccLoIdx];
463 bool VccHiBit = WriteSet[VccHiIdx];
466 WriteSet |= PendingVALUWriteSet;
467 WriteSet[VccLoIdx] = VccLoBit;
468 WriteSet[VccHiIdx] = VccHiBit;
471 WriteSet[VccLoIdx] = VccLoBit || PendingVALUWriteSet[VccLoIdx];
472 WriteSet[VccHiIdx] = VccHiBit || PendingVALUWriteSet[VccHiIdx];
475 PendingSALUWriteSet.reset();
476 PendingVALUWriteSet.reset();
479 for (MachineInstr &
MI :
MBB) {
480 if (
MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
481 (
MI.getOperand(0).getImm() & ConstantMaskBits) ==
485 MachineOperand &MaskOp =
MI.getOperand(0);
488 static_cast<unsigned>(MaskOp.
getImm())));
495 CommitWrites(
static_cast<unsigned>(
MI.getOperand(0).getImm()));
501 if (PrevWait && (
MI.isCall() ||
MI.isReturn() ||
MI.isBranch() ||
502 MI.isTerminator())) {
507 if (
MI.isTerminator())
512 if (
MI.isMetaInstruction())
517 if (!IsVALU && !IsSALU)
520 for (
const MachineOperand &
Op :
MI.operands()) {
524 if (!TRI->isSGPRReg(*MRI,
Reg))
527 std::optional<unsigned> RegNumber = sgprNumber(
Reg, *TRI);
530 unsigned RegN = *RegNumber;
536 PendingSALUWriteSet.set(RegN, RegN + SGPRCount);
538 PendingVALUWriteSet.set(RegN, RegN + SGPRCount);
543 WriteSet.find_prev(RegN + SGPRCount) >= (
signed)RegN) {
557 if (!ST->hasVALUReadSGPRHazard() && !ST->hasVALUMaskWriteHazard())
566 CullSGPRHazardsOnFunctionBoundary =
569 CullSGPRHazardsAtMemWait =
572 CullSGPRHazardsMemWaitThreshold =
573 static_cast<unsigned>(MF.
getFunction().getFnAttributeAsParsedInteger(
574 "amdgpu-sgpr-hazard-mem-wait-cull-threshold",
575 CullSGPRHazardsMemWaitThreshold));
577 TII = ST->getInstrInfo();
578 TRI = ST->getRegisterInfo();
580 DsNopCount = ST->isWave64() ? WAVE64_NOPS : WAVE32_NOPS;
584 if (ST->hasVALUMaskWriteHazard())
585 return ST->isWave64() ? runWaitMerging(MF) :
false;
589 !CullSGPRHazardsOnFunctionBoundary) {
592 MachineBasicBlock &EntryBlock = MF.
front();
593 BlockState[&EntryBlock].In.Tracked.set();
604 SetVector<MachineBasicBlock *> Worklist;
607 while (!Worklist.
empty()) {
609 bool Changed = runOnMachineBasicBlock(
MBB,
false);
612 HazardState NewState = BlockState[&
MBB].Out;
616 auto &SuccState = BlockState[Succ];
617 if (Succ->getSinglePredecessor() && !Succ->isEntryBlock()) {
618 if (SuccState.In != NewState) {
619 SuccState.In = NewState;
622 }
else if (SuccState.In.merge(NewState)) {
645 AMDGPUWaitSGPRHazardsLegacy() : MachineFunctionPass(ID) {}
648 return AMDGPUWaitSGPRHazards().run(MF);
651 void getAnalysisUsage(AnalysisUsage &AU)
const override {
659char AMDGPUWaitSGPRHazardsLegacy::ID = 0;
664 "AMDGPU Insert waits for SGPR read hazards",
false,
false)
669 if (AMDGPUWaitSGPRHazards().run(MF))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static cl::opt< bool > GlobalCullSGPRHazardsAtMemWait("amdgpu-sgpr-hazard-mem-wait-cull", cl::init(false), cl::Hidden, cl::desc("Cull hazards on memory waits"))
static cl::opt< unsigned > GlobalCullSGPRHazardsMemWaitThreshold("amdgpu-sgpr-hazard-mem-wait-cull-threshold", cl::init(8), cl::Hidden, cl::desc("Number of tracked SGPRs before initiating hazard cull on memory " "wait"))
static cl::opt< bool > GlobalCullSGPRHazardsOnFunctionBoundary("amdgpu-sgpr-hazard-boundary-cull", cl::init(false), cl::Hidden, cl::desc("Cull hazards on function boundaries"))
Function Alias Analysis false
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
Promote Memory to Register
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Interface definition for SIInstrInfo.
This file implements the SmallBitVector class.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
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.
instr_iterator instr_begin()
Instructions::iterator instr_iterator
instr_iterator instr_end()
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
iterator_range< succ_iterator > successors()
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.
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.
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)
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()
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
int getNumOccurrences() const
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)
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
@ Emitted
Assigned address, still materializing.
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
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.