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