1 //===--------------------- Scheduler.cpp ------------------------*- 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 // A scheduler for processor resource units and processor resource groups.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/MCA/HardwareUnits/Scheduler.h"
14 #include "llvm/Support/Debug.h"
15 #include "llvm/Support/raw_ostream.h"
16 
17 namespace llvm {
18 namespace mca {
19 
20 #define DEBUG_TYPE "llvm-mca"
21 
22 void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) {
23   // Ensure we have a valid (non-null) strategy object.
24   Strategy = S ? std::move(S) : llvm::make_unique<DefaultSchedulerStrategy>();
25 }
26 
27 // Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy.
28 SchedulerStrategy::~SchedulerStrategy() = default;
29 DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default;
30 
31 #ifndef NDEBUG
32 void Scheduler::dump() const {
33   dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n';
34   dbgs() << "[SCHEDULER]: ReadySet size is: " << ReadySet.size() << '\n';
35   dbgs() << "[SCHEDULER]: IssuedSet size is: " << IssuedSet.size() << '\n';
36   Resources->dump();
37 }
38 #endif
39 
40 Scheduler::Status Scheduler::isAvailable(const InstRef &IR) const {
41   const InstrDesc &Desc = IR.getInstruction()->getDesc();
42 
43   switch (Resources->canBeDispatched(Desc.Buffers)) {
44   case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
45     return Scheduler::SC_BUFFERS_FULL;
46   case ResourceStateEvent::RS_RESERVED:
47     return Scheduler::SC_DISPATCH_GROUP_STALL;
48   case ResourceStateEvent::RS_BUFFER_AVAILABLE:
49     break;
50   }
51 
52   // Give lower priority to LSUnit stall events.
53   switch (LSU.isAvailable(IR)) {
54   case LSUnit::LSU_LQUEUE_FULL:
55     return Scheduler::SC_LOAD_QUEUE_FULL;
56   case LSUnit::LSU_SQUEUE_FULL:
57     return Scheduler::SC_STORE_QUEUE_FULL;
58   case LSUnit::LSU_AVAILABLE:
59     return Scheduler::SC_AVAILABLE;
60   }
61 
62   llvm_unreachable("Don't know how to process this LSU state result!");
63 }
64 
65 void Scheduler::issueInstructionImpl(
66     InstRef &IR,
67     SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources) {
68   Instruction *IS = IR.getInstruction();
69   const InstrDesc &D = IS->getDesc();
70 
71   // Issue the instruction and collect all the consumed resources
72   // into a vector. That vector is then used to notify the listener.
73   Resources->issueInstruction(D, UsedResources);
74 
75   // Notify the instruction that it started executing.
76   // This updates the internal state of each write.
77   IS->execute(IR.getSourceIndex());
78 
79   if (IS->isExecuting())
80     IssuedSet.emplace_back(IR);
81   else if (IS->isExecuted())
82     LSU.onInstructionExecuted(IR);
83 }
84 
85 // Release the buffered resources and issue the instruction.
86 void Scheduler::issueInstruction(
87     InstRef &IR,
88     SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources,
89     SmallVectorImpl<InstRef> &ReadyInstructions) {
90   const Instruction &Inst = *IR.getInstruction();
91   bool HasDependentUsers = Inst.hasDependentUsers();
92 
93   Resources->releaseBuffers(Inst.getDesc().Buffers);
94   issueInstructionImpl(IR, UsedResources);
95   // Instructions that have been issued during this cycle might have unblocked
96   // other dependent instructions. Dependent instructions may be issued during
97   // this same cycle if operands have ReadAdvance entries.  Promote those
98   // instructions to the ReadySet and notify the caller that those are ready.
99   if (HasDependentUsers && promoteToPendingSet())
100     promoteToReadySet(ReadyInstructions);
101 }
102 
103 bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
104   // Scan the set of waiting instructions and promote them to the
105   // ready set if operands are all ready.
106   unsigned PromotedElements = 0;
107   for (auto I = PendingSet.begin(), E = PendingSet.end(); I != E;) {
108     InstRef &IR = *I;
109     if (!IR)
110       break;
111 
112     // Check if there are still unsolved memory dependencies.
113     Instruction &IS = *IR.getInstruction();
114     if (IS.isMemOp()) {
115       unsigned CriticalMemDep = LSU.isReady(IR);
116       if (CriticalMemDep != IR.getSourceIndex()) {
117         IS.setCriticalMemDep(CriticalMemDep);
118         ++I;
119         continue;
120       }
121     }
122 
123     // Check if this instruction is now ready. In case, force
124     // a transition in state using method 'update()'.
125     if (!IS.isReady() && !IS.updatePending()) {
126       ++I;
127       continue;
128     }
129     LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
130                       << " promoted to the READY set.\n");
131 
132     Ready.emplace_back(IR);
133     ReadySet.emplace_back(IR);
134 
135     IR.invalidate();
136     ++PromotedElements;
137     std::iter_swap(I, E - PromotedElements);
138   }
139 
140   PendingSet.resize(PendingSet.size() - PromotedElements);
141   return PromotedElements;
142 }
143 
144 bool Scheduler::promoteToPendingSet() {
145   // Scan the set of waiting instructions and promote them to the
146   // pending set if operands are all ready.
147   unsigned RemovedElements = 0;
148   for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) {
149     InstRef &IR = *I;
150     if (!IR)
151       break;
152 
153     // Check if this instruction is now ready. In case, force
154     // a transition in state using method 'update()'.
155     Instruction &IS = *IR.getInstruction();
156     if (IS.isDispatched() && !IS.updateDispatched()) {
157       ++I;
158       continue;
159     }
160     LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
161                       << " promoted to the PENDING set.\n");
162 
163     PendingSet.emplace_back(IR);
164 
165     IR.invalidate();
166     ++RemovedElements;
167     std::iter_swap(I, E - RemovedElements);
168   }
169 
170   WaitSet.resize(WaitSet.size() - RemovedElements);
171   return RemovedElements;
172 }
173 
174 InstRef Scheduler::select() {
175   unsigned QueueIndex = ReadySet.size();
176   for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) {
177     InstRef &IR = ReadySet[I];
178     if (QueueIndex == ReadySet.size() ||
179         Strategy->compare(IR, ReadySet[QueueIndex])) {
180       const InstrDesc &D = IR.getInstruction()->getDesc();
181       uint64_t BusyResourceMask = Resources->checkAvailability(D);
182       IR.getInstruction()->updateCriticalResourceMask(BusyResourceMask);
183       BusyResourceUnits |= BusyResourceMask;
184       if (!BusyResourceMask)
185         QueueIndex = I;
186     }
187   }
188 
189   if (QueueIndex == ReadySet.size())
190     return InstRef();
191 
192   // We found an instruction to issue.
193   InstRef IR = ReadySet[QueueIndex];
194   std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]);
195   ReadySet.pop_back();
196   return IR;
197 }
198 
199 void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) {
200   unsigned RemovedElements = 0;
201   for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) {
202     InstRef &IR = *I;
203     if (!IR)
204       break;
205     Instruction &IS = *IR.getInstruction();
206     if (!IS.isExecuted()) {
207       LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
208                         << " is still executing.\n");
209       ++I;
210       continue;
211     }
212 
213     // Instruction IR has completed execution.
214     LSU.onInstructionExecuted(IR);
215     Executed.emplace_back(IR);
216     ++RemovedElements;
217     IR.invalidate();
218     std::iter_swap(I, E - RemovedElements);
219   }
220 
221   IssuedSet.resize(IssuedSet.size() - RemovedElements);
222 }
223 
224 void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed,
225                            SmallVectorImpl<InstRef> &Executed,
226                            SmallVectorImpl<InstRef> &Ready) {
227   // Release consumed resources.
228   Resources->cycleEvent(Freed);
229 
230   for (InstRef &IR : IssuedSet)
231     IR.getInstruction()->cycleEvent();
232   updateIssuedSet(Executed);
233 
234   for (InstRef &IR : PendingSet)
235     IR.getInstruction()->cycleEvent();
236 
237   for (InstRef &IR : WaitSet)
238     IR.getInstruction()->cycleEvent();
239 
240   promoteToPendingSet();
241   promoteToReadySet(Ready);
242 
243   NumDispatchedToThePendingSet = 0;
244   BusyResourceUnits = 0;
245 }
246 
247 bool Scheduler::mustIssueImmediately(const InstRef &IR) const {
248   const InstrDesc &Desc = IR.getInstruction()->getDesc();
249   if (Desc.isZeroLatency())
250     return true;
251   // Instructions that use an in-order dispatch/issue processor resource must be
252   // issued immediately to the pipeline(s). Any other in-order buffered
253   // resources (i.e. BufferSize=1) is consumed.
254   return Desc.MustIssueImmediately;
255 }
256 
257 bool Scheduler::dispatch(const InstRef &IR) {
258   const Instruction &IS = *IR.getInstruction();
259   const InstrDesc &Desc = IS.getDesc();
260   Resources->reserveBuffers(Desc.Buffers);
261 
262   // If necessary, reserve queue entries in the load-store unit (LSU).
263   if (IS.isMemOp())
264     LSU.dispatch(IR);
265 
266   if (IS.isPending()) {
267     LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR
268                       << " to the PendingSet\n");
269     PendingSet.push_back(IR);
270     ++NumDispatchedToThePendingSet;
271     return false;
272   }
273 
274   if (!IS.isReady() ||
275       (IS.isMemOp() && LSU.isReady(IR) != IR.getSourceIndex())) {
276     LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n");
277     WaitSet.push_back(IR);
278     return false;
279   }
280 
281   // Don't add a zero-latency instruction to the Ready queue.
282   // A zero-latency instruction doesn't consume any scheduler resources. That is
283   // because it doesn't need to be executed, and it is often removed at register
284   // renaming stage. For example, register-register moves are often optimized at
285   // register renaming stage by simply updating register aliases. On some
286   // targets, zero-idiom instructions (for example: a xor that clears the value
287   // of a register) are treated specially, and are often eliminated at register
288   // renaming stage.
289   if (!mustIssueImmediately(IR)) {
290     LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n");
291     ReadySet.push_back(IR);
292   }
293 
294   return true;
295 }
296 
297 } // namespace mca
298 } // namespace llvm
299