//===- GCNMinRegStrategy.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file defines and imlements the class GCNMinRegScheduler, which /// implements an experimental, simple scheduler whose main goal is to learn /// ways about consuming less possible registers for a region. /// //===----------------------------------------------------------------------===// #include "llvm/CodeGen/ScheduleDAG.h" using namespace llvm; #define DEBUG_TYPE "machine-scheduler" namespace { class GCNMinRegScheduler { struct Candidate : ilist_node { const SUnit *SU; int Priority; Candidate(const SUnit *SU_, int Priority_ = 0) : SU(SU_), Priority(Priority_) {} }; SpecificBumpPtrAllocator Alloc; using Queue = simple_ilist; Queue RQ; // Ready queue std::vector NumPreds; bool isScheduled(const SUnit *SU) const { assert(!SU->isBoundaryNode()); return NumPreds[SU->NodeNum] == std::numeric_limits::max(); } void setIsScheduled(const SUnit *SU) { assert(!SU->isBoundaryNode()); NumPreds[SU->NodeNum] = std::numeric_limits::max(); } unsigned getNumPreds(const SUnit *SU) const { assert(!SU->isBoundaryNode()); assert(NumPreds[SU->NodeNum] != std::numeric_limits::max()); return NumPreds[SU->NodeNum]; } unsigned decNumPreds(const SUnit *SU) { assert(!SU->isBoundaryNode()); assert(NumPreds[SU->NodeNum] != std::numeric_limits::max()); return --NumPreds[SU->NodeNum]; } void initNumPreds(const decltype(ScheduleDAG::SUnits) &SUnits); int getReadySuccessors(const SUnit *SU) const; int getNotReadySuccessors(const SUnit *SU) const; template unsigned findMax(unsigned Num, Calc C); Candidate* pickCandidate(); void bumpPredsPriority(const SUnit *SchedSU, int Priority); void releaseSuccessors(const SUnit* SU, int Priority); public: std::vector schedule(ArrayRef TopRoots, const ScheduleDAG &DAG); }; } // end anonymous namespace void GCNMinRegScheduler::initNumPreds(const decltype(ScheduleDAG::SUnits) &SUnits) { NumPreds.resize(SUnits.size()); for (unsigned I = 0; I < SUnits.size(); ++I) NumPreds[I] = SUnits[I].NumPredsLeft; } int GCNMinRegScheduler::getReadySuccessors(const SUnit *SU) const { unsigned NumSchedSuccs = 0; for (auto SDep : SU->Succs) { bool wouldBeScheduled = true; for (auto PDep : SDep.getSUnit()->Preds) { auto PSU = PDep.getSUnit(); assert(!PSU->isBoundaryNode()); if (PSU != SU && !isScheduled(PSU)) { wouldBeScheduled = false; break; } } NumSchedSuccs += wouldBeScheduled ? 1 : 0; } return NumSchedSuccs; } int GCNMinRegScheduler::getNotReadySuccessors(const SUnit *SU) const { return SU->Succs.size() - getReadySuccessors(SU); } template unsigned GCNMinRegScheduler::findMax(unsigned Num, Calc C) { assert(!RQ.empty() && Num <= RQ.size()); using T = decltype(C(*RQ.begin())) ; T Max = std::numeric_limits::min(); unsigned NumMax = 0; for (auto I = RQ.begin(); Num; --Num) { T Cur = C(*I); if (Cur >= Max) { if (Cur > Max) { Max = Cur; NumMax = 1; } else ++NumMax; auto &Cand = *I++; RQ.remove(Cand); RQ.push_front(Cand); continue; } ++I; } return NumMax; } GCNMinRegScheduler::Candidate* GCNMinRegScheduler::pickCandidate() { do { unsigned Num = RQ.size(); if (Num == 1) break; LLVM_DEBUG(dbgs() << "\nSelecting max priority candidates among " << Num << '\n'); Num = findMax(Num, [=](const Candidate &C) { return C.Priority; }); if (Num == 1) break; LLVM_DEBUG(dbgs() << "\nSelecting min non-ready producing candidate among " << Num << '\n'); Num = findMax(Num, [=](const Candidate &C) { auto SU = C.SU; int Res = getNotReadySuccessors(SU); LLVM_DEBUG(dbgs() << "SU(" << SU->NodeNum << ") would left non-ready " << Res << " successors, metric = " << -Res << '\n'); return -Res; }); if (Num == 1) break; LLVM_DEBUG(dbgs() << "\nSelecting most producing candidate among " << Num << '\n'); Num = findMax(Num, [=](const Candidate &C) { auto SU = C.SU; auto Res = getReadySuccessors(SU); LLVM_DEBUG(dbgs() << "SU(" << SU->NodeNum << ") would make ready " << Res << " successors, metric = " << Res << '\n'); return Res; }); if (Num == 1) break; Num = Num ? Num : RQ.size(); LLVM_DEBUG( dbgs() << "\nCan't find best candidate, selecting in program order among " << Num << '\n'); Num = findMax(Num, [=](const Candidate &C) { return -(int64_t)C.SU->NodeNum; }); assert(Num == 1); } while (false); return &RQ.front(); } void GCNMinRegScheduler::bumpPredsPriority(const SUnit *SchedSU, int Priority) { SmallPtrSet Set; for (const auto &S : SchedSU->Succs) { if (S.getSUnit()->isBoundaryNode() || isScheduled(S.getSUnit()) || S.getKind() != SDep::Data) continue; for (const auto &P : S.getSUnit()->Preds) { auto PSU = P.getSUnit(); assert(!PSU->isBoundaryNode()); if (PSU != SchedSU && !isScheduled(PSU)) { Set.insert(PSU); } } } SmallVector Worklist(Set.begin(), Set.end()); while (!Worklist.empty()) { auto SU = Worklist.pop_back_val(); assert(!SU->isBoundaryNode()); for (const auto &P : SU->Preds) { if (!P.getSUnit()->isBoundaryNode() && !isScheduled(P.getSUnit()) && Set.insert(P.getSUnit()).second) Worklist.push_back(P.getSUnit()); } } LLVM_DEBUG(dbgs() << "Make the predecessors of SU(" << SchedSU->NodeNum << ")'s non-ready successors of " << Priority << " priority in ready queue: "); for (auto &C : RQ) { if (Set.count(C.SU)) { C.Priority = Priority; LLVM_DEBUG(dbgs() << " SU(" << C.SU->NodeNum << ')'); } } LLVM_DEBUG(dbgs() << '\n'); } void GCNMinRegScheduler::releaseSuccessors(const SUnit* SU, int Priority) { for (const auto &S : SU->Succs) { auto SuccSU = S.getSUnit(); if (S.isWeak()) continue; assert(SuccSU->isBoundaryNode() || getNumPreds(SuccSU) > 0); if (!SuccSU->isBoundaryNode() && decNumPreds(SuccSU) == 0) RQ.push_front(*new (Alloc.Allocate()) Candidate(SuccSU, Priority)); } } std::vector GCNMinRegScheduler::schedule(ArrayRef TopRoots, const ScheduleDAG &DAG) { const auto &SUnits = DAG.SUnits; std::vector Schedule; Schedule.reserve(SUnits.size()); initNumPreds(SUnits); int StepNo = 0; for (auto SU : TopRoots) { RQ.push_back(*new (Alloc.Allocate()) Candidate(SU, StepNo)); } releaseSuccessors(&DAG.EntrySU, StepNo); while (!RQ.empty()) { LLVM_DEBUG(dbgs() << "\n=== Picking candidate, Step = " << StepNo << "\n" "Ready queue:"; for (auto &C : RQ) dbgs() << ' ' << C.SU->NodeNum << "(P" << C.Priority << ')'; dbgs() << '\n';); auto C = pickCandidate(); assert(C); RQ.remove(*C); auto SU = C->SU; LLVM_DEBUG(dbgs() << "Selected "; DAG.dumpNode(*SU)); releaseSuccessors(SU, StepNo); Schedule.push_back(SU); setIsScheduled(SU); if (getReadySuccessors(SU) == 0) bumpPredsPriority(SU, StepNo); ++StepNo; } assert(SUnits.size() == Schedule.size()); return Schedule; } namespace llvm { std::vector makeMinRegSchedule(ArrayRef TopRoots, const ScheduleDAG &DAG) { GCNMinRegScheduler S; return S.schedule(TopRoots, DAG); } } // end namespace llvm