1 //===- GCNMinRegStrategy.cpp ----------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file defines and implements the class GCNMinRegScheduler, which
11 /// implements an experimental, simple scheduler whose main goal is to learn
12 /// ways about consuming less possible registers for a region.
14 //===----------------------------------------------------------------------===//
16 #include "llvm/CodeGen/ScheduleDAG.h"
19 #define DEBUG_TYPE "machine-scheduler"
23 class GCNMinRegScheduler
{
24 struct Candidate
: ilist_node
<Candidate
> {
28 Candidate(const SUnit
*SU_
, int Priority_
= 0)
29 : SU(SU_
), Priority(Priority_
) {}
32 SpecificBumpPtrAllocator
<Candidate
> Alloc
;
33 using Queue
= simple_ilist
<Candidate
>;
34 Queue RQ
; // Ready queue
36 std::vector
<unsigned> NumPreds
;
38 bool isScheduled(const SUnit
*SU
) const {
39 assert(!SU
->isBoundaryNode());
40 return NumPreds
[SU
->NodeNum
] == std::numeric_limits
<unsigned>::max();
43 void setIsScheduled(const SUnit
*SU
) {
44 assert(!SU
->isBoundaryNode());
45 NumPreds
[SU
->NodeNum
] = std::numeric_limits
<unsigned>::max();
48 unsigned getNumPreds(const SUnit
*SU
) const {
49 assert(!SU
->isBoundaryNode());
50 assert(NumPreds
[SU
->NodeNum
] != std::numeric_limits
<unsigned>::max());
51 return NumPreds
[SU
->NodeNum
];
54 unsigned decNumPreds(const SUnit
*SU
) {
55 assert(!SU
->isBoundaryNode());
56 assert(NumPreds
[SU
->NodeNum
] != std::numeric_limits
<unsigned>::max());
57 return --NumPreds
[SU
->NodeNum
];
60 void initNumPreds(const decltype(ScheduleDAG::SUnits
) &SUnits
);
62 int getReadySuccessors(const SUnit
*SU
) const;
63 int getNotReadySuccessors(const SUnit
*SU
) const;
65 template <typename Calc
>
66 unsigned findMax(unsigned Num
, Calc C
);
68 Candidate
* pickCandidate();
70 void bumpPredsPriority(const SUnit
*SchedSU
, int Priority
);
71 void releaseSuccessors(const SUnit
* SU
, int Priority
);
74 std::vector
<const SUnit
*> schedule(ArrayRef
<const SUnit
*> TopRoots
,
75 const ScheduleDAG
&DAG
);
78 } // end anonymous namespace
80 void GCNMinRegScheduler::initNumPreds(const decltype(ScheduleDAG::SUnits
) &SUnits
) {
81 NumPreds
.resize(SUnits
.size());
82 for (unsigned I
= 0; I
< SUnits
.size(); ++I
)
83 NumPreds
[I
] = SUnits
[I
].NumPredsLeft
;
86 int GCNMinRegScheduler::getReadySuccessors(const SUnit
*SU
) const {
87 unsigned NumSchedSuccs
= 0;
88 for (auto SDep
: SU
->Succs
) {
89 bool wouldBeScheduled
= true;
90 for (auto PDep
: SDep
.getSUnit()->Preds
) {
91 auto *PSU
= PDep
.getSUnit();
92 assert(!PSU
->isBoundaryNode());
93 if (PSU
!= SU
&& !isScheduled(PSU
)) {
94 wouldBeScheduled
= false;
98 NumSchedSuccs
+= wouldBeScheduled
? 1 : 0;
100 return NumSchedSuccs
;
103 int GCNMinRegScheduler::getNotReadySuccessors(const SUnit
*SU
) const {
104 return SU
->Succs
.size() - getReadySuccessors(SU
);
107 template <typename Calc
>
108 unsigned GCNMinRegScheduler::findMax(unsigned Num
, Calc C
) {
109 assert(!RQ
.empty() && Num
<= RQ
.size());
111 using T
= decltype(C(*RQ
.begin())) ;
113 T Max
= std::numeric_limits
<T
>::min();
115 for (auto I
= RQ
.begin(); Num
; --Num
) {
133 GCNMinRegScheduler::Candidate
* GCNMinRegScheduler::pickCandidate() {
135 unsigned Num
= RQ
.size();
138 LLVM_DEBUG(dbgs() << "\nSelecting max priority candidates among " << Num
140 Num
= findMax(Num
, [=](const Candidate
&C
) { return C
.Priority
; });
143 LLVM_DEBUG(dbgs() << "\nSelecting min non-ready producing candidate among "
145 Num
= findMax(Num
, [=](const Candidate
&C
) {
146 const auto *SU
= C
.SU
;
147 int Res
= getNotReadySuccessors(SU
);
148 LLVM_DEBUG(dbgs() << "SU(" << SU
->NodeNum
<< ") would left non-ready "
149 << Res
<< " successors, metric = " << -Res
<< '\n');
154 LLVM_DEBUG(dbgs() << "\nSelecting most producing candidate among " << Num
156 Num
= findMax(Num
, [=](const Candidate
&C
) {
157 const auto *SU
= C
.SU
;
158 auto Res
= getReadySuccessors(SU
);
159 LLVM_DEBUG(dbgs() << "SU(" << SU
->NodeNum
<< ") would make ready " << Res
160 << " successors, metric = " << Res
<< '\n');
165 Num
= Num
? Num
: RQ
.size();
168 << "\nCan't find best candidate, selecting in program order among "
170 Num
= findMax(Num
, [=](const Candidate
&C
) { return -(int64_t)C
.SU
->NodeNum
; });
177 void GCNMinRegScheduler::bumpPredsPriority(const SUnit
*SchedSU
, int Priority
) {
178 SmallPtrSet
<const SUnit
*, 32> Set
;
179 for (const auto &S
: SchedSU
->Succs
) {
180 if (S
.getSUnit()->isBoundaryNode() || isScheduled(S
.getSUnit()) ||
181 S
.getKind() != SDep::Data
)
183 for (const auto &P
: S
.getSUnit()->Preds
) {
184 auto *PSU
= P
.getSUnit();
185 assert(!PSU
->isBoundaryNode());
186 if (PSU
!= SchedSU
&& !isScheduled(PSU
)) {
191 SmallVector
<const SUnit
*, 32> Worklist(Set
.begin(), Set
.end());
192 while (!Worklist
.empty()) {
193 const auto *SU
= Worklist
.pop_back_val();
194 assert(!SU
->isBoundaryNode());
195 for (const auto &P
: SU
->Preds
) {
196 if (!P
.getSUnit()->isBoundaryNode() && !isScheduled(P
.getSUnit()) &&
197 Set
.insert(P
.getSUnit()).second
)
198 Worklist
.push_back(P
.getSUnit());
201 LLVM_DEBUG(dbgs() << "Make the predecessors of SU(" << SchedSU
->NodeNum
202 << ")'s non-ready successors of " << Priority
203 << " priority in ready queue: ");
205 if (Set
.count(C
.SU
)) {
206 C
.Priority
= Priority
;
207 LLVM_DEBUG(dbgs() << " SU(" << C
.SU
->NodeNum
<< ')');
210 LLVM_DEBUG(dbgs() << '\n');
213 void GCNMinRegScheduler::releaseSuccessors(const SUnit
* SU
, int Priority
) {
214 for (const auto &S
: SU
->Succs
) {
215 auto *SuccSU
= S
.getSUnit();
218 assert(SuccSU
->isBoundaryNode() || getNumPreds(SuccSU
) > 0);
219 if (!SuccSU
->isBoundaryNode() && decNumPreds(SuccSU
) == 0)
220 RQ
.push_front(*new (Alloc
.Allocate()) Candidate(SuccSU
, Priority
));
224 std::vector
<const SUnit
*>
225 GCNMinRegScheduler::schedule(ArrayRef
<const SUnit
*> TopRoots
,
226 const ScheduleDAG
&DAG
) {
227 const auto &SUnits
= DAG
.SUnits
;
228 std::vector
<const SUnit
*> Schedule
;
229 Schedule
.reserve(SUnits
.size());
231 initNumPreds(SUnits
);
235 for (const auto *SU
: TopRoots
) {
236 RQ
.push_back(*new (Alloc
.Allocate()) Candidate(SU
, StepNo
));
238 releaseSuccessors(&DAG
.EntrySU
, StepNo
);
240 while (!RQ
.empty()) {
241 LLVM_DEBUG(dbgs() << "\n=== Picking candidate, Step = " << StepNo
246 << ' ' << C
.SU
->NodeNum
<< "(P" << C
.Priority
<< ')';
249 auto *C
= pickCandidate();
252 const auto *SU
= C
->SU
;
253 LLVM_DEBUG(dbgs() << "Selected "; DAG
.dumpNode(*SU
));
255 releaseSuccessors(SU
, StepNo
);
256 Schedule
.push_back(SU
);
259 if (getReadySuccessors(SU
) == 0)
260 bumpPredsPriority(SU
, StepNo
);
264 assert(SUnits
.size() == Schedule
.size());
271 std::vector
<const SUnit
*> makeMinRegSchedule(ArrayRef
<const SUnit
*> TopRoots
,
272 const ScheduleDAG
&DAG
) {
273 GCNMinRegScheduler S
;
274 return S
.schedule(TopRoots
, DAG
);
277 } // end namespace llvm