LLVM 24.0.0git
AMDGPUCoExecSchedStrategy.h
Go to the documentation of this file.
1//===- AMDGPUCoExecSchedStrategy.h - CoExec Scheduling Strategy -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// Coexecution-focused scheduling strategy for AMDGPU.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_LIB_TARGET_AMDGPU_AMDGPUCOEXECSCHEDSTRATEGY_H
15#define LLVM_LIB_TARGET_AMDGPU_AMDGPUCOEXECSCHEDSTRATEGY_H
16
17#include "AMDGPUCoExecInfo.h"
18#include "GCNSchedStrategy.h"
20
21namespace llvm {
22
23namespace AMDGPU {
25constexpr unsigned DS = 16;
26} // namespace DefaultBufferSizes
27
28/// AMDGPU-specific scheduling decision reasons. These provide more granularity
29/// than the generic CandReason enum for debugging purposes.
34 CritResourceBalance, // tryCriticalResource chose based on resource pressure
35 CritResourceDep, // tryCriticalResourceDependency chose based on enabling
37};
38
40 switch (R) {
42 return "None";
44 return "Stall";
46 return "MemoryPipeline";
48 return "CritResource";
50 return "CritResourceDep";
52 llvm_unreachable("Unknown AMDGPUSchedReason");
53 }
54 llvm_unreachable("Unknown AMDGPUSchedReason");
55}
56
57} // End namespace AMDGPU
58
59//===----------------------------------------------------------------------===//
60// Hardware Unit Information
61//===----------------------------------------------------------------------===//
62
63/// HardwareUnitInfo is a wrapper class which maps to some real hardware
64/// resource. This is used to model hardware resource pressure per region, and
65/// guide scheduling heuristics.
67private:
68 /// PrioritySUs maintains a list of the SUs we want to prioritize scheduling
69 /// for this HardwareUnit. This is used for agreement between
70 /// tryCriticalResourceDependency and tryCriticalResource: we schedule the
71 /// dependencies for a SU on critical resource, then schedule that same SU on
72 /// the critical resource. This agreement results in shorter live ranges and
73 /// more regular HardwareUnit access patterns. SUs are prioritized based on
74 /// depth for top-down scheduling.
76 /// All the SUs in the region that consume this resource.
78 /// All the SUs for this HardwareUnit that have already been scheduled.
79 SmallVector<SUnit *, 16> ScheduledSUs;
80 /// The total number of busy cycles for this HardwareUnit for a given region.
81 unsigned TotalCycles = 0;
82 /// InstructionFlavor mapping.
84 /// Whether or not instructions on this HardwareUnit may produce a window in
85 /// which instructions in other HardwareUnits can coexecute. For example, WMMA
86 /// / MFMA instructions may take multiple cycles, which may be overlapped with
87 /// instructions on other HardwareUnits.
88 bool ProducesCoexecWindow = false;
89 /// How many instructions can be held simultaneously for this HardwareUnit.
90 /// A value of 0 means there is no limit. A value of 1 models an unbuffered
91 /// resource with a single in-flight instruction.
92 ///
93 /// This may approximate the hardware. For example, for LDS instructions
94 /// it is a well-known phenomena that oversubscribing the LDS unit results in
95 /// longer latency for the LDS instructions. While it is true that there is a
96 /// hard limit to the amount of simulatenous in-flight LDS instructions, good
97 /// scheduling would also cool off the LDS to avoid other forms of hardware
98 /// contention and increasing LDS latency. Thus, we limit the amount of LDS
99 /// instructions we are willing to schedule close together, though this does
100 /// not correspond 1:1 with a hardware mechanism.
101 unsigned BufferSize = 0;
102 /// How many cycles it takes for an instruction to clear the buffer.
103 ///
104 /// Again, this may be an apprxoimation. For example, for memory FIFOs, the
105 /// actual amount of cycles it will take to clear it is dependent on how
106 /// quickly prior instructions evacuate the FIFO, which is based on runtime
107 /// behavior which is not modelled in the compiler.
108 unsigned BufferCycles = 0;
109
110public:
112
113 unsigned size() { return AllSUs.size(); }
114
115 unsigned getTotalCycles() { return TotalCycles; }
116
117 void setType(unsigned TheType) {
119 Type = (AMDGPU::InstructionFlavor)(TheType);
120 }
121
122 AMDGPU::InstructionFlavor getType() const { return Type; }
123
124 bool producesCoexecWindow() const { return ProducesCoexecWindow; }
125
126 void setProducesCoexecWindow(bool Val) { ProducesCoexecWindow = Val; }
127
128 bool contains(SUnit *SU) const { return AllSUs.contains(SU); }
129
130 void setBufferSize(unsigned Size) { BufferSize = Size; }
131
132 unsigned getBufferSize() { return BufferSize; }
133
134 /// \returns the next cycle where there is space in the buffer.
135 unsigned getBufferAvailableCycle(unsigned CurrCycle) {
136 // An unlimited buffer is always available.
137 if (BufferSize == 0)
138 return CurrCycle;
139
140 // Buffer is available now.
141 if (ScheduledSUs.size() < BufferSize)
142 return CurrCycle;
143
144 return BufferCycles +
145 ScheduledSUs[ScheduledSUs.size() - BufferSize]->TopReadyCycle;
146 }
147
148 /// \returns the most recently scheduled SU for this HardwareUnit.
150 unsigned ScheduledCount = ScheduledSUs.size();
151 if (!ScheduledCount)
152 return nullptr;
153
154 return ScheduledSUs[ScheduledCount - 1];
155 }
156
157 /// \returns the SUnit with higher priority or nullptr if they are the same.
158 /// This method looks through the PrioritySUs to determine if one SU is more
159 /// prioritized than the other. If neither are in the PrioritySUs list, then
160 /// neither have priority over each other.
162 for (SUnit *SUOrder : PrioritySUs) {
163 if (SUOrder == SU)
164 return SU;
165
166 if (SUOrder == Other)
167 return Other;
168 }
169 return nullptr;
170 }
171
172 void reset() {
173 AllSUs.clear();
174 PrioritySUs.clear();
175 ScheduledSUs.clear();
176 TotalCycles = 0;
178 ProducesCoexecWindow = false;
179 BufferSize = 0;
180 BufferCycles = 0;
181 }
182
183 /// \returns the next SU in PrioritySUs that is not ready. If \p LookDeep is
184 /// set, we will look beyond the PrioritySUs (if all the PrioritySUs are
185 /// ready) to AllSUs to attempt to find a target SU. When looking through
186 /// AllSUs we sort pick the target SU by minimal depth for top-down
187 /// scheduling. getNextTargetSU is useful for determining which SU on this
188 /// HardwareUnit we are trying to schedule - this info helps us determine
189 /// which dependencies to schedule. LookDeep is useful if the dependencies are
190 /// long latency (e.g. memory instructions). If we have many long latency
191 /// dependencies, it is beneficial to enable SUs multiple levels ahead.
192 SUnit *getNextTargetSU(bool LookDeep = false) const;
193 /// Insert the \p SU into AllSUs and account its \p BlockingCycles into
194 /// the TotalCycles. This maintains the list of PrioritySUs.
195 void insert(SUnit *SU, unsigned BlockingCycles);
196 /// Update the state for \p SU being scheduled by removing it from the AllSUs
197 /// and reducing its \p BlockingCycles from the TotalCycles. This maintains
198 /// the list of PrioritySUs.
199 void markScheduled(SUnit *SU, unsigned BlockingCycles);
200 /// After we've collected all the region pressure for this HWUI, correct for
201 /// any specifics of the behavior of this resource. For example, if the
202 /// HardwareUnit can hold N instructions simultaneously, then there is no
203 /// penalty for scheduling N instructions back to back.
204 void finalizeCycles();
205};
206
207//===----------------------------------------------------------------------===//
208// Candidate Heuristics
209//===----------------------------------------------------------------------===//
210
211/// CandidateHeuristics contains state and implementations to facilitate making
212/// per instruction scheduling decisions; it contains methods used in
213/// tryCandidate to decide which instruction to schedule next.
215protected:
216 struct StallCosts {
217 unsigned Ready = 0;
218 unsigned Structural = 0;
219 unsigned Latency = 0;
220 unsigned Carried = 0;
221 unsigned Buffer = 0;
222 unsigned Fence = 0;
223 unsigned Effective = 0;
224 };
225
232
233 /// Walk over the region and collect characteristics for the various
234 /// heuristics.
236
237 /// \returns the maximum blocking cycles according to the SchedModel for a
238 /// given MCSchedClassDesc \p SC.
239 unsigned getMaxBlockingCycles(const MCSchedClassDesc *SC,
240 const MachineInstr *MI);
241
242 /// Compute the blocking cycles for the appropriate HardwareUnit given an \p
243 /// SU.
244 unsigned getHWUICyclesForSU(SUnit *SU);
245 /// Compute the blocking cycles for the appropriate HardwareUnit given an \p
246 /// MI.
248
249 /// Estimate the block carried latency from loads for a given \p SU. This is
250 /// essentially global scheduling info that our local scheduling
251 /// infrastructure lacks the necessary infrastructure to accurately measure.
252 /// Thus, this method just attempts to find a reasonable upper bound for
253 /// carried load latency to avoid long stalls.
254 unsigned getCarriedLatency(SUnit *SU);
255
257
258public:
260
262 const TargetRegisterInfo *TRI);
263
264 /// Update the state to reflect that \p SU is going to be scheduled.
265 void updateForScheduling(SUnit *SU);
266
267 /// Given a \p Flavor , find the corresponding HardwareUnit. \returns the
268 /// mapped HardwareUnit.
270
271 /// Sort the HardwarUnitInfo vector. After sorting, the HWUI that are highest
272 /// priority are first. Priority is determined by maximizing coexecution and
273 /// keeping the critical HardwareUnit busy.
274 void sortHWUIResources();
275
276 unsigned getStructuralStallCycles(SchedBoundary &Zone, SUnit *SU);
277
280 SchedBoundary &Zone);
281
282 /// Prioritize instructions involved the memory pipeline. Currently we don't
283 /// have any modelling of pipelined loads, so we control the layout of the
284 /// pipeline per iteration by giving the user some control over the stalls
285 /// (e.g. between s_barrier_signal and s_barrier_wait) and scheduling the
286 /// pipeline instructions as soon as they are ready.
287 ///
288 /// TODO -- add better modelling and heuristics for pipelining based
289 /// scheduling.
292 SchedBoundary &Zone);
293
294 /// Check for critical resource consumption. Prefer the candidate that uses
295 /// the most prioritized HardwareUnit. If both candidates use the same
296 /// HarwareUnit, prefer the candidate with higher priority on that
297 /// HardwareUnit.
300 SchedBoundary *Zone) const;
301
302 /// Check for dependencies of instructions that use prioritized HardwareUnits.
303 /// Prefer the candidate that is a dependency of an instruction that uses the
304 /// most prioritized HardwareUnit. If both candidates enable the same
305 /// HardwareUnit, prefer the candidate that enables the higher priority
306 /// instruction on that HardwareUnit.
307 bool
310 SchedBoundary *Zone) const;
311
312 void dumpRegionSummary();
313};
314
316protected:
319
320#ifndef NDEBUG
321 void dumpPickSummary(SUnit *SU, bool IsTopNode, SchedCandidate &Cand);
322#endif
323
325 SchedBoundary *Zone);
326 void pickNodeFromQueue(SchedBoundary &Zone, const CandPolicy &ZonePolicy,
327 const RegPressureTracker &RPTracker,
328 SchedCandidate &Cand, bool &PickedPending,
329 bool IsBottomUp);
330
331public:
333
336 unsigned NumRegionInstrs) override;
337 void initialize(ScheduleDAGMI *DAG) override;
338 SUnit *pickNode(bool &IsTopNode) override;
339 void schedNode(SUnit *SU, bool IsTopNode) override;
340};
341
344
345} // End namespace llvm
346
347#endif // LLVM_LIB_TARGET_AMDGPU_AMDGPUCOEXECSCHEDSTRATEGY_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Shared types for co-execution modeling used by GCNHazardRecognizer and the schedulers.
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
IRTranslator LLVM IR MI
Register const TargetRegisterInfo * TRI
void initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs) override
Optionally override the per-region scheduling policy.
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)
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)
CandidateHeuristics contains state and implementations to facilitate making per instruction schedulin...
void updateForScheduling(SUnit *SU)
Update the state to reflect that SU is going to be scheduled.
unsigned getCarriedLatency(SUnit *SU)
Estimate the block carried latency from loads for a given SU.
HardwareUnitInfo * getHWUIFromFlavor(AMDGPU::InstructionFlavor Flavor)
Given a Flavor , find the corresponding HardwareUnit.
DenseMap< MachineInstr *, unsigned > CarriedLatencies
StallCosts getStallCosts(SUnit *SU, SchedBoundary &Zone)
void sortHWUIResources()
Sort the HardwarUnitInfo vector.
void collectRegionSummary()
Walk over the region and collect characteristics for the various heuristics.
bool tryCriticalResource(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for critical resource consumption.
unsigned getHWUICyclesForSU(SUnit *SU)
Compute the blocking cycles for the appropriate HardwareUnit given an SU.
bool tryCriticalResourceDependency(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for dependencies of instructions that use prioritized HardwareUnits.
bool tryEffectiveStall(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
unsigned getHWUICyclesForMI(MachineInstr *MI)
Compute the blocking cycles for the appropriate HardwareUnit given an MI.
SmallVector< HardwareUnitInfo, 8 > HWUInfo
const TargetSchedModel * SchedModel
void initialize(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel, const TargetRegisterInfo *TRI)
unsigned getMaxBlockingCycles(const MCSchedClassDesc *SC, const MachineInstr *MI)
bool tryMemoryPipeline(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
Prioritize instructions involved the memory pipeline.
unsigned getStructuralStallCycles(SchedBoundary &Zone, SUnit *SU)
GCNSchedStrategy(const MachineSchedContext *C)
ScheduleDAGMILive * DAG
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...
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
MachineInstrBundleIterator< MachineInstr > iterator
Representation of each machine instruction.
Track the current register pressure at some position in the instruction stream, and remember the high...
Scheduling unit. This is a node in the scheduling DAG.
Each Scheduling boundary is associated with ready queues.
A ScheduleDAG for scheduling lists of MachineInstr.
ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply schedules machine instructions ac...
A SetVector that performs no allocations if smaller than a certain size.
Definition SetVector.h:345
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
Provide an instruction scheduling machine model to CodeGen passes.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
InstructionFlavor
Classification of instructions by execution characteristics.
AMDGPUSchedReason
AMDGPU-specific scheduling decision reasons.
StringRef getReasonName(AMDGPUSchedReason R)
This is an optimization pass for GlobalISel generic memory operations.
ScheduleDAGInstrs * createGCNNoopPostMachineScheduler(MachineSchedContext *C)
@ Other
Any other memory.
Definition ModRef.h:68
ScheduleDAGInstrs * createGCNCoExecMachineScheduler(MachineSchedContext *C)
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...
Summarize the scheduling resources required for an instruction of a particular scheduling class.
Definition MCSchedule.h:129
MachineSchedContext provides enough context from the MachineScheduler pass for the target to instanti...