LLVM 24.0.0git
AMDGPUIGroupLP.cpp
Go to the documentation of this file.
1//===--- AMDGPUIGroupLP.cpp - AMDGPU IGroupLP ------------===//
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 This file defines a set of schedule DAG mutations that can be used to
10// override default scheduler behavior to enforce specific scheduling patterns.
11// They should be used in cases where runtime performance considerations such as
12// inter-wavefront interactions, mean that compile-time heuristics cannot
13// predict the optimal instruction ordering, or in kernels where optimum
14// instruction scheduling is important enough to warrant manual intervention.
15//
16//===----------------------------------------------------------------------===//
17
18#include "AMDGPUIGroupLP.h"
19#include "SIInstrInfo.h"
24
25using namespace llvm;
26using namespace llvm::AMDGPU;
27
28#define DEBUG_TYPE "igrouplp"
29
30namespace {
31
32static cl::opt<bool> EnableExactSolver(
33 "amdgpu-igrouplp-exact-solver", cl::Hidden,
34 cl::desc("Whether to use the exponential time solver to fit "
35 "the instructions to the pipeline as closely as "
36 "possible."),
37 cl::init(false));
38
39static cl::opt<unsigned> CutoffForExact(
40 "amdgpu-igrouplp-exact-solver-cutoff", cl::init(0), cl::Hidden,
41 cl::desc("The maximum number of scheduling group conflicts "
42 "which we attempt to solve with the exponential time "
43 "exact solver. Problem sizes greater than this will"
44 "be solved by the less accurate greedy algorithm. Selecting "
45 "solver by size is superseded by manually selecting "
46 "the solver (e.g. by amdgpu-igrouplp-exact-solver"));
47
48static cl::opt<uint64_t> MaxBranchesExplored(
49 "amdgpu-igrouplp-exact-solver-max-branches", cl::init(0), cl::Hidden,
50 cl::desc("The amount of branches that we are willing to explore with"
51 "the exact algorithm before giving up."));
52
53static cl::opt<bool> UseCostHeur(
54 "amdgpu-igrouplp-exact-solver-cost-heur", cl::init(true), cl::Hidden,
55 cl::desc("Whether to use the cost heuristic to make choices as we "
56 "traverse the search space using the exact solver. Defaulted "
57 "to on, and if turned off, we will use the node order -- "
58 "attempting to put the later nodes in the later sched groups. "
59 "Experimentally, results are mixed, so this should be set on a "
60 "case-by-case basis."));
61
62// Components of the mask that determines which instruction types may be may be
63// classified into a SchedGroup.
64enum class SchedGroupMask {
65 NONE = 0u,
66 ALU = 1u << 0,
67 VALU = 1u << 1,
68 SALU = 1u << 2,
69 MFMA = 1u << 3,
70 VMEM = 1u << 4,
71 VMEM_READ = 1u << 5,
72 VMEM_WRITE = 1u << 6,
73 DS = 1u << 7,
74 DS_READ = 1u << 8,
75 DS_WRITE = 1u << 9,
76 TRANS = 1u << 10,
77 LDSDMA = 1u << 11,
78 ALL = ALU | VALU | SALU | MFMA | VMEM | VMEM_READ | VMEM_WRITE | DS |
79 DS_READ | DS_WRITE | TRANS | LDSDMA,
80 LLVM_MARK_AS_BITMASK_ENUM(/* LargestFlag = */ ALL)
81};
82
83class SchedGroup;
84
85// InstructionRule class is used to enact a filter which determines whether or
86// not an SU maps to a given SchedGroup. It contains complementary data
87// structures (e.g Cache) to help those filters.
88class InstructionRule {
89protected:
90 const SIInstrInfo *TII;
91 unsigned SGID;
92 // A cache made available to the Filter to store SUnits for subsequent
93 // invocations of the Filter
94 std::optional<SmallVector<SUnit *, 4>> Cache;
95
96public:
97 virtual bool
98 apply(const SUnit *, const ArrayRef<SUnit *>,
100 return true;
101 };
102
103 InstructionRule(const SIInstrInfo *TII, unsigned SGID,
104 bool NeedsCache = false)
105 : TII(TII), SGID(SGID) {
106 if (NeedsCache) {
107 Cache = SmallVector<SUnit *, 4>();
108 }
109 }
110
111 virtual ~InstructionRule() = default;
112};
113
114using SUnitsToCandidateSGsMap = DenseMap<SUnit *, SmallVector<int, 4>>;
115
116// Classify instructions into groups to enable fine tuned control over the
117// scheduler. These groups may be more specific than current SchedModel
118// instruction classes.
119class SchedGroup {
120private:
121 // Mask that defines which instruction types can be classified into this
122 // SchedGroup. The instruction types correspond to the mask from SCHED_BARRIER
123 // and SCHED_GROUP_BARRIER.
124 SchedGroupMask SGMask;
125
126 // Maximum number of SUnits that can be added to this group.
127 std::optional<unsigned> MaxSize;
128
129 // SchedGroups will only synchronize with other SchedGroups that have the same
130 // SyncID.
131 int SyncID = 0;
132
133 // SGID is used to map instructions to candidate SchedGroups
134 unsigned SGID;
135
136 // The different rules each instruction in this SchedGroup must conform to
138
139 // Count of the number of created SchedGroups, used to initialize SGID.
140 static unsigned NumSchedGroups;
141
142 // Use SGMask to determine whether we can classify MI as a member of this
143 // SchedGroup object.
144 bool canAddMI(const MachineInstr &MI) const;
145
146public:
147 // Collection of SUnits that are classified as members of this group.
148 SmallVector<SUnit *, 32> Collection;
149
151 const SIInstrInfo *TII;
152
153 // Try to add and edge from SU A to SU B.
154 bool tryAddEdge(SUnit *A, SUnit *B);
155
156 // Returns true if SU can be added to this SchedGroup.
157 bool canAddSU(SUnit &SU) const;
158
159 // Add DAG dependencies from all SUnits in this SchedGroup and this SU. If
160 // MakePred is true, SU will be a predecessor of the SUnits in this
161 // SchedGroup, otherwise SU will be a successor.
162 void link(SUnit &SU, bool MakePred = false);
163
164 // Add DAG dependencies and track which edges are added, and the count of
165 // missed edges
166 int link(SUnit &SU, bool MakePred,
167 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges);
168
169 // Add DAG dependencies from all SUnits in this SchedGroup and this SU.
170 // Use the predicate to determine whether SU should be a predecessor (P =
171 // true) or a successor (P = false) of this SchedGroup.
172 void link(SUnit &SU, function_ref<bool(const SUnit *A, const SUnit *B)> P);
173
174 // Add DAG dependencies such that SUnits in this group shall be ordered
175 // before SUnits in OtherGroup.
176 void link(SchedGroup &OtherGroup);
177
178 // Returns true if no more instructions may be added to this group.
179 bool isFull() const { return MaxSize && Collection.size() >= *MaxSize; }
180
181 // Append a constraint that SUs must meet in order to fit into this
182 // SchedGroup. Since many rules involve the relationship between a SchedGroup
183 // and the SUnits in other SchedGroups, rules are checked at Pipeline Solve
184 // time (rather than SchedGroup init time.)
185 void addRule(std::shared_ptr<InstructionRule> NewRule) {
186 Rules.push_back(NewRule);
187 }
188
189 // Returns true if the SU matches all rules
190 bool allowedByRules(const SUnit *SU,
191 SmallVectorImpl<SchedGroup> &SyncPipe) const {
192 for (auto &Rule : Rules) {
193 if (!Rule->apply(SU, Collection, SyncPipe))
194 return false;
195 }
196 return true;
197 }
198
199 // Add SU to the SchedGroup.
200 void add(SUnit &SU) {
201 LLVM_DEBUG(dbgs() << "For SchedGroup with mask "
202 << format_hex((int)SGMask, 10, true) << " adding "
203 << *SU.getInstr());
204 Collection.push_back(&SU);
205 }
206
207 // Remove last element in the SchedGroup
208 void pop() { Collection.pop_back(); }
209
210 template <class T>
211 void findCandidateSUnits(T Begin, T End,
212 SUnitsToCandidateSGsMap &SyncedInstrs);
213
214 /// Find each SUnit in the DAG that could potentially be added to
215 /// this SchedGroup and add the SGID to the candidate SchedGroups
216 /// for SU in \p SyncedInstrs.
217 void findCandidateSUnits(SUnitsToCandidateSGsMap &SyncedInstrs);
218
219 int getSyncID() { return SyncID; }
220
221 int getSGID() { return SGID; }
222
223 SchedGroupMask getMask() { return SGMask; }
224
225 SchedGroup(SchedGroupMask SGMask, std::optional<unsigned> MaxSize,
226 ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
227 : SGMask(SGMask), MaxSize(MaxSize), DAG(DAG), TII(TII) {
228 SGID = NumSchedGroups++;
229 }
230
231 SchedGroup(SchedGroupMask SGMask, std::optional<unsigned> MaxSize, int SyncID,
232 ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
233 : SGMask(SGMask), MaxSize(MaxSize), SyncID(SyncID), DAG(DAG), TII(TII) {
234 SGID = NumSchedGroups++;
235 }
236};
237
238using SUToCandSGsPair = std::pair<SUnit *, SmallVector<int, 4>>;
239using SUsToCandSGsVec = SmallVector<SUToCandSGsPair, 4>;
240
241// The PipelineSolver is used to assign SUnits to SchedGroups in a pipeline
242// in non-trivial cases. For example, if the requested pipeline is
243// {VMEM_READ, VALU, MFMA, VMEM_READ} and we encounter a VMEM_READ instruction
244// in the DAG, then we will have an instruction that can not be trivially
245// assigned to a SchedGroup. The PipelineSolver class implements two algorithms
246// to find a good solution to the pipeline -- a greedy algorithm and an exact
247// algorithm. The exact algorithm has an exponential time complexity and should
248// only be used for small sized problems or medium sized problems where an exact
249// solution is highly desired.
250class PipelineSolver {
251 [[maybe_unused]] ScheduleDAGMI *DAG;
252
253 // Instructions that can be assigned to multiple SchedGroups
255 SmallVector<SUsToCandSGsVec, 4> PipelineInstrs;
257 // The current working pipeline
259 // The pipeline that has the best solution found so far
261
262 // Whether or not we actually have any SyncedInstrs to try to solve.
263 bool NeedsSolver = false;
264
265 // Compute an estimate of the size of search tree -- the true size is
266 // the product of each conflictedInst.Matches.size() across all SyncPipelines
267 unsigned computeProblemSize();
268
269 // The cost penalty of not assigning a SU to a SchedGroup
270 int MissPenalty = 0;
271
272 // Costs in terms of the number of edges we are unable to add
273 int BestCost = -1;
274 int CurrCost = 0;
275
276 // Index pointing to the conflicting instruction that is currently being
277 // fitted
278 int CurrConflInstNo = 0;
279 // Index to the pipeline that is currently being fitted
280 int CurrSyncGroupIdx = 0;
281 // The first non trivial pipeline
282 int BeginSyncGroupIdx = 0;
283
284 // How many branches we have explored
285 uint64_t BranchesExplored = 0;
286
287 // The direction in which we process the candidate SchedGroups per SU
288 bool IsBottomUp = true;
289
290 // Update indices to fit next conflicting instruction
291 void advancePosition();
292 // Recede indices to attempt to find better fit for previous conflicting
293 // instruction
294 void retreatPosition();
295
296 // The exponential time algorithm which finds the provably best fit
297 bool solveExact();
298 // The polynomial time algorithm which attempts to find a good fit
299 bool solveGreedy();
300 // Find the best SchedGroup for the current SU using the heuristic given all
301 // current information. One step in the greedy algorithm. Templated against
302 // the SchedGroup iterator (either reverse or forward).
303 template <typename T>
304 void greedyFind(std::list<std::pair<SUnit *, SUnit *>> &AddedEdges, T I, T E);
305 // Whether or not the current solution is optimal
306 bool checkOptimal();
307 // Populate the ready list, prioiritizing fewest missed edges first
308 // Templated against the SchedGroup iterator (either reverse or forward).
309 template <typename T>
310 void populateReadyList(SmallVectorImpl<std::pair<int, int>> &ReadyList, T I,
311 T E);
312 // Add edges corresponding to the SchedGroups as assigned by solver
313 void makePipeline();
314 // Link the SchedGroups in the best found pipeline.
315 // Tmplated against the SchedGroup iterator (either reverse or forward).
316 template <typename T> void linkSchedGroups(T I, T E);
317 // Add the edges from the SU to the other SchedGroups in pipeline, and
318 // return the number of edges missed.
319 int addEdges(SmallVectorImpl<SchedGroup> &SyncPipeline, SUnit *SU, int SGID,
320 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges);
321
322 /// This class is used to build the edge set implied by an
323 /// assignment of an SUnit to a SchedGroup and to compute the cost
324 /// (edges that cannot be assigned without introducing cycles) of
325 /// the assignment.
326 class EdgeSetBuilder {
327 SUnit *SU;
328 SmallVectorImpl<SchedGroup> &SyncPipeline;
329 bool IsBottomUp;
330 DenseSet<SUnit *> InitialPreds;
331 DenseSet<SUnit *> Succs;
332 bool Initialized = false;
333
334 /// Compute reachability via DFS. If ComputePreds is true, follows
335 /// predecessor edges; otherwise follows successor edges.
336 template <bool ComputePreds>
337 static void computeReachable(DenseSet<SUnit *> &Reachable, SUnit *Start);
338
339 /// Compute all nodes that can reach Start via predecessor edges, including
340 /// Start itself.
341 static void computePreds(DenseSet<SUnit *> &Preds, SUnit *Start);
342
343 /// Compute all nodes reachable from Start via successor edges, including
344 /// Start itself.
345 static void computeSuccs(DenseSet<SUnit *> &Succs, SUnit *Start);
346
347 public:
348 EdgeSetBuilder(SUnit *SU, SmallVectorImpl<SchedGroup> &SyncPipeline,
349 bool IsBottomUp)
350 : SU(SU), SyncPipeline(SyncPipeline), IsBottomUp(IsBottomUp) {}
351
352 /// Determine the edges implied by assigning SU to the SchedGroup
353 /// with ID SGID. Edges are added to NewEdges unless they
354 /// introduce cycles. Return the number of edges that cannot be
355 /// added.
356 int build(int SGID, std::list<std::pair<SUnit *, SUnit *>> &NewEdges);
357
358 private:
359 template <typename T>
360 int buildImpl(int SGID, const iterator_range<T> SchedGroups,
361 std::list<std::pair<SUnit *, SUnit *>> &NewEdges);
362 };
363
364 /// Link the pipeline as if \p SU was in the SchedGroup with ID \p SGID. It
365 /// returns the cost (in terms of missed pipeline edges), and tracks the edges
366 /// added in \p AddedEdges
367 template <typename T>
368 int linkSUnit(SUnit *SU, int SGID,
369 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges, T I, T E);
370 /// Remove the edges passed via \p AddedEdges
371 void removeEdges(const std::list<std::pair<SUnit *, SUnit *>> &AddedEdges);
372 // Convert the passed in maps to arrays for bidirectional iterators
373 void convertSyncMapsToArrays();
374
375 void reset();
376
377public:
378 // Invoke the solver to map instructions to instruction groups. Heuristic &&
379 // command-line-option determines to use exact or greedy algorithm.
380 void solve();
381
382 PipelineSolver(DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
384 ScheduleDAGMI *DAG, bool IsBottomUp = true)
385 : DAG(DAG), SyncedInstrs(SyncedInstrs),
386 SyncedSchedGroups(SyncedSchedGroups), IsBottomUp(IsBottomUp) {
387
388 for (auto &PipelineInstrs : SyncedInstrs) {
389 if (!PipelineInstrs.second.empty()) {
390 NeedsSolver = true;
391 break;
392 }
393 }
394
395 if (!NeedsSolver)
396 return;
397
398 convertSyncMapsToArrays();
399
400 CurrPipeline = BestPipeline;
401
402 while (static_cast<size_t>(BeginSyncGroupIdx) < PipelineInstrs.size() &&
403 PipelineInstrs[BeginSyncGroupIdx].empty())
404 ++BeginSyncGroupIdx;
405
406 if (static_cast<size_t>(BeginSyncGroupIdx) >= PipelineInstrs.size())
407 return;
408 }
409};
410
411void PipelineSolver::reset() {
412
413 for (auto &SyncPipeline : CurrPipeline) {
414 for (auto &SG : SyncPipeline) {
415 SmallVector<SUnit *, 32> TempCollection = SG.Collection;
416 SG.Collection.clear();
417 auto *SchedBarr = llvm::find_if(TempCollection, [](SUnit *SU) {
418 return SU->getInstr()->getOpcode() == AMDGPU::SCHED_GROUP_BARRIER;
419 });
420 if (SchedBarr != TempCollection.end())
421 SG.Collection.push_back(*SchedBarr);
422 }
423 }
424
425 CurrSyncGroupIdx = BeginSyncGroupIdx;
426 CurrConflInstNo = 0;
427 CurrCost = 0;
428}
429
430void PipelineSolver::convertSyncMapsToArrays() {
431 for (auto &SyncPipe : SyncedSchedGroups) {
432 BestPipeline.insert(BestPipeline.begin(), SyncPipe.second);
433 }
434
435 int PipelineIDx = SyncedInstrs.size() - 1;
436 PipelineInstrs.resize(SyncedInstrs.size());
437 for (auto &SyncInstrMap : SyncedInstrs) {
438 for (auto &SUsToCandSGs : SyncInstrMap.second) {
439 if (PipelineInstrs[PipelineIDx].empty()) {
440 PipelineInstrs[PipelineIDx].push_back(
441 std::pair(SUsToCandSGs.first, SUsToCandSGs.second));
442 continue;
443 }
444 auto *SortPosition = PipelineInstrs[PipelineIDx].begin();
445 // Insert them in sorted order -- this allows for good parsing order in
446 // the greedy algorithm
447 while (SortPosition != PipelineInstrs[PipelineIDx].end() &&
448 SUsToCandSGs.first->NodeNum > SortPosition->first->NodeNum)
449 ++SortPosition;
450 PipelineInstrs[PipelineIDx].insert(
451 SortPosition, std::pair(SUsToCandSGs.first, SUsToCandSGs.second));
452 }
453 --PipelineIDx;
454 }
455}
456
457template <typename T> void PipelineSolver::linkSchedGroups(T I, T E) {
458 for (; I != E; ++I) {
459 auto &GroupA = *I;
460 for (auto J = std::next(I); J != E; ++J) {
461 auto &GroupB = *J;
462 GroupA.link(GroupB);
463 }
464 }
465}
466
467void PipelineSolver::makePipeline() {
468 // Preserve the order of barrier for subsequent SchedGroupBarrier mutations
469 for (auto &SyncPipeline : BestPipeline) {
470 LLVM_DEBUG(dbgs() << "Printing SchedGroups\n");
471 for (auto &SG : SyncPipeline) {
472 LLVM_DEBUG(dbgs() << "SchedGroup with SGID " << SG.getSGID()
473 << " has: \n");
474 SUnit *SGBarr = nullptr;
475 for (auto &SU : SG.Collection) {
476 if (SU->getInstr()->getOpcode() == AMDGPU::SCHED_GROUP_BARRIER)
477 SGBarr = SU;
478 LLVM_DEBUG(dbgs() << "SU(" << SU->NodeNum << ")\n");
479 }
480 // Command line requested IGroupLP doesn't have SGBarr
481 if (!SGBarr)
482 continue;
483 SG.link(*SGBarr, false);
484 }
485 }
486
487 for (auto &SyncPipeline : BestPipeline) {
488 IsBottomUp ? linkSchedGroups(SyncPipeline.rbegin(), SyncPipeline.rend())
489 : linkSchedGroups(SyncPipeline.begin(), SyncPipeline.end());
490 }
491}
492
493template <typename T>
494int PipelineSolver::linkSUnit(
495 SUnit *SU, int SGID, std::list<std::pair<SUnit *, SUnit *>> &AddedEdges,
496 T I, T E) {
497 bool MakePred = false;
498 int AddedCost = 0;
499 for (; I < E; ++I) {
500 if (I->getSGID() == SGID) {
501 MakePred = true;
502 continue;
503 }
504 auto Group = *I;
505 AddedCost += Group.link(*SU, MakePred, AddedEdges);
506 assert(AddedCost >= 0);
507 }
508 return AddedCost;
509}
510
511template <bool ComputePreds>
512void PipelineSolver::EdgeSetBuilder::computeReachable(
513 DenseSet<SUnit *> &Reachable, SUnit *Start) {
514 if (!Reachable.insert(Start).second)
515 return;
516
517 SmallVector<SUnit *, 32> WorkList = {Start};
518
519 while (!WorkList.empty()) {
520 SUnit *Current = WorkList.pop_back_val();
521
522 for (const SDep &Dep : ComputePreds ? Current->Preds : Current->Succs) {
523 if (Reachable.insert(Dep.getSUnit()).second)
524 WorkList.push_back(Dep.getSUnit());
525 }
526 }
527}
528
529void PipelineSolver::EdgeSetBuilder::computePreds(DenseSet<SUnit *> &Preds,
530 SUnit *Start) {
531 computeReachable</*ComputePreds*/ true>(Preds, Start);
532}
533
534void PipelineSolver::EdgeSetBuilder::computeSuccs(DenseSet<SUnit *> &Succs,
535 SUnit *Start) {
536 computeReachable</*ComputePreds*/ false>(Succs, Start);
537}
538
539int PipelineSolver::EdgeSetBuilder::build(
540 int SGID, std::list<std::pair<SUnit *, SUnit *>> &NewEdges) {
541 if (!Initialized) {
542 computePreds(InitialPreds, SU);
543 computeSuccs(Succs, SU);
544 Initialized = true;
545 }
546
547 // See comment in addEdges concerning the iterator direction.
548 return IsBottomUp ? buildImpl(SGID, reverse(SyncPipeline), NewEdges)
549 : buildImpl(SGID,
550 llvm::make_range(SyncPipeline.begin(),
551 SyncPipeline.end()),
552 NewEdges);
553}
554
555template <typename T>
556int PipelineSolver::EdgeSetBuilder::buildImpl(
557 int SGID, iterator_range<T> SchedGroups,
558 std::list<std::pair<SUnit *, SUnit *>> &NewEdges) {
559
560 // Determine the edges that will be added to the DAG if SU is
561 // assigned to the SchedGroup SG with the given SGID. It might be
562 // impossible to add some edges because they would introduce
563 // cycles. The number of such edges is counted and returned, all
564 // other edges are added to NewEdges.
565 //
566 // SU is made a successor of SUnits in SchedGroups before SG, and a
567 // predecessor of SUnits after SG. In each case, the cycle check
568 // requires reachability information for the opposing direction.
569
570 // Nodes U that can reach SU (U ~> SU).
571 // Will be extended as new edges are added and hence cannot be
572 // shared between calls to this function, in contrast to Succs.
573 DenseSet<SUnit *> Preds = InitialPreds;
574
575 int MissedEdges = 0;
576 bool MakePred = false;
577 for (SchedGroup &SG : SchedGroups) {
578 if (SG.getSGID() == SGID) {
579 MakePred = true;
580 continue;
581 }
582
583 for (SUnit *A : SG.Collection) {
584 if (A->getInstr()->getOpcode() == AMDGPU::SCHED_GROUP_BARRIER)
585 continue;
586
587 if (MakePred) {
588 // Try add SU -> A.
589 if (Preds.contains(A)) { // Would add cycle since A ~> SU.
590 ++MissedEdges;
591 continue;
592 }
593 // Succs does not need to be updated, since it will not be
594 // queried after entering the MakePred case.
595 NewEdges.emplace_back(SU, A);
596 continue;
597 }
598
599 // Try add A -> SU.
600 if (Succs.contains(A)) { // Would add cycle since SU ~> A.
601 ++MissedEdges;
602 continue;
603 }
604 NewEdges.emplace_back(A, SU);
605 computePreds(Preds, A);
606 }
607 }
608
609 return MissedEdges;
610}
611
612int PipelineSolver::addEdges(
613 SmallVectorImpl<SchedGroup> &SyncPipeline, SUnit *SU, int SGID,
614 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges) {
615
616 // For IsBottomUp, the first SchedGroup in SyncPipeline contains the
617 // instructions that are the ultimate successors in the resultant mutation.
618 // Therefore, in such a configuration, the SchedGroups occurring before the
619 // candidate SGID are successors of the candidate SchedGroup, thus the current
620 // SU should be linked as a predecessor to SUs in those SchedGroups. The
621 // opposite is true if !IsBottomUp. IsBottomUp occurs in the case of multiple
622 // SCHED_GROUP_BARRIERS, or if a user specifies IGLP_OPT SchedGroups using
623 // IsBottomUp (in reverse).
624 return IsBottomUp ? linkSUnit(SU, SGID, AddedEdges, SyncPipeline.rbegin(),
625 SyncPipeline.rend())
626 : linkSUnit(SU, SGID, AddedEdges, SyncPipeline.begin(),
627 SyncPipeline.end());
628}
629
630void PipelineSolver::removeEdges(
631 const std::list<std::pair<SUnit *, SUnit *>> &EdgesToRemove) {
632 // Only remove the edges that we have added when testing
633 // the fit.
634 for (auto &PredSuccPair : EdgesToRemove) {
635 SUnit *Pred = PredSuccPair.first;
636 SUnit *Succ = PredSuccPair.second;
637
638 auto *Match = llvm::find_if(Succ->Preds, [&Pred](SDep &P) {
639 return P.getSUnit() == Pred && P.isArtificial();
640 });
641 if (Match != Succ->Preds.end())
642 Succ->removePred(*Match);
643 }
644}
645
646void PipelineSolver::advancePosition() {
647 ++CurrConflInstNo;
648
649 if (static_cast<size_t>(CurrConflInstNo) >=
650 PipelineInstrs[CurrSyncGroupIdx].size()) {
651 CurrConflInstNo = 0;
652 ++CurrSyncGroupIdx;
653 // Advance to next non-trivial pipeline
654 while (static_cast<size_t>(CurrSyncGroupIdx) < PipelineInstrs.size() &&
655 PipelineInstrs[CurrSyncGroupIdx].empty())
656 ++CurrSyncGroupIdx;
657 }
658}
659
660void PipelineSolver::retreatPosition() {
661 assert(CurrConflInstNo >= 0);
662 assert(CurrSyncGroupIdx >= 0);
663
664 if (CurrConflInstNo > 0) {
665 --CurrConflInstNo;
666 return;
667 }
668
669 if (CurrConflInstNo == 0) {
670 // If we return to the starting position, we have explored
671 // the entire tree
672 if (CurrSyncGroupIdx == BeginSyncGroupIdx)
673 return;
674
675 --CurrSyncGroupIdx;
676 // Go to previous non-trivial pipeline
677 while (PipelineInstrs[CurrSyncGroupIdx].empty())
678 --CurrSyncGroupIdx;
679
680 CurrConflInstNo = PipelineInstrs[CurrSyncGroupIdx].size() - 1;
681 }
682}
683
684bool PipelineSolver::checkOptimal() {
685 if (static_cast<size_t>(CurrSyncGroupIdx) == PipelineInstrs.size()) {
686 if (BestCost == -1 || CurrCost < BestCost) {
687 BestPipeline = CurrPipeline;
688 BestCost = CurrCost;
689 LLVM_DEBUG(dbgs() << "Found Fit with cost " << BestCost << "\n");
690 }
691 assert(BestCost >= 0);
692 }
693
694 bool DoneExploring = false;
695 if (MaxBranchesExplored > 0 && BranchesExplored >= MaxBranchesExplored)
696 DoneExploring = true;
697
698 return (DoneExploring || BestCost == 0);
699}
700
701template <typename T>
702void PipelineSolver::populateReadyList(
703 SmallVectorImpl<std::pair<int, int>> &ReadyList, T I, T E) {
704 SUToCandSGsPair CurrSU = PipelineInstrs[CurrSyncGroupIdx][CurrConflInstNo];
705 auto SyncPipeline = CurrPipeline[CurrSyncGroupIdx];
706 assert(CurrSU.second.size() >= 1);
707
708 for (; I != E; ++I) {
709 std::list<std::pair<SUnit *, SUnit *>> AddedEdges;
710 int CandSGID = *I;
711 SchedGroup *Match = llvm::find_if(SyncPipeline, [CandSGID](SchedGroup &SG) {
712 return SG.getSGID() == CandSGID;
713 });
714 assert(Match);
715
716 if (UseCostHeur) {
717 if (Match->isFull()) {
718 ReadyList.push_back(std::pair(*I, MissPenalty));
719 continue;
720 }
721
722 int TempCost = addEdges(SyncPipeline, CurrSU.first, CandSGID, AddedEdges);
723 ReadyList.push_back(std::pair(*I, TempCost));
724 removeEdges(AddedEdges);
725 } else
726 ReadyList.push_back(std::pair(*I, -1));
727 }
728
729 if (UseCostHeur)
730 std::sort(ReadyList.begin(), ReadyList.end(), llvm::less_second());
731
732 assert(ReadyList.size() == CurrSU.second.size());
733}
734
735bool PipelineSolver::solveExact() {
736 if (checkOptimal())
737 return true;
738
739 if (static_cast<size_t>(CurrSyncGroupIdx) == PipelineInstrs.size())
740 return false;
741
742 assert(static_cast<size_t>(CurrSyncGroupIdx) < PipelineInstrs.size());
743 assert(static_cast<size_t>(CurrConflInstNo) <
744 PipelineInstrs[CurrSyncGroupIdx].size());
745 SUToCandSGsPair CurrSU = PipelineInstrs[CurrSyncGroupIdx][CurrConflInstNo];
746 LLVM_DEBUG(dbgs() << "Fitting SU(" << CurrSU.first->NodeNum
747 << ") in Pipeline # " << CurrSyncGroupIdx << "\n");
748
749 // SchedGroup -> Cost pairs
751 // Prioritize the candidate sched groups in terms of lowest cost first
752 IsBottomUp ? populateReadyList(ReadyList, CurrSU.second.rbegin(),
753 CurrSU.second.rend())
754 : populateReadyList(ReadyList, CurrSU.second.begin(),
755 CurrSU.second.end());
756
757 auto *I = ReadyList.begin();
758 auto *E = ReadyList.end();
759 for (; I != E; ++I) {
760 // If we are trying SGs in least cost order, and the current SG is cost
761 // infeasible, then all subsequent SGs will also be cost infeasible, so we
762 // can prune.
763 if (BestCost != -1 && (CurrCost + I->second > BestCost))
764 return false;
765
766 int CandSGID = I->first;
767 int AddedCost = 0;
768 std::list<std::pair<SUnit *, SUnit *>> AddedEdges;
769 auto &SyncPipeline = CurrPipeline[CurrSyncGroupIdx];
770 SchedGroup *Match = llvm::find_if(SyncPipeline, [CandSGID](SchedGroup &SG) {
771 return SG.getSGID() == CandSGID;
772 });
773 assert(Match);
774
775 if (Match->isFull())
776 continue;
777
778 if (!Match->allowedByRules(CurrSU.first, SyncPipeline))
779 continue;
780
781 LLVM_DEBUG(dbgs() << "Assigning to SchedGroup with Mask "
782 << (int)Match->getMask() << "and ID " << CandSGID
783 << "\n");
784 Match->add(*CurrSU.first);
785 AddedCost = addEdges(SyncPipeline, CurrSU.first, CandSGID, AddedEdges);
786 LLVM_DEBUG(dbgs() << "Cost of Assignment: " << AddedCost << "\n");
787 CurrCost += AddedCost;
788 advancePosition();
789 ++BranchesExplored;
790 bool FinishedExploring = false;
791 // If the Cost after adding edges is greater than a known solution,
792 // backtrack
793 if (CurrCost < BestCost || BestCost == -1) {
794 if (solveExact()) {
795 FinishedExploring = BestCost != 0;
796 if (!FinishedExploring)
797 return true;
798 }
799 }
800
801 retreatPosition();
802 CurrCost -= AddedCost;
803 removeEdges(AddedEdges);
804 Match->pop();
805 CurrPipeline[CurrSyncGroupIdx] = SyncPipeline;
806 if (FinishedExploring)
807 return true;
808 }
809
810 // Try the pipeline where the current instruction is omitted
811 // Potentially if we omit a problematic instruction from the pipeline,
812 // all the other instructions can nicely fit.
813 CurrCost += MissPenalty;
814 advancePosition();
815
816 LLVM_DEBUG(dbgs() << "NOT Assigned (" << CurrSU.first->NodeNum << ")\n");
817
818 bool FinishedExploring = false;
819 if (CurrCost < BestCost || BestCost == -1) {
820 if (solveExact()) {
821 bool FinishedExploring = BestCost != 0;
822 if (!FinishedExploring)
823 return true;
824 }
825 }
826
827 retreatPosition();
828 CurrCost -= MissPenalty;
829 return FinishedExploring;
830}
831
832template <typename T>
833void PipelineSolver::greedyFind(
834 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges, T I, T E) {
835 SUToCandSGsPair CurrSU = PipelineInstrs[CurrSyncGroupIdx][CurrConflInstNo];
836
837 struct GroupInfo {
838 SchedGroup *SG;
839 std::list<std::pair<SUnit *, SUnit *>> Edges;
840 int Cost = 0;
841 };
842 std::optional<GroupInfo> Best;
843
844 auto &SyncPipeline = CurrPipeline[CurrSyncGroupIdx];
845 LLVM_DEBUG(dbgs() << "Fitting SU(" << CurrSU.first->NodeNum
846 << ") in Pipeline # " << CurrSyncGroupIdx << "\n");
847
848 EdgeSetBuilder Builder(CurrSU.first, SyncPipeline, IsBottomUp);
849
850 // Since we have added the potential SchedGroups from bottom up, but
851 // traversed the DAG from top down, parse over the groups from last to
852 // first. If we fail to do this for the greedy algorithm, the solution will
853 // likely not be good in more complex cases.
854 for (; I != E; ++I) {
855 int CandSGID = *I;
856 SchedGroup *Match = llvm::find_if(SyncPipeline, [CandSGID](SchedGroup &SG) {
857 return SG.getSGID() == CandSGID;
858 });
859 assert(Match);
860
861 LLVM_DEBUG(dbgs() << "Trying SGID # " << CandSGID << " with Mask "
862 << (int)Match->getMask() << "\n");
863
864 if (Match->isFull()) {
865 LLVM_DEBUG(dbgs() << "SGID # " << CandSGID << " is full\n");
866 continue;
867 }
868 if (!Match->allowedByRules(CurrSU.first, SyncPipeline)) {
869 LLVM_DEBUG(dbgs() << "SGID # " << CandSGID << " has conflicting rule\n");
870 continue;
871 }
872
873 std::list<std::pair<SUnit *, SUnit *>> TempEdges;
874 int TempCost = Builder.build(CandSGID, TempEdges);
875 LLVM_DEBUG(dbgs() << "Cost of Group " << TempCost << "\n");
876
877 if (!Best || TempCost < Best->Cost) {
878 Best = {Match, TempEdges, TempCost};
879 if (Best->Cost == 0)
880 break;
881 }
882 }
883
884 if (Best) {
885 SchedGroup *SG = Best->SG;
886 std::list<std::pair<SUnit *, SUnit *>> &Edges = Best->Edges;
887
888 SG->add(*CurrSU.first);
889 if (AddedEdges.empty())
890 AddedEdges = Edges;
891 else
892 AddedEdges.splice(std::prev(AddedEdges.cend()), Edges);
893
894 for (const std::pair<SUnit *, SUnit *> &E : Edges) {
895 if (!SG->tryAddEdge(E.first, E.second))
896 llvm_unreachable("Edges known to be insertable.");
897 }
898
899 LLVM_DEBUG(dbgs() << "Best Group has ID: " << SG->getSGID() << " and Mask"
900 << (int)SG->getMask() << "\n");
901 BestCost += Best->Cost;
902 } else
903 BestCost += MissPenalty;
904}
905
906bool PipelineSolver::solveGreedy() {
907 BestCost = 0;
908 std::list<std::pair<SUnit *, SUnit *>> AddedEdges;
909
910 while (static_cast<size_t>(CurrSyncGroupIdx) < PipelineInstrs.size()) {
911 SUToCandSGsPair CurrSU = PipelineInstrs[CurrSyncGroupIdx][CurrConflInstNo];
912 IsBottomUp
913 ? greedyFind(AddedEdges, CurrSU.second.rbegin(), CurrSU.second.rend())
914 : greedyFind(AddedEdges, CurrSU.second.begin(), CurrSU.second.end());
915 advancePosition();
916 }
917 BestPipeline = CurrPipeline;
918 removeEdges(AddedEdges);
919 return false;
920}
921
922unsigned PipelineSolver::computeProblemSize() {
923 unsigned ProblemSize = 0;
924 for (auto &PipeConflicts : PipelineInstrs) {
925 ProblemSize += PipeConflicts.size();
926 }
927
928 return ProblemSize;
929}
930
931void PipelineSolver::solve() {
932 if (!NeedsSolver)
933 return;
934
935 unsigned ProblemSize = computeProblemSize();
936 assert(ProblemSize > 0);
937
938 bool BelowCutoff = (CutoffForExact > 0) && ProblemSize <= CutoffForExact;
939 MissPenalty = (ProblemSize / 2) + 1;
940
941 LLVM_DEBUG(DAG->dump());
942 if (EnableExactSolver || BelowCutoff) {
943 LLVM_DEBUG(dbgs() << "Starting Greedy pipeline solver\n");
944 solveGreedy();
945 reset();
946 LLVM_DEBUG(dbgs() << "Greedy produced best cost of " << BestCost << "\n");
947 if (BestCost > 0) {
948 LLVM_DEBUG(dbgs() << "Starting EXACT pipeline solver\n");
949 solveExact();
950 LLVM_DEBUG(dbgs() << "Exact produced best cost of " << BestCost << "\n");
951 }
952 } else { // Use the Greedy Algorithm by default
953 LLVM_DEBUG(dbgs() << "Starting GREEDY pipeline solver\n");
954 solveGreedy();
955 LLVM_DEBUG(dbgs() << "Greedy produced best cost of " << BestCost << "\n");
956 }
957
958 makePipeline();
959 LLVM_DEBUG(dbgs() << "After applying mutation\n");
960 LLVM_DEBUG(DAG->dump());
961}
962
963// Implement a IGLP scheduling strategy.
964class IGLPStrategy {
965protected:
967
968 const SIInstrInfo *TII;
969
970public:
971 /// Add SchedGroups to \p SyncedSchedGroups to implement this Strategy.
972 virtual bool applyIGLPStrategy(
974 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
976
977 // Returns true if this strategy should be applied to a ScheduleDAG.
978 virtual bool shouldApplyStrategy(ScheduleDAGInstrs *DAG,
980
981 bool IsBottomUp = true;
982
983 IGLPStrategy(ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
984 : DAG(DAG), TII(TII) {}
985
986 virtual ~IGLPStrategy() = default;
987};
988
989class MFMASmallGemmOpt final : public IGLPStrategy {
990private:
991public:
992 bool applyIGLPStrategy(
994 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
996
997 bool shouldApplyStrategy(ScheduleDAGInstrs *DAG,
999 return true;
1000 }
1001
1002 MFMASmallGemmOpt(ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
1003 : IGLPStrategy(DAG, TII) {
1004 IsBottomUp = true;
1005 }
1006};
1007
1008bool MFMASmallGemmOpt::applyIGLPStrategy(
1010 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
1012 // Count the number of MFMA instructions.
1013 unsigned MFMACount = 0;
1014 for (const MachineInstr &I : *DAG)
1015 if (TII->isMFMAorWMMA(I))
1016 ++MFMACount;
1017
1018 const unsigned PipelineSyncID = 0;
1019 SchedGroup *SG = nullptr;
1020 for (unsigned I = 0; I < MFMACount * 3; ++I) {
1021 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1022 SchedGroupMask::DS, 2, PipelineSyncID, DAG, TII);
1023 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1024
1025 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1026 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
1027 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1028 }
1029
1030 return true;
1031}
1032
1033class MFMAExpInterleaveOpt final : public IGLPStrategy {
1034private:
1035 // The count of TRANS SUs involved in the interleaved pipeline
1036 static unsigned TransPipeCount;
1037 // The count of MFMA SUs involved in the interleaved pipeline
1038 static unsigned MFMAPipeCount;
1039 // The count of Add SUs involved in the interleaved pipeline
1040 static unsigned AddPipeCount;
1041 // The number of transitive MFMA successors for each TRANS SU
1042 static unsigned MFMAEnablement;
1043 // The number of transitive TRANS predecessors for each MFMA SU
1044 static unsigned ExpRequirement;
1045 // The count of independent "chains" of MFMA instructions in the pipeline
1046 static unsigned MFMAChains;
1047 // Whether or not the pipeline has V_CVT instructions
1048 static bool HasCvt;
1049 // Whether or not there are instructions between the TRANS instruction and
1050 // V_CVT
1051 static bool HasChainBetweenCvt;
1052 // The first occuring DS_READ which feeds an MFMA chain
1053 static std::optional<unsigned> FirstPipeDSR;
1054 // The MFMAPipe SUs with no MFMA predecessors
1055 SmallVector<SUnit *, 4> MFMAChainSeeds;
1056 // Compute the heuristics for the pipeline, returning whether or not the DAG
1057 // is well formatted for the mutation
1058 bool analyzeDAG(const SIInstrInfo *TII);
1059
1060 /// Whether or not the instruction is a transitive predecessor of an MFMA
1061 /// instruction
1062 class IsPipeExp final : public InstructionRule {
1063 public:
1064 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1065 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1066
1067 auto *DAG = SyncPipe[0].DAG;
1068
1069 if (Cache->empty()) {
1070 auto I = DAG->SUnits.rbegin();
1071 auto E = DAG->SUnits.rend();
1072 for (; I != E; I++) {
1073 if (TII->isMFMAorWMMA(*I->getInstr()))
1074 Cache->push_back(&*I);
1075 }
1076 if (Cache->empty())
1077 return false;
1078 }
1079
1080 auto Reaches = any_of(*Cache, [&SU, &DAG](SUnit *TargetSU) {
1081 return DAG->IsReachable(TargetSU, const_cast<SUnit *>(SU));
1082 });
1083
1084 return Reaches;
1085 }
1086 IsPipeExp(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
1087 : InstructionRule(TII, SGID, NeedsCache) {}
1088 };
1089
1090 /// Whether or not the instruction is a transitive predecessor of the
1091 /// \p Number th MFMA of the MFMAs occuring after a TRANS instruction
1092 class EnablesNthMFMA final : public InstructionRule {
1093 private:
1094 unsigned Number = 1;
1095
1096 public:
1097 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1098 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1099 bool FoundTrans = false;
1100 unsigned Counter = 1;
1101 auto *DAG = SyncPipe[0].DAG;
1102
1103 if (Cache->empty()) {
1104 auto I = DAG->SUnits.begin();
1105 auto E = DAG->SUnits.end();
1106 for (; I != E; I++) {
1107 if (FoundTrans && TII->isMFMAorWMMA(*I->getInstr())) {
1108 if (Counter == Number) {
1109 Cache->push_back(&*I);
1110 break;
1111 }
1112 ++Counter;
1113 }
1114 if (!FoundTrans && TII->isTRANS(I->getInstr()->getOpcode()))
1115 FoundTrans = true;
1116 }
1117 if (Cache->empty())
1118 return false;
1119 }
1120
1121 return DAG->IsReachable((*Cache)[0], const_cast<SUnit *>(SU));
1122 }
1123
1124 EnablesNthMFMA(unsigned Number, const SIInstrInfo *TII, unsigned SGID,
1125 bool NeedsCache = false)
1126 : InstructionRule(TII, SGID, NeedsCache), Number(Number) {}
1127 };
1128
1129 /// Whether or not the instruction enables the exact MFMA that is the \p
1130 /// Number th MFMA in the chain starting with \p ChainSeed
1131 class EnablesNthMFMAInChain final : public InstructionRule {
1132 private:
1133 unsigned Number = 1;
1134 SUnit *ChainSeed;
1135
1136 public:
1137 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1138 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1139 auto *DAG = SyncPipe[0].DAG;
1140
1141 if (!SU || !TII->isMFMAorWMMA(*ChainSeed->getInstr()))
1142 return false;
1143
1144 if (Cache->empty()) {
1145 auto *TempSU = ChainSeed;
1146 auto Depth = Number;
1147 while (Depth > 0) {
1148 --Depth;
1149 bool Found = false;
1150 for (auto &Succ : TempSU->Succs) {
1151 if (TII->isMFMAorWMMA(*Succ.getSUnit()->getInstr())) {
1152 TempSU = Succ.getSUnit();
1153 Found = true;
1154 break;
1155 }
1156 }
1157 if (!Found)
1158 return false;
1159 }
1160
1161 Cache->push_back(TempSU);
1162 }
1163 // If we failed to find the instruction to be placed into the cache, we
1164 // would have already exited.
1165 assert(!Cache->empty());
1166
1167 return DAG->IsReachable((*Cache)[0], const_cast<SUnit *>(SU));
1168 }
1169
1170 EnablesNthMFMAInChain(unsigned Number, SUnit *ChainSeed,
1171 const SIInstrInfo *TII, unsigned SGID,
1172 bool NeedsCache = false)
1173 : InstructionRule(TII, SGID, NeedsCache), Number(Number),
1174 ChainSeed(ChainSeed) {}
1175 };
1176
1177 /// Whether or not the instruction has less than \p Size immediate successors.
1178 /// If \p HasIntermediary is true, this tests also whether all successors of
1179 /// the SUnit have less than \p Size successors.
1180 class LessThanNSuccs final : public InstructionRule {
1181 private:
1182 unsigned Size = 1;
1183 bool HasIntermediary = false;
1184
1185 public:
1186 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1187 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1188 if (!SyncPipe.size())
1189 return false;
1190
1191 unsigned SuccSize = llvm::count_if(SU->Succs, [](const SDep &Succ) {
1192 return Succ.getKind() == SDep::Data;
1193 });
1194 if (SuccSize >= Size)
1195 return false;
1196
1197 if (HasIntermediary) {
1198 for (auto Succ : SU->Succs) {
1199 unsigned SuccSize =
1200 llvm::count_if(Succ.getSUnit()->Succs, [](const SDep &SuccSucc) {
1201 return SuccSucc.getKind() == SDep::Data;
1202 });
1203 if (SuccSize >= Size)
1204 return false;
1205 }
1206 }
1207
1208 return true;
1209 }
1210 LessThanNSuccs(unsigned Size, const SIInstrInfo *TII, unsigned SGID,
1211 bool HasIntermediary = false, bool NeedsCache = false)
1212 : InstructionRule(TII, SGID, NeedsCache), Size(Size),
1213 HasIntermediary(HasIntermediary) {}
1214 };
1215
1216 /// Whether or not the instruction has greater than or equal to \p Size
1217 /// immediate successors. If \p HasIntermediary is true, this tests also
1218 /// whether all successors of the SUnit have greater than or equal to \p Size
1219 /// successors.
1220 class GreaterThanOrEqualToNSuccs final : public InstructionRule {
1221 private:
1222 unsigned Size = 1;
1223 bool HasIntermediary = false;
1224
1225 public:
1226 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1227 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1228 if (!SyncPipe.size())
1229 return false;
1230
1231 unsigned SuccSize = llvm::count_if(SU->Succs, [](const SDep &Succ) {
1232 return Succ.getKind() == SDep::Data;
1233 });
1234 if (SuccSize >= Size)
1235 return true;
1236
1237 if (HasIntermediary) {
1238 for (auto Succ : SU->Succs) {
1239 unsigned SuccSize =
1240 llvm::count_if(Succ.getSUnit()->Succs, [](const SDep &SuccSucc) {
1241 return SuccSucc.getKind() == SDep::Data;
1242 });
1243 if (SuccSize >= Size)
1244 return true;
1245 }
1246 }
1247
1248 return false;
1249 }
1250 GreaterThanOrEqualToNSuccs(unsigned Size, const SIInstrInfo *TII,
1251 unsigned SGID, bool HasIntermediary = false,
1252 bool NeedsCache = false)
1253 : InstructionRule(TII, SGID, NeedsCache), Size(Size),
1254 HasIntermediary(HasIntermediary) {}
1255 };
1256
1257 // Whether or not the instruction is a relevant V_CVT instruction.
1258 class IsCvt final : public InstructionRule {
1259 public:
1260 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1261 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1262 auto Opc = SU->getInstr()->getOpcode();
1263 return Opc == AMDGPU::V_CVT_F16_F32_e32 ||
1264 Opc == AMDGPU::V_CVT_I32_F32_e32;
1265 }
1266 IsCvt(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
1267 : InstructionRule(TII, SGID, NeedsCache) {}
1268 };
1269
1270 // Whether or not the instruction is FMA_F32.
1271 class IsFMA final : public InstructionRule {
1272 public:
1273 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1274 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1275 return SU->getInstr()->getOpcode() == AMDGPU::V_FMA_F32_e64 ||
1276 SU->getInstr()->getOpcode() == AMDGPU::V_PK_FMA_F32;
1277 }
1278 IsFMA(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
1279 : InstructionRule(TII, SGID, NeedsCache) {}
1280 };
1281
1282 // Whether or not the instruction is a V_ADD_F32 instruction.
1283 class IsPipeAdd final : public InstructionRule {
1284 public:
1285 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1286 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1287 return SU->getInstr()->getOpcode() == AMDGPU::V_ADD_F32_e32;
1288 }
1289 IsPipeAdd(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
1290 : InstructionRule(TII, SGID, NeedsCache) {}
1291 };
1292
1293 /// Whether or not the instruction is an immediate RAW successor
1294 /// of the SchedGroup \p Distance steps before.
1295 class IsSuccOfPrevNthGroup final : public InstructionRule {
1296 private:
1297 unsigned Distance = 1;
1298
1299 public:
1300 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1301 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1302 SchedGroup *OtherGroup = nullptr;
1303 if (!SyncPipe.size())
1304 return false;
1305
1306 for (auto &PipeSG : SyncPipe) {
1307 if ((unsigned)PipeSG.getSGID() == SGID - Distance)
1308 OtherGroup = &PipeSG;
1309 }
1310
1311 if (!OtherGroup)
1312 return false;
1313 if (!OtherGroup->Collection.size())
1314 return true;
1315
1316 for (auto &OtherEle : OtherGroup->Collection) {
1317 for (auto &Succ : OtherEle->Succs) {
1318 if (Succ.getSUnit() == SU && Succ.getKind() == SDep::Data)
1319 return true;
1320 }
1321 }
1322
1323 return false;
1324 }
1325 IsSuccOfPrevNthGroup(unsigned Distance, const SIInstrInfo *TII,
1326 unsigned SGID, bool NeedsCache = false)
1327 : InstructionRule(TII, SGID, NeedsCache), Distance(Distance) {}
1328 };
1329
1330 /// Whether or not the instruction is a transitive successor of any
1331 /// instruction the the SchedGroup \p Distance steps before.
1332 class IsReachableFromPrevNthGroup final : public InstructionRule {
1333 private:
1334 unsigned Distance = 1;
1335
1336 public:
1337 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1338 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1339 SchedGroup *OtherGroup = nullptr;
1340 if (!SyncPipe.size())
1341 return false;
1342
1343 for (auto &PipeSG : SyncPipe) {
1344 if ((unsigned)PipeSG.getSGID() == SGID - Distance)
1345 OtherGroup = &PipeSG;
1346 }
1347
1348 if (!OtherGroup)
1349 return false;
1350 if (!OtherGroup->Collection.size())
1351 return true;
1352
1353 auto *DAG = SyncPipe[0].DAG;
1354
1355 for (auto &OtherEle : OtherGroup->Collection)
1356 if (DAG->IsReachable(const_cast<SUnit *>(SU), OtherEle))
1357 return true;
1358
1359 return false;
1360 }
1361 IsReachableFromPrevNthGroup(unsigned Distance, const SIInstrInfo *TII,
1362 unsigned SGID, bool NeedsCache = false)
1363 : InstructionRule(TII, SGID, NeedsCache), Distance(Distance) {}
1364 };
1365
1366 /// Whether or not the instruction occurs after the SU with NodeNUm \p Number
1367 class OccursAtOrAfterNode final : public InstructionRule {
1368 private:
1369 unsigned Number = 1;
1370
1371 public:
1372 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1373 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1374
1375 return SU->NodeNum >= Number;
1376 }
1377 OccursAtOrAfterNode(unsigned Number, const SIInstrInfo *TII, unsigned SGID,
1378 bool NeedsCache = false)
1379 : InstructionRule(TII, SGID, NeedsCache), Number(Number) {}
1380 };
1381
1382 /// Whether or not the SU is exactly the \p Number th MFMA in the chain
1383 /// starting with \p ChainSeed
1384 class IsExactMFMA final : public InstructionRule {
1385 private:
1386 unsigned Number = 1;
1387 SUnit *ChainSeed;
1388
1389 public:
1390 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1391 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1392 if (!SU || !TII->isMFMAorWMMA(*ChainSeed->getInstr()))
1393 return false;
1394
1395 if (Cache->empty()) {
1396 auto *TempSU = ChainSeed;
1397 auto Depth = Number;
1398 while (Depth > 0) {
1399 --Depth;
1400 bool Found = false;
1401 for (auto &Succ : TempSU->Succs) {
1402 if (TII->isMFMAorWMMA(*Succ.getSUnit()->getInstr())) {
1403 TempSU = Succ.getSUnit();
1404 Found = true;
1405 break;
1406 }
1407 }
1408 if (!Found) {
1409 return false;
1410 }
1411 }
1412 Cache->push_back(TempSU);
1413 }
1414 // If we failed to find the instruction to be placed into the cache, we
1415 // would have already exited.
1416 assert(!Cache->empty());
1417
1418 return (*Cache)[0] == SU;
1419 }
1420
1421 IsExactMFMA(unsigned Number, SUnit *ChainSeed, const SIInstrInfo *TII,
1422 unsigned SGID, bool NeedsCache = false)
1423 : InstructionRule(TII, SGID, NeedsCache), Number(Number),
1424 ChainSeed(ChainSeed) {}
1425 };
1426
1427 // Whether the instruction occurs after the first TRANS instruction. This
1428 // implies the instruction can not be a predecessor of the first TRANS
1429 // insruction
1430 class OccursAfterExp final : public InstructionRule {
1431 public:
1432 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
1433 SmallVectorImpl<SchedGroup> &SyncPipe) override {
1434
1435 auto *DAG = SyncPipe[0].DAG;
1436 if (Cache->empty()) {
1437 for (auto &SU : DAG->SUnits)
1438 if (TII->isTRANS(SU.getInstr()->getOpcode())) {
1439 Cache->push_back(&SU);
1440 break;
1441 }
1442 if (Cache->empty())
1443 return false;
1444 }
1445
1446 return SU->NodeNum > (*Cache)[0]->NodeNum;
1447 }
1448
1449 OccursAfterExp(const SIInstrInfo *TII, unsigned SGID,
1450 bool NeedsCache = false)
1451 : InstructionRule(TII, SGID, NeedsCache) {}
1452 };
1453
1454public:
1455 bool applyIGLPStrategy(
1457 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
1459
1460 bool shouldApplyStrategy(ScheduleDAGInstrs *DAG,
1462
1463 MFMAExpInterleaveOpt(ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
1464 : IGLPStrategy(DAG, TII) {
1465 IsBottomUp = false;
1466 }
1467};
1468
1469unsigned MFMAExpInterleaveOpt::TransPipeCount = 0;
1470unsigned MFMAExpInterleaveOpt::MFMAPipeCount = 0;
1471unsigned MFMAExpInterleaveOpt::AddPipeCount = 0;
1472unsigned MFMAExpInterleaveOpt::MFMAEnablement = 0;
1473unsigned MFMAExpInterleaveOpt::ExpRequirement = 0;
1474unsigned MFMAExpInterleaveOpt::MFMAChains = 0;
1475bool MFMAExpInterleaveOpt::HasCvt = false;
1476bool MFMAExpInterleaveOpt::HasChainBetweenCvt = false;
1477std::optional<unsigned> MFMAExpInterleaveOpt::FirstPipeDSR = std::nullopt;
1478
1479bool MFMAExpInterleaveOpt::analyzeDAG(const SIInstrInfo *TII) {
1480 SmallVector<SUnit *, 10> ExpPipeCands;
1481 SmallVector<SUnit *, 10> MFMAPipeCands;
1482 SmallVector<SUnit *, 10> MFMAPipeSUs;
1485
1486 auto isBitPack = [](unsigned Opc) {
1487 return Opc == AMDGPU::V_PACK_B32_F16_e64 || Opc == AMDGPU::V_PERM_B32_e64;
1488 };
1489
1490 auto isCvt = [](unsigned Opc) {
1491 return Opc == AMDGPU::V_CVT_F16_F32_e32 || Opc == AMDGPU::V_CVT_I32_F32_e32;
1492 };
1493
1494 auto isAdd = [](unsigned Opc) { return Opc == AMDGPU::V_ADD_F32_e32; };
1495
1496 AddPipeCount = 0;
1497 for (SUnit &SU : DAG->SUnits) {
1498 auto Opc = SU.getInstr()->getOpcode();
1499 if (TII->isTRANS(Opc)) {
1500 // Avoid counting a potential bonus V_EXP which all the MFMA depend on
1501 if (SU.Succs.size() >= 7)
1502 continue;
1503 for (auto &Succ : SU.Succs) {
1504 if (Succ.getSUnit()->Succs.size() >= 7)
1505 continue;
1506 }
1507 ExpPipeCands.push_back(&SU);
1508 }
1509
1510 if (TII->isMFMAorWMMA(*SU.getInstr()))
1511 MFMAPipeCands.push_back(&SU);
1512
1513 if (isBitPack(Opc))
1514 PackSUs.push_back(&SU);
1515
1516 if (isCvt(Opc))
1517 CvtSUs.push_back(&SU);
1518
1519 if (isAdd(Opc))
1520 ++AddPipeCount;
1521 }
1522
1523 if (!(PackSUs.size() && MFMAPipeCands.size() && ExpPipeCands.size()))
1524 return false;
1525
1526 TransPipeCount = 0;
1527
1528 std::optional<SUnit *> TempMFMA;
1529 std::optional<SUnit *> TempExp;
1530 // Count the number of EXPs that reach an MFMA
1531 for (auto &PredSU : ExpPipeCands) {
1532 for (auto &SuccSU : MFMAPipeCands) {
1533 if (DAG->IsReachable(SuccSU, PredSU)) {
1534 if (!TempExp) {
1535 TempExp = PredSU;
1536 TempMFMA = SuccSU;
1537 }
1538 MFMAPipeSUs.push_back(SuccSU);
1539 ++TransPipeCount;
1540 break;
1541 }
1542 }
1543 }
1544
1545 if (!(TempExp && TempMFMA))
1546 return false;
1547
1548 HasChainBetweenCvt = none_of((*TempExp)->Succs, [&isCvt](SDep &Succ) {
1549 return isCvt(Succ.getSUnit()->getInstr()->getOpcode());
1550 });
1551
1552 // Count the number of MFMAs that are reached by an EXP
1553 for (auto &SuccSU : MFMAPipeCands) {
1554 if (MFMAPipeSUs.size() &&
1555 any_of(MFMAPipeSUs, [&SuccSU](SUnit *PotentialMatch) {
1556 return PotentialMatch->NodeNum == SuccSU->NodeNum;
1557 }))
1558 continue;
1559
1560 for (auto &PredSU : ExpPipeCands) {
1561 if (DAG->IsReachable(SuccSU, PredSU)) {
1562 MFMAPipeSUs.push_back(SuccSU);
1563 break;
1564 }
1565 }
1566 }
1567
1568 MFMAPipeCount = MFMAPipeSUs.size();
1569
1570 assert(TempExp && TempMFMA);
1571 assert(MFMAPipeCount > 0);
1572
1573 std::optional<SUnit *> TempCvt;
1574 for (auto &SuccSU : CvtSUs) {
1575 if (DAG->IsReachable(SuccSU, *TempExp)) {
1576 TempCvt = SuccSU;
1577 break;
1578 }
1579 }
1580
1581 HasCvt = false;
1582 if (TempCvt.has_value()) {
1583 for (auto &SuccSU : MFMAPipeSUs) {
1584 if (DAG->IsReachable(SuccSU, *TempCvt)) {
1585 HasCvt = true;
1586 break;
1587 }
1588 }
1589 }
1590
1591 MFMAChains = 0;
1592 for (auto &MFMAPipeSU : MFMAPipeSUs) {
1593 if (is_contained(MFMAChainSeeds, MFMAPipeSU))
1594 continue;
1595 if (none_of(MFMAPipeSU->Preds, [&TII](SDep &Succ) {
1596 return TII->isMFMAorWMMA(*Succ.getSUnit()->getInstr());
1597 })) {
1598 MFMAChainSeeds.push_back(MFMAPipeSU);
1599 ++MFMAChains;
1600 }
1601 }
1602
1603 if (!MFMAChains)
1604 return false;
1605
1606 for (auto Pred : MFMAChainSeeds[0]->Preds) {
1607 if (TII->isDS(Pred.getSUnit()->getInstr()->getOpcode()) &&
1608 Pred.getSUnit()->getInstr()->mayLoad())
1609 FirstPipeDSR = Pred.getSUnit()->NodeNum;
1610 }
1611
1612 // The number of bit pack operations that depend on a single V_EXP
1613 unsigned PackSuccCount =
1614 llvm::count_if(PackSUs, [this, &TempExp](SUnit *VPack) {
1615 return DAG->IsReachable(VPack, *TempExp);
1616 });
1617
1618 // The number of bit pack operations an MFMA depends on
1619 unsigned PackPredCount =
1620 llvm::count_if((*TempMFMA)->Preds, [&isBitPack](SDep &Pred) {
1621 auto Opc = Pred.getSUnit()->getInstr()->getOpcode();
1622 return isBitPack(Opc);
1623 });
1624
1625 auto *PackPred = llvm::find_if((*TempMFMA)->Preds, [&isBitPack](SDep &Pred) {
1626 auto Opc = Pred.getSUnit()->getInstr()->getOpcode();
1627 return isBitPack(Opc);
1628 });
1629
1630 if (PackPred == (*TempMFMA)->Preds.end())
1631 return false;
1632
1633 MFMAEnablement = 0;
1634 ExpRequirement = 0;
1635 // How many MFMAs depend on a single bit pack operation
1636 MFMAEnablement =
1637 llvm::count_if(PackPred->getSUnit()->Succs, [&TII](SDep &Succ) {
1638 return TII->isMFMAorWMMA(*Succ.getSUnit()->getInstr());
1639 });
1640
1641 // The number of MFMAs that depend on a single V_EXP
1642 MFMAEnablement *= PackSuccCount;
1643
1644 // The number of V_EXPs required to resolve all dependencies for an MFMA
1645 ExpRequirement =
1646 llvm::count_if(ExpPipeCands, [this, &PackPred](SUnit *ExpBase) {
1647 return DAG->IsReachable(PackPred->getSUnit(), ExpBase);
1648 });
1649
1650 ExpRequirement *= PackPredCount;
1651 return true;
1652}
1653
1654bool MFMAExpInterleaveOpt::shouldApplyStrategy(ScheduleDAGInstrs *DAG,
1656 const GCNSubtarget &ST = DAG->MF.getSubtarget<GCNSubtarget>();
1657 const SIInstrInfo *TII = ST.getInstrInfo();
1658
1660 MFMAChainSeeds.clear();
1661 if (Phase != AMDGPU::SchedulingPhase::PostRA && !analyzeDAG(TII))
1662 return false;
1663
1664 return true;
1665}
1666
1667bool MFMAExpInterleaveOpt::applyIGLPStrategy(
1669 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
1671
1672 bool IsSmallKernelType =
1673 MFMAEnablement == 2 && ExpRequirement == 4 && TransPipeCount == 32;
1674 bool IsLargeKernelType =
1675 MFMAEnablement == 4 && ExpRequirement == 4 && TransPipeCount == 64;
1676
1677 if (!(IsSmallKernelType || IsLargeKernelType))
1678 return false;
1679
1680 const GCNSubtarget &ST = DAG->MF.getSubtarget<GCNSubtarget>();
1681 const SIInstrInfo *TII = ST.getInstrInfo();
1682
1683 unsigned PipelineSyncID = 0;
1684 SchedGroup *SG = nullptr;
1685
1686 unsigned MFMAChain = 0;
1687 unsigned PositionInChain = 0;
1688 unsigned CurrMFMAForTransPosition = 0;
1689
1690 auto incrementTransPosition = [&MFMAChain, &PositionInChain,
1691 &CurrMFMAForTransPosition]() {
1692 CurrMFMAForTransPosition += MFMAEnablement;
1693 PositionInChain = (CurrMFMAForTransPosition / MFMAChains);
1694 MFMAChain = CurrMFMAForTransPosition % MFMAChains;
1695 };
1696
1697 auto getNextTransPositionInChain = [&CurrMFMAForTransPosition]() {
1698 auto TempMFMAForTrans = CurrMFMAForTransPosition + MFMAEnablement;
1699 return (TempMFMAForTrans / MFMAChains);
1700 };
1701
1702 auto getNextTransMFMAChain = [&CurrMFMAForTransPosition]() {
1703 auto TempMFMAForTrans = CurrMFMAForTransPosition + MFMAEnablement;
1704 return TempMFMAForTrans % MFMAChains;
1705 };
1706
1707 unsigned CurrMFMAPosition = 0;
1708 unsigned MFMAChainForMFMA = 0;
1709 unsigned PositionInChainForMFMA = 0;
1710
1711 auto incrementMFMAPosition = [&CurrMFMAPosition, &MFMAChainForMFMA,
1712 &PositionInChainForMFMA]() {
1713 ++CurrMFMAPosition;
1714 MFMAChainForMFMA = CurrMFMAPosition % MFMAChains;
1715 PositionInChainForMFMA = CurrMFMAPosition / MFMAChains;
1716 };
1717
1718 bool IsPostRA = Phase == AMDGPU::SchedulingPhase::PostRA;
1719 assert(IsPostRA || MFMAChainSeeds.size() == MFMAChains);
1720
1721 bool UsesFMA = IsSmallKernelType || !IsPostRA;
1722 bool UsesDSRead = IsLargeKernelType && !IsPostRA && FirstPipeDSR;
1723 bool UsesCvt = HasCvt && (IsSmallKernelType || !IsPostRA);
1724 bool UsesVALU = IsSmallKernelType;
1725
1726 // PHASE 1: "Prefetch"
1727 if (UsesFMA) {
1728 // First Round FMA
1729 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1730 SchedGroupMask::VALU, ExpRequirement, PipelineSyncID, DAG, TII);
1731 if (!IsPostRA && MFMAChains) {
1732 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1733 PositionInChain, MFMAChainSeeds[MFMAChain], TII, SG->getSGID(),
1734 true));
1735 } else
1736 SG->addRule(
1737 std::make_shared<EnablesNthMFMA>(1, TII, SG->getSGID(), true));
1738 SG->addRule(std::make_shared<IsFMA>(TII, SG->getSGID()));
1739 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1740
1741 // Second Round FMA
1742 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1743 SchedGroupMask::VALU, ExpRequirement, PipelineSyncID, DAG, TII);
1744 if (!IsPostRA && MFMAChains) {
1745 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1746 getNextTransPositionInChain(),
1747 MFMAChainSeeds[getNextTransMFMAChain()], TII, SG->getSGID(), true));
1748 } else
1749 SG->addRule(std::make_shared<EnablesNthMFMA>(MFMAEnablement + 1, TII,
1750 SG->getSGID(), true));
1751 SG->addRule(std::make_shared<IsFMA>(TII, SG->getSGID()));
1752 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1753 }
1754
1755 if (UsesDSRead) {
1756 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1757 SchedGroupMask::DS_READ, 2, PipelineSyncID, DAG, TII);
1758 SG->addRule(std::make_shared<OccursAtOrAfterNode>(*FirstPipeDSR, TII,
1759 SG->getSGID()));
1760 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1761 }
1762
1763 // First Round EXP
1764 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1765 SchedGroupMask::TRANS, ExpRequirement, PipelineSyncID, DAG, TII);
1766 if (!IsPostRA && MFMAChains)
1767 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1768 PositionInChain, MFMAChainSeeds[MFMAChain], TII, SG->getSGID(), true));
1769 else
1770 SG->addRule(std::make_shared<EnablesNthMFMA>(1, TII, SG->getSGID(), true));
1771 SG->addRule(std::make_shared<IsPipeExp>(TII, SG->getSGID(), true));
1772 SG->addRule(std::make_shared<LessThanNSuccs>(8, TII, SG->getSGID(),
1773 HasChainBetweenCvt));
1774 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1775
1776 incrementTransPosition();
1777
1778 // First Round CVT, Third Round FMA, Second Round EXP; interleaved
1779 for (unsigned I = 0; I < ExpRequirement; I++) {
1780 // First Round CVT
1781 if (UsesCvt) {
1782 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1783 SchedGroupMask::VALU, 1, PipelineSyncID, DAG, TII);
1784 SG->addRule(std::make_shared<IsCvt>(TII, SG->getSGID()));
1785 if (HasChainBetweenCvt)
1786 SG->addRule(std::make_shared<IsReachableFromPrevNthGroup>(
1787 1 + (2 + UsesFMA) * I, TII, SG->getSGID()));
1788 else
1789 SG->addRule(std::make_shared<IsSuccOfPrevNthGroup>(
1790 1 + (2 + UsesFMA) * I, TII, SG->getSGID()));
1791 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1792 }
1793
1794 // Third Round FMA
1795 if (UsesFMA) {
1796 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1797 SchedGroupMask::VALU, 1, PipelineSyncID, DAG, TII);
1798 if (!IsPostRA && MFMAChains) {
1799 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1800 getNextTransPositionInChain(),
1801 MFMAChainSeeds[getNextTransMFMAChain()], TII, SG->getSGID(), true));
1802 } else
1803 SG->addRule(std::make_shared<EnablesNthMFMA>(2 * MFMAEnablement + 1,
1804 TII, SG->getSGID(), true));
1805 SG->addRule(std::make_shared<IsFMA>(TII, SG->getSGID()));
1806 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1807 }
1808
1809 // Second Round EXP
1810 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1811 SchedGroupMask::TRANS, 1, PipelineSyncID, DAG, TII);
1812 if (!IsPostRA && MFMAChains)
1813 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1814 PositionInChain, MFMAChainSeeds[MFMAChain], TII, SG->getSGID(),
1815 true));
1816 else
1817 SG->addRule(std::make_shared<EnablesNthMFMA>(MFMAEnablement + 1, TII,
1818 SG->getSGID(), true));
1819 SG->addRule(std::make_shared<IsPipeExp>(TII, SG->getSGID(), true));
1820 SG->addRule(std::make_shared<LessThanNSuccs>(8, TII, SG->getSGID(),
1821 HasChainBetweenCvt));
1822 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1823 }
1824
1825 // The "extra" EXP which enables all MFMA
1826 // TODO: UsesExtraExp
1827 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1828 SchedGroupMask::TRANS, 1, PipelineSyncID, DAG, TII);
1829 SG->addRule(std::make_shared<IsPipeExp>(TII, SG->getSGID(), true));
1830 SG->addRule(std::make_shared<GreaterThanOrEqualToNSuccs>(
1831 8, TII, SG->getSGID(), HasChainBetweenCvt));
1832 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1833
1834 // PHASE 2: Main Interleave Loop
1835
1836 // The number of MFMAs per iteration
1837 unsigned MFMARatio =
1838 MFMAEnablement > ExpRequirement ? MFMAEnablement / ExpRequirement : 1;
1839 // The number of Exps per iteration
1840 unsigned ExpRatio =
1841 MFMAEnablement > ExpRequirement ? 1 : ExpRequirement / MFMAEnablement;
1842 // The reamaining Exps
1843 unsigned RemainingExp = TransPipeCount > (2 * ExpRequirement)
1844 ? TransPipeCount - (2 * ExpRequirement)
1845 : 0;
1846 unsigned ExpLoopCount = RemainingExp / ExpRatio;
1847 // In loop MFMAs
1848 unsigned MFMAInLoop = MFMAPipeCount > (MFMAEnablement * 2)
1849 ? MFMAPipeCount - (MFMAEnablement * 2)
1850 : 0;
1851 unsigned MFMALoopCount = MFMAInLoop / MFMARatio;
1852 unsigned VALUOps =
1853 AddPipeCount < MFMAPipeCount ? 1 : AddPipeCount / MFMAPipeCount;
1854 unsigned LoopSize = std::min(ExpLoopCount, MFMALoopCount);
1855
1856 for (unsigned I = 0; I < LoopSize; I++) {
1857 if (!(I * ExpRatio % ExpRequirement))
1858 incrementTransPosition();
1859
1860 // Round N MFMA
1861 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1862 SchedGroupMask::MFMA, MFMARatio, PipelineSyncID, DAG, TII);
1863 if (!IsPostRA && MFMAChains)
1864 SG->addRule(std::make_shared<IsExactMFMA>(
1865 PositionInChainForMFMA, MFMAChainSeeds[MFMAChainForMFMA], TII,
1866 SG->getSGID(), true));
1867 else
1868 SG->addRule(std::make_shared<OccursAfterExp>(TII, SG->getSGID(), true));
1869 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1870 incrementMFMAPosition();
1871
1872 if (UsesVALU) {
1873 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1874 SchedGroupMask::VALU, VALUOps, PipelineSyncID, DAG, TII);
1875 SG->addRule(std::make_shared<IsPipeAdd>(TII, SG->getSGID()));
1876 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1877 }
1878
1879 if (UsesDSRead && !(I % 4)) {
1880 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1881 SchedGroupMask::DS_READ, 2, PipelineSyncID, DAG, TII);
1882 SG->addRule(std::make_shared<OccursAtOrAfterNode>(*FirstPipeDSR, TII,
1883 SG->getSGID()));
1884 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1885 }
1886
1887 // CVT, EXP, FMA Interleaving
1888 for (unsigned J = 0; J < ExpRatio; J++) {
1889 auto MFMAOffset = (1 + UsesVALU) * MFMARatio * (I + 1);
1890 auto MaxMFMAOffset =
1891 (1 + UsesVALU) * ExpRequirement * MFMARatio / ExpRatio;
1892
1893 // Round N + 1 CVT
1894 if (UsesCvt) {
1895 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1896 SchedGroupMask::VALU, 1, PipelineSyncID, DAG, TII);
1897 SG->addRule(std::make_shared<IsCvt>(TII, SG->getSGID()));
1898 auto BaseDiff = (2 + UsesFMA) * (ExpRequirement - 1) + 1;
1899 auto DSROffset = I / 4 + 1;
1900 auto MaxDSROffset = MaxMFMAOffset / 4;
1901 // TODO: UsesExtraExp
1902 auto ExpOffset = I * ExpRatio + J >= ExpRequirement ? 0 : 1;
1903 auto CurrentOffset = UsesDSRead * std::min(MaxDSROffset, DSROffset) +
1904 std::min(MaxMFMAOffset, MFMAOffset) + BaseDiff +
1905 ExpOffset;
1906 if (HasChainBetweenCvt)
1907 SG->addRule(std::make_shared<IsReachableFromPrevNthGroup>(
1908 CurrentOffset, TII, SG->getSGID()));
1909 else
1910 SG->addRule(std::make_shared<IsSuccOfPrevNthGroup>(CurrentOffset, TII,
1911 SG->getSGID()));
1912 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1913 }
1914
1915 // Round N + 3 FMA
1916 if (UsesFMA) {
1917 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1918 SchedGroupMask::VALU, 1, PipelineSyncID, DAG, TII);
1919 if (!IsPostRA && MFMAChains)
1920 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1921 getNextTransPositionInChain(),
1922 MFMAChainSeeds[getNextTransMFMAChain()], TII, SG->getSGID(),
1923 true));
1924 else
1925 SG->addRule(std::make_shared<EnablesNthMFMA>(
1926 (((I * ExpRatio + J) / ExpRequirement) + 3) * MFMAEnablement + 1,
1927 TII, SG->getSGID(), true));
1928 SG->addRule(std::make_shared<IsFMA>(TII, SG->getSGID()));
1929 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1930 }
1931
1932 // Round N + 2 Exp
1933 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1934 SchedGroupMask::TRANS, 1, PipelineSyncID, DAG, TII);
1935 if (!IsPostRA && MFMAChains)
1936 SG->addRule(std::make_shared<EnablesNthMFMAInChain>(
1937 PositionInChain, MFMAChainSeeds[MFMAChain], TII, SG->getSGID(),
1938 true));
1939 else
1940 SG->addRule(std::make_shared<EnablesNthMFMA>(
1941 (((I * ExpRatio + J) / ExpRequirement) + 2) * MFMAEnablement + 1,
1942 TII, SG->getSGID(), true));
1943 SG->addRule(std::make_shared<IsPipeExp>(TII, SG->getSGID(), true));
1944 SG->addRule(std::make_shared<LessThanNSuccs>(8, TII, SG->getSGID(),
1945 HasChainBetweenCvt));
1946 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1947 }
1948 }
1949
1950 // PHASE 3: Remaining MFMAs
1951 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1952 SchedGroupMask::MFMA, MFMAEnablement * 2, PipelineSyncID, DAG, TII);
1953 SG->addRule(std::make_shared<OccursAfterExp>(TII, SG->getSGID(), true));
1954 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1955 return true;
1956}
1957
1958class MFMAExpSimpleInterleaveOpt final : public IGLPStrategy {
1959public:
1960 bool applyIGLPStrategy(
1962 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
1964
1965 bool shouldApplyStrategy(ScheduleDAGInstrs *DAG,
1966 AMDGPU::SchedulingPhase Phase) override {
1967 return true;
1968 }
1969
1970 MFMAExpSimpleInterleaveOpt(ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
1971 : IGLPStrategy(DAG, TII) {
1972 IsBottomUp = true;
1973 }
1974};
1975
1976bool MFMAExpSimpleInterleaveOpt::applyIGLPStrategy(
1978 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
1980 // Count the number of MFMA instructions.
1981 unsigned MFMACount = 0;
1982 for (const MachineInstr &I : *DAG)
1983 if (TII->isMFMAorWMMA(I))
1984 ++MFMACount;
1985
1986 const unsigned PipelineSyncID = 0;
1987 for (unsigned I = 0; I < MFMACount * 3; ++I) {
1988 SchedGroup *SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1989 SchedGroupMask::TRANS, 1, PipelineSyncID, DAG, TII);
1990 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1991
1992 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
1993 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
1994 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
1995 }
1996
1997 return true;
1998}
1999
2000class MFMASmallGemmSingleWaveOpt final : public IGLPStrategy {
2001private:
2002 // Whether the DS_READ is a predecessor of first four MFMA in region
2003 class EnablesInitialMFMA final : public InstructionRule {
2004 public:
2005 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
2006 SmallVectorImpl<SchedGroup> &SyncPipe) override {
2007 if (!SyncPipe.size())
2008 return false;
2009 int MFMAsFound = 0;
2010 if (!Cache->size()) {
2011 for (auto &Elt : SyncPipe[0].DAG->SUnits) {
2012 if (TII->isMFMAorWMMA(*Elt.getInstr())) {
2013 ++MFMAsFound;
2014 if (MFMAsFound > 4)
2015 break;
2016 Cache->push_back(&Elt);
2017 }
2018 }
2019 }
2020
2021 auto *DAG = SyncPipe[0].DAG;
2022 for (auto &Elt : *Cache) {
2023 if (DAG->IsReachable(Elt, const_cast<SUnit *>(SU)))
2024 return true;
2025 }
2026 return false;
2027 }
2028
2029 EnablesInitialMFMA(const SIInstrInfo *TII, unsigned SGID,
2030 bool NeedsCache = false)
2031 : InstructionRule(TII, SGID, NeedsCache) {}
2032 };
2033
2034 // Whether the MI is a V_PERM and is a predecessor of a common DS_WRITE
2035 class IsPermForDSW final : public InstructionRule {
2036 public:
2037 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
2038 SmallVectorImpl<SchedGroup> &SyncPipe) override {
2039 auto *MI = SU->getInstr();
2040 if (MI->getOpcode() != AMDGPU::V_PERM_B32_e64)
2041 return false;
2042
2043 bool FitsInGroup = false;
2044 // Does the VALU have a DS_WRITE successor
2045 if (!Collection.size()) {
2046 for (auto &Succ : SU->Succs) {
2047 SUnit *SuccUnit = Succ.getSUnit();
2048 if (TII->isDS(*SuccUnit->getInstr()) &&
2049 SuccUnit->getInstr()->mayStore()) {
2050 Cache->push_back(SuccUnit);
2051 FitsInGroup = true;
2052 }
2053 }
2054 return FitsInGroup;
2055 }
2056
2057 // Does the VALU have a DS_WRITE successor that is the same as other
2058 // VALU already in the group. The V_PERMs will all share 1 DS_W succ
2059 return llvm::any_of(*Cache, [&SU](SUnit *Elt) {
2060 return llvm::any_of(SU->Succs, [&Elt](const SDep &ThisSucc) {
2061 return ThisSucc.getSUnit() == Elt;
2062 });
2063 });
2064 }
2065
2066 IsPermForDSW(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
2067 : InstructionRule(TII, SGID, NeedsCache) {}
2068 };
2069
2070 // Whether the SU is a successor of any element in previous SchedGroup
2071 class IsSuccOfPrevGroup final : public InstructionRule {
2072 public:
2073 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
2074 SmallVectorImpl<SchedGroup> &SyncPipe) override {
2075 SchedGroup *OtherGroup = nullptr;
2076 for (auto &PipeSG : SyncPipe) {
2077 if ((unsigned)PipeSG.getSGID() == SGID - 1) {
2078 OtherGroup = &PipeSG;
2079 }
2080 }
2081
2082 if (!OtherGroup)
2083 return false;
2084 if (!OtherGroup->Collection.size())
2085 return true;
2086
2087 // Does the previous VALU have this DS_Write as a successor
2088 return any_of(OtherGroup->Collection, [&SU](SUnit *Elt) {
2089 return any_of(Elt->Succs,
2090 [&SU](SDep &Succ) { return Succ.getSUnit() == SU; });
2091 });
2092 }
2093 IsSuccOfPrevGroup(const SIInstrInfo *TII, unsigned SGID,
2094 bool NeedsCache = false)
2095 : InstructionRule(TII, SGID, NeedsCache) {}
2096 };
2097
2098 // Whether the combined load width of group is 128 bits
2099 class VMEMSize final : public InstructionRule {
2100 public:
2101 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
2102 SmallVectorImpl<SchedGroup> &SyncPipe) override {
2103 auto *MI = SU->getInstr();
2104 if (MI->getOpcode() == TargetOpcode::BUNDLE)
2105 return false;
2106 if (!Collection.size())
2107 return true;
2108
2109 int NumBits = 0;
2110
2111 auto TRI = TII->getRegisterInfo();
2112 auto &MRI = MI->getMF()->getRegInfo();
2113 for (auto &Elt : Collection) {
2114 auto Op = Elt->getInstr()->getOperand(0);
2115 auto Size =
2116 TRI.getRegSizeInBits(*TRI.getRegClassForOperandReg(MRI, Op));
2117 NumBits += Size;
2118 }
2119
2120 if (NumBits < 128) {
2121 assert(TII->isVMEM(*MI) && MI->mayLoad());
2122 if (NumBits + TRI.getRegSizeInBits(*TRI.getRegClassForOperandReg(
2123 MRI, MI->getOperand(0))) <=
2124 128)
2125 return true;
2126 }
2127
2128 return false;
2129 }
2130
2131 VMEMSize(const SIInstrInfo *TII, unsigned SGID, bool NeedsCache = false)
2132 : InstructionRule(TII, SGID, NeedsCache) {}
2133 };
2134
2135 /// Whether the SU shares a V_PERM predecessor with any SU in the SchedGroup
2136 /// that is \p Distance steps away
2137 class SharesPredWithPrevNthGroup final : public InstructionRule {
2138 private:
2139 unsigned Distance = 1;
2140
2141 public:
2142 bool apply(const SUnit *SU, const ArrayRef<SUnit *> Collection,
2143 SmallVectorImpl<SchedGroup> &SyncPipe) override {
2144 SchedGroup *OtherGroup = nullptr;
2145 if (!SyncPipe.size())
2146 return false;
2147
2148 if (!Cache->size()) {
2149
2150 for (auto &PipeSG : SyncPipe) {
2151 if ((unsigned)PipeSG.getSGID() == SGID - Distance) {
2152 OtherGroup = &PipeSG;
2153 }
2154 }
2155
2156 if (!OtherGroup)
2157 return false;
2158 if (!OtherGroup->Collection.size())
2159 return true;
2160
2161 for (auto &OtherEle : OtherGroup->Collection) {
2162 for (auto &Pred : OtherEle->Preds) {
2163 if (Pred.getSUnit()->getInstr()->getOpcode() ==
2164 AMDGPU::V_PERM_B32_e64)
2165 Cache->push_back(Pred.getSUnit());
2166 }
2167 }
2168
2169 // If the other group has no PERM preds, then this group won't share any
2170 if (!Cache->size())
2171 return false;
2172 }
2173
2174 auto *DAG = SyncPipe[0].DAG;
2175 // Does the previous DS_WRITE share a V_PERM predecessor with this
2176 // VMEM_READ
2177 return llvm::any_of(*Cache, [&SU, &DAG](SUnit *Elt) {
2178 return DAG->IsReachable(const_cast<SUnit *>(SU), Elt);
2179 });
2180 }
2181 SharesPredWithPrevNthGroup(unsigned Distance, const SIInstrInfo *TII,
2182 unsigned SGID, bool NeedsCache = false)
2183 : InstructionRule(TII, SGID, NeedsCache), Distance(Distance) {}
2184 };
2185
2186public:
2187 bool applyIGLPStrategy(
2189 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
2191
2192 bool shouldApplyStrategy(ScheduleDAGInstrs *DAG,
2193 AMDGPU::SchedulingPhase Phase) override {
2194 return true;
2195 }
2196
2197 MFMASmallGemmSingleWaveOpt(ScheduleDAGInstrs *DAG, const SIInstrInfo *TII)
2198 : IGLPStrategy(DAG, TII) {
2199 IsBottomUp = false;
2200 }
2201};
2202
2203static unsigned DSWCount = 0;
2204static unsigned DSWWithPermCount = 0;
2205static unsigned DSWWithSharedVMEMCount = 0;
2206
2207bool MFMASmallGemmSingleWaveOpt::applyIGLPStrategy(
2208 DenseMap<int, SUnitsToCandidateSGsMap> &SyncedInstrs,
2209 DenseMap<int, SmallVector<SchedGroup, 4>> &SyncedSchedGroups,
2211 unsigned MFMACount = 0;
2212 unsigned DSRCount = 0;
2213
2214 bool IsInitial = Phase == AMDGPU::SchedulingPhase::Initial;
2215
2216 assert((!IsInitial || (DSWCount == 0 && DSWWithPermCount == 0 &&
2217 DSWWithSharedVMEMCount == 0)) &&
2218 "DSWCounters should be zero in pre-RA scheduling!");
2219 SmallVector<SUnit *, 6> DSWithPerms;
2220 for (auto &SU : DAG->SUnits) {
2221 auto *I = SU.getInstr();
2222 if (TII->isMFMAorWMMA(*I))
2223 ++MFMACount;
2224 else if (TII->isDS(*I)) {
2225 if (I->mayLoad())
2226 ++DSRCount;
2227 else if (I->mayStore() && IsInitial) {
2228 ++DSWCount;
2229 for (auto Pred : SU.Preds) {
2230 if (Pred.getSUnit()->getInstr()->getOpcode() ==
2231 AMDGPU::V_PERM_B32_e64) {
2232 DSWithPerms.push_back(&SU);
2233 break;
2234 }
2235 }
2236 }
2237 }
2238 }
2239
2240 if (IsInitial) {
2241 DSWWithPermCount = DSWithPerms.size();
2242 auto *I = DSWithPerms.begin();
2243 auto *E = DSWithPerms.end();
2244
2245 // Get the count of DS_WRITES with V_PERM predecessors which
2246 // have loop carried dependencies (WAR) on the same VMEM_READs.
2247 // We consider partial overlap as a miss -- in other words,
2248 // for a given DS_W, we only consider another DS_W as matching
2249 // if there is a corresponding (in terms of the VMEM_R it uses) V_PERM pred
2250 // for every V_PERM pred of this DS_W.
2251 DenseMap<MachineInstr *, SUnit *> VMEMLookup;
2253 for (; I != E; I++) {
2254 SUnit *Cand = nullptr;
2255 bool MissedAny = false;
2256 for (auto &Pred : (*I)->Preds) {
2257 if (Pred.getSUnit()->getInstr()->getOpcode() != AMDGPU::V_PERM_B32_e64)
2258 continue;
2259
2260 if (Cand && llvm::is_contained(Counted, Cand))
2261 break;
2262
2263 for (auto &Succ : Pred.getSUnit()->Succs) {
2264 auto *MI = Succ.getSUnit()->getInstr();
2265 if (!TII->isVMEM(*MI) || !MI->mayLoad())
2266 continue;
2267
2268 if (MissedAny || !VMEMLookup.size()) {
2269 MissedAny = true;
2270 VMEMLookup[MI] = *I;
2271 continue;
2272 }
2273
2274 auto [It, Inserted] = VMEMLookup.try_emplace(MI, *I);
2275 if (Inserted) {
2276 MissedAny = true;
2277 continue;
2278 }
2279
2280 Cand = It->second;
2281 if (llvm::is_contained(Counted, Cand)) {
2282 MissedAny = true;
2283 break;
2284 }
2285 }
2286 }
2287 if (!MissedAny && Cand) {
2288 DSWWithSharedVMEMCount += 2;
2289 Counted.push_back(Cand);
2290 Counted.push_back(*I);
2291 }
2292 }
2293 }
2294
2295 assert(DSWWithSharedVMEMCount <= DSWWithPermCount);
2296 SchedGroup *SG;
2297 unsigned PipelineSyncID = 0;
2298 // For kernels with V_PERM, there are enough VALU to mix in between MFMAs
2299 if (DSWWithPermCount) {
2300 for (unsigned I = 0; I < MFMACount; I++) {
2301 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2302 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2303 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2304
2305 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2306 SchedGroupMask::VALU, 2, PipelineSyncID, DAG, TII);
2307 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2308 }
2309 }
2310
2311 PipelineSyncID = 1;
2312 // Phase 1: Break up DS_READ and MFMA clusters.
2313 // First DS_READ to make ready initial MFMA, then interleave MFMA with DS_READ
2314 // prefetch
2315
2316 // Make ready initial MFMA
2317 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2318 SchedGroupMask::DS_READ, 4, PipelineSyncID, DAG, TII);
2319 SG->addRule(std::make_shared<EnablesInitialMFMA>(TII, SG->getSGID(), true));
2320 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2321
2322 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2323 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2324 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2325
2326 // Interleave MFMA with DS_READ prefetch
2327 for (unsigned I = 4; I < DSRCount; ++I) {
2328 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2329 SchedGroupMask::DS_READ, 1, PipelineSyncID, DAG, TII);
2330 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2331
2332 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2333 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2334 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2335 }
2336
2337 // Phase 2a: Loop carried dependency with V_PERM
2338 // Schedule VPerm & DS_WRITE as closely as possible to the VMEM_READ they
2339 // depend on. Interleave MFMA to keep XDL unit busy throughout.
2340 for (unsigned I = DSWWithSharedVMEMCount; I < DSWWithPermCount; ++I) {
2341 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2342 SchedGroupMask::VALU, 4, PipelineSyncID, DAG, TII);
2343 SG->addRule(std::make_shared<IsPermForDSW>(TII, SG->getSGID(), true));
2344 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2345
2346 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2347 SchedGroupMask::DS_WRITE, 1, PipelineSyncID, DAG, TII);
2348 SG->addRule(std::make_shared<IsSuccOfPrevGroup>(TII, SG->getSGID()));
2349 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2350
2351 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2352 SchedGroupMask::VMEM_READ, 4, PipelineSyncID, DAG, TII);
2353 SG->addRule(std::make_shared<SharesPredWithPrevNthGroup>(
2354 1, TII, SG->getSGID(), true));
2355 SG->addRule(std::make_shared<VMEMSize>(TII, SG->getSGID()));
2356 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2357
2358 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2359 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2360 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2361
2362 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2363 SchedGroupMask::VMEM_READ, 4, PipelineSyncID, DAG, TII);
2364 SG->addRule(std::make_shared<SharesPredWithPrevNthGroup>(
2365 3, TII, SG->getSGID(), true));
2366 SG->addRule(std::make_shared<VMEMSize>(TII, SG->getSGID()));
2367 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2368
2369 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2370 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2371 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2372 }
2373
2374 // Phase 2b: Loop carried dependency without V_PERM
2375 // Schedule DS_WRITE as closely as possible to the VMEM_READ they depend on.
2376 // Interleave MFMA to keep XDL unit busy throughout.
2377 for (unsigned I = DSWWithPermCount; I < DSWCount; I++) {
2378 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2379 SchedGroupMask::DS_WRITE, 1, PipelineSyncID, DAG, TII);
2380 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2381
2382 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2383 SchedGroupMask::VMEM_READ, 4, PipelineSyncID, DAG, TII);
2384 SG->addRule(std::make_shared<VMEMSize>(TII, SG->getSGID()));
2385 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2386
2387 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2388 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2389 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2390 }
2391
2392 // Phase 2c: Loop carried dependency with V_PERM, VMEM_READs are
2393 // ultimately used by two DS_WRITE
2394 // Schedule VPerm & DS_WRITE as closely as possible to the VMEM_READ they
2395 // depend on. Interleave MFMA to keep XDL unit busy throughout.
2396
2397 for (unsigned I = 0; I < DSWWithSharedVMEMCount; ++I) {
2398 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2399 SchedGroupMask::VALU, 4, PipelineSyncID, DAG, TII);
2400 SG->addRule(std::make_shared<IsPermForDSW>(TII, SG->getSGID(), true));
2401 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2402
2403 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2404 SchedGroupMask::DS_WRITE, 1, PipelineSyncID, DAG, TII);
2405 SG->addRule(std::make_shared<IsSuccOfPrevGroup>(TII, SG->getSGID()));
2406 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2407
2408 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2409 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2410 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2411
2412 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2413 SchedGroupMask::VALU, 4, PipelineSyncID, DAG, TII);
2414 SG->addRule(std::make_shared<IsPermForDSW>(TII, SG->getSGID(), true));
2415 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2416
2417 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2418 SchedGroupMask::DS_WRITE, 1, PipelineSyncID, DAG, TII);
2419 SG->addRule(std::make_shared<IsSuccOfPrevGroup>(TII, SG->getSGID()));
2420 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2421
2422 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2423 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2424 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2425
2426 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2427 SchedGroupMask::VMEM_READ, 4, PipelineSyncID, DAG, TII);
2428 SG->addRule(std::make_shared<SharesPredWithPrevNthGroup>(
2429 2, TII, SG->getSGID(), true));
2430 SG->addRule(std::make_shared<VMEMSize>(TII, SG->getSGID()));
2431 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2432
2433 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2434 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2435 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2436
2437 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2438 SchedGroupMask::VMEM_READ, 4, PipelineSyncID, DAG, TII);
2439 SG->addRule(std::make_shared<SharesPredWithPrevNthGroup>(
2440 4, TII, SG->getSGID(), true));
2441 SG->addRule(std::make_shared<VMEMSize>(TII, SG->getSGID()));
2442 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2443
2444 SG = &SyncedSchedGroups[PipelineSyncID].emplace_back(
2445 SchedGroupMask::MFMA, 1, PipelineSyncID, DAG, TII);
2446 SG->findCandidateSUnits(SyncedInstrs[SG->getSyncID()]);
2447 }
2448
2449 return true;
2450}
2451
2452static std::unique_ptr<IGLPStrategy>
2453createIGLPStrategy(IGLPStrategyID ID, ScheduleDAGInstrs *DAG,
2454 const SIInstrInfo *TII) {
2455 switch (ID) {
2456 case MFMASmallGemmOptID:
2457 return std::make_unique<MFMASmallGemmOpt>(DAG, TII);
2459 return std::make_unique<MFMASmallGemmSingleWaveOpt>(DAG, TII);
2461 return std::make_unique<MFMAExpInterleaveOpt>(DAG, TII);
2463 return std::make_unique<MFMAExpSimpleInterleaveOpt>(DAG, TII);
2464 }
2465
2466 llvm_unreachable("Unknown IGLPStrategyID");
2467}
2468
2469class IGroupLPDAGMutation : public ScheduleDAGMutation {
2470private:
2471 const SIInstrInfo *TII;
2472
2473 ScheduleDAGMI *DAG;
2474
2475 // Organize lists of SchedGroups by their SyncID. SchedGroups /
2476 // SCHED_GROUP_BARRIERs with different SyncIDs will have no edges added
2477 // between then.
2478 DenseMap<int, SmallVector<SchedGroup, 4>> SyncedSchedGroups;
2479
2480 // Used to track instructions that can be mapped to multiple sched groups
2481 DenseMap<int, SUnitsToCandidateSGsMap> SyncedInstrs;
2482
2483 // Add DAG edges that enforce SCHED_BARRIER ordering.
2484 void addSchedBarrierEdges(SUnit &SU);
2485
2486 // Use a SCHED_BARRIER's mask to identify instruction SchedGroups that should
2487 // not be reordered accross the SCHED_BARRIER. This is used for the base
2488 // SCHED_BARRIER, and not SCHED_GROUP_BARRIER. The difference is that
2489 // SCHED_BARRIER will always block all instructions that can be classified
2490 // into a particular SchedClass, whereas SCHED_GROUP_BARRIER has a fixed size
2491 // and may only synchronize with some SchedGroups. Returns the inverse of
2492 // Mask. SCHED_BARRIER's mask describes which instruction types should be
2493 // allowed to be scheduled across it. Invert the mask to get the
2494 // SchedGroupMask of instructions that should be barred.
2495 SchedGroupMask invertSchedBarrierMask(SchedGroupMask Mask) const;
2496
2497 // Create SchedGroups for a SCHED_GROUP_BARRIER.
2498 void initSchedGroupBarrierPipelineStage(
2499 std::vector<SUnit>::reverse_iterator RIter);
2500
2501 bool initIGLPOpt(SUnit &SU);
2502
2503public:
2504 void apply(ScheduleDAGInstrs *DAGInstrs) override;
2505
2506 // The order in which the PipelineSolver should process the candidate
2507 // SchedGroup for a PipelineInstr. BOTTOM_UP will try to add SUs to the last
2508 // created SchedGroup first, and will consider that as the ultimate
2509 // predecessor group when linking. TOP_DOWN instead links and processes the
2510 // first created SchedGroup first.
2511 bool IsBottomUp = true;
2512
2513 // The scheduling phase this application of IGLP corresponds with.
2514 AMDGPU::SchedulingPhase Phase = AMDGPU::SchedulingPhase::Initial;
2515
2516 IGroupLPDAGMutation() = default;
2517 IGroupLPDAGMutation(AMDGPU::SchedulingPhase Phase) : Phase(Phase) {}
2518};
2519
2520unsigned SchedGroup::NumSchedGroups = 0;
2521
2522bool SchedGroup::tryAddEdge(SUnit *A, SUnit *B) {
2523 return A != B && DAG->addEdge(B, SDep(A, SDep::Artificial));
2524}
2525
2526bool SchedGroup::canAddMI(const MachineInstr &MI) const {
2527 bool Result = false;
2528 if (MI.isMetaInstruction())
2529 Result = false;
2530
2531 else if (MI.isInlineAsm()) {
2532 const SIRegisterInfo &TRI = TII->getRegisterInfo();
2533 auto &MRI = MI.getParent()->getParent()->getRegInfo();
2534 bool SGPR_used = false, SGPR_big_def = false, VGPR_used = false,
2535 VMFMA_used = false, VReg32_used = false, MayLoad = MI.mayLoad(),
2536 MayStore = MI.mayStore();
2537 for (const MachineOperand &Operand : MI.operands())
2538 if (Operand.isReg()) {
2539 const TargetRegisterClass &RegClass =
2540 *TRI.getRegClassForOperandReg(MRI, Operand);
2541 if (TRI.hasVGPRs(&RegClass)) {
2542 VGPR_used = true;
2543 if (Operand.isUse() && TRI.getRegSizeInBits(RegClass) == 32)
2544 VReg32_used = true;
2545 }
2546 // > 128 bit registers are usually only used by MFMA instructions, so
2547 // we're using that as a heuristic to guess the schedule group mask of
2548 // the inline asm.
2549 if (TRI.hasAGPRs(&RegClass) || TRI.getRegSizeInBits(RegClass) > 128)
2550 VMFMA_used = true;
2551 if (TRI.hasSGPRs(&RegClass))
2552 SGPR_used = true;
2553 if (TRI.getRegSizeInBits(RegClass) > 64 && Operand.isDef())
2554 SGPR_big_def = true;
2555 }
2556
2557 typedef std::underlying_type_t<SchedGroupMask> SGMask_t;
2558 SGMask_t InlineAsmMask = 0;
2559 if (VGPR_used && !VMFMA_used && !MayLoad && !MayStore)
2560 InlineAsmMask |= (SGMask_t)SchedGroupMask::VALU;
2561 if (SGPR_used && !VGPR_used && !MayLoad && !MayStore)
2562 InlineAsmMask |= (SGMask_t)SchedGroupMask::SALU;
2563 if (VMFMA_used)
2564 InlineAsmMask |= (SGMask_t)SchedGroupMask::MFMA;
2565 if (VGPR_used && MayLoad)
2566 InlineAsmMask |= (SGMask_t)(VReg32_used ? SchedGroupMask::DS_READ
2567 : SchedGroupMask::VMEM_READ);
2568 if (VGPR_used && MayStore)
2569 InlineAsmMask |= (SGMask_t)(VReg32_used ? SchedGroupMask::DS_WRITE
2570 : SchedGroupMask::VMEM_WRITE);
2571 if (SGPR_big_def)
2572 InlineAsmMask |= (SGMask_t)SchedGroupMask::DS_READ;
2573 if (InlineAsmMask & (SGMask_t)SchedGroupMask::VALU ||
2574 InlineAsmMask & (SGMask_t)SchedGroupMask::SALU)
2575 InlineAsmMask |= (SGMask_t)SchedGroupMask::ALU;
2576 if (InlineAsmMask & (SGMask_t)SchedGroupMask::DS_READ ||
2577 InlineAsmMask & (SGMask_t)SchedGroupMask::DS_WRITE)
2578 InlineAsmMask |= (SGMask_t)SchedGroupMask::DS;
2579 if (InlineAsmMask & (SGMask_t)SchedGroupMask::VMEM_READ ||
2580 InlineAsmMask & (SGMask_t)SchedGroupMask::VMEM_WRITE)
2581 InlineAsmMask |= (SGMask_t)SchedGroupMask::VMEM;
2582
2583 Result = ((SGMask_t)SGMask & InlineAsmMask) != 0;
2584 }
2585
2586 else if (((SGMask & SchedGroupMask::ALU) != SchedGroupMask::NONE) &&
2587 (TII->isVALU(MI, /*AllowLDSDMA=*/true) || TII->isMFMAorWMMA(MI) ||
2588 TII->isSALU(MI) || TII->isTRANS(MI)))
2589 Result = !MI.mayLoadOrStore();
2590
2591 else if (((SGMask & SchedGroupMask::VALU) != SchedGroupMask::NONE) &&
2592 TII->isVALU(MI, /*AllowLDSDMA=*/true) && !TII->isMFMAorWMMA(MI) &&
2593 !TII->isTRANS(MI) && !TII->isLDSDMA(MI)) {
2594 // Some memory instructions may be marked as VALU (e.g. BUFFER_LOAD_*_LDS).
2595 // For our purposes, these shall not be classified as VALU as this results
2596 // in unexpected behavior.
2597 Result = !MI.mayLoadOrStore();
2598 }
2599
2600 else if (((SGMask & SchedGroupMask::SALU) != SchedGroupMask::NONE) &&
2601 TII->isSALU(MI))
2602 Result = !MI.mayLoadOrStore();
2603
2604 else if (((SGMask & SchedGroupMask::MFMA) != SchedGroupMask::NONE) &&
2605 TII->isMFMAorWMMA(MI))
2606 Result = true;
2607
2608 else if (((SGMask & SchedGroupMask::VMEM) != SchedGroupMask::NONE) &&
2609 (TII->isVMEM(MI) || TII->isLDSDMA(MI)))
2610 Result = true;
2611
2612 else if (((SGMask & SchedGroupMask::VMEM_READ) != SchedGroupMask::NONE) &&
2613 MI.mayLoad() && TII->isVMEM(MI) && !TII->isLDSDMA(MI))
2614 Result = true;
2615
2616 else if (((SGMask & SchedGroupMask::VMEM_WRITE) != SchedGroupMask::NONE) &&
2617 MI.mayStore() && TII->isVMEM(MI) && !TII->isLDSDMA(MI))
2618 Result = true;
2619
2620 else if (((SGMask & SchedGroupMask::DS) != SchedGroupMask::NONE) &&
2621 (TII->isDS(MI) || TII->isLDSDMA(MI)))
2622 Result = true;
2623
2624 else if (((SGMask & SchedGroupMask::DS_READ) != SchedGroupMask::NONE) &&
2625 MI.mayLoad() && TII->isDS(MI))
2626 Result = true;
2627
2628 else if (((SGMask & SchedGroupMask::DS_WRITE) != SchedGroupMask::NONE) &&
2629 MI.mayStore() && TII->isDS(MI))
2630 Result = true;
2631
2632 else if (((SGMask & SchedGroupMask::TRANS) != SchedGroupMask::NONE) &&
2633 TII->isTRANS(MI))
2634 Result = true;
2635
2636 else if (((SGMask & SchedGroupMask::LDSDMA) != SchedGroupMask::NONE) &&
2637 TII->isLDSDMA(MI))
2638 Result = true;
2639
2640 LLVM_DEBUG(
2641 dbgs() << "For SchedGroup with mask " << format_hex((int)SGMask, 10, true)
2642 << (Result ? " could classify " : " unable to classify ") << MI);
2643
2644 return Result;
2645}
2646
2647int SchedGroup::link(SUnit &SU, bool MakePred,
2648 std::list<std::pair<SUnit *, SUnit *>> &AddedEdges) {
2649 int MissedEdges = 0;
2650 for (auto *A : Collection) {
2651 SUnit *B = &SU;
2652 if (A == B || A->getInstr()->getOpcode() == AMDGPU::SCHED_GROUP_BARRIER)
2653 continue;
2654 if (MakePred)
2655 std::swap(A, B);
2656
2657 if (DAG->IsReachable(B, A))
2658 continue;
2659
2660 // tryAddEdge returns false if there is a dependency that makes adding
2661 // the A->B edge impossible, otherwise it returns true;
2662 bool Added = tryAddEdge(A, B);
2663 if (Added)
2664 AddedEdges.emplace_back(A, B);
2665 else
2666 ++MissedEdges;
2667 }
2668
2669 return MissedEdges;
2670}
2671
2672void SchedGroup::link(SUnit &SU, bool MakePred) {
2673 for (auto *A : Collection) {
2674 SUnit *B = &SU;
2675 if (A->getInstr()->getOpcode() == AMDGPU::SCHED_GROUP_BARRIER)
2676 continue;
2677 if (MakePred)
2678 std::swap(A, B);
2679
2680 tryAddEdge(A, B);
2681 }
2682}
2683
2684void SchedGroup::link(SUnit &SU,
2685 function_ref<bool(const SUnit *A, const SUnit *B)> P) {
2686 for (auto *A : Collection) {
2687 SUnit *B = &SU;
2688 if (P(A, B))
2689 std::swap(A, B);
2690
2691 tryAddEdge(A, B);
2692 }
2693}
2694
2695void SchedGroup::link(SchedGroup &OtherGroup) {
2696 for (auto *B : OtherGroup.Collection)
2697 link(*B);
2698}
2699
2700bool SchedGroup::canAddSU(SUnit &SU) const {
2701 MachineInstr &MI = *SU.getInstr();
2702 if (MI.getOpcode() != TargetOpcode::BUNDLE)
2703 return canAddMI(MI);
2704
2705 // Special case for bundled MIs.
2706 const MachineBasicBlock *MBB = MI.getParent();
2707 MachineBasicBlock::instr_iterator B = MI.getIterator(), E = ++B;
2708 while (E != MBB->end() && E->isBundledWithPred())
2709 ++E;
2710
2711 // Return true if all of the bundled MIs can be added to this group.
2712 return std::all_of(B, E, [this](MachineInstr &MI) { return canAddMI(MI); });
2713}
2714
2715template <class T>
2716void SchedGroup::findCandidateSUnits(T Begin, T End,
2717 SUnitsToCandidateSGsMap &SyncedInstrs) {
2718 for (SUnit &SU : make_range(Begin, End)) {
2719 if (canAddSU(SU))
2720 SyncedInstrs[&SU].push_back(SGID);
2721 }
2722}
2723
2724void SchedGroup::findCandidateSUnits(SUnitsToCandidateSGsMap &SyncedInstrs) {
2725 findCandidateSUnits(DAG->SUnits.rbegin(), DAG->SUnits.rend(), SyncedInstrs);
2726}
2727
2728void IGroupLPDAGMutation::apply(ScheduleDAGInstrs *DAGInstrs) {
2729 const TargetSchedModel *TSchedModel = DAGInstrs->getSchedModel();
2730 if (!TSchedModel || DAGInstrs->SUnits.empty())
2731 return;
2732
2733 LLVM_DEBUG(dbgs() << "Applying IGroupLPDAGMutation...\n");
2734 const GCNSubtarget &ST = DAGInstrs->MF.getSubtarget<GCNSubtarget>();
2735 TII = ST.getInstrInfo();
2736 DAG = static_cast<ScheduleDAGMI *>(DAGInstrs);
2737 SyncedSchedGroups.clear();
2738 SyncedInstrs.clear();
2739 bool FoundSB = false;
2740 bool FoundIGLP = false;
2741 bool ShouldApplyIGLP = false;
2742 for (auto R = DAG->SUnits.rbegin(), E = DAG->SUnits.rend(); R != E; ++R) {
2743 unsigned Opc = R->getInstr()->getOpcode();
2744 // SCHED_[GROUP_]BARRIER and IGLP are mutually exclusive.
2745 if (Opc == AMDGPU::SCHED_BARRIER) {
2746 addSchedBarrierEdges(*R);
2747 FoundSB = true;
2748 } else if (Opc == AMDGPU::SCHED_GROUP_BARRIER) {
2749 initSchedGroupBarrierPipelineStage(R);
2750 FoundSB = true;
2751 } else if (Opc == AMDGPU::IGLP_OPT) {
2752 if (!FoundSB && !FoundIGLP) {
2753 FoundIGLP = true;
2754 ShouldApplyIGLP = initIGLPOpt(*R);
2755 }
2756 }
2757 }
2758
2759 if (FoundSB || (FoundIGLP && ShouldApplyIGLP)) {
2760 PipelineSolver PS(SyncedSchedGroups, SyncedInstrs, DAG, IsBottomUp);
2761 // PipelineSolver performs the mutation by adding the edges it
2762 // determined as the best
2763 PS.solve();
2764 return;
2765 }
2766}
2767
2768void IGroupLPDAGMutation::addSchedBarrierEdges(SUnit &SchedBarrier) {
2769 MachineInstr &MI = *SchedBarrier.getInstr();
2770 assert(MI.getOpcode() == AMDGPU::SCHED_BARRIER);
2771 LLVM_DEBUG(dbgs() << "Building SchedGroup for SchedBarrier with Mask: "
2772 << MI.getOperand(0).getImm() << "\n");
2773 auto InvertedMask =
2774 invertSchedBarrierMask((SchedGroupMask)MI.getOperand(0).getImm());
2775 SchedGroup SG(InvertedMask, std::nullopt, DAG, TII);
2776
2777 for (SUnit &SU : DAG->SUnits)
2778 if (SG.canAddSU(SU))
2779 SG.add(SU);
2780
2781 // Preserve original instruction ordering relative to the SCHED_BARRIER.
2782 SG.link(
2783 SchedBarrier,
2784 (function_ref<bool(const SUnit *A, const SUnit *B)>)[](
2785 const SUnit *A, const SUnit *B) { return A->NodeNum > B->NodeNum; });
2786}
2787
2788SchedGroupMask
2789IGroupLPDAGMutation::invertSchedBarrierMask(SchedGroupMask Mask) const {
2790 // Invert mask and erase bits for types of instructions that are implied to be
2791 // allowed past the SCHED_BARRIER.
2792 SchedGroupMask InvertedMask = ~Mask;
2793
2794 static constexpr std::pair<SchedGroupMask, SchedGroupMask> ImpliedGroups[] = {
2795 {SchedGroupMask::ALU, SchedGroupMask::VALU | SchedGroupMask::SALU |
2796 SchedGroupMask::MFMA | SchedGroupMask::TRANS},
2797 {SchedGroupMask::VMEM, SchedGroupMask::VMEM_READ |
2798 SchedGroupMask::VMEM_WRITE |
2799 SchedGroupMask::LDSDMA},
2800 {SchedGroupMask::DS, SchedGroupMask::DS_READ | SchedGroupMask::DS_WRITE |
2801 SchedGroupMask::LDSDMA},
2802 };
2803
2804 for (auto [Aggregate, Members] : ImpliedGroups) {
2805 // Aggregate allowed past the barrier implies all its members are too.
2806 if ((InvertedMask & Aggregate) == SchedGroupMask::NONE)
2807 InvertedMask &= ~Members;
2808 // Any member allowed past the barrier implies the aggregate is too.
2809 else if ((InvertedMask & Members) != Members)
2810 InvertedMask &= ~Aggregate;
2811 }
2812
2813 LLVM_DEBUG(dbgs() << "After Inverting, SchedGroup Mask: " << (int)InvertedMask
2814 << "\n");
2815
2816 return InvertedMask;
2817}
2818
2819void IGroupLPDAGMutation::initSchedGroupBarrierPipelineStage(
2820 std::vector<SUnit>::reverse_iterator RIter) {
2821 MachineInstr &SGB = *RIter->getInstr();
2822 assert(SGB.getOpcode() == AMDGPU::SCHED_GROUP_BARRIER);
2823 int32_t SGMask = SGB.getOperand(0).getImm();
2824 int32_t Size = SGB.getOperand(1).getImm();
2825 int32_t SyncID = SGB.getOperand(2).getImm();
2826
2827 Size++; // Make room for the SCHED_GROUP_BARRIER instruction
2828 auto &SG = SyncedSchedGroups[SyncID].emplace_back((SchedGroupMask)SGMask,
2829 Size, SyncID, DAG, TII);
2830 SG.add(*RIter);
2831 SG.findCandidateSUnits(RIter, SG.DAG->SUnits.rend(),
2832 SyncedInstrs[SG.getSyncID()]);
2833}
2834
2835bool IGroupLPDAGMutation::initIGLPOpt(SUnit &SU) {
2836 IGLPStrategyID StrategyID =
2838 auto S = createIGLPStrategy(StrategyID, DAG, TII);
2839 if (!S->shouldApplyStrategy(DAG, Phase))
2840 return false;
2841
2842 IsBottomUp = S->IsBottomUp;
2843 return S->applyIGLPStrategy(SyncedInstrs, SyncedSchedGroups, Phase);
2844}
2845
2846} // namespace
2847
2848/// \p Phase specifes whether or not this is a reentry into the
2849/// IGroupLPDAGMutation. Since there may be multiple scheduling passes on the
2850/// same scheduling region (e.g. pre and post-RA scheduling / multiple
2851/// scheduling "phases"), we can reenter this mutation framework more than once
2852/// for a given region.
2853std::unique_ptr<ScheduleDAGMutation>
2855 return std::make_unique<IGroupLPDAGMutation>(Phase);
2856}
aarch64 falkor hwpf fix Falkor HW Prefetch Fix Late Phase
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
AMDGPU Rewrite AGPR Copy MFMA
MachineBasicBlock & MBB
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
static std::pair< Value *, APInt > getMask(Value *WideMask, unsigned Factor, ElementCount LeafValueEC)
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
#define T
#define P(N)
Interface definition for SIInstrInfo.
#define LLVM_DEBUG(...)
Definition Debug.h:119
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
std::pair< iterator, bool > try_emplace(KeyT &&Key, Ts &&...Args)
Definition DenseMap.h:299
unsigned size() const
Definition DenseMap.h:172
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
const HexagonRegisterInfo & getRegisterInfo() const
Instructions::iterator instr_iterator
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool mayStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly modify memory.
const MachineOperand & getOperand(unsigned i) const
int64_t getImm() const
Scheduling dependency.
Definition ScheduleDAG.h:52
SUnit * getSUnit() const
@ Data
Regular data dependence (aka true-dependence).
Definition ScheduleDAG.h:56
@ Artificial
Arbitrary strong DAG edge (no real dependence).
Definition ScheduleDAG.h:75
Scheduling unit. This is a node in the scheduling DAG.
unsigned NodeNum
Entry # of node in the node vector.
LLVM_ABI void removePred(const SDep &D)
Removes the specified edge as a pred of the current node if it exists.
SmallVector< SDep, 4 > Succs
All sunit successors.
SmallVector< SDep, 4 > Preds
All sunit predecessors.
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
A ScheduleDAG for scheduling lists of MachineInstr.
const TargetSchedModel * getSchedModel() const
Gets the machine model for instruction scheduling.
bool addEdge(SUnit *SuccSU, const SDep &PredDep)
Add a DAG edge to the given SU with the given predecessor dependence data.
bool IsReachable(SUnit *SU, SUnit *TargetSU)
IsReachable - Checks if SU is reachable from TargetSU.
void dump() const override
ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply schedules machine instructions ac...
std::vector< SUnit > SUnits
The scheduling units.
MachineFunction & MF
Machine function.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition DenseSet.h:182
An efficient, type-erasing, non-owning reference to a callable.
A range adaptor for a pair of iterators.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
IGLPStrategyID
Operand 0 immediate for IGLP_OPT pseudo instructions.
@ MFMASmallGemmSingleWaveOptID
void apply(Opt *O, const Mod &M, const Mods &... Ms)
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
std::unique_ptr< ScheduleDAGMutation > createIGroupLPDAGMutation(AMDGPU::SchedulingPhase Phase)
Phase specifes whether or not this is a reentry into the IGroupLPDAGMutation.
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1746
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1753
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
FormattedNumber format_hex(uint64_t N, unsigned Width, bool Upper=false)
format_hex - Output N as a fixed width hexadecimal.
Definition Format.h:164
DWARFExpression::Operation Op
auto count_if(R &&Range, UnaryPredicate P)
Wrapper function around std::count_if to count the number of times an element satisfying a given pred...
Definition STLExtras.h:2019
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1772
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:880
Function object to check whether the second component of a container supported by std::get (like std:...
Definition STLExtras.h:1448