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