blob: 18d0e74a7744cd46ad99ee900eafefb8985f45a2 [file] [log] [blame]
Matt Davis673412e2018-08-24 22:59:13 +00001//===--------------------- ResourceManager.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/// The classes here represent processor resource units and their management
12/// strategy. These classes are managed by the Scheduler.
13///
14//===----------------------------------------------------------------------===//
15
Matt Davis271ce762018-08-27 17:16:32 +000016#include "HardwareUnits/ResourceManager.h"
Matt Davis673412e2018-08-24 22:59:13 +000017#include "Support.h"
18#include "llvm/Support/Debug.h"
19#include "llvm/Support/raw_ostream.h"
20
21namespace mca {
22
23using namespace llvm;
24
25#define DEBUG_TYPE "llvm-mca"
26ResourceStrategy::~ResourceStrategy() = default;
27
28void DefaultResourceStrategy::skipMask(uint64_t Mask) {
29 NextInSequenceMask &= (~Mask);
30 if (!NextInSequenceMask) {
31 NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
32 RemovedFromNextInSequence = 0;
33 }
34}
35
36uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) {
37 // This method assumes that ReadyMask cannot be zero.
38 uint64_t CandidateMask = llvm::PowerOf2Floor(NextInSequenceMask);
39 while (!(ReadyMask & CandidateMask)) {
40 skipMask(CandidateMask);
41 CandidateMask = llvm::PowerOf2Floor(NextInSequenceMask);
42 }
43 return CandidateMask;
44}
45
46void DefaultResourceStrategy::used(uint64_t Mask) {
47 if (Mask > NextInSequenceMask) {
48 RemovedFromNextInSequence |= Mask;
49 return;
50 }
51 skipMask(Mask);
52}
53
54ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index,
55 uint64_t Mask)
56 : ProcResourceDescIndex(Index), ResourceMask(Mask),
57 BufferSize(Desc.BufferSize) {
58 if (llvm::countPopulation(ResourceMask) > 1)
59 ResourceSizeMask = ResourceMask ^ llvm::PowerOf2Floor(ResourceMask);
60 else
61 ResourceSizeMask = (1ULL << Desc.NumUnits) - 1;
62 ReadyMask = ResourceSizeMask;
63 AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
64 Unavailable = false;
65}
66
67bool ResourceState::isReady(unsigned NumUnits) const {
68 return (!isReserved() || isADispatchHazard()) &&
69 llvm::countPopulation(ReadyMask) >= NumUnits;
70}
71
72ResourceStateEvent ResourceState::isBufferAvailable() const {
73 if (isADispatchHazard() && isReserved())
74 return RS_RESERVED;
75 if (!isBuffered() || AvailableSlots)
76 return RS_BUFFER_AVAILABLE;
77 return RS_BUFFER_UNAVAILABLE;
78}
79
80#ifndef NDEBUG
81void ResourceState::dump() const {
82 dbgs() << "MASK: " << ResourceMask << ", SIZE_MASK: " << ResourceSizeMask
83 << ", RDYMASK: " << ReadyMask << ", BufferSize=" << BufferSize
84 << ", AvailableSlots=" << AvailableSlots
85 << ", Reserved=" << Unavailable << '\n';
86}
87#endif
88
89static unsigned getResourceStateIndex(uint64_t Mask) {
90 return std::numeric_limits<uint64_t>::digits - llvm::countLeadingZeros(Mask);
91}
92
93static std::unique_ptr<ResourceStrategy>
94getStrategyFor(const ResourceState &RS) {
95 if (RS.isAResourceGroup() || RS.getNumUnits() > 1)
96 return llvm::make_unique<DefaultResourceStrategy>(RS.getReadyMask());
97 return std::unique_ptr<ResourceStrategy>(nullptr);
98}
99
100ResourceManager::ResourceManager(const MCSchedModel &SM)
101 : ProcResID2Mask(SM.getNumProcResourceKinds()) {
102 computeProcResourceMasks(SM, ProcResID2Mask);
103 Resources.resize(SM.getNumProcResourceKinds());
104 Strategies.resize(SM.getNumProcResourceKinds());
105
106 for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
107 uint64_t Mask = ProcResID2Mask[I];
108 unsigned Index = getResourceStateIndex(Mask);
109 Resources[Index] =
110 llvm::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);
111 Strategies[Index] = getStrategyFor(*Resources[Index]);
112 }
113}
114
115void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
116 uint64_t ResourceMask) {
117 unsigned Index = getResourceStateIndex(ResourceMask);
118 assert(Index < Resources.size() && "Invalid processor resource index!");
119 assert(S && "Unexpected null strategy in input!");
120 Strategies[Index] = std::move(S);
121}
122
123unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
124 return Resources[getResourceStateIndex(Mask)]->getProcResourceID();
125}
126
127unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
128 return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
129}
130
131// Returns the actual resource consumed by this Use.
132// First, is the primary resource ID.
133// Second, is the specific sub-resource ID.
134ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {
135 unsigned Index = getResourceStateIndex(ResourceID);
136 ResourceState &RS = *Resources[Index];
137 assert(RS.isReady() && "No available units to select!");
138
139 // Special case where RS is not a group, and it only declares a single
140 // resource unit.
141 if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)
142 return std::make_pair(ResourceID, RS.getReadyMask());
143
144 uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());
145 if (RS.isAResourceGroup())
146 return selectPipe(SubResourceID);
147 return std::make_pair(ResourceID, SubResourceID);
148}
149
150void ResourceManager::use(const ResourceRef &RR) {
151 // Mark the sub-resource referenced by RR as used.
152 ResourceState &RS = *Resources[getResourceStateIndex(RR.first)];
153 RS.markSubResourceAsUsed(RR.second);
154 // If there are still available units in RR.first,
155 // then we are done.
156 if (RS.isReady())
157 return;
158
159 // Notify to other resources that RR.first is no longer available.
160 for (std::unique_ptr<ResourceState> &Res : Resources) {
161 ResourceState &Current = *Res;
162 if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
163 continue;
164
165 if (Current.containsResource(RR.first)) {
166 unsigned Index = getResourceStateIndex(Current.getResourceMask());
167 Current.markSubResourceAsUsed(RR.first);
168 Strategies[Index]->used(RR.first);
169 }
170 }
171}
172
173void ResourceManager::release(const ResourceRef &RR) {
174 ResourceState &RS = *Resources[getResourceStateIndex(RR.first)];
175 bool WasFullyUsed = !RS.isReady();
176 RS.releaseSubResource(RR.second);
177 if (!WasFullyUsed)
178 return;
179
180 for (std::unique_ptr<ResourceState> &Res : Resources) {
181 ResourceState &Current = *Res;
182 if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
183 continue;
184
185 if (Current.containsResource(RR.first))
186 Current.releaseSubResource(RR.first);
187 }
188}
189
190ResourceStateEvent
191ResourceManager::canBeDispatched(ArrayRef<uint64_t> Buffers) const {
192 ResourceStateEvent Result = ResourceStateEvent::RS_BUFFER_AVAILABLE;
193 for (uint64_t Buffer : Buffers) {
194 ResourceState &RS = *Resources[getResourceStateIndex(Buffer)];
195 Result = RS.isBufferAvailable();
196 if (Result != ResourceStateEvent::RS_BUFFER_AVAILABLE)
197 break;
198 }
199 return Result;
200}
201
202void ResourceManager::reserveBuffers(ArrayRef<uint64_t> Buffers) {
203 for (const uint64_t Buffer : Buffers) {
204 ResourceState &RS = *Resources[getResourceStateIndex(Buffer)];
205 assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE);
206 RS.reserveBuffer();
207
208 if (RS.isADispatchHazard()) {
209 assert(!RS.isReserved());
210 RS.setReserved();
211 }
212 }
213}
214
215void ResourceManager::releaseBuffers(ArrayRef<uint64_t> Buffers) {
216 for (const uint64_t R : Buffers)
217 Resources[getResourceStateIndex(R)]->releaseBuffer();
218}
219
220bool ResourceManager::canBeIssued(const InstrDesc &Desc) const {
221 return std::all_of(Desc.Resources.begin(), Desc.Resources.end(),
222 [&](const std::pair<uint64_t, const ResourceUsage> &E) {
223 unsigned NumUnits =
224 E.second.isReserved() ? 0U : E.second.NumUnits;
225 unsigned Index = getResourceStateIndex(E.first);
226 return Resources[Index]->isReady(NumUnits);
227 });
228}
229
230// Returns true if all resources are in-order, and there is at least one
231// resource which is a dispatch hazard (BufferSize = 0).
232bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) const {
233 if (!canBeIssued(Desc))
234 return false;
235 bool AllInOrderResources = all_of(Desc.Buffers, [&](uint64_t BufferMask) {
236 unsigned Index = getResourceStateIndex(BufferMask);
237 const ResourceState &Resource = *Resources[Index];
238 return Resource.isInOrder() || Resource.isADispatchHazard();
239 });
240 if (!AllInOrderResources)
241 return false;
242
243 return any_of(Desc.Buffers, [&](uint64_t BufferMask) {
244 return Resources[getResourceStateIndex(BufferMask)]->isADispatchHazard();
245 });
246}
247
248void ResourceManager::issueInstruction(
249 const InstrDesc &Desc,
250 SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes) {
251 for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
252 const CycleSegment &CS = R.second.CS;
253 if (!CS.size()) {
254 releaseResource(R.first);
255 continue;
256 }
257
258 assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
259 if (!R.second.isReserved()) {
260 ResourceRef Pipe = selectPipe(R.first);
261 use(Pipe);
262 BusyResources[Pipe] += CS.size();
263 // Replace the resource mask with a valid processor resource index.
264 const ResourceState &RS = *Resources[getResourceStateIndex(Pipe.first)];
265 Pipe.first = RS.getProcResourceID();
266 Pipes.emplace_back(
267 std::pair<ResourceRef, double>(Pipe, static_cast<double>(CS.size())));
268 } else {
269 assert((countPopulation(R.first) > 1) && "Expected a group!");
270 // Mark this group as reserved.
271 assert(R.second.isReserved());
272 reserveResource(R.first);
273 BusyResources[ResourceRef(R.first, R.first)] += CS.size();
274 }
275 }
276}
277
278void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
279 for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
280 if (BR.second)
281 BR.second--;
282 if (!BR.second) {
283 // Release this resource.
284 const ResourceRef &RR = BR.first;
285
286 if (countPopulation(RR.first) == 1)
287 release(RR);
288
289 releaseResource(RR.first);
290 ResourcesFreed.push_back(RR);
291 }
292 }
293
294 for (const ResourceRef &RF : ResourcesFreed)
295 BusyResources.erase(RF);
296}
297
298void ResourceManager::reserveResource(uint64_t ResourceID) {
299 ResourceState &Resource = *Resources[getResourceStateIndex(ResourceID)];
300 assert(!Resource.isReserved());
301 Resource.setReserved();
302}
303
304void ResourceManager::releaseResource(uint64_t ResourceID) {
305 ResourceState &Resource = *Resources[getResourceStateIndex(ResourceID)];
306 Resource.clearReserved();
307}
308
309} // namespace mca