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 "llvm/MCA/Stages/ExecuteStage.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/Support/Debug.h"
21 
22 #define DEBUG_TYPE "llvm-mca"
23 
24 namespace llvm {
25 namespace mca {
26 
toHWStallEventType(Scheduler::Status Status)27 HWStallEvent::GenericEventType toHWStallEventType(Scheduler::Status Status) {
28   switch (Status) {
29   case Scheduler::SC_LOAD_QUEUE_FULL:
30     return HWStallEvent::LoadQueueFull;
31   case Scheduler::SC_STORE_QUEUE_FULL:
32     return HWStallEvent::StoreQueueFull;
33   case Scheduler::SC_BUFFERS_FULL:
34     return HWStallEvent::SchedulerQueueFull;
35   case Scheduler::SC_DISPATCH_GROUP_STALL:
36     return HWStallEvent::DispatchGroupStall;
37   case Scheduler::SC_AVAILABLE:
38     return HWStallEvent::Invalid;
39   }
40 
41   llvm_unreachable("Don't know how to process this StallKind!");
42 }
43 
isAvailable(const InstRef & IR) const44 bool ExecuteStage::isAvailable(const InstRef &IR) const {
45   if (Scheduler::Status S = HWS.isAvailable(IR)) {
46     HWStallEvent::GenericEventType ET = toHWStallEventType(S);
47     notifyEvent<HWStallEvent>(HWStallEvent(ET, IR));
48     return false;
49   }
50 
51   return true;
52 }
53 
issueInstruction(InstRef & IR)54 Error ExecuteStage::issueInstruction(InstRef &IR) {
55   SmallVector<std::pair<ResourceRef, ResourceCycles>, 4> Used;
56   SmallVector<InstRef, 4> Ready;
57   HWS.issueInstruction(IR, Used, Ready);
58 
59   notifyReservedOrReleasedBuffers(IR, /* Reserved */ false);
60 
61   notifyInstructionIssued(IR, Used);
62   if (IR.getInstruction()->isExecuted()) {
63     notifyInstructionExecuted(IR);
64     // FIXME: add a buffer of executed instructions.
65     if (Error S = moveToTheNextStage(IR))
66       return S;
67   }
68 
69   for (const InstRef &I : Ready)
70     notifyInstructionReady(I);
71   return ErrorSuccess();
72 }
73 
issueReadyInstructions()74 Error ExecuteStage::issueReadyInstructions() {
75   InstRef IR = HWS.select();
76   while (IR) {
77     if (Error Err = issueInstruction(IR))
78       return Err;
79 
80     // Select the next instruction to issue.
81     IR = HWS.select();
82   }
83 
84   return ErrorSuccess();
85 }
86 
cycleStart()87 Error ExecuteStage::cycleStart() {
88   SmallVector<ResourceRef, 8> Freed;
89   SmallVector<InstRef, 4> Executed;
90   SmallVector<InstRef, 4> Ready;
91 
92   HWS.cycleEvent(Freed, Executed, Ready);
93 
94   for (const ResourceRef &RR : Freed)
95     notifyResourceAvailable(RR);
96 
97   for (InstRef &IR : Executed) {
98     notifyInstructionExecuted(IR);
99     // FIXME: add a buffer of executed instructions.
100     if (Error S = moveToTheNextStage(IR))
101       return S;
102   }
103 
104   for (const InstRef &IR : Ready)
105     notifyInstructionReady(IR);
106 
107   return issueReadyInstructions();
108 }
109 
110 #ifndef NDEBUG
verifyInstructionEliminated(const InstRef & IR)111 static void verifyInstructionEliminated(const InstRef &IR) {
112   const Instruction &Inst = *IR.getInstruction();
113   assert(Inst.isEliminated() && "Instruction was not eliminated!");
114   assert(Inst.isReady() && "Instruction in an inconsistent state!");
115 
116   // Ensure that instructions eliminated at register renaming stage are in a
117   // consistent state.
118   const InstrDesc &Desc = Inst.getDesc();
119   assert(!Desc.MayLoad && !Desc.MayStore && "Cannot eliminate a memory op!");
120 }
121 #endif
122 
handleInstructionEliminated(InstRef & IR)123 Error ExecuteStage::handleInstructionEliminated(InstRef &IR) {
124 #ifndef NDEBUG
125   verifyInstructionEliminated(IR);
126 #endif
127   notifyInstructionReady(IR);
128   notifyInstructionIssued(IR, {});
129   IR.getInstruction()->forceExecuted();
130   notifyInstructionExecuted(IR);
131   return moveToTheNextStage(IR);
132 }
133 
134 // Schedule the instruction for execution on the hardware.
execute(InstRef & IR)135 Error ExecuteStage::execute(InstRef &IR) {
136   assert(isAvailable(IR) && "Scheduler is not available!");
137 
138 #ifndef NDEBUG
139   // Ensure that the HWS has not stored this instruction in its queues.
140   HWS.sanityCheck(IR);
141 #endif
142 
143   if (IR.getInstruction()->isEliminated())
144     return handleInstructionEliminated(IR);
145 
146   // Reserve a slot in each buffered resource. Also, mark units with
147   // BufferSize=0 as reserved. Resources with a buffer size of zero will only
148   // be released after MCIS is issued, and all the ResourceCycles for those
149   // units have been consumed.
150   HWS.dispatch(IR);
151   notifyReservedOrReleasedBuffers(IR, /* Reserved */ true);
152   if (!HWS.isReady(IR))
153     return ErrorSuccess();
154 
155   // If we did not return early, then the scheduler is ready for execution.
156   notifyInstructionReady(IR);
157 
158   // If we cannot issue immediately, the HWS will add IR to its ready queue for
159   // execution later, so we must return early here.
160   if (!HWS.mustIssueImmediately(IR))
161     return ErrorSuccess();
162 
163   // Issue IR to the underlying pipelines.
164   return issueInstruction(IR);
165 }
166 
notifyInstructionExecuted(const InstRef & IR) const167 void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) const {
168   LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n');
169   notifyEvent<HWInstructionEvent>(
170       HWInstructionEvent(HWInstructionEvent::Executed, IR));
171 }
172 
notifyInstructionReady(const InstRef & IR) const173 void ExecuteStage::notifyInstructionReady(const InstRef &IR) const {
174   LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n');
175   notifyEvent<HWInstructionEvent>(
176       HWInstructionEvent(HWInstructionEvent::Ready, IR));
177 }
178 
notifyResourceAvailable(const ResourceRef & RR) const179 void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) const {
180   LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.'
181                     << RR.second << "]\n");
182   for (HWEventListener *Listener : getListeners())
183     Listener->onResourceAvailable(RR);
184 }
185 
notifyInstructionIssued(const InstRef & IR,MutableArrayRef<std::pair<ResourceRef,ResourceCycles>> Used) const186 void ExecuteStage::notifyInstructionIssued(
187     const InstRef &IR,
188     MutableArrayRef<std::pair<ResourceRef, ResourceCycles>> Used) const {
189   LLVM_DEBUG({
190     dbgs() << "[E] Instruction Issued: #" << IR << '\n';
191     for (const std::pair<ResourceRef, ResourceCycles> &Resource : Used) {
192       dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
193              << Resource.first.second << "], ";
194       dbgs() << "cycles: " << Resource.second << '\n';
195     }
196   });
197 
198   // Replace resource masks with valid resource processor IDs.
199   for (std::pair<ResourceRef, ResourceCycles> &Use : Used)
200     Use.first.first = HWS.getResourceID(Use.first.first);
201 
202   notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
203 }
204 
notifyReservedOrReleasedBuffers(const InstRef & IR,bool Reserved) const205 void ExecuteStage::notifyReservedOrReleasedBuffers(const InstRef &IR,
206                                                    bool Reserved) const {
207   const InstrDesc &Desc = IR.getInstruction()->getDesc();
208   if (Desc.Buffers.empty())
209     return;
210 
211   SmallVector<unsigned, 4> BufferIDs(Desc.Buffers.begin(), Desc.Buffers.end());
212   std::transform(Desc.Buffers.begin(), Desc.Buffers.end(), BufferIDs.begin(),
213                  [&](uint64_t Op) { return HWS.getResourceID(Op); });
214   if (Reserved) {
215     for (HWEventListener *Listener : getListeners())
216       Listener->onReservedBuffers(IR, BufferIDs);
217     return;
218   }
219 
220   for (HWEventListener *Listener : getListeners())
221     Listener->onReleasedBuffers(IR, BufferIDs);
222 }
223 
224 } // namespace mca
225 } // namespace llvm
226