xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AMDGPU/GCNMinRegStrategy.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===- GCNMinRegStrategy.cpp ----------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric ///
95ffd83dbSDimitry Andric /// \file
10349cc55cSDimitry Andric /// This file defines and implements the class GCNMinRegScheduler, which
115ffd83dbSDimitry Andric /// implements an experimental, simple scheduler whose main goal is to learn
125ffd83dbSDimitry Andric /// ways about consuming less possible registers for a region.
135ffd83dbSDimitry Andric ///
145ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #include "llvm/CodeGen/ScheduleDAG.h"
170b57cec5SDimitry Andric using namespace llvm;
180b57cec5SDimitry Andric 
190b57cec5SDimitry Andric #define DEBUG_TYPE "machine-scheduler"
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric namespace {
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric class GCNMinRegScheduler {
240b57cec5SDimitry Andric   struct Candidate : ilist_node<Candidate> {
250b57cec5SDimitry Andric     const SUnit *SU;
260b57cec5SDimitry Andric     int Priority;
270b57cec5SDimitry Andric 
Candidate__anon11f939720111::GCNMinRegScheduler::Candidate280b57cec5SDimitry Andric     Candidate(const SUnit *SU_, int Priority_ = 0)
290b57cec5SDimitry Andric       : SU(SU_), Priority(Priority_) {}
300b57cec5SDimitry Andric   };
310b57cec5SDimitry Andric 
320b57cec5SDimitry Andric   SpecificBumpPtrAllocator<Candidate> Alloc;
330b57cec5SDimitry Andric   using Queue = simple_ilist<Candidate>;
340b57cec5SDimitry Andric   Queue RQ; // Ready queue
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   std::vector<unsigned> NumPreds;
370b57cec5SDimitry Andric 
isScheduled(const SUnit * SU) const380b57cec5SDimitry Andric   bool isScheduled(const SUnit *SU) const {
390b57cec5SDimitry Andric     assert(!SU->isBoundaryNode());
400b57cec5SDimitry Andric     return NumPreds[SU->NodeNum] == std::numeric_limits<unsigned>::max();
410b57cec5SDimitry Andric   }
420b57cec5SDimitry Andric 
setIsScheduled(const SUnit * SU)430b57cec5SDimitry Andric   void setIsScheduled(const SUnit *SU)  {
440b57cec5SDimitry Andric     assert(!SU->isBoundaryNode());
450b57cec5SDimitry Andric     NumPreds[SU->NodeNum] = std::numeric_limits<unsigned>::max();
460b57cec5SDimitry Andric   }
470b57cec5SDimitry Andric 
getNumPreds(const SUnit * SU) const480b57cec5SDimitry Andric   unsigned getNumPreds(const SUnit *SU) const {
490b57cec5SDimitry Andric     assert(!SU->isBoundaryNode());
500b57cec5SDimitry Andric     assert(NumPreds[SU->NodeNum] != std::numeric_limits<unsigned>::max());
510b57cec5SDimitry Andric     return NumPreds[SU->NodeNum];
520b57cec5SDimitry Andric   }
530b57cec5SDimitry Andric 
decNumPreds(const SUnit * SU)540b57cec5SDimitry Andric   unsigned decNumPreds(const SUnit *SU) {
550b57cec5SDimitry Andric     assert(!SU->isBoundaryNode());
560b57cec5SDimitry Andric     assert(NumPreds[SU->NodeNum] != std::numeric_limits<unsigned>::max());
570b57cec5SDimitry Andric     return --NumPreds[SU->NodeNum];
580b57cec5SDimitry Andric   }
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   void initNumPreds(const decltype(ScheduleDAG::SUnits) &SUnits);
610b57cec5SDimitry Andric 
620b57cec5SDimitry Andric   int getReadySuccessors(const SUnit *SU) const;
630b57cec5SDimitry Andric   int getNotReadySuccessors(const SUnit *SU) const;
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric   template <typename Calc>
660b57cec5SDimitry Andric   unsigned findMax(unsigned Num, Calc C);
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric   Candidate* pickCandidate();
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   void bumpPredsPriority(const SUnit *SchedSU, int Priority);
710b57cec5SDimitry Andric   void releaseSuccessors(const SUnit* SU, int Priority);
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric public:
740b57cec5SDimitry Andric   std::vector<const SUnit*> schedule(ArrayRef<const SUnit*> TopRoots,
750b57cec5SDimitry Andric                                      const ScheduleDAG &DAG);
760b57cec5SDimitry Andric };
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric } // end anonymous namespace
790b57cec5SDimitry Andric 
initNumPreds(const decltype(ScheduleDAG::SUnits) & SUnits)800b57cec5SDimitry Andric void GCNMinRegScheduler::initNumPreds(const decltype(ScheduleDAG::SUnits) &SUnits) {
810b57cec5SDimitry Andric   NumPreds.resize(SUnits.size());
820b57cec5SDimitry Andric   for (unsigned I = 0; I < SUnits.size(); ++I)
830b57cec5SDimitry Andric     NumPreds[I] = SUnits[I].NumPredsLeft;
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric 
getReadySuccessors(const SUnit * SU) const860b57cec5SDimitry Andric int GCNMinRegScheduler::getReadySuccessors(const SUnit *SU) const {
870b57cec5SDimitry Andric   unsigned NumSchedSuccs = 0;
880b57cec5SDimitry Andric   for (auto SDep : SU->Succs) {
890b57cec5SDimitry Andric     bool wouldBeScheduled = true;
900b57cec5SDimitry Andric     for (auto PDep : SDep.getSUnit()->Preds) {
910b57cec5SDimitry Andric       auto PSU = PDep.getSUnit();
920b57cec5SDimitry Andric       assert(!PSU->isBoundaryNode());
930b57cec5SDimitry Andric       if (PSU != SU && !isScheduled(PSU)) {
940b57cec5SDimitry Andric         wouldBeScheduled = false;
950b57cec5SDimitry Andric         break;
960b57cec5SDimitry Andric       }
970b57cec5SDimitry Andric     }
980b57cec5SDimitry Andric     NumSchedSuccs += wouldBeScheduled ? 1 : 0;
990b57cec5SDimitry Andric   }
1000b57cec5SDimitry Andric   return NumSchedSuccs;
1010b57cec5SDimitry Andric }
1020b57cec5SDimitry Andric 
getNotReadySuccessors(const SUnit * SU) const1030b57cec5SDimitry Andric int GCNMinRegScheduler::getNotReadySuccessors(const SUnit *SU) const {
1040b57cec5SDimitry Andric   return SU->Succs.size() - getReadySuccessors(SU);
1050b57cec5SDimitry Andric }
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric template <typename Calc>
findMax(unsigned Num,Calc C)1080b57cec5SDimitry Andric unsigned GCNMinRegScheduler::findMax(unsigned Num, Calc C) {
1090b57cec5SDimitry Andric   assert(!RQ.empty() && Num <= RQ.size());
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   using T = decltype(C(*RQ.begin())) ;
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   T Max = std::numeric_limits<T>::min();
1140b57cec5SDimitry Andric   unsigned NumMax = 0;
1150b57cec5SDimitry Andric   for (auto I = RQ.begin(); Num; --Num) {
1160b57cec5SDimitry Andric     T Cur = C(*I);
1170b57cec5SDimitry Andric     if (Cur >= Max) {
1180b57cec5SDimitry Andric       if (Cur > Max) {
1190b57cec5SDimitry Andric         Max = Cur;
1200b57cec5SDimitry Andric         NumMax = 1;
1210b57cec5SDimitry Andric       } else
1220b57cec5SDimitry Andric         ++NumMax;
1230b57cec5SDimitry Andric       auto &Cand = *I++;
1240b57cec5SDimitry Andric       RQ.remove(Cand);
1250b57cec5SDimitry Andric       RQ.push_front(Cand);
1260b57cec5SDimitry Andric       continue;
1270b57cec5SDimitry Andric     }
1280b57cec5SDimitry Andric     ++I;
1290b57cec5SDimitry Andric   }
1300b57cec5SDimitry Andric   return NumMax;
1310b57cec5SDimitry Andric }
1320b57cec5SDimitry Andric 
pickCandidate()1330b57cec5SDimitry Andric GCNMinRegScheduler::Candidate* GCNMinRegScheduler::pickCandidate() {
1340b57cec5SDimitry Andric   do {
1350b57cec5SDimitry Andric     unsigned Num = RQ.size();
1360b57cec5SDimitry Andric     if (Num == 1) break;
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\nSelecting max priority candidates among " << Num
1390b57cec5SDimitry Andric                       << '\n');
1400b57cec5SDimitry Andric     Num = findMax(Num, [=](const Candidate &C) { return C.Priority; });
1410b57cec5SDimitry Andric     if (Num == 1) break;
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\nSelecting min non-ready producing candidate among "
1440b57cec5SDimitry Andric                       << Num << '\n');
1450b57cec5SDimitry Andric     Num = findMax(Num, [=](const Candidate &C) {
1460b57cec5SDimitry Andric       auto SU = C.SU;
1470b57cec5SDimitry Andric       int Res = getNotReadySuccessors(SU);
1480b57cec5SDimitry Andric       LLVM_DEBUG(dbgs() << "SU(" << SU->NodeNum << ") would left non-ready "
1490b57cec5SDimitry Andric                         << Res << " successors, metric = " << -Res << '\n');
1500b57cec5SDimitry Andric       return -Res;
1510b57cec5SDimitry Andric     });
1520b57cec5SDimitry Andric     if (Num == 1) break;
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\nSelecting most producing candidate among " << Num
1550b57cec5SDimitry Andric                       << '\n');
1560b57cec5SDimitry Andric     Num = findMax(Num, [=](const Candidate &C) {
1570b57cec5SDimitry Andric       auto SU = C.SU;
1580b57cec5SDimitry Andric       auto Res = getReadySuccessors(SU);
1590b57cec5SDimitry Andric       LLVM_DEBUG(dbgs() << "SU(" << SU->NodeNum << ") would make ready " << Res
1600b57cec5SDimitry Andric                         << " successors, metric = " << Res << '\n');
1610b57cec5SDimitry Andric       return Res;
1620b57cec5SDimitry Andric     });
1630b57cec5SDimitry Andric     if (Num == 1) break;
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric     Num = Num ? Num : RQ.size();
1660b57cec5SDimitry Andric     LLVM_DEBUG(
1670b57cec5SDimitry Andric         dbgs()
1680b57cec5SDimitry Andric         << "\nCan't find best candidate, selecting in program order among "
1690b57cec5SDimitry Andric         << Num << '\n');
1700b57cec5SDimitry Andric     Num = findMax(Num, [=](const Candidate &C) { return -(int64_t)C.SU->NodeNum; });
1710b57cec5SDimitry Andric     assert(Num == 1);
1720b57cec5SDimitry Andric   } while (false);
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   return &RQ.front();
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric 
bumpPredsPriority(const SUnit * SchedSU,int Priority)1770b57cec5SDimitry Andric void GCNMinRegScheduler::bumpPredsPriority(const SUnit *SchedSU, int Priority) {
1780b57cec5SDimitry Andric   SmallPtrSet<const SUnit*, 32> Set;
1790b57cec5SDimitry Andric   for (const auto &S : SchedSU->Succs) {
1800b57cec5SDimitry Andric     if (S.getSUnit()->isBoundaryNode() || isScheduled(S.getSUnit()) ||
1810b57cec5SDimitry Andric         S.getKind() != SDep::Data)
1820b57cec5SDimitry Andric       continue;
1830b57cec5SDimitry Andric     for (const auto &P : S.getSUnit()->Preds) {
1840b57cec5SDimitry Andric       auto PSU = P.getSUnit();
1850b57cec5SDimitry Andric       assert(!PSU->isBoundaryNode());
1860b57cec5SDimitry Andric       if (PSU != SchedSU && !isScheduled(PSU)) {
1870b57cec5SDimitry Andric         Set.insert(PSU);
1880b57cec5SDimitry Andric       }
1890b57cec5SDimitry Andric     }
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric   SmallVector<const SUnit*, 32> Worklist(Set.begin(), Set.end());
1920b57cec5SDimitry Andric   while (!Worklist.empty()) {
1930b57cec5SDimitry Andric     auto SU = Worklist.pop_back_val();
1940b57cec5SDimitry Andric     assert(!SU->isBoundaryNode());
1950b57cec5SDimitry Andric     for (const auto &P : SU->Preds) {
1960b57cec5SDimitry Andric       if (!P.getSUnit()->isBoundaryNode() && !isScheduled(P.getSUnit()) &&
1970b57cec5SDimitry Andric           Set.insert(P.getSUnit()).second)
1980b57cec5SDimitry Andric         Worklist.push_back(P.getSUnit());
1990b57cec5SDimitry Andric     }
2000b57cec5SDimitry Andric   }
2010b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << "Make the predecessors of SU(" << SchedSU->NodeNum
2020b57cec5SDimitry Andric                     << ")'s non-ready successors of " << Priority
2030b57cec5SDimitry Andric                     << " priority in ready queue: ");
2040b57cec5SDimitry Andric   for (auto &C : RQ) {
2055ffd83dbSDimitry Andric     if (Set.count(C.SU)) {
2060b57cec5SDimitry Andric       C.Priority = Priority;
2070b57cec5SDimitry Andric       LLVM_DEBUG(dbgs() << " SU(" << C.SU->NodeNum << ')');
2080b57cec5SDimitry Andric     }
2090b57cec5SDimitry Andric   }
2100b57cec5SDimitry Andric   LLVM_DEBUG(dbgs() << '\n');
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric 
releaseSuccessors(const SUnit * SU,int Priority)2130b57cec5SDimitry Andric void GCNMinRegScheduler::releaseSuccessors(const SUnit* SU, int Priority) {
2140b57cec5SDimitry Andric   for (const auto &S : SU->Succs) {
2150b57cec5SDimitry Andric     auto SuccSU = S.getSUnit();
2160b57cec5SDimitry Andric     if (S.isWeak())
2170b57cec5SDimitry Andric       continue;
2180b57cec5SDimitry Andric     assert(SuccSU->isBoundaryNode() || getNumPreds(SuccSU) > 0);
2190b57cec5SDimitry Andric     if (!SuccSU->isBoundaryNode() && decNumPreds(SuccSU) == 0)
2200b57cec5SDimitry Andric       RQ.push_front(*new (Alloc.Allocate()) Candidate(SuccSU, Priority));
2210b57cec5SDimitry Andric   }
2220b57cec5SDimitry Andric }
2230b57cec5SDimitry Andric 
2240b57cec5SDimitry Andric std::vector<const SUnit*>
schedule(ArrayRef<const SUnit * > TopRoots,const ScheduleDAG & DAG)2250b57cec5SDimitry Andric GCNMinRegScheduler::schedule(ArrayRef<const SUnit*> TopRoots,
2260b57cec5SDimitry Andric                              const ScheduleDAG &DAG) {
2270b57cec5SDimitry Andric   const auto &SUnits = DAG.SUnits;
2280b57cec5SDimitry Andric   std::vector<const SUnit*> Schedule;
2290b57cec5SDimitry Andric   Schedule.reserve(SUnits.size());
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   initNumPreds(SUnits);
2320b57cec5SDimitry Andric 
2330b57cec5SDimitry Andric   int StepNo = 0;
2340b57cec5SDimitry Andric 
235*bdd1243dSDimitry Andric   for (const auto *SU : TopRoots) {
2360b57cec5SDimitry Andric     RQ.push_back(*new (Alloc.Allocate()) Candidate(SU, StepNo));
2370b57cec5SDimitry Andric   }
2380b57cec5SDimitry Andric   releaseSuccessors(&DAG.EntrySU, StepNo);
2390b57cec5SDimitry Andric 
2400b57cec5SDimitry Andric   while (!RQ.empty()) {
2410b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "\n=== Picking candidate, Step = " << StepNo
2420b57cec5SDimitry Andric                       << "\n"
2430b57cec5SDimitry Andric                          "Ready queue:";
2440b57cec5SDimitry Andric                for (auto &C
2450b57cec5SDimitry Andric                     : RQ) dbgs()
2460b57cec5SDimitry Andric                << ' ' << C.SU->NodeNum << "(P" << C.Priority << ')';
2470b57cec5SDimitry Andric                dbgs() << '\n';);
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric     auto C = pickCandidate();
2500b57cec5SDimitry Andric     assert(C);
2510b57cec5SDimitry Andric     RQ.remove(*C);
2520b57cec5SDimitry Andric     auto SU = C->SU;
2530b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Selected "; DAG.dumpNode(*SU));
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric     releaseSuccessors(SU, StepNo);
2560b57cec5SDimitry Andric     Schedule.push_back(SU);
2570b57cec5SDimitry Andric     setIsScheduled(SU);
2580b57cec5SDimitry Andric 
2590b57cec5SDimitry Andric     if (getReadySuccessors(SU) == 0)
2600b57cec5SDimitry Andric       bumpPredsPriority(SU, StepNo);
2610b57cec5SDimitry Andric 
2620b57cec5SDimitry Andric     ++StepNo;
2630b57cec5SDimitry Andric   }
2640b57cec5SDimitry Andric   assert(SUnits.size() == Schedule.size());
2650b57cec5SDimitry Andric 
2660b57cec5SDimitry Andric   return Schedule;
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric namespace llvm {
2700b57cec5SDimitry Andric 
makeMinRegSchedule(ArrayRef<const SUnit * > TopRoots,const ScheduleDAG & DAG)2710b57cec5SDimitry Andric std::vector<const SUnit*> makeMinRegSchedule(ArrayRef<const SUnit*> TopRoots,
2720b57cec5SDimitry Andric                                              const ScheduleDAG &DAG) {
2730b57cec5SDimitry Andric   GCNMinRegScheduler S;
2740b57cec5SDimitry Andric   return S.schedule(TopRoots, DAG);
2750b57cec5SDimitry Andric }
2760b57cec5SDimitry Andric 
2770b57cec5SDimitry Andric } // end namespace llvm
278