22#define DEBUG_TYPE "machine-scheduler"
33 void schedule()
override {}
49 if (
MI.isDebugInstr())
52 unsigned Opc =
MI.getOpcode();
55 if (
Opc == AMDGPU::ATOMIC_FENCE ||
Opc == AMDGPU::S_WAIT_ASYNCCNT ||
56 Opc == AMDGPU::S_WAIT_TENSORCNT ||
Opc == AMDGPU::S_BARRIER_WAIT ||
57 Opc == AMDGPU::S_BARRIER_SIGNAL_IMM)
88 for (
SUnit *PrioritySU : PrioritySUs) {
89 if (!PrioritySU->isTopReady())
96 unsigned MinDepth = std::numeric_limits<unsigned int>::max();
97 SUnit *TargetSU =
nullptr;
98 for (
auto *SU : AllSUs) {
102 if (SU->isTopReady())
105 if (SU->getDepth() < MinDepth) {
106 MinDepth = SU->getDepth();
114 if (!AllSUs.insert(SU))
117 TotalCycles += BlockingCycles;
119 if (PrioritySUs.empty()) {
120 PrioritySUs.insert(SU);
124 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
125 if (SUDepth > CurrDepth)
128 if (SUDepth == CurrDepth) {
129 PrioritySUs.insert(SU);
135 PrioritySUs.insert(SU);
142 if (TotalCycles == 0)
145 ScheduledSUs.push_back(SU);
147 PrioritySUs.remove(SU);
151 if (BufferSize <= 1 || (ScheduledSUs.size() % BufferSize == 0))
152 TotalCycles -= std::min(TotalCycles, BlockingCycles);
156 if (PrioritySUs.empty()) {
157 for (
auto SU : AllSUs) {
158 if (PrioritySUs.empty()) {
159 PrioritySUs.insert(SU);
163 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
164 if (SUDepth > CurrDepth)
167 if (SUDepth == CurrDepth) {
168 PrioritySUs.insert(SU);
174 PrioritySUs.insert(SU);
180 if (BufferSize == 0 || AllSUs.empty())
185 BufferCycles = TotalCycles / AllSUs.size();
202 TotalCycles /= BufferSize;
208 if (HWUICand.getType() == Flavor) {
221 unsigned ReleaseAtCycle = 0;
226 ReleaseAtCycle = std::max(ReleaseAtCycle, (
unsigned)PI->ReleaseAtCycle);
228 return ReleaseAtCycle;
250 for (
unsigned I = 0;
I <
HWUInfo.size();
I++) {
267 for (
auto &SU :
DAG->SUnits) {
273 HWUI.finalizeCycles();
281 <<
" (" <<
DAG->SUnits.size() <<
" SUs) ===\n";
283 dbgs() <<
"\nHWUI Resource Pressure:\n";
285 if (HWUI.getTotalCycles() == 0)
289 dbgs() <<
" " << Name <<
": " << HWUI.getTotalCycles() <<
" cycles, "
290 << HWUI.size() <<
" instrs\n";
299 if (
A.producesCoexecWindow() !=
B.producesCoexecWindow())
300 return A.producesCoexecWindow();
303 if (
A.getTotalCycles() !=
B.getTotalCycles())
304 return A.getTotalCycles() >
B.getTotalCycles();
307 if (
A.size() !=
B.size())
308 return A.size() <
B.size();
311 return static_cast<unsigned>(
A.getType()) <
312 static_cast<unsigned>(
B.getType());
320 auto HasPrioritySU = [
this, &Cand, &TryCand](
unsigned ResourceIdx) {
337 auto TryEnablesResource = [&Cand, &TryCand,
this](
unsigned ResourceIdx) {
347 TargetSU != Cand.
SU &&
DAG->IsReachable(TargetSU, Cand.
SU);
348 bool TryCandEnables =
349 TargetSU != TryCand.
SU &&
DAG->IsReachable(TargetSU, TryCand.
SU);
351 if (!CandEnables && !TryCandEnables)
354 if (CandEnables && !TryCandEnables) {
361 if (!CandEnables && TryCandEnables) {
370 if (CandHeight > TryCandHeight) {
377 if (CandHeight < TryCandHeight) {
389 for (
unsigned I = 0;
I <
HWUInfo.size();
I++) {
392 if (!HasPrioritySU(
I))
395 bool Enabled = TryEnablesResource(
I);
406 for (
unsigned I = 0;
I <
HWUInfo.size();
I++) {
410 bool TryCandUsesCrit = HWUI.
contains(TryCand.
SU);
412 if (!CandUsesCrit && !TryCandUsesCrit)
415 if (CandUsesCrit != TryCandUsesCrit) {
436 if (Match == Cand.
SU) {
465 "coexec scheduler only supports top-down scheduling");
485 Top.HazardRec = std::make_unique<GCNHazardRecognizer>(
490 Heurs.updateForScheduling(SU);
496 "coexec scheduler only supports top-down scheduling");
498 if (
DAG->top() ==
DAG->bottom()) {
500 Bot.Available.empty() &&
Bot.Pending.empty() &&
"ReadyQ garbage");
504 bool PickedPending =
false;
510 PickedPending =
false;
516 PickedPending,
false);
530 unsigned CurrentCycle =
Top.getCurrCycle();
531 if (ReadyCycle > CurrentCycle)
532 Top.bumpCycle(ReadyCycle);
535 while (
Top.checkHazard(SU))
536 Top.bumpCycle(
Top.getCurrCycle() + 1);
538 Top.releasePending();
549 assert(IsTopNode &&
"coexec scheduler must only schedule from top boundary");
556 bool &PickedPending,
bool IsBottomUp) {
557 assert(Zone.
isTop() &&
"coexec scheduler only supports top boundary");
558 assert(!IsBottomUp &&
"coexec scheduler only supports top-down scheduling");
562 unsigned SGPRPressure = 0;
563 unsigned VGPRPressure = 0;
564 PickedPending =
false;
565 if (
DAG->isTrackingPressure()) {
567 SGPRPressure =
Pressure[AMDGPU::RegisterPressureSets::SReg_32];
568 VGPRPressure =
Pressure[AMDGPU::RegisterPressureSets::VGPR_32];
575 auto EvaluateQueue = [&](
ReadyQueue &Q,
bool FromPending) {
576 for (
SUnit *SU : Q) {
579 VGPRPressure, IsBottomUp);
586 PickedPending = FromPending;
598 EvaluateQueue(Zone.
Pending,
true);
605 unsigned Cycle = IsTopNode ?
Top.getCurrCycle() :
Bot.getCurrCycle();
607 dbgs() <<
"=== Pick @ Cycle " << Cycle <<
" ===\n";
615 dbgs() <<
" Reason: ";
643 if (
DAG->isTrackingPressure() &&
653 bool SameBoundary = Zone !=
nullptr;
660 Heurs.sortHWUIResources();
661 if (
Heurs.tryCriticalResource(TryCand, Cand, Zone)) {
666 if (
Heurs.tryCriticalResourceDependency(TryCand, Cand, Zone)) {
680 bool CandIsClusterSucc =
682 bool TryCandIsClusterSucc =
685 if (
tryGreater(TryCandIsClusterSucc, CandIsClusterSucc, TryCand, Cand,
697 if (
DAG->isTrackingPressure() &&
706 !
Rem.IsAcyclicLatencyLimited &&
tryLatency(TryCand, Cand, *Zone))
723 auto getBufferFullStalls = [
this, &Zone](
SUnit *SU) ->
unsigned {
736 if (BufferReadyCycle <= CurrCycle)
739 return BufferReadyCycle - CurrCycle;
746 unsigned Structural = 0;
748 unsigned Effective = 0;
753 auto GetStallCosts = [&](
SUnit *SU) {
754 unsigned ReadyCycle = Zone.
isTop() ? SU->TopReadyCycle : SU->BotReadyCycle;
756 Costs.Ready = ReadyCycle > CurrCycle ? ReadyCycle - CurrCycle : 0;
759 Costs.Buffer = getBufferFullStalls(SU);
761 std::max({Costs.Ready, Costs.Structural, Costs.Latency, Costs.Buffer});
765 StallCosts TryCosts = GetStallCosts(TryCand.
SU);
766 StallCosts CandCosts = GetStallCosts(Cand.
SU);
768 LLVM_DEBUG(
if (TryCosts.Effective || CandCosts.Effective) {
769 dbgs() <<
"Effective stalls: try=" << TryCosts.Effective
770 <<
" (ready=" << TryCosts.Ready <<
", struct=" << TryCosts.Structural
771 <<
", lat=" << TryCosts.Latency <<
", buffer=" << TryCosts.Buffer
772 <<
") cand=" << CandCosts.Effective <<
" (ready=" << CandCosts.Ready
773 <<
", struct=" << CandCosts.Structural
774 <<
", lat=" << CandCosts.Latency <<
", buffer=" << CandCosts.Buffer
778 return tryLess(TryCosts.Effective, CandCosts.Effective, TryCand, Cand,
Stall);
783 LLVM_DEBUG(
dbgs() <<
"AMDGPU coexec preRA scheduler selected for "
784 <<
C->MF->getName() <<
'\n');
786 C, std::make_unique<AMDGPUCoExecSchedStrategy>(
C));
794 <<
C->MF->getName() <<
'\n');
795 return new GCNNoopPostScheduleDAG(
C);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static SUnit * pickOnlyChoice(SchedBoundary &Zone)
Coexecution-focused scheduling strategy for AMDGPU.
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Register const TargetRegisterInfo * TRI
AMDGPU::AMDGPUSchedReason LastAMDGPUReason
void initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs) override
Optionally override the per-region scheduling policy.
CandidateHeuristics Heurs
SUnit * pickNode(bool &IsTopNode) override
Pick the next node to schedule, or return NULL.
void pickNodeFromQueue(SchedBoundary &Zone, const CandPolicy &ZonePolicy, const RegPressureTracker &RPTracker, SchedCandidate &Cand, bool &PickedPending, bool IsBottomUp)
bool tryEffectiveStall(SchedCandidate &Cand, SchedCandidate &TryCand, SchedBoundary &Zone)
void initialize(ScheduleDAGMI *DAG) override
Initialize the strategy after building the DAG for a new region.
void schedNode(SUnit *SU, bool IsTopNode) override
Notify MachineSchedStrategy that ScheduleDAGMI has scheduled an instruction and updated scheduled/rem...
AMDGPUCoExecSchedStrategy(const MachineSchedContext *C)
void dumpPickSummary(SUnit *SU, bool IsTopNode, SchedCandidate &Cand)
bool tryCandidateCoexec(SchedCandidate &Cand, SchedCandidate &TryCand, SchedBoundary *Zone)
Represent a constant reference to an array (0 or more elements consecutively in memory),...
void updateForScheduling(SUnit *SU)
Update the state to reflect that SU is going to be scheduled.
HardwareUnitInfo * getHWUIFromFlavor(AMDGPU::InstructionFlavor Flavor)
Given a Flavor , find the corresponding HardwareUnit.
void sortHWUIResources()
Sort the HardwarUnitInfo vector.
bool tryCriticalResource(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for critical resource consumption.
bool tryCriticalResourceDependency(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for dependencies of instructions that use prioritized HardwareUnits.
SmallVector< HardwareUnitInfo, 8 > HWUInfo
const SIRegisterInfo * SRI
const TargetSchedModel * SchedModel
void collectHWUIPressure()
Walk over the region and collect total usage per HardwareUnit.
void initialize(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel, const TargetRegisterInfo *TRI)
unsigned getHWUICyclesForInst(SUnit *SU)
Compute the blocking cycles for the appropriate HardwareUnit given an SU.
GCNDownwardRPTracker DownwardTracker
bool useGCNTrackers() const
GCNSchedStrategy(const MachineSchedContext *C)
SmallVector< GCNSchedStageID, 4 > SchedStages
void schedNode(SUnit *SU, bool IsTopNode) override
Notify MachineSchedStrategy that ScheduleDAGMI has scheduled an instruction and updated scheduled/rem...
std::vector< unsigned > Pressure
void initialize(ScheduleDAGMI *DAG) override
Initialize the strategy after building the DAG for a new region.
void printCandidateDecision(const SchedCandidate &Current, const SchedCandidate &Preferred)
unsigned getStructuralStallCycles(SchedBoundary &Zone, SUnit *SU) const
Estimate how many cycles SU must wait due to structural hazards at the current boundary cycle.
void initCandidate(SchedCandidate &Cand, SUnit *SU, bool AtTop, const RegPressureTracker &RPTracker, const SIRegisterInfo *SRI, unsigned SGPRPressure, unsigned VGPRPressure, bool IsBottomUp)
MachineSchedPolicy RegionPolicy
const TargetSchedModel * SchedModel
static const char * getReasonStr(GenericSchedulerBase::CandReason Reason)
const TargetRegisterInfo * TRI
SchedCandidate TopCand
Candidate last picked from Top boundary.
HardwareUnitInfo is a wrapper class which maps to some real hardware resource.
void markScheduled(SUnit *SU, unsigned BlockingCycles)
Update the state for SU being scheduled by removing it from the AllSUs and reducing its BlockingCycle...
bool contains(SUnit *SU) const
SUnit * getNextTargetSU(bool LookDeep=false) const
void insert(SUnit *SU, unsigned BlockingCycles)
Insert the SU into AllSUs and account its BlockingCycles into the TotalCycles.
void finalizeCycles()
After we've collected all the region pressure for this HWUI, correct for any specifics of the behavio...
AMDGPU::InstructionFlavor getType() const
unsigned getBufferAvailableCycle(unsigned CurrCycle)
SUnit * getHigherPriority(SUnit *SU, SUnit *Other) const
int getNumber() const
MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...
MachineInstrBundleIterator< MachineInstr > iterator
Representation of each machine instruction.
LLVM_ABI void print(raw_ostream &OS, bool IsStandalone=true, bool SkipOpers=false, bool SkipDebugLoc=false, bool AddNewLine=true, const TargetInstrInfo *TII=nullptr) const
Print this MI to OS.
virtual void initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs)
Optionally override the per-region scheduling policy.
Helpers for implementing custom MachineSchedStrategy classes.
Track the current register pressure at some position in the instruction stream, and remember the high...
const std::vector< unsigned > & getRegSetPressureAtPos() const
Get the register set pressure at the current position, which may be less than the pressure across the...
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
static bool isSALU(const MachineInstr &MI)
static bool isMFMAorWMMA(const MachineInstr &MI)
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isTRANS(const MachineInstr &MI)
static bool isLDSDMA(const MachineInstr &MI)
Scheduling unit. This is a node in the scheduling DAG.
unsigned TopReadyCycle
Cycle relative to start when node is ready.
unsigned NodeNum
Entry # of node in the node vector.
unsigned getHeight() const
Returns the height of this node, which is the length of the maximum path down to any node which has n...
unsigned getDepth() const
Returns the depth of this node, which is the length of the maximum path up to any node which has no p...
bool isScheduled
True once scheduled.
unsigned ParentClusterIdx
The parent cluster id.
bool isBottomReady() const
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
Each Scheduling boundary is associated with ready queues.
LLVM_ABI unsigned getLatencyStallCycles(SUnit *SU)
Get the difference between the given SUnit's ready time and the current cycle.
LLVM_ABI SUnit * pickOnlyChoice()
Call this before applying any other heuristics to the Available queue.
unsigned getCurrCycle() const
Number of cycles to issue the instructions scheduled in this zone.
A ScheduleDAG for scheduling lists of MachineInstr.
ScheduleDAGMILive is an implementation of ScheduleDAGInstrs that schedules machine instructions while...
ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply schedules machine instructions ac...
void addMutation(std::unique_ptr< ScheduleDAGMutation > Mutation)
Add a postprocessing step to the DAG builder.
Represent a constant reference to a string, i.e.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
Provide an instruction scheduling machine model to CodeGen passes.
const MCWriteProcResEntry * ProcResIter
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
InstructionFlavor
Classification of instructions by execution characteristics.
constexpr StringRef getFlavorName(InstructionFlavor F)
InstructionFlavor classifyFlavor(const MachineInstr &MI, const SIInstrInfo &SII)
Classify MI into the execution flavor that drives both the scheduler's slot preferences and the hazar...
StringRef getReasonName(AMDGPUSchedReason R)
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI int biasPhysReg(const SUnit *SU, bool isTop, bool BiasPRegsExtra=false)
Minimize physical register live ranges.
LLVM_ABI unsigned getWeakLeft(const SUnit *SU, bool isTop)
std::unique_ptr< ScheduleDAGMutation > createIGroupLPDAGMutation(AMDGPU::SchedulingPhase Phase)
Phase specifes whether or not this is a reentry into the IGroupLPDAGMutation.
LLVM_ABI bool tryPressure(const PressureChange &TryP, const PressureChange &CandP, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason, const TargetRegisterInfo *TRI, const MachineFunction &MF)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
ScheduleDAGInstrs * createGCNNoopPostMachineScheduler(MachineSchedContext *C)
LLVM_ABI bool tryLatency(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
ScheduleDAGInstrs * createGCNCoExecMachineScheduler(MachineSchedContext *C)
bool isTheSameCluster(unsigned A, unsigned B)
Return whether the input cluster ID's are the same and valid.
LLVM_ABI bool tryGreater(int TryVal, int CandVal, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason)
LLVM_ABI bool tryLess(int TryVal, int CandVal, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason)
Return true if this heuristic determines order.
@ Enabled
Convert any .debug_str_offsets tables to DWARF64 if needed.
LLVM_ABI cl::opt< MISched::Direction > PreRADirection
Policy for scheduling the next instruction in the candidate's zone.
Store the state used by GenericScheduler heuristics, required for the lifetime of one invocation of p...
void setBest(SchedCandidate &Best)
LLVM_ABI void initResourceDelta(const ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel)
SchedResourceDelta ResDelta
Status of an instruction's critical resource consumption.
Summarize the scheduling resources required for an instruction of a particular scheduling class.
MachineSchedContext provides enough context from the MachineScheduler pass for the target to instanti...
PressureChange CurrentMax