• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===---------------------- ExecuteStage.cpp --------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 /// \file
10 ///
11 /// This file defines the execution stage of an instruction pipeline.
12 ///
13 /// The ExecuteStage is responsible for managing the hardware scheduler
14 /// and issuing notifications that an instruction has been executed.
15 ///
16 //===----------------------------------------------------------------------===//
17 
18 #include "ExecuteStage.h"
19 #include "Scheduler.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/Support/Debug.h"
22 
23 #define DEBUG_TYPE "llvm-mca"
24 
25 namespace mca {
26 
27 using namespace llvm;
28 
29 // Reclaim the simulated resources used by the scheduler.
reclaimSchedulerResources()30 void ExecuteStage::reclaimSchedulerResources() {
31   SmallVector<ResourceRef, 8> ResourcesFreed;
32   HWS.reclaimSimulatedResources(ResourcesFreed);
33   for (const ResourceRef &RR : ResourcesFreed)
34     notifyResourceAvailable(RR);
35 }
36 
37 // Update the scheduler's instruction queues.
updateSchedulerQueues()38 void ExecuteStage::updateSchedulerQueues() {
39   SmallVector<InstRef, 4> InstructionIDs;
40   HWS.updateIssuedQueue(InstructionIDs);
41   for (const InstRef &IR : InstructionIDs)
42     notifyInstructionExecuted(IR);
43   InstructionIDs.clear();
44 
45   HWS.updatePendingQueue(InstructionIDs);
46   for (const InstRef &IR : InstructionIDs)
47     notifyInstructionReady(IR);
48 }
49 
50 // Issue instructions that are waiting in the scheduler's ready queue.
issueReadyInstructions()51 void ExecuteStage::issueReadyInstructions() {
52   SmallVector<InstRef, 4> InstructionIDs;
53   InstRef IR = HWS.select();
54   while (IR.isValid()) {
55     SmallVector<std::pair<ResourceRef, double>, 4> Used;
56     HWS.issueInstruction(IR, Used);
57 
58     // Reclaim instruction resources and perform notifications.
59     const InstrDesc &Desc = IR.getInstruction()->getDesc();
60     notifyReleasedBuffers(Desc.Buffers);
61     notifyInstructionIssued(IR, Used);
62     if (IR.getInstruction()->isExecuted())
63       notifyInstructionExecuted(IR);
64 
65     // Instructions that have been issued during this cycle might have unblocked
66     // other dependent instructions. Dependent instructions may be issued during
67     // this same cycle if operands have ReadAdvance entries.  Promote those
68     // instructions to the ReadyQueue and tell to the caller that we need
69     // another round of 'issue()'.
70     HWS.promoteToReadyQueue(InstructionIDs);
71     for (const InstRef &I : InstructionIDs)
72       notifyInstructionReady(I);
73     InstructionIDs.clear();
74 
75     // Select the next instruction to issue.
76     IR = HWS.select();
77   }
78 }
79 
80 // The following routine is the maintenance routine of the ExecuteStage.
81 // It is responsible for updating the hardware scheduler (HWS), including
82 // reclaiming the HWS's simulated hardware resources, as well as updating the
83 // HWS's queues.
84 //
85 // This routine also processes the instructions that are ready for issuance.
86 // These instructions are managed by the HWS's ready queue and can be accessed
87 // via the Scheduler::select() routine.
88 //
89 // Notifications are issued to this stage's listeners when instructions are
90 // moved between the HWS's queues.  In particular, when an instruction becomes
91 // ready or executed.
cycleStart()92 void ExecuteStage::cycleStart() {
93   reclaimSchedulerResources();
94   updateSchedulerQueues();
95   issueReadyInstructions();
96 }
97 
98 // Schedule the instruction for execution on the hardware.
execute(InstRef & IR)99 bool ExecuteStage::execute(InstRef &IR) {
100 #ifndef NDEBUG
101   // Ensure that the HWS has not stored this instruction in its queues.
102   HWS.sanityCheck(IR);
103 #endif
104   // Reserve a slot in each buffered resource. Also, mark units with
105   // BufferSize=0 as reserved. Resources with a buffer size of zero will only
106   // be released after MCIS is issued, and all the ResourceCycles for those
107   // units have been consumed.
108   const InstrDesc &Desc = IR.getInstruction()->getDesc();
109   HWS.reserveBuffers(Desc.Buffers);
110   notifyReservedBuffers(Desc.Buffers);
111 
112   // Obtain a slot in the LSU.  If we cannot reserve resources, return true, so
113   // that succeeding stages can make progress.
114   if (!HWS.reserveResources(IR))
115     return true;
116 
117   // If we did not return early, then the scheduler is ready for execution.
118   notifyInstructionReady(IR);
119 
120   // Don't add a zero-latency instruction to the Wait or Ready queue.
121   // A zero-latency instruction doesn't consume any scheduler resources. That is
122   // because it doesn't need to be executed, and it is often removed at register
123   // renaming stage. For example, register-register moves are often optimized at
124   // register renaming stage by simply updating register aliases. On some
125   // targets, zero-idiom instructions (for example: a xor that clears the value
126   // of a register) are treated specially, and are often eliminated at register
127   // renaming stage.
128   //
129   // Instructions that use an in-order dispatch/issue processor resource must be
130   // issued immediately to the pipeline(s). Any other in-order buffered
131   // resources (i.e. BufferSize=1) is consumed.
132   //
133   // If we cannot issue immediately, the HWS will add IR to its ready queue for
134   // execution later, so we must return early here.
135   if (!HWS.issueImmediately(IR))
136     return true;
137 
138   LLVM_DEBUG(dbgs() << "[SCHEDULER] Instruction #" << IR
139                     << " issued immediately\n");
140 
141   // Issue IR.  The resources for this issuance will be placed in 'Used.'
142   SmallVector<std::pair<ResourceRef, double>, 4> Used;
143   HWS.issueInstruction(IR, Used);
144 
145   // Perform notifications.
146   notifyReleasedBuffers(Desc.Buffers);
147   notifyInstructionIssued(IR, Used);
148   if (IR.getInstruction()->isExecuted())
149     notifyInstructionExecuted(IR);
150 
151   return true;
152 }
153 
notifyInstructionExecuted(const InstRef & IR)154 void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) {
155   HWS.onInstructionExecuted(IR);
156   LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n');
157   notifyEvent<HWInstructionEvent>(
158       HWInstructionEvent(HWInstructionEvent::Executed, IR));
159   RCU.onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
160 }
161 
notifyInstructionReady(const InstRef & IR)162 void ExecuteStage::notifyInstructionReady(const InstRef &IR) {
163   LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n');
164   notifyEvent<HWInstructionEvent>(
165       HWInstructionEvent(HWInstructionEvent::Ready, IR));
166 }
167 
notifyResourceAvailable(const ResourceRef & RR)168 void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) {
169   LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.'
170                     << RR.second << "]\n");
171   for (HWEventListener *Listener : getListeners())
172     Listener->onResourceAvailable(RR);
173 }
174 
notifyInstructionIssued(const InstRef & IR,ArrayRef<std::pair<ResourceRef,double>> Used)175 void ExecuteStage::notifyInstructionIssued(
176     const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) {
177   LLVM_DEBUG({
178     dbgs() << "[E] Instruction Issued: #" << IR << '\n';
179     for (const std::pair<ResourceRef, unsigned> &Resource : Used) {
180       dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
181              << Resource.first.second << "], ";
182       dbgs() << "cycles: " << Resource.second << '\n';
183     }
184   });
185   notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
186 }
187 
notifyReservedBuffers(ArrayRef<uint64_t> Buffers)188 void ExecuteStage::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) {
189   if (Buffers.empty())
190     return;
191 
192   SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
193   std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
194                  [&](uint64_t Op) { return HWS.getResourceID(Op); });
195   for (HWEventListener *Listener : getListeners())
196     Listener->onReservedBuffers(BufferIDs);
197 }
198 
notifyReleasedBuffers(ArrayRef<uint64_t> Buffers)199 void ExecuteStage::notifyReleasedBuffers(ArrayRef<uint64_t> Buffers) {
200   if (Buffers.empty())
201     return;
202 
203   SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
204   std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
205                  [&](uint64_t Op) { return HWS.getResourceID(Op); });
206   for (HWEventListener *Listener : getListeners())
207     Listener->onReleasedBuffers(BufferIDs);
208 }
209 
210 } // namespace mca
211