1 //===--------------------- Scheduler.cpp ------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // A scheduler for processor resource units and processor resource groups.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/MCA/HardwareUnits/Scheduler.h"
15 #include "llvm/Support/Debug.h"
16 #include "llvm/Support/raw_ostream.h"
21 #define DEBUG_TYPE "llvm-mca"
23 void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) {
24 // Ensure we have a valid (non-null) strategy object.
25 Strategy = S ? std::move(S) : llvm::make_unique<DefaultSchedulerStrategy>();
28 // Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy.
29 SchedulerStrategy::~SchedulerStrategy() = default;
30 DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default;
33 void Scheduler::dump() const {
34 dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n';
35 dbgs() << "[SCHEDULER]: ReadySet size is: " << ReadySet.size() << '\n';
36 dbgs() << "[SCHEDULER]: IssuedSet size is: " << IssuedSet.size() << '\n';
41 Scheduler::Status Scheduler::isAvailable(const InstRef &IR) const {
42 const InstrDesc &Desc = IR.getInstruction()->getDesc();
44 switch (Resources->canBeDispatched(Desc.Buffers)) {
45 case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
46 return Scheduler::SC_BUFFERS_FULL;
47 case ResourceStateEvent::RS_RESERVED:
48 return Scheduler::SC_DISPATCH_GROUP_STALL;
49 case ResourceStateEvent::RS_BUFFER_AVAILABLE:
53 // Give lower priority to LSUnit stall events.
54 switch (LSU.isAvailable(IR)) {
55 case LSUnit::LSU_LQUEUE_FULL:
56 return Scheduler::SC_LOAD_QUEUE_FULL;
57 case LSUnit::LSU_SQUEUE_FULL:
58 return Scheduler::SC_STORE_QUEUE_FULL;
59 case LSUnit::LSU_AVAILABLE:
60 return Scheduler::SC_AVAILABLE;
63 llvm_unreachable("Don't know how to process this LSU state result!");
66 void Scheduler::issueInstructionImpl(
68 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources) {
69 Instruction *IS = IR.getInstruction();
70 const InstrDesc &D = IS->getDesc();
72 // Issue the instruction and collect all the consumed resources
73 // into a vector. That vector is then used to notify the listener.
74 Resources->issueInstruction(D, UsedResources);
76 // Notify the instruction that it started executing.
77 // This updates the internal state of each write.
80 if (IS->isExecuting())
81 IssuedSet.emplace_back(IR);
82 else if (IS->isExecuted())
83 LSU.onInstructionExecuted(IR);
86 // Release the buffered resources and issue the instruction.
87 void Scheduler::issueInstruction(
89 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources,
90 SmallVectorImpl<InstRef> &ReadyInstructions) {
91 const Instruction &Inst = *IR.getInstruction();
92 bool HasDependentUsers = Inst.hasDependentUsers();
94 Resources->releaseBuffers(Inst.getDesc().Buffers);
95 issueInstructionImpl(IR, UsedResources);
96 // Instructions that have been issued during this cycle might have unblocked
97 // other dependent instructions. Dependent instructions may be issued during
98 // this same cycle if operands have ReadAdvance entries. Promote those
99 // instructions to the ReadySet and notify the caller that those are ready.
100 if (HasDependentUsers)
101 promoteToReadySet(ReadyInstructions);
104 void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
105 // Scan the set of waiting instructions and promote them to the
106 // ready queue if operands are all ready.
107 unsigned RemovedElements = 0;
108 for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) {
113 // Check if this instruction is now ready. In case, force
114 // a transition in state using method 'update()'.
115 Instruction &IS = *IR.getInstruction();
119 // Check if there are still unsolved data dependencies.
125 Ready.emplace_back(IR);
126 ReadySet.emplace_back(IR);
130 std::iter_swap(I, E - RemovedElements);
133 WaitSet.resize(WaitSet.size() - RemovedElements);
136 InstRef Scheduler::select() {
137 unsigned QueueIndex = ReadySet.size();
138 for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) {
139 const InstRef &IR = ReadySet[I];
140 if (QueueIndex == ReadySet.size() ||
141 Strategy->compare(IR, ReadySet[QueueIndex])) {
142 const InstrDesc &D = IR.getInstruction()->getDesc();
143 if (Resources->canBeIssued(D))
148 if (QueueIndex == ReadySet.size())
151 // We found an instruction to issue.
152 InstRef IR = ReadySet[QueueIndex];
153 std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]);
158 void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) {
159 unsigned RemovedElements = 0;
160 for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) {
164 Instruction &IS = *IR.getInstruction();
165 if (!IS.isExecuted()) {
166 LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
167 << " is still executing.\n");
172 // Instruction IR has completed execution.
173 LSU.onInstructionExecuted(IR);
174 Executed.emplace_back(IR);
177 std::iter_swap(I, E - RemovedElements);
180 IssuedSet.resize(IssuedSet.size() - RemovedElements);
183 void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed,
184 SmallVectorImpl<InstRef> &Executed,
185 SmallVectorImpl<InstRef> &Ready) {
186 // Release consumed resources.
187 Resources->cycleEvent(Freed);
189 // Propagate the cycle event to the 'Issued' and 'Wait' sets.
190 for (InstRef &IR : IssuedSet)
191 IR.getInstruction()->cycleEvent();
193 updateIssuedSet(Executed);
195 for (InstRef &IR : WaitSet)
196 IR.getInstruction()->cycleEvent();
198 promoteToReadySet(Ready);
201 bool Scheduler::mustIssueImmediately(const InstRef &IR) const {
202 const InstrDesc &Desc = IR.getInstruction()->getDesc();
203 if (Desc.isZeroLatency())
205 // Instructions that use an in-order dispatch/issue processor resource must be
206 // issued immediately to the pipeline(s). Any other in-order buffered
207 // resources (i.e. BufferSize=1) is consumed.
208 return Desc.MustIssueImmediately;
211 void Scheduler::dispatch(const InstRef &IR) {
212 const InstrDesc &Desc = IR.getInstruction()->getDesc();
213 Resources->reserveBuffers(Desc.Buffers);
215 // If necessary, reserve queue entries in the load-store unit (LSU).
216 bool IsMemOp = Desc.MayLoad || Desc.MayStore;
221 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n");
222 WaitSet.push_back(IR);
226 // Don't add a zero-latency instruction to the Ready queue.
227 // A zero-latency instruction doesn't consume any scheduler resources. That is
228 // because it doesn't need to be executed, and it is often removed at register
229 // renaming stage. For example, register-register moves are often optimized at
230 // register renaming stage by simply updating register aliases. On some
231 // targets, zero-idiom instructions (for example: a xor that clears the value
232 // of a register) are treated specially, and are often eliminated at register
234 if (!mustIssueImmediately(IR)) {
235 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n");
236 ReadySet.push_back(IR);
240 bool Scheduler::isReady(const InstRef &IR) const {
241 const InstrDesc &Desc = IR.getInstruction()->getDesc();
242 bool IsMemOp = Desc.MayLoad || Desc.MayStore;
243 return IR.getInstruction()->isReady() && (!IsMemOp || LSU.isReady(IR));