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