LLVM 24.0.0git
Scheduler.h
Go to the documentation of this file.
1//===- Scheduler.h ----------------------------------------------*- C++ -*-===//
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//
9// This is the bottom-up list scheduler used by the vectorizer. It is used for
10// checking the legality of vectorization and for scheduling instructions in
11// such a way that makes vectorization possible, if legal.
12//
13// The legality check is performed by `trySchedule(Instrs)`, which will try to
14// schedule the IR until all instructions in `Instrs` can be scheduled together
15// back-to-back. If this fails then it is illegal to vectorize `Instrs`.
16//
17// Internally the scheduler uses the vectorizer-specific DependencyGraph class.
18//
19//===----------------------------------------------------------------------===//
20
21#ifndef LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SCHEDULER_H
22#define LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SCHEDULER_H
23
28#include <variant>
29
30namespace llvm::sandboxir {
31
33public:
34 bool operator()(const DGNode *N1, const DGNode *N2) {
35 // Given that the DAG does not model dependencies such that PHIs are always
36 // at the top, or terminators always at the bottom, we need to force the
37 // priority here in the comparator of the ready list container.
38 auto *I1 = N1->getInstruction();
39 auto *I2 = N2->getInstruction();
40 bool IsTerm1 = I1->isTerminator();
41 bool IsTerm2 = I2->isTerminator();
42 if (IsTerm1 != IsTerm2)
43 // Terminators have the lowest priority.
44 return IsTerm1 > IsTerm2;
45 bool IsPHI1 = isa<PHINode>(I1);
46 bool IsPHI2 = isa<PHINode>(I2);
47 if (IsPHI1 != IsPHI2)
48 // PHIs have the highest priority.
49 return IsPHI1 < IsPHI2;
50 // Otherwise rely on the instruction order.
51 return I2->comesBefore(I1);
52 }
53};
54
55/// The list holding nodes that are ready to schedule. Used by the scheduler.
57 PriorityCmp Cmp;
58 /// Control/Other dependencies are not modeled by the DAG to save memory.
59 /// These have to be modeled in the ready list for correctness.
60 /// This means that the list will hold back nodes that need to meet such
61 /// unmodeled dependencies.
63 /// Helper set for O(1) lookups.
65
66public:
67 ReadyListContainer() : List(Cmp) {}
68 void insert(DGNode *N) {
69#ifndef NDEBUG
70 assert(!N->scheduled() && "Don't insert a scheduled node!");
71 assert(!contains(N) && "Node already exists in ready list!");
72#endif
73 List.push(N);
74 Set.insert(N);
75 assert(List.size() == Set.size() && "List and Set out-of-sync!");
76 }
78 auto *Back = List.top();
79 List.pop();
80 Set.erase(Back);
81 assert(List.size() == Set.size() && "List and Set out-of-sync!");
82 return Back;
83 }
84 bool empty() const {
85 assert(List.empty() == Set.empty() && "List and Set out-of-sync!");
86 return List.empty();
87 }
88 void clear() {
89 List.clear();
90 Set.clear();
91 }
92 bool contains(DGNode *N) const {
93#ifndef NDEBUG
94 // TODO: We should eventually remove this check.
95 auto ListContains = [this](DGNode *N) {
96 auto ListCopy = List;
97 while (!ListCopy.empty()) {
98 DGNode *Top = ListCopy.top();
99 if (Top == N)
100 return true;
101 ListCopy.pop();
102 }
103 return false;
104 };
105 assert(ListContains(N) == Set.contains(N) && "List and Set out-of-sync!");
106#endif
107 return Set.contains(N);
108 }
109 /// \Removes \p N if found in the ready list. Note: this is linear time!
110 void remove(DGNode *N) {
111 auto It = Set.find(N);
112 if (It != Set.end()) {
113 Set.erase(It);
114 // TODO: Use a more efficient data-structure for the ready list because
115 // the priority queue does not support fast removals.
116 List.erase_one(N);
117 assert(List.size() == Set.size() && "List and Set out-of-sync!");
118 }
119 }
120#ifndef NDEBUG
121 void dump(raw_ostream &OS) const;
122 LLVM_DUMP_METHOD void dump() const;
123#endif // NDEBUG
124};
125
126/// The nodes that need to be scheduled back-to-back in a single scheduling
127/// cycle form a SchedBundle.
129public:
131
132private:
133 ContainerTy Nodes;
134
135 /// Called by the DGNode destructor to avoid accessing freed memory.
136 void eraseFromBundle(DGNode *N) { llvm::erase(Nodes, N); }
137 friend void DGNode::setSchedBundle(SchedBundle &); // For eraseFromBunde().
138 friend DGNode::~DGNode(); // For eraseFromBundle().
139
140public:
141 SchedBundle() = default;
142 SchedBundle(ContainerTy &&Nodes) : Nodes(std::move(Nodes)) {
143 for (auto *N : this->Nodes)
144 N->setSchedBundle(*this);
145 }
146 /// Copy CTOR (unimplemented).
147 SchedBundle(const SchedBundle &Other) = delete;
148 /// Copy Assignment (unimplemented).
151 for (auto *N : this->Nodes)
152 N->clearSchedBundle();
153 }
154 bool empty() const { return Nodes.empty(); }
155 /// Singleton bundles are created when scheduling instructions temporarily to
156 /// fill in the schedule until we schedule the vector bundle. These are
157 /// non-vector bundles containing just a single instruction.
158 bool isSingleton() const { return Nodes.size() == 1u; }
159 DGNode *back() const { return Nodes.back(); }
162 iterator begin() { return Nodes.begin(); }
163 iterator end() { return Nodes.end(); }
164 const_iterator begin() const { return Nodes.begin(); }
165 const_iterator end() const { return Nodes.end(); }
166 /// \Returns the bundle node that comes before the others in program order.
167 LLVM_ABI DGNode *getTop() const;
168 /// \Returns the bundle node that comes after the others in program order.
169 LLVM_ABI DGNode *getBot() const;
170 /// Move all bundle instructions to \p Where back-to-back.
172 /// \Returns true if all nodes in the bundle are ready.
173 bool ready(SchedDirection Dir) const {
174 return all_of(Nodes, [](const auto *N) { return N->ready(); });
175 }
176#ifndef NDEBUG
177 void dump(raw_ostream &OS) const;
178 LLVM_DUMP_METHOD void dump() const;
179#endif
180};
181
182/// The scheduling point in the context of the Scheduler points to the
183/// top-of-schedule (i.e., the top-most instruction of the top bundle) during
184/// bottom-up scheduling or the bottom of the schedule (i.e., the bottom-most
185/// instruction of the bottom bundle) during top-down.
186///
187/// This class can be thought of as an extended BB::iterator, one that can
188/// not only point to after the last instruction in a BB (i.e., BB.end()), but
189/// also before the first instruction (i.e., something equivalent to
190/// prev(BB.begin()), which is not a legal BasicBlock::iterator).
191///
192/// This is needed for symmetric implementations of top-down and bottom-up
193/// scheduling. More specifically, if this is the first scheduling attempt we
194/// need the scheduling front to still point to a hypothetical last scheduling
195/// point. In bottom-up this can be at BB.end() but in top-down this can be
196/// before BB.begin(). This is why a BasicBlock::iterator is not suitable for
197/// this.
198class SchedulingPoint {
199 /// If Where contains a Block, then we are pointing before BB.begin(),
200 /// otherwise if it contains an iterator then we point to anywhere in the BB
201 /// or at BB.end().
202 std::variant<BasicBlock::iterator, BasicBlock *> Where;
203
204 /// Creates a scheduling point pointing before the beginning of BB.
205 SchedulingPoint(BasicBlock &BB) : Where(&BB) {}
206
207public:
208 /// Creates a scheduling point pointing at \p It, meaning any instruction in a
209 /// BB or BB.end().
211 /// Returns a SchedulingPoint that points to \p It.
212 static SchedulingPoint createAt(BasicBlock::iterator It) {
213 return SchedulingPoint(It);
214 }
215 /// Returns a SchedulingPoint that points to one element before \p It.
216 static SchedulingPoint createBefore(BasicBlock::iterator It) {
217 BasicBlock &BB = *It.getNodeParent();
218 if (It == BB.begin())
219 return SchedulingPoint(BB);
220 return SchedulingPoint(std::prev(It));
221 }
222 /// Returns a SchedulingPoint that points to one element after \p It.
223 static SchedulingPoint createAfter(BasicBlock::iterator It) {
224 assert(It != It.getNodeParent()->end() && "Already at end!");
225 return SchedulingPoint(std::next(It));
226 }
227
228 /// If the SchedulingPoint points to before the beginning of a BB, then this
229 /// returns that BB, else returns nullptr.
231 if (std::holds_alternative<BasicBlock::iterator>(Where))
232 return nullptr;
233 return std::get<BasicBlock *>(Where);
234 }
235 /// If the SchedulingPoint points after the last instruction in the BB then
236 /// this returns the corresponding BasicBlock, nullptr otherwise.
238 if (std::holds_alternative<BasicBlock *>(Where))
239 return nullptr;
240 auto It = std::get<BasicBlock::iterator>(Where);
241 return It == It.getNodeParent()->end() ? It.getNodeParent() : nullptr;
242 }
243 /// Returns the instruction pointed to by this SchedulingPoint or null if we
244 /// are before/after BB.
247 return nullptr;
248 return &*std::get<BasicBlock::iterator>(Where);
249 }
250 /// Cast to Instruction *. Asserts that we are pointing to an instruction and
251 /// not before/after the beginning/end of a BB.
252 operator Instruction *() const { return atInstrOrNull(); }
253 /// Returns the corresponding BB::iterator. Asserts that we are not pointing
254 /// before BB begin.
256 assert(!atBeforeBeginOrNull() && "Expected in/after BB!");
257 return std::get<BasicBlock::iterator>(Where);
258 }
259 operator BasicBlock::iterator() const { return getIterator(); }
260 /// Returns the SchedulingPoint pointing after this.
261 SchedulingPoint getNext() const {
262 assert(!atEndOrNull() && "Expected before/in BB!");
264 return BB->begin();
265 return std::next(getIterator());
266 }
267 /// Returns the SchedulingPoint pointing before this.
268 SchedulingPoint getPrev() const {
269 assert(!atBeforeBeginOrNull() && "Expected in/after BB!");
270 auto It = getIterator();
271 auto *BB = It.getNodeParent();
272 if (It == BB->begin())
273 return *BB;
274 return std::prev(It);
275 }
276 bool operator==(const SchedulingPoint &Other) const {
277 return Where == Other.Where;
278 }
279#ifndef NDEBUG
280 /// Returns true if the scheduling point is after \p I in program order.
281 bool comesBefore(Instruction &I) const {
282 if (BasicBlock *BB = atEndOrNull()) {
283 // All instructions are before BB end.
284 assert(BB == I.getParent() && "We don't support crossing BBs!");
285 return false;
286 }
287 if (BasicBlock *BB = atBeforeBeginOrNull()) {
288 // Before begin is always before any instruction.
289 assert(BB == I.getParent() && "We don't support crossing BBs!");
290 return true;
291 }
292 Instruction *SchedPointI = atInstrOrNull();
293 assert(SchedPointI != nullptr && "Should have been already handled!");
294 return SchedPointI->comesBefore(&I);
295 }
296 void print(raw_ostream &OS) const;
297 LLVM_DUMP_METHOD void dump() const;
298#endif
299};
300
301/// The list scheduler.
302class Scheduler {
303 /// This is a list-scheduler and this is the list containing the instructions
304 /// that are ready, meaning that all their dependency successors have already
305 /// been scheduled.
306 ReadyListContainer ReadyList;
307 /// The dependency graph is used by the scheduler to determine the legal
308 /// ordering of instructions.
309 DependencyGraph DAG;
310 friend class SchedulerInternalsAttorney; // For DAG.
311 Context &Ctx;
312 /// This is the top of the schedule during bottom-up scheduling and the bottom
313 /// of the schedule during top-down. It points to the position of the last
314 /// top-most/bottom-most instruction scheduled. It may get updated after every
315 /// trySchedule() attempt, regardless of whether scheduling succeeded or not.
316 /// It is nullopt if we have not scheduled before.
317 std::optional<SchedulingPoint> ScheduleTopItOpt;
318 // TODO: This is wasting memory in exchange for fast removal using a raw ptr.
320 /// The BB that we are currently scheduling.
321 BasicBlock *ScheduledBB = nullptr;
322 /// The ID of the callback we register with Sandbox IR.
323 std::optional<Context::CallbackID> CreateInstrCB;
324 /// Called by Sandbox IR's callback system, after \p I has been created.
325 /// NOTE: This should run after DAG's callback has run.
326 // TODO: Perhaps call DAG's notify function from within this one?
327 LLVM_ABI void notifyCreateInstr(Instruction *I);
328
329 /// \Returns a scheduling bundle containing \p Instrs.
330 SchedBundle *createBundle(ArrayRef<Instruction *> Instrs);
331 void eraseBundle(SchedBundle *SB);
332 /// Schedule nodes until we can schedule \p Instrs back-to-back.
333 bool tryScheduleUntil(ArrayRef<Instruction *> Instrs);
334 /// Schedules all nodes in \p Bndl, marks them as scheduled, updates the
335 /// UnscheduledSuccs counter of all dependency predecessors, and adds any of
336 /// them that become ready to the ready list.
337 void scheduleAndUpdateReadyList(SchedBundle &Bndl);
338 /// The scheduling state of the instructions in the bundle.
339 enum class BndlSchedState {
340 NoneScheduled, ///> No instruction in the bundle was previously scheduled.
341 AlreadyScheduled, ///> At least one instruction in the bundle belongs to a
342 /// different non-singleton scheduling bundle.
343 TemporarilyScheduled, ///> Instructions were temporarily scheduled as
344 /// singleton bundles or some of them were not
345 /// scheduled at all. None of them were in a vector
346 ///(non-singleton) bundle.
347 FullyScheduled, ///> All instrs in the bundle were previously scheduled and
348 /// were in the same SchedBundle.
349 };
350 /// \Returns whether none/some/all of \p Instrs have been scheduled.
351 LLVM_ABI BndlSchedState
352 getBndlSchedState(ArrayRef<Instruction *> Instrs) const;
353 /// Destroy the top-most part of the schedule that includes \p Instrs.
354 void trimSchedule(ArrayRef<Instruction *> Instrs);
355 /// Disable copies.
356 Scheduler(const Scheduler &) = delete;
357 Scheduler &operator=(const Scheduler &) = delete;
358
360#ifndef NDEBUG
361 /// Asserts that \p Instrs are above the scheduling frontier if scheduling
362 /// bottom-up or below it if scheduling top-down.
363 void assertSameDirection(ArrayRef<Instruction *> Instrs) const;
364#endif
365
366public:
368 : DAG(Dir, AA, Ctx), Ctx(Ctx), Dir(Dir) {
369 // NOTE: The scheduler's callback depends on the DAG's callback running
370 // before it and updating the DAG accordingly.
371 CreateInstrCB = Ctx.registerCreateInstrCallback(
372 [this](Instruction *I) { notifyCreateInstr(I); });
373 }
375 if (CreateInstrCB)
376 Ctx.unregisterCreateInstrCallback(*CreateInstrCB);
377 }
378 /// Tries to build a schedule that includes all of \p Instrs scheduled at the
379 /// same scheduling cycle. This essentially checks that there are no
380 /// dependencies among \p Instrs. This function may involve scheduling
381 /// intermediate instructions or canceling and re-scheduling if needed.
382 /// \Returns true on success, false otherwise.
384 /// Clear the scheduler's state, including the DAG.
385 void clear() {
386 Bndls.clear();
387 // TODO: clear view once it lands.
388 DAG.clear();
389 ReadyList.clear();
390 ScheduleTopItOpt = std::nullopt;
391 ScheduledBB = nullptr;
392 assert(Bndls.empty() && DAG.empty() && ReadyList.empty() &&
393 !ScheduleTopItOpt && ScheduledBB == nullptr &&
394 "Expected empty state!");
395 }
396
397#ifndef NDEBUG
398 void dump(raw_ostream &OS) const;
399 LLVM_DUMP_METHOD void dump() const;
400#endif
401};
402
403/// A client-attorney class for accessing the Scheduler's internals (used for
404/// unit tests).
406public:
407 static DependencyGraph &getDAG(Scheduler &Sched) { return Sched.DAG; }
408 using BndlSchedState = Scheduler::BndlSchedState;
411 return Sched.getBndlSchedState(Instrs);
412 }
413};
414
415} // namespace llvm::sandboxir
416
417#endif // LLVM_TRANSFORMS_VECTORIZE_SANDBOXVECTORIZER_SCHEDULER_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define LLVM_ABI
Definition Compiler.h:215
#define LLVM_DUMP_METHOD
Mark debug helper function definitions like dump() that should not be stripped from debug builds.
Definition Compiler.h:678
#define I(x, y, z)
Definition MD5.cpp:57
PostRA Machine Instruction Scheduler
This file defines the PriorityQueue class.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
iterator begin()
Instruction iterator methods.
Definition BasicBlock.h:446
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
PriorityQueue - This class behaves like std::priority_queue and provides a few additional convenience...
Implements a dense probed hash-table based set with some number of buckets stored inline.
Definition DenseSet.h:293
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
A DependencyGraph Node that points to an Instruction and contains memory dependency edges.
void setSchedBundle(SchedBundle &SB)
Instruction * getInstruction() const
A sandboxir::User with operands, opcode and linked with previous/next instructions in an instruction ...
Definition Instruction.h:43
bool comesBefore(const Instruction *Other) const
Given an instruction Other in the same basic block as this instruction, return true if this instructi...
bool operator()(const DGNode *N1, const DGNode *N2)
Definition Scheduler.h:34
The list holding nodes that are ready to schedule. Used by the scheduler.
Definition Scheduler.h:56
LLVM_DUMP_METHOD void dump() const
Definition Scheduler.cpp:63
void remove(DGNode *N)
\Removes N if found in the ready list. Note: this is linear time!
Definition Scheduler.h:110
bool contains(DGNode *N) const
Definition Scheduler.h:92
The nodes that need to be scheduled back-to-back in a single scheduling cycle form a SchedBundle.
Definition Scheduler.h:128
LLVM_ABI DGNode * getBot() const
\Returns the bundle node that comes after the others in program order.
Definition Scheduler.cpp:24
SchedBundle(ContainerTy &&Nodes)
Definition Scheduler.h:142
SchedBundle & operator=(const SchedBundle &Other)=delete
Copy Assignment (unimplemented).
LLVM_ABI DGNode * getTop() const
\Returns the bundle node that comes before the others in program order.
Definition Scheduler.cpp:15
bool isSingleton() const
Singleton bundles are created when scheduling instructions temporarily to fill in the schedule until ...
Definition Scheduler.h:158
SmallVector< DGNode *, 4 > ContainerTy
Definition Scheduler.h:130
const_iterator begin() const
Definition Scheduler.h:164
LLVM_DUMP_METHOD void dump() const
Definition Scheduler.cpp:48
SchedBundle(const SchedBundle &Other)=delete
Copy CTOR (unimplemented).
ContainerTy::iterator iterator
Definition Scheduler.h:160
const_iterator end() const
Definition Scheduler.h:165
ContainerTy::const_iterator const_iterator
Definition Scheduler.h:161
LLVM_ABI void cluster(BasicBlock::iterator Where)
Move all bundle instructions to Where back-to-back.
Definition Scheduler.cpp:33
bool ready(SchedDirection Dir) const
\Returns true if all nodes in the bundle are ready.
Definition Scheduler.h:173
A client-attorney class for accessing the Scheduler's internals (used for unit tests).
Definition Scheduler.h:405
static BndlSchedState getBndlSchedState(const Scheduler &Sched, ArrayRef< Instruction * > Instrs)
Definition Scheduler.h:409
Scheduler::BndlSchedState BndlSchedState
Definition Scheduler.h:408
static DependencyGraph & getDAG(Scheduler &Sched)
Definition Scheduler.h:407
The list scheduler.
Definition Scheduler.h:302
friend class SchedulerInternalsAttorney
Definition Scheduler.h:310
LLVM_DUMP_METHOD void dump() const
LLVM_ABI bool trySchedule(ArrayRef< Instruction * > Instrs)
Tries to build a schedule that includes all of Instrs scheduled at the same scheduling cycle.
void clear()
Clear the scheduler's state, including the DAG.
Definition Scheduler.h:385
Scheduler(AAResults &AA, Context &Ctx, SchedDirection Dir)
Definition Scheduler.h:367
SchedulingPoint getNext() const
Returns the SchedulingPoint pointing after this.
Definition Scheduler.h:261
BasicBlock * atEndOrNull() const
If the SchedulingPoint points after the last instruction in the BB then this returns the correspondin...
Definition Scheduler.h:237
Instruction * atInstrOrNull() const
Returns the instruction pointed to by this SchedulingPoint or null if we are before/after BB.
Definition Scheduler.h:245
LLVM_DUMP_METHOD void dump() const
Definition Scheduler.cpp:77
SchedulingPoint getPrev() const
Returns the SchedulingPoint pointing before this.
Definition Scheduler.h:268
BasicBlock::iterator getIterator() const
Returns the corresponding BB::iterator.
Definition Scheduler.h:255
BasicBlock * atBeforeBeginOrNull() const
If the SchedulingPoint points to before the beginning of a BB, then this returns that BB,...
Definition Scheduler.h:230
static SchedulingPoint createAt(BasicBlock::iterator It)
Returns a SchedulingPoint that points to It.
Definition Scheduler.h:212
static SchedulingPoint createBefore(BasicBlock::iterator It)
Returns a SchedulingPoint that points to one element before It.
Definition Scheduler.h:216
SchedulingPoint(BasicBlock::iterator It)
Creates a scheduling point pointing at It, meaning any instruction in a BB or BB.end().
Definition Scheduler.h:210
bool operator==(const SchedulingPoint &Other) const
Definition Scheduler.h:276
void print(raw_ostream &OS) const
Definition Scheduler.cpp:68
static SchedulingPoint createAfter(BasicBlock::iterator It)
Returns a SchedulingPoint that points to one element after It.
Definition Scheduler.h:223
bool comesBefore(Instruction &I) const
Returns true if the scheduling point is after I in program order.
Definition Scheduler.h:281
Abstract Attribute helper functions.
Definition Attributor.h:165
BasicBlock(llvm::BasicBlock *BB, Context &SBCtx)
Definition BasicBlock.h:75
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
void erase(Container &C, ValueType V)
Wrapper function to remove a value from a container:
Definition STLExtras.h:2200
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
@ Other
Any other memory.
Definition ModRef.h:68
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1917
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
#define N