LLVM 20.0.0git
Scheduler.cpp
Go to the documentation of this file.
1//===- Scheduler.cpp ------------------------------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
11
12namespace llvm::sandboxir {
13
14// TODO: Check if we can cache top/bottom to reduce compile-time.
16 DGNode *TopN = Nodes.front();
17 for (auto *N : drop_begin(Nodes)) {
18 if (N->getInstruction()->comesBefore(TopN->getInstruction()))
19 TopN = N;
20 }
21 return TopN;
22}
23
25 DGNode *BotN = Nodes.front();
26 for (auto *N : drop_begin(Nodes)) {
27 if (BotN->getInstruction()->comesBefore(N->getInstruction()))
28 BotN = N;
29 }
30 return BotN;
31}
32
34 for (auto *N : Nodes) {
35 auto *I = N->getInstruction();
36 if (I->getIterator() == Where)
37 ++Where; // Try to maintain bundle order.
38 I->moveBefore(*Where.getNodeParent(), Where);
39 }
40}
41
42#ifndef NDEBUG
44 for (auto *N : Nodes)
45 OS << *N;
46}
47
48void SchedBundle::dump() const {
49 dump(dbgs());
50 dbgs() << "\n";
51}
52#endif // NDEBUG
53
54#ifndef NDEBUG
56 auto ListCopy = List;
57 while (!ListCopy.empty()) {
58 OS << *ListCopy.top() << "\n";
59 ListCopy.pop();
60 }
61}
62
64 dump(dbgs());
65 dbgs() << "\n";
66}
67#endif // NDEBUG
68
69void Scheduler::scheduleAndUpdateReadyList(SchedBundle &Bndl) {
70 // Find where we should schedule the instructions.
71 assert(ScheduleTopItOpt && "Should have been set by now!");
72 auto Where = *ScheduleTopItOpt;
73 // Move all instructions in `Bndl` to `Where`.
74 Bndl.cluster(Where);
75 // Update the last scheduled bundle.
76 ScheduleTopItOpt = Bndl.getTop()->getInstruction()->getIterator();
77 // Set nodes as "scheduled" and decrement the UnsceduledSuccs counter of all
78 // dependency predecessors.
79 for (DGNode *N : Bndl) {
80 N->setScheduled(true);
81 for (auto *DepN : N->preds(DAG)) {
82 // TODO: preds() should not return nullptr.
83 if (DepN == nullptr)
84 continue;
85 DepN->decrUnscheduledSuccs();
86 if (DepN->ready())
87 ReadyList.insert(DepN);
88 }
89 }
90}
91
92SchedBundle *Scheduler::createBundle(ArrayRef<Instruction *> Instrs) {
94 Nodes.reserve(Instrs.size());
95 for (auto *I : Instrs)
96 Nodes.push_back(DAG.getNode(I));
97 auto BndlPtr = std::make_unique<SchedBundle>(std::move(Nodes));
98 auto *Bndl = BndlPtr.get();
99 Bndls[Bndl] = std::move(BndlPtr);
100 return Bndl;
101}
102
103void Scheduler::eraseBundle(SchedBundle *SB) { Bndls.erase(SB); }
104
105bool Scheduler::tryScheduleUntil(ArrayRef<Instruction *> Instrs) {
106 // Use a set of instructions, instead of `Instrs` for fast lookups.
107 DenseSet<Instruction *> InstrsToDefer(Instrs.begin(), Instrs.end());
108 // This collects the nodes that correspond to instructions found in `Instrs`
109 // that have just become ready. These nodes won't be scheduled right away.
110 SmallVector<DGNode *, 8> DeferredNodes;
111
112 // Keep scheduling ready nodes until we either run out of ready nodes (i.e.,
113 // ReadyList is empty), or all nodes that correspond to `Instrs` (the nodes of
114 // which are collected in DeferredNodes) are all ready to schedule.
115 while (!ReadyList.empty()) {
116 auto *ReadyN = ReadyList.pop();
117 if (InstrsToDefer.contains(ReadyN->getInstruction())) {
118 // If the ready instruction is one of those in `Instrs`, then we don't
119 // schedule it right away. Instead we defer it until we can schedule it
120 // along with the rest of the instructions in `Instrs`, at the same
121 // time in a single scheduling bundle.
122 DeferredNodes.push_back(ReadyN);
123 bool ReadyToScheduleDeferred = DeferredNodes.size() == Instrs.size();
124 if (ReadyToScheduleDeferred) {
125 scheduleAndUpdateReadyList(*createBundle(Instrs));
126 return true;
127 }
128 } else {
129 // If the ready instruction is not found in `Instrs`, then we wrap it in a
130 // scheduling bundle and schedule it right away.
131 scheduleAndUpdateReadyList(*createBundle({ReadyN->getInstruction()}));
132 }
133 }
134 assert(DeferredNodes.size() != Instrs.size() &&
135 "We should have succesfully scheduled and early-returned!");
136 return false;
137}
138
139Scheduler::BndlSchedState
140Scheduler::getBndlSchedState(ArrayRef<Instruction *> Instrs) const {
141 assert(!Instrs.empty() && "Expected non-empty bundle");
142 bool PartiallyScheduled = false;
143 bool FullyScheduled = true;
144 for (auto *I : Instrs) {
145 auto *N = DAG.getNode(I);
146 if (N != nullptr && N->scheduled())
147 PartiallyScheduled = true;
148 else
149 FullyScheduled = false;
150 }
151 if (FullyScheduled) {
152 // If not all instrs in the bundle are in the same SchedBundle then this
153 // should be considered as partially-scheduled, because we will need to
154 // re-schedule.
155 SchedBundle *SB = DAG.getNode(Instrs[0])->getSchedBundle();
156 assert(SB != nullptr && "FullyScheduled assumes that there is an SB!");
157 if (any_of(drop_begin(Instrs), [this, SB](sandboxir::Value *SBV) {
158 return DAG.getNode(cast<sandboxir::Instruction>(SBV))
159 ->getSchedBundle() != SB;
160 }))
161 FullyScheduled = false;
162 }
163 return FullyScheduled ? BndlSchedState::FullyScheduled
164 : PartiallyScheduled ? BndlSchedState::PartiallyOrDifferentlyScheduled
165 : BndlSchedState::NoneScheduled;
166}
167
168void Scheduler::trimSchedule(ArrayRef<Instruction *> Instrs) {
169 Instruction *TopI = &*ScheduleTopItOpt.value();
170 Instruction *LowestI = VecUtils::getLowest(Instrs);
171 // Destroy the schedule bundles from LowestI all the way to the top.
172 for (auto *I = LowestI, *E = TopI->getPrevNode(); I != E;
173 I = I->getPrevNode()) {
174 auto *N = DAG.getNode(I);
175 if (auto *SB = N->getSchedBundle())
176 eraseBundle(SB);
177 }
178 // TODO: For now we clear the DAG. Trim view once it gets implemented.
179 Bndls.clear();
180 DAG.clear();
181
182 // Since we are scheduling NewRegion from scratch, we clear the ready lists.
183 // The nodes currently in the list may not be ready after clearing the View.
184 ReadyList.clear();
185}
186
188 assert(all_of(drop_begin(Instrs),
189 [Instrs](Instruction *I) {
190 return I->getParent() == (*Instrs.begin())->getParent();
191 }) &&
192 "Instrs not in the same BB!");
193 auto SchedState = getBndlSchedState(Instrs);
194 switch (SchedState) {
195 case BndlSchedState::FullyScheduled:
196 // Nothing to do.
197 return true;
198 case BndlSchedState::PartiallyOrDifferentlyScheduled:
199 // If one or more instrs are already scheduled we need to destroy the
200 // top-most part of the schedule that includes the instrs in the bundle and
201 // re-schedule.
202 trimSchedule(Instrs);
203 [[fallthrough]];
204 case BndlSchedState::NoneScheduled: {
205 // TODO: Set the window of the DAG that we are interested in.
206 // We start scheduling at the bottom instr of Instrs.
207 ScheduleTopItOpt = std::next(VecUtils::getLowest(Instrs)->getIterator());
208
209 // Extend the DAG to include Instrs.
211 // Add nodes to ready list.
212 for (auto &I : Extension) {
213 auto *N = DAG.getNode(&I);
214 if (N->ready())
215 ReadyList.insert(N);
216 }
217 // Try schedule all nodes until we can schedule Instrs back-to-back.
218 return tryScheduleUntil(Instrs);
219 }
220 }
221 llvm_unreachable("Unhandled BndlSchedState enum");
222}
223
224#ifndef NDEBUG
226 OS << "ReadyList:\n";
227 ReadyList.dump(OS);
228}
229void Scheduler::dump() const { dump(dbgs()); }
230#endif // NDEBUG
231
232} // namespace llvm::sandboxir
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define I(x, y, z)
Definition: MD5.cpp:58
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
iterator begin() const
Definition: ArrayRef.h:156
void reserve(size_type N)
Definition: SmallVector.h:663
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:52
Iterator for Instructions in a `BasicBlock.
Definition: BasicBlock.h:23
BasicBlock * getNodeParent() const
\Returns the parent BB.
Definition: BasicBlock.cpp:43
A DependencyGraph Node that points to an Instruction and contains memory dependency edges.
SchedBundle * getSchedBundle() const
\Returns the scheduling bundle that this node belongs to, or nullptr.
Instruction * getInstruction() const
DGNode * getNode(Instruction *I) const
Interval< Instruction > extend(ArrayRef< Instruction * > Instrs)
Build/extend the dependency graph such that it includes Instrs.
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition: Instruction.h:42
BBIterator getIterator() const
\Returns a BasicBlock::iterator for this Instruction.
Definition: Instruction.cpp:38
bool comesBefore(const Instruction *Other) const
Given an instruction Other in the same basic block as this instruction, return true if this instructi...
Definition: Instruction.h:214
LLVM_DUMP_METHOD void dump() const
Definition: Scheduler.cpp:63
void dump(raw_ostream &OS) const
Definition: Scheduler.cpp:55
The nodes that need to be scheduled back-to-back in a single scheduling cycle form a SchedBundle.
Definition: Scheduler.h:65
DGNode * getBot() const
\Returns the bundle node that comes after the others in program order.
Definition: Scheduler.cpp:24
DGNode * getTop() const
\Returns the bundle node that comes before the others in program order.
Definition: Scheduler.cpp:15
SmallVector< DGNode *, 4 > ContainerTy
Definition: Scheduler.h:67
LLVM_DUMP_METHOD void dump() const
Definition: Scheduler.cpp:48
void cluster(BasicBlock::iterator Where)
Move all bundle instructions to Where back-to-back.
Definition: Scheduler.cpp:33
LLVM_DUMP_METHOD void dump() const
Definition: Scheduler.cpp:229
bool trySchedule(ArrayRef< Instruction * > Instrs)
Definition: Scheduler.cpp:187
static Instruction * getLowest(ArrayRef< Instruction * > Instrs)
Definition: VecUtils.h:103
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition: STLExtras.h:329
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1739
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1746
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
#define N