blob: a3aad18894509a1c2060010dfd90f028fe6ae9f8 [file] [log] [blame]
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +00001//===--------------------- Dispatch.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 implements methods declared by class RegisterFile, DispatchUnit
12/// and RetireControlUnit.
13///
14//===----------------------------------------------------------------------===//
15
16#include "Dispatch.h"
17#include "Backend.h"
Clement Courbet844f22d2018-03-13 13:11:01 +000018#include "HWEventListener.h"
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +000019#include "Scheduler.h"
20#include "llvm/Support/Debug.h"
21
22using namespace llvm;
23
24#define DEBUG_TYPE "llvm-mca"
25
26namespace mca {
27
28void RegisterFile::addRegisterMapping(WriteState &WS) {
29 unsigned RegID = WS.getRegisterID();
30 assert(RegID && "Adding an invalid register definition?");
31
32 RegisterMappings[RegID] = &WS;
33 for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I)
34 RegisterMappings[*I] = &WS;
35 if (MaxUsedMappings == NumUsedMappings)
36 MaxUsedMappings++;
37 NumUsedMappings++;
38 TotalMappingsCreated++;
39 // If this is a partial update, then we are done.
40 if (!WS.fullyUpdatesSuperRegs())
41 return;
42
43 for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I)
44 RegisterMappings[*I] = &WS;
45}
46
47void RegisterFile::invalidateRegisterMapping(const WriteState &WS) {
48 unsigned RegID = WS.getRegisterID();
49 bool ShouldInvalidateSuperRegs = WS.fullyUpdatesSuperRegs();
50
51 assert(RegID != 0 && "Invalidating an already invalid register?");
52 assert(WS.getCyclesLeft() != -512 &&
53 "Invalidating a write of unknown cycles!");
54 assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!");
55 if (!RegisterMappings[RegID])
56 return;
57
58 assert(NumUsedMappings);
59 NumUsedMappings--;
60
61 if (RegisterMappings[RegID] == &WS)
62 RegisterMappings[RegID] = nullptr;
63
64 for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I)
65 if (RegisterMappings[*I] == &WS)
66 RegisterMappings[*I] = nullptr;
67
68 if (!ShouldInvalidateSuperRegs)
69 return;
70
71 for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I)
72 if (RegisterMappings[*I] == &WS)
73 RegisterMappings[*I] = nullptr;
74}
75
76// Update the number of used mappings in the event of instruction retired.
77// This mehod delegates to the register file the task of invalidating
78// register mappings that were created for instruction IS.
79void DispatchUnit::invalidateRegisterMappings(const Instruction &IS) {
80 for (const std::unique_ptr<WriteState> &WS : IS.getDefs()) {
81 DEBUG(dbgs() << "[RAT] Invalidating mapping for: ");
82 DEBUG(WS->dump());
83 RAT->invalidateRegisterMapping(*WS.get());
84 }
85}
86
87void RegisterFile::collectWrites(SmallVectorImpl<WriteState *> &Writes,
88 unsigned RegID) const {
89 assert(RegID && RegID < RegisterMappings.size());
90 WriteState *WS = RegisterMappings[RegID];
91 if (WS) {
92 DEBUG(dbgs() << "Found a dependent use of RegID=" << RegID << '\n');
93 Writes.push_back(WS);
94 }
95
96 // Handle potential partial register updates.
97 for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
98 WS = RegisterMappings[*I];
99 if (WS && std::find(Writes.begin(), Writes.end(), WS) == Writes.end()) {
100 DEBUG(dbgs() << "Found a dependent use of subReg " << *I << " (part of "
101 << RegID << ")\n");
102 Writes.push_back(WS);
103 }
104 }
105}
106
107bool RegisterFile::isAvailable(unsigned NumRegWrites) {
108 if (!TotalMappings)
109 return true;
110 if (NumRegWrites > TotalMappings) {
111 // The user specified a too small number of registers.
112 // Artificially set the number of temporaries to NumRegWrites.
113 errs() << "warning: not enough temporaries in the register file. "
114 << "The register file size has been automatically increased to "
115 << NumRegWrites << '\n';
116 TotalMappings = NumRegWrites;
117 }
118
119 return NumRegWrites + NumUsedMappings <= TotalMappings;
120}
121
122#ifndef NDEBUG
123void RegisterFile::dump() const {
124 for (unsigned I = 0, E = MRI.getNumRegs(); I < E; ++I)
125 if (RegisterMappings[I]) {
126 dbgs() << MRI.getName(I) << ", " << I << ", ";
127 RegisterMappings[I]->dump();
128 }
129
130 dbgs() << "TotalMappingsCreated: " << TotalMappingsCreated
131 << ", MaxUsedMappings: " << MaxUsedMappings
132 << ", NumUsedMappings: " << NumUsedMappings << '\n';
133}
134#endif
135
136// Reserves a number of slots, and returns a new token.
137unsigned RetireControlUnit::reserveSlot(unsigned Index, unsigned NumMicroOps) {
138 assert(isAvailable(NumMicroOps));
139 unsigned NormalizedQuantity =
140 std::min(NumMicroOps, static_cast<unsigned>(Queue.size()));
141 // Zero latency instructions may have zero mOps. Artificially bump this
142 // value to 1. Although zero latency instructions don't consume scheduler
143 // resources, they still consume one slot in the retire queue.
144 NormalizedQuantity = std::max(NormalizedQuantity, 1U);
145 unsigned TokenID = NextAvailableSlotIdx;
146 Queue[NextAvailableSlotIdx] = {Index, NormalizedQuantity, false};
147 NextAvailableSlotIdx += NormalizedQuantity;
148 NextAvailableSlotIdx %= Queue.size();
149 AvailableSlots -= NormalizedQuantity;
150 return TokenID;
151}
152
153void DispatchUnit::notifyInstructionDispatched(unsigned Index) {
Clement Courbet844f22d2018-03-13 13:11:01 +0000154 DEBUG(dbgs() << "[E] Instruction Dispatched: " << Index << '\n');
155 Owner->notifyInstructionEvent(
156 HWInstructionEvent(HWInstructionEvent::Dispatched, Index));
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000157}
158
159void DispatchUnit::notifyInstructionRetired(unsigned Index) {
Clement Courbet844f22d2018-03-13 13:11:01 +0000160 DEBUG(dbgs() << "[E] Instruction Retired: " << Index << '\n');
161 Owner->notifyInstructionEvent(
162 HWInstructionEvent(HWInstructionEvent::Retired, Index));
163
164 const Instruction &IS = Owner->getInstruction(Index);
165 invalidateRegisterMappings(IS);
166 Owner->eraseInstruction(Index);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000167}
168
169void RetireControlUnit::cycleEvent() {
170 if (isEmpty())
171 return;
172
173 unsigned NumRetired = 0;
174 while (!isEmpty()) {
175 if (MaxRetirePerCycle != 0 && NumRetired == MaxRetirePerCycle)
176 break;
177 RUToken &Current = Queue[CurrentInstructionSlotIdx];
178 assert(Current.NumSlots && "Reserved zero slots?");
179 if (!Current.Executed)
180 break;
181 Owner->notifyInstructionRetired(Current.Index);
182 CurrentInstructionSlotIdx += Current.NumSlots;
183 CurrentInstructionSlotIdx %= Queue.size();
184 AvailableSlots += Current.NumSlots;
185 NumRetired++;
186 }
187}
188
189void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
190 assert(Queue.size() > TokenID);
191 assert(Queue[TokenID].Executed == false && Queue[TokenID].Index != ~0U);
192 Queue[TokenID].Executed = true;
193}
194
195#ifndef NDEBUG
196void RetireControlUnit::dump() const {
197 dbgs() << "Retire Unit: { Total Slots=" << Queue.size()
198 << ", Available Slots=" << AvailableSlots << " }\n";
199}
200#endif
201
202bool DispatchUnit::checkRAT(const InstrDesc &Desc) {
203 unsigned NumWrites = Desc.Writes.size();
204 if (RAT->isAvailable(NumWrites))
205 return true;
206 DispatchStalls[DS_RAT_REG_UNAVAILABLE]++;
207 return false;
208}
209
210bool DispatchUnit::checkRCU(const InstrDesc &Desc) {
211 unsigned NumMicroOps = Desc.NumMicroOps;
212 if (RCU->isAvailable(NumMicroOps))
213 return true;
214 DispatchStalls[DS_RCU_TOKEN_UNAVAILABLE]++;
215 return false;
216}
217
218bool DispatchUnit::checkScheduler(const InstrDesc &Desc) {
219 // If this is a zero-latency instruction, then it bypasses
220 // the scheduler.
221 switch (SC->canBeDispatched(Desc)) {
222 case Scheduler::HWS_AVAILABLE:
223 return true;
224 case Scheduler::HWS_QUEUE_UNAVAILABLE:
225 DispatchStalls[DS_SQ_TOKEN_UNAVAILABLE]++;
226 break;
227 case Scheduler::HWS_LD_QUEUE_UNAVAILABLE:
228 DispatchStalls[DS_LDQ_TOKEN_UNAVAILABLE]++;
229 break;
230 case Scheduler::HWS_ST_QUEUE_UNAVAILABLE:
231 DispatchStalls[DS_STQ_TOKEN_UNAVAILABLE]++;
232 break;
233 case Scheduler::HWS_DISPATCH_GROUP_RESTRICTION:
234 DispatchStalls[DS_DISPATCH_GROUP_RESTRICTION]++;
235 }
236
237 return false;
238}
239
240unsigned DispatchUnit::dispatch(unsigned IID, Instruction *NewInst) {
241 assert(!CarryOver && "Cannot dispatch another instruction!");
242 unsigned NumMicroOps = NewInst->getDesc().NumMicroOps;
243 if (NumMicroOps > DispatchWidth) {
244 assert(AvailableEntries == DispatchWidth);
245 AvailableEntries = 0;
246 CarryOver = NumMicroOps - DispatchWidth;
247 } else {
248 assert(AvailableEntries >= NumMicroOps);
249 AvailableEntries -= NumMicroOps;
250 }
251
252 // Reserve slots in the RCU.
253 unsigned RCUTokenID = RCU->reserveSlot(IID, NumMicroOps);
Andrea Di Biagio373c38a2018-03-08 20:21:55 +0000254 NewInst->setRCUTokenID(RCUTokenID);
Clement Courbet844f22d2018-03-13 13:11:01 +0000255 notifyInstructionDispatched(IID);
Andrea Di Biagio3a6b0922018-03-08 13:05:02 +0000256
257 SC->scheduleInstruction(IID, NewInst);
258 return RCUTokenID;
259}
260
261#ifndef NDEBUG
262void DispatchUnit::dump() const {
263 RAT->dump();
264 RCU->dump();
265
266 unsigned DSRAT = DispatchStalls[DS_RAT_REG_UNAVAILABLE];
267 unsigned DSRCU = DispatchStalls[DS_RCU_TOKEN_UNAVAILABLE];
268 unsigned DSSCHEDQ = DispatchStalls[DS_SQ_TOKEN_UNAVAILABLE];
269 unsigned DSLQ = DispatchStalls[DS_LDQ_TOKEN_UNAVAILABLE];
270 unsigned DSSQ = DispatchStalls[DS_STQ_TOKEN_UNAVAILABLE];
271
272 dbgs() << "STALLS --- RAT: " << DSRAT << ", RCU: " << DSRCU
273 << ", SCHED_QUEUE: " << DSSCHEDQ << ", LOAD_QUEUE: " << DSLQ
274 << ", STORE_QUEUE: " << DSSQ << '\n';
275}
276#endif
277
278} // namespace mca