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