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