25#define DEBUG_TYPE "amdgpu-wait-sgpr-hazards"
29 cl::desc(
"Cull hazards on function boundaries"));
34 cl::desc(
"Cull hazards on memory waits"));
38 cl::desc(
"Number of tracked SGPRs before initiating hazard cull on memory "
43class AMDGPUWaitSGPRHazards {
51 bool CullSGPRHazardsOnFunctionBoundary;
52 bool CullSGPRHazardsAtMemWait;
53 unsigned CullSGPRHazardsMemWaitThreshold;
55 AMDGPUWaitSGPRHazards() =
default;
58 static std::optional<unsigned> sgprNumber(
Register Reg,
65 case AMDGPU::SGPR_NULL:
66 case AMDGPU::SGPR_NULL64:
72 unsigned RegN = TRI.getHWRegIndex(
Reg);
79 return Reg == AMDGPU::VCC ||
Reg == AMDGPU::VCC_LO ||
Reg == AMDGPU::VCC_HI;
90 while (
I->isBundledWithPred())
96 if (
I->getOpcode() != AMDGPU::S_GETPC_B64)
100 const unsigned NewBytes = 4;
102 "Unexpected instruction insertion in bundle");
105 while (NextMI != End && NextMI->isBundledWithPred()) {
106 for (
auto &Operand : NextMI->operands()) {
107 if (Operand.isGlobal())
108 Operand.setOffset(Operand.getOffset() + NewBytes);
115 static constexpr unsigned None = 0;
116 static constexpr unsigned SALU = (1 << 0);
117 static constexpr unsigned VALU = (1 << 1);
119 std::bitset<64> Tracked;
120 std::bitset<128> SALUHazards;
121 std::bitset<128> VALUHazards;
122 unsigned VCCHazard = None;
123 bool ActiveFlat =
false;
125 bool merge(
const HazardState &
RHS) {
126 HazardState Orig(*
this);
128 return (*
this != Orig);
132 return Tracked ==
RHS.Tracked && SALUHazards ==
RHS.SALUHazards &&
133 VALUHazards ==
RHS.VALUHazards && VCCHazard ==
RHS.VCCHazard &&
134 ActiveFlat ==
RHS.ActiveFlat;
140 Tracked |=
RHS.Tracked;
141 SALUHazards |=
RHS.SALUHazards;
142 VALUHazards |=
RHS.VALUHazards;
143 VCCHazard |=
RHS.VCCHazard;
144 ActiveFlat |=
RHS.ActiveFlat;
148 struct BlockHazardState {
153 DenseMap<const MachineBasicBlock *, BlockHazardState> BlockState;
155 static constexpr unsigned WAVE32_NOPS = 4;
156 static constexpr unsigned WAVE64_NOPS = 8;
158 void insertHazardCull(MachineBasicBlock &
MBB,
161 unsigned Count = DsNopCount;
166 unsigned mergeMasks(
unsigned Mask1,
unsigned Mask2) {
197 auto MBB =
MI->getParent();
202 if (It->getOpcode() != AMDGPU::S_WAITCNT_DEPCTR)
205 It->getOperand(0).setImm(mergeMasks(Mask, It->getOperand(0).getImm()));
209 bool runOnMachineBasicBlock(MachineBasicBlock &
MBB,
bool Emit) {
210 enum { WA_VALU = 0x1, WA_SALU = 0x2, WA_VCC = 0x4 };
212 HazardState State = BlockState[&
MBB].In;
213 SmallSet<Register, 8> SeenRegs;
220 if (
MI->isMetaInstruction())
224 if (
MI->getOpcode() == AMDGPU::DS_NOP) {
225 if (++DsNops >= DsNopCount)
226 State.Tracked.reset();
234 State.ActiveFlat =
true;
240 State.VCCHazard = HazardState::None;
241 State.SALUHazards.reset();
242 State.VALUHazards.reset();
247 if (
MI->getOpcode() == AMDGPU::S_WAITCNT_DEPCTR) {
248 unsigned int Mask =
MI->getOperand(0).getImm();
250 State.VCCHazard &= ~HazardState::VALU;
252 State.SALUHazards.reset();
253 State.VCCHazard &= ~HazardState::SALU;
256 State.VALUHazards.reset();
261 if (CullSGPRHazardsAtMemWait &&
262 (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT ||
263 MI->getOpcode() == AMDGPU::S_WAIT_SAMPLECNT ||
264 MI->getOpcode() == AMDGPU::S_WAIT_BVHCNT) &&
265 (
MI->getOperand(0).isImm() &&
MI->getOperand(0).getImm() == 0) &&
266 (State.Tracked.count() >= CullSGPRHazardsMemWaitThreshold)) {
267 if (
MI->getOpcode() == AMDGPU::S_WAIT_LOADCNT && State.ActiveFlat) {
268 State.ActiveFlat =
false;
270 State.Tracked.reset();
272 insertHazardCull(
MBB,
MI);
280 if (!IsVALU && !IsSALU)
285 auto processOperand = [&](
const MachineOperand &
Op,
bool IsUse) {
290 if (!TRI->isSGPRReg(*MRI,
Reg))
297 auto RegNumber = sgprNumber(
Reg, *TRI);
303 unsigned RegN = *RegNumber;
304 unsigned PairN = (RegN >> 1) & 0x3f;
308 if (!State.Tracked[PairN]) {
310 State.Tracked.set(PairN);
321 if (State.VCCHazard & HazardState::VALU)
322 State.VCCHazard = HazardState::None;
324 State.VALUHazards.reset();
328 for (uint8_t RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
329 Wait |= State.SALUHazards[RegN + RegIdx] ? WA_SALU : 0;
330 Wait |= IsVALU && State.VALUHazards[RegN + RegIdx] ? WA_VALU : 0;
332 if (isVCC(
Reg) && State.VCCHazard) {
335 if (State.VCCHazard & HazardState::SALU)
337 if (State.VCCHazard & HazardState::VALU)
343 State.VCCHazard = IsSALU ? HazardState::SALU : HazardState::VALU;
345 for (uint8_t RegIdx = 0; RegIdx < SGPRCount; ++RegIdx) {
347 State.SALUHazards.set(RegN + RegIdx);
349 State.VALUHazards.set(RegN + RegIdx);
356 (
MI->isCall() ||
MI->isReturn() ||
MI->isIndirectBranch()) &&
357 MI->getOpcode() != AMDGPU::S_ENDPGM &&
358 MI->getOpcode() != AMDGPU::S_ENDPGM_SAVED;
361 const bool HasImplicitVCC =
368 if (State.VCCHazard & HazardState::VALU)
370 if (State.SALUHazards.any() || (State.VCCHazard & HazardState::SALU))
372 if (State.VALUHazards.any())
374 if (CullSGPRHazardsOnFunctionBoundary && State.Tracked.any()) {
375 State.Tracked.reset();
377 insertHazardCull(
MBB,
MI);
382 for (
const MachineOperand &
Op :
MI->all_uses()) {
383 if (
Op.isImplicit() &&
384 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
386 processOperand(
Op,
true);
394 State.VCCHazard &= ~HazardState::VALU;
397 if (
Wait & WA_SALU) {
398 State.SALUHazards.reset();
399 State.VCCHazard &= ~HazardState::SALU;
402 if (
Wait & WA_VALU) {
403 State.VALUHazards.reset();
407 if (!mergeConsecutiveWaitAlus(
MI, Mask)) {
409 TII->get(AMDGPU::S_WAITCNT_DEPCTR))
418 if (
MI->isCall() && !CullSGPRHazardsOnFunctionBoundary)
423 for (
const MachineOperand &
Op :
MI->all_defs()) {
424 if (
Op.isImplicit() &&
425 (!HasImplicitVCC || !
Op.isReg() || !isVCC(
Op.getReg())))
427 processOperand(
Op,
false);
431 BlockHazardState &BS = BlockState[&
MBB];
432 bool Changed = State != BS.Out;
434 assert(!
Changed &&
"Hazard state should not change on emit pass");
442 bool runWaitMerging(MachineFunction &MF) {
452 const unsigned VccLoIdx = *sgprNumber(AMDGPU::VCC_LO, *TRI);
453 const unsigned VccHiIdx = *sgprNumber(AMDGPU::VCC_HI, *TRI);
455 for (MachineBasicBlock &
MBB : MF) {
456 SmallBitVector WriteSet(128), PendingSALUWriteSet(128),
457 PendingVALUWriteSet(128);
458 MachineInstr *PrevWait =
nullptr;
460 auto CommitWrites = [&](
unsigned Mask) {
462 WriteSet |= PendingSALUWriteSet;
463 bool VccLoBit = WriteSet[VccLoIdx];
464 bool VccHiBit = WriteSet[VccHiIdx];
467 WriteSet |= PendingVALUWriteSet;
468 WriteSet[VccLoIdx] = VccLoBit;
469 WriteSet[VccHiIdx] = VccHiBit;
472 WriteSet[VccLoIdx] = VccLoBit || PendingVALUWriteSet[VccLoIdx];
473 WriteSet[VccHiIdx] = VccHiBit || PendingVALUWriteSet[VccHiIdx];
476 PendingSALUWriteSet.reset();
477 PendingVALUWriteSet.reset();
480 for (MachineInstr &
MI :
MBB) {
481 if (
MI.isMetaInstruction())
484 if (
MI.getOpcode() == AMDGPU::S_WAITCNT_DEPCTR &&
485 (
MI.getOperand(0).getImm() & ConstantMaskBits) ==
489 MachineOperand &MaskOp =
MI.getOperand(0);
498 CommitWrites(
MI.getOperand(0).getImm());
504 if (PrevWait && (
MI.isCall() ||
MI.isReturn() ||
MI.isBranch())) {
512 if (!IsVALU && !IsSALU)
515 for (
const MachineOperand &
Op :
MI.operands()) {
519 if (!TRI->isSGPRReg(*MRI,
Reg))
522 std::optional<unsigned> RegNumber = sgprNumber(
Reg, *TRI);
525 unsigned RegN = *RegNumber;
531 PendingSALUWriteSet.set(RegN, RegN + SGPRCount);
533 PendingVALUWriteSet.set(RegN, RegN + SGPRCount);
538 WriteSet.find_prev(RegN + SGPRCount) >= (
signed)RegN) {
550 bool run(MachineFunction &MF) {
552 if (!ST->hasVALUReadSGPRHazard() && !ST->hasVALUMaskWriteHazard())
561 CullSGPRHazardsOnFunctionBoundary =
564 CullSGPRHazardsAtMemWait =
567 CullSGPRHazardsMemWaitThreshold =
569 "amdgpu-sgpr-hazard-mem-wait-cull-threshold",
570 CullSGPRHazardsMemWaitThreshold);
572 TII = ST->getInstrInfo();
573 TRI = ST->getRegisterInfo();
575 DsNopCount = ST->isWave64() ? WAVE64_NOPS : WAVE32_NOPS;
579 if (ST->hasVALUMaskWriteHazard())
580 return ST->isWave64() ? runWaitMerging(MF) :
false;
584 !CullSGPRHazardsOnFunctionBoundary) {
587 MachineBasicBlock &EntryBlock = MF.
front();
588 BlockState[&EntryBlock].In.Tracked.set();
599 SetVector<MachineBasicBlock *> Worklist;
602 while (!Worklist.
empty()) {
604 bool Changed = runOnMachineBasicBlock(
MBB,
false);
607 HazardState NewState = BlockState[&
MBB].Out;
611 auto &SuccState = BlockState[Succ];
612 if (Succ->getSinglePredecessor() && !Succ->isEntryBlock()) {
613 if (SuccState.In != NewState) {
614 SuccState.In = NewState;
617 }
else if (SuccState.In.merge(NewState)) {
640 AMDGPUWaitSGPRHazardsLegacy() : MachineFunctionPass(ID) {}
642 bool runOnMachineFunction(MachineFunction &MF)
override {
643 return AMDGPUWaitSGPRHazards().run(MF);
646 void getAnalysisUsage(AnalysisUsage &AU)
const override {
654char AMDGPUWaitSGPRHazardsLegacy::ID = 0;
659 "AMDGPU Insert waits for SGPR read hazards",
false,
false)
664 if (AMDGPUWaitSGPRHazards().run(MF))
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Provides AMDGPU specific target descriptions.
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 a set that has insertion order iteration characteristics.
This file implements the SmallBitVector class.
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.
instr_iterator instr_begin()
Instructions::iterator instr_iterator
instr_iterator instr_end()
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)
DXILDebugInfoMap run(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)
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)
IterT prev_nodbg(IterT It, IterT Begin, bool SkipPseudoOp=true)
Decrement It, then continue decrementing it while it points to a debug instruction.