Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / llvm / lib / MCA / Stages / ExecuteStage.cpp
blob7714d4ff8aedeae01a801caf93c31220e0e36048
1 //===---------------------- ExecuteStage.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 /// \file
9 ///
10 /// This file defines the execution stage of an instruction pipeline.
11 ///
12 /// The ExecuteStage is responsible for managing the hardware scheduler
13 /// and issuing notifications that an instruction has been executed.
14 ///
15 //===----------------------------------------------------------------------===//
17 #include "llvm/MCA/Stages/ExecuteStage.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/Support/Debug.h"
21 #define DEBUG_TYPE "llvm-mca"
23 namespace llvm {
24 namespace mca {
26 HWStallEvent::GenericEventType toHWStallEventType(Scheduler::Status Status) {
27 switch (Status) {
28 case Scheduler::SC_LOAD_QUEUE_FULL:
29 return HWStallEvent::LoadQueueFull;
30 case Scheduler::SC_STORE_QUEUE_FULL:
31 return HWStallEvent::StoreQueueFull;
32 case Scheduler::SC_BUFFERS_FULL:
33 return HWStallEvent::SchedulerQueueFull;
34 case Scheduler::SC_DISPATCH_GROUP_STALL:
35 return HWStallEvent::DispatchGroupStall;
36 case Scheduler::SC_AVAILABLE:
37 return HWStallEvent::Invalid;
40 llvm_unreachable("Don't know how to process this StallKind!");
43 bool ExecuteStage::isAvailable(const InstRef &IR) const {
44 if (Scheduler::Status S = HWS.isAvailable(IR)) {
45 HWStallEvent::GenericEventType ET = toHWStallEventType(S);
46 notifyEvent<HWStallEvent>(HWStallEvent(ET, IR));
47 return false;
50 return true;
53 Error ExecuteStage::issueInstruction(InstRef &IR) {
54 SmallVector<ResourceUse, 4> Used;
55 SmallVector<InstRef, 4> Pending;
56 SmallVector<InstRef, 4> Ready;
58 HWS.issueInstruction(IR, Used, Pending, Ready);
59 Instruction &IS = *IR.getInstruction();
60 NumIssuedOpcodes += IS.getNumMicroOps();
62 notifyReservedOrReleasedBuffers(IR, /* Reserved */ false);
64 notifyInstructionIssued(IR, Used);
65 if (IS.isExecuted()) {
66 notifyInstructionExecuted(IR);
67 // FIXME: add a buffer of executed instructions.
68 if (Error S = moveToTheNextStage(IR))
69 return S;
72 for (const InstRef &I : Pending)
73 notifyInstructionPending(I);
75 for (const InstRef &I : Ready)
76 notifyInstructionReady(I);
77 return ErrorSuccess();
80 Error ExecuteStage::issueReadyInstructions() {
81 InstRef IR = HWS.select();
82 while (IR) {
83 if (Error Err = issueInstruction(IR))
84 return Err;
86 // Select the next instruction to issue.
87 IR = HWS.select();
90 return ErrorSuccess();
93 Error ExecuteStage::cycleStart() {
94 SmallVector<ResourceRef, 8> Freed;
95 SmallVector<InstRef, 4> Executed;
96 SmallVector<InstRef, 4> Pending;
97 SmallVector<InstRef, 4> Ready;
99 HWS.cycleEvent(Freed, Executed, Pending, Ready);
100 NumDispatchedOpcodes = 0;
101 NumIssuedOpcodes = 0;
103 for (const ResourceRef &RR : Freed)
104 notifyResourceAvailable(RR);
106 for (InstRef &IR : Executed) {
107 notifyInstructionExecuted(IR);
108 // FIXME: add a buffer of executed instructions.
109 if (Error S = moveToTheNextStage(IR))
110 return S;
113 for (const InstRef &IR : Pending)
114 notifyInstructionPending(IR);
116 for (const InstRef &IR : Ready)
117 notifyInstructionReady(IR);
119 return issueReadyInstructions();
122 Error ExecuteStage::cycleEnd() {
123 if (!EnablePressureEvents)
124 return ErrorSuccess();
126 // Always conservatively report any backpressure events if the dispatch logic
127 // was stalled due to unavailable scheduler resources.
128 if (!HWS.hadTokenStall() && NumDispatchedOpcodes <= NumIssuedOpcodes)
129 return ErrorSuccess();
131 SmallVector<InstRef, 8> Insts;
132 uint64_t Mask = HWS.analyzeResourcePressure(Insts);
133 if (Mask) {
134 LLVM_DEBUG(dbgs() << "[E] Backpressure increased because of unavailable "
135 "pipeline resources: "
136 << format_hex(Mask, 16) << '\n');
137 HWPressureEvent Ev(HWPressureEvent::RESOURCES, Insts, Mask);
138 notifyEvent(Ev);
141 SmallVector<InstRef, 8> RegDeps;
142 SmallVector<InstRef, 8> MemDeps;
143 HWS.analyzeDataDependencies(RegDeps, MemDeps);
144 if (RegDeps.size()) {
145 LLVM_DEBUG(
146 dbgs() << "[E] Backpressure increased by register dependencies\n");
147 HWPressureEvent Ev(HWPressureEvent::REGISTER_DEPS, RegDeps);
148 notifyEvent(Ev);
151 if (MemDeps.size()) {
152 LLVM_DEBUG(dbgs() << "[E] Backpressure increased by memory dependencies\n");
153 HWPressureEvent Ev(HWPressureEvent::MEMORY_DEPS, MemDeps);
154 notifyEvent(Ev);
157 return ErrorSuccess();
160 #ifndef NDEBUG
161 static void verifyInstructionEliminated(const InstRef &IR) {
162 const Instruction &Inst = *IR.getInstruction();
163 assert(Inst.isEliminated() && "Instruction was not eliminated!");
164 assert(Inst.isReady() && "Instruction in an inconsistent state!");
166 // Ensure that instructions eliminated at register renaming stage are in a
167 // consistent state.
168 assert(!Inst.getMayLoad() && !Inst.getMayStore() &&
169 "Cannot eliminate a memory op!");
171 #endif
173 Error ExecuteStage::handleInstructionEliminated(InstRef &IR) {
174 #ifndef NDEBUG
175 verifyInstructionEliminated(IR);
176 #endif
177 notifyInstructionPending(IR);
178 notifyInstructionReady(IR);
179 notifyInstructionIssued(IR, {});
180 IR.getInstruction()->forceExecuted();
181 notifyInstructionExecuted(IR);
182 return moveToTheNextStage(IR);
185 // Schedule the instruction for execution on the hardware.
186 Error ExecuteStage::execute(InstRef &IR) {
187 assert(isAvailable(IR) && "Scheduler is not available!");
189 #ifndef NDEBUG
190 // Ensure that the HWS has not stored this instruction in its queues.
191 HWS.instructionCheck(IR);
192 #endif
194 if (IR.getInstruction()->isEliminated())
195 return handleInstructionEliminated(IR);
197 // Reserve a slot in each buffered resource. Also, mark units with
198 // BufferSize=0 as reserved. Resources with a buffer size of zero will only
199 // be released after MCIS is issued, and all the ReleaseAtCycles for those
200 // units have been consumed.
201 bool IsReadyInstruction = HWS.dispatch(IR);
202 const Instruction &Inst = *IR.getInstruction();
203 unsigned NumMicroOps = Inst.getNumMicroOps();
204 NumDispatchedOpcodes += NumMicroOps;
205 notifyReservedOrReleasedBuffers(IR, /* Reserved */ true);
207 if (!IsReadyInstruction) {
208 if (Inst.isPending())
209 notifyInstructionPending(IR);
210 return ErrorSuccess();
213 notifyInstructionPending(IR);
215 // If we did not return early, then the scheduler is ready for execution.
216 notifyInstructionReady(IR);
218 // If we cannot issue immediately, the HWS will add IR to its ready queue for
219 // execution later, so we must return early here.
220 if (!HWS.mustIssueImmediately(IR))
221 return ErrorSuccess();
223 // Issue IR to the underlying pipelines.
224 return issueInstruction(IR);
227 void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) const {
228 LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n');
229 notifyEvent<HWInstructionEvent>(
230 HWInstructionEvent(HWInstructionEvent::Executed, IR));
233 void ExecuteStage::notifyInstructionPending(const InstRef &IR) const {
234 LLVM_DEBUG(dbgs() << "[E] Instruction Pending: #" << IR << '\n');
235 notifyEvent<HWInstructionEvent>(
236 HWInstructionEvent(HWInstructionEvent::Pending, IR));
239 void ExecuteStage::notifyInstructionReady(const InstRef &IR) const {
240 LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n');
241 notifyEvent<HWInstructionEvent>(
242 HWInstructionEvent(HWInstructionEvent::Ready, IR));
245 void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) const {
246 LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.'
247 << RR.second << "]\n");
248 for (HWEventListener *Listener : getListeners())
249 Listener->onResourceAvailable(RR);
252 void ExecuteStage::notifyInstructionIssued(
253 const InstRef &IR, MutableArrayRef<ResourceUse> Used) const {
254 LLVM_DEBUG({
255 dbgs() << "[E] Instruction Issued: #" << IR << '\n';
256 for (const ResourceUse &Use : Used) {
257 assert(Use.second.getDenominator() == 1 && "Invalid cycles!");
258 dbgs() << "[E] Resource Used: [" << Use.first.first << '.'
259 << Use.first.second << "], ";
260 dbgs() << "cycles: " << Use.second.getNumerator() << '\n';
264 // Replace resource masks with valid resource processor IDs.
265 for (ResourceUse &Use : Used)
266 Use.first.first = HWS.getResourceID(Use.first.first);
268 notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
271 void ExecuteStage::notifyReservedOrReleasedBuffers(const InstRef &IR,
272 bool Reserved) const {
273 uint64_t UsedBuffers = IR.getInstruction()->getDesc().UsedBuffers;
274 if (!UsedBuffers)
275 return;
277 SmallVector<unsigned, 4> BufferIDs(llvm::popcount(UsedBuffers), 0);
278 for (unsigned I = 0, E = BufferIDs.size(); I < E; ++I) {
279 uint64_t CurrentBufferMask = UsedBuffers & (-UsedBuffers);
280 BufferIDs[I] = HWS.getResourceID(CurrentBufferMask);
281 UsedBuffers ^= CurrentBufferMask;
284 if (Reserved) {
285 for (HWEventListener *Listener : getListeners())
286 Listener->onReservedBuffers(IR, BufferIDs);
287 return;
290 for (HWEventListener *Listener : getListeners())
291 Listener->onReleasedBuffers(IR, BufferIDs);
294 } // namespace mca
295 } // namespace llvm