| //===- GCNMinRegStrategy.cpp ----------------------------------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/SmallPtrSet.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/ilist_node.h" | |
| #include "llvm/ADT/simple_ilist.h" | |
| #include "llvm/CodeGen/ScheduleDAG.h" | |
| #include "llvm/Support/Allocator.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <cassert> | |
| #include <cstdint> | |
| #include <limits> | |
| #include <vector> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "machine-scheduler" | |
| namespace { | |
| class GCNMinRegScheduler { | |
| struct Candidate : ilist_node<Candidate> { | |
| const SUnit *SU; | |
| int Priority; | |
| Candidate(const SUnit *SU_, int Priority_ = 0) | |
| : SU(SU_), Priority(Priority_) {} | |
| }; | |
| SpecificBumpPtrAllocator<Candidate> Alloc; | |
| using Queue = simple_ilist<Candidate>; | |
| Queue RQ; // Ready queue | |
| std::vector<unsigned> NumPreds; | |
| bool isScheduled(const SUnit *SU) const { | |
| assert(!SU->isBoundaryNode()); | |
| return NumPreds[SU->NodeNum] == std::numeric_limits<unsigned>::max(); | |
| } | |
| void setIsScheduled(const SUnit *SU) { | |
| assert(!SU->isBoundaryNode()); | |
| NumPreds[SU->NodeNum] = std::numeric_limits<unsigned>::max(); | |
| } | |
| unsigned getNumPreds(const SUnit *SU) const { | |
| assert(!SU->isBoundaryNode()); | |
| assert(NumPreds[SU->NodeNum] != std::numeric_limits<unsigned>::max()); | |
| return NumPreds[SU->NodeNum]; | |
| } | |
| unsigned decNumPreds(const SUnit *SU) { | |
| assert(!SU->isBoundaryNode()); | |
| assert(NumPreds[SU->NodeNum] != std::numeric_limits<unsigned>::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 <typename Calc> | |
| 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<const SUnit*> schedule(ArrayRef<const SUnit*> 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 <typename Calc> | |
| unsigned GCNMinRegScheduler::findMax(unsigned Num, Calc C) { | |
| assert(!RQ.empty() && Num <= RQ.size()); | |
| using T = decltype(C(*RQ.begin())) ; | |
| T Max = std::numeric_limits<T>::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<const SUnit*, 32> 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<const SUnit*, 32> 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: "); | |
| const auto SetEnd = Set.end(); | |
| for (auto &C : RQ) { | |
| if (Set.find(C.SU) != SetEnd) { | |
| 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<const SUnit*> | |
| GCNMinRegScheduler::schedule(ArrayRef<const SUnit*> TopRoots, | |
| const ScheduleDAG &DAG) { | |
| const auto &SUnits = DAG.SUnits; | |
| std::vector<const SUnit*> 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 "; SU->dump(&DAG)); | |
| 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<const SUnit*> makeMinRegSchedule(ArrayRef<const SUnit*> TopRoots, | |
| const ScheduleDAG &DAG) { | |
| GCNMinRegScheduler S; | |
| return S.schedule(TopRoots, DAG); | |
| } | |
| } // end namespace llvm |