LLVM 23.0.0git
ResourceManager.cpp
Go to the documentation of this file.
1//===--------------------- ResourceManager.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/// The classes here represent processor resource units and their management
11/// strategy. These classes are managed by the Scheduler.
12///
13//===----------------------------------------------------------------------===//
14
16#include "llvm/MCA/Support.h"
17#include "llvm/Support/Debug.h"
19
20namespace llvm {
21namespace mca {
22
23#define DEBUG_TYPE "llvm-mca"
25
26static uint64_t selectImpl(uint64_t CandidateMask,
27 uint64_t &NextInSequenceMask) {
28 // The upper bit set in CandidateMask identifies our next candidate resource.
29 CandidateMask = 1ULL << getResourceStateIndex(CandidateMask);
30 NextInSequenceMask &= (CandidateMask | (CandidateMask - 1));
31 return CandidateMask;
32}
33
35 // This method assumes that ReadyMask cannot be zero.
36 uint64_t CandidateMask = ReadyMask & NextInSequenceMask;
37 if (CandidateMask)
38 return selectImpl(CandidateMask, NextInSequenceMask);
39
40 NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
41 RemovedFromNextInSequence = 0;
42 CandidateMask = ReadyMask & NextInSequenceMask;
43 if (CandidateMask)
44 return selectImpl(CandidateMask, NextInSequenceMask);
45
46 NextInSequenceMask = ResourceUnitMask;
47 CandidateMask = ReadyMask & NextInSequenceMask;
48 return selectImpl(CandidateMask, NextInSequenceMask);
49}
50
52 if (Mask > NextInSequenceMask) {
53 RemovedFromNextInSequence |= Mask;
54 return;
55 }
56
57 NextInSequenceMask &= (~Mask);
58 if (NextInSequenceMask)
59 return;
60
61 NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
62 RemovedFromNextInSequence = 0;
63}
64
65static uint64_t computeResourceSizeMask(uint64_t Mask, bool IsAGroup,
66 unsigned NumUnits) {
67 if (IsAGroup)
68 return Mask ^ (1ULL << getResourceStateIndex(Mask));
69 return (1ULL << NumUnits) - 1;
70}
71
72ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index,
73 uint64_t Mask)
74 : ProcResourceDescIndex(Index), ResourceMask(Mask),
75 IsAGroup(llvm::popcount(ResourceMask) > 1),
76 ResourceSizeMask(computeResourceSizeMask(Mask, IsAGroup, Desc.NumUnits)),
77 BufferSize(Desc.BufferSize) {
78 ReadyMask = ResourceSizeMask;
79 AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
80 Unavailable = false;
81}
82
83bool ResourceState::isReady(unsigned NumUnits) const {
84 return (!isReserved() || isADispatchHazard()) &&
85 (unsigned)llvm::popcount(ReadyMask) >= NumUnits;
86}
87
88ResourceStateEvent ResourceState::isBufferAvailable() const {
89 if (isADispatchHazard() && isReserved())
90 return RS_RESERVED;
91 if (!isBuffered() || AvailableSlots)
94}
95
96#ifndef NDEBUG
97void ResourceState::dump() const {
98 dbgs() << "MASK=" << format_hex(ResourceMask, 16)
99 << ", SZMASK=" << format_hex(ResourceSizeMask, 16)
100 << ", RDYMASK=" << format_hex(ReadyMask, 16)
101 << ", BufferSize=" << BufferSize
102 << ", AvailableSlots=" << AvailableSlots
103 << ", Reserved=" << Unavailable << '\n';
104}
105#endif
106
107static std::unique_ptr<ResourceStrategy>
109 if (RS.isAResourceGroup() || RS.getNumUnits() > 1)
110 return std::make_unique<DefaultResourceStrategy>(RS.getReadyMask());
111 return std::unique_ptr<ResourceStrategy>(nullptr);
112}
113
115 : Resources(SM.getNumProcResourceKinds() - 1),
116 Strategies(SM.getNumProcResourceKinds() - 1),
117 Resource2Groups(SM.getNumProcResourceKinds() - 1, 0),
118 ProcResID2Mask(SM.getNumProcResourceKinds(), 0),
119 ResIndex2ProcResID(SM.getNumProcResourceKinds() - 1, 0),
120 ProcResUnitMask(0), ReservedResourceGroups(0), AvailableBuffers(~0ULL),
121 ReservedBuffers(0) {
122 computeProcResourceMasks(SM, ProcResID2Mask);
123
124 // initialize vector ResIndex2ProcResID.
125 for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
126 unsigned Index = getResourceStateIndex(ProcResID2Mask[I]);
127 ResIndex2ProcResID[Index] = I;
128 }
129
130 for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
131 uint64_t Mask = ProcResID2Mask[I];
132 unsigned Index = getResourceStateIndex(Mask);
133 Resources[Index] =
134 std::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);
135 Strategies[Index] = getStrategyFor(*Resources[Index]);
136 }
137
138 for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
139 uint64_t Mask = ProcResID2Mask[I];
140 unsigned Index = getResourceStateIndex(Mask);
141 const ResourceState &RS = *Resources[Index];
142 if (!RS.isAResourceGroup()) {
143 ProcResUnitMask |= Mask;
144 continue;
145 }
146
147 uint64_t GroupMaskIdx = 1ULL << Index;
148 Mask -= GroupMaskIdx;
149 while (Mask) {
150 // Extract lowest set isolated bit.
151 uint64_t Unit = Mask & (-Mask);
152 unsigned IndexUnit = getResourceStateIndex(Unit);
153 Resource2Groups[IndexUnit] |= GroupMaskIdx;
154 Mask ^= Unit;
155 }
156 }
157
158 AvailableProcResUnits = ProcResUnitMask;
159}
160
161void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
162 uint64_t ResourceMask) {
163 unsigned Index = getResourceStateIndex(ResourceMask);
164 assert(Index < Resources.size() && "Invalid processor resource index!");
165 assert(S && "Unexpected null strategy in input!");
166 Strategies[Index] = std::move(S);
167}
168
169unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
170 return ResIndex2ProcResID[getResourceStateIndex(Mask)];
171}
172
173unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
174 return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
175}
176
177// Returns the actual resource consumed by this Use.
178// First, is the primary resource ID.
179// Second, is the specific sub-resource ID.
180ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {
181 unsigned Index = getResourceStateIndex(ResourceID);
182 assert(Index < Resources.size() && "Invalid resource use!");
183 ResourceState &RS = *Resources[Index];
184 assert(RS.isReady() && "No available units to select!");
185
186 // Special case where RS is not a group, and it only declares a single
187 // resource unit.
188 if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)
189 return std::make_pair(ResourceID, RS.getReadyMask());
190
191 uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());
192 if (RS.isAResourceGroup())
193 return selectPipe(SubResourceID);
194 return std::make_pair(ResourceID, SubResourceID);
195}
196
197void ResourceManager::use(const ResourceRef &RR) {
198 // Mark the sub-resource referenced by RR as used.
199 unsigned RSID = getResourceStateIndex(RR.first);
200 ResourceState &RS = *Resources[RSID];
201 RS.markSubResourceAsUsed(RR.second);
202 // Remember to update the resource strategy for non-group resources with
203 // multiple units.
204 if (RS.getNumUnits() > 1)
205 Strategies[RSID]->used(RR.second);
206
207 // If there are still available units in RR.first,
208 // then we are done.
209 if (RS.isReady())
210 return;
211
212 AvailableProcResUnits ^= RR.first;
213
214 // Notify groups that RR.first is no longer available.
215 uint64_t Users = Resource2Groups[RSID];
216 while (Users) {
217 // Extract lowest set isolated bit.
218 unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
219 ResourceState &CurrentUser = *Resources[GroupIndex];
220 CurrentUser.markSubResourceAsUsed(RR.first);
221 Strategies[GroupIndex]->used(RR.first);
222 // Reset lowest set bit.
223 Users &= Users - 1;
224 }
225}
226
227void ResourceManager::release(const ResourceRef &RR) {
228 unsigned RSID = getResourceStateIndex(RR.first);
229 ResourceState &RS = *Resources[RSID];
230 bool WasFullyUsed = !RS.isReady();
231 RS.releaseSubResource(RR.second);
232 if (!WasFullyUsed)
233 return;
234
235 AvailableProcResUnits ^= RR.first;
236
237 // Notify groups that RR.first is now available again.
238 uint64_t Users = Resource2Groups[RSID];
239 while (Users) {
240 unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
241 ResourceState &CurrentUser = *Resources[GroupIndex];
242 CurrentUser.releaseSubResource(RR.first);
243 Users &= Users - 1;
244 }
245}
246
248ResourceManager::canBeDispatched(uint64_t ConsumedBuffers) const {
249 if (ConsumedBuffers & ReservedBuffers)
250 return ResourceStateEvent::RS_RESERVED;
251 if (ConsumedBuffers & (~AvailableBuffers))
252 return ResourceStateEvent::RS_BUFFER_UNAVAILABLE;
253 return ResourceStateEvent::RS_BUFFER_AVAILABLE;
254}
255
256void ResourceManager::reserveBuffers(uint64_t ConsumedBuffers) {
257 while (ConsumedBuffers) {
258 uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
259 ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
260 ConsumedBuffers ^= CurrentBuffer;
261 assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE);
262 if (!RS.reserveBuffer())
263 AvailableBuffers ^= CurrentBuffer;
264 if (RS.isADispatchHazard()) {
265 // Reserve this buffer now, and release it once pipeline resources
266 // consumed by the instruction become available again.
267 // We do this to simulate an in-order dispatch/issue of instructions.
268 ReservedBuffers ^= CurrentBuffer;
269 }
270 }
271}
272
273void ResourceManager::releaseBuffers(uint64_t ConsumedBuffers) {
274 AvailableBuffers |= ConsumedBuffers;
275 while (ConsumedBuffers) {
276 uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
277 ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
278 ConsumedBuffers ^= CurrentBuffer;
279 RS.releaseBuffer();
280 // Do not unreserve dispatch hazard resource buffers. Wait until all
281 // pipeline resources have been freed too.
282 }
283}
284
285uint64_t ResourceManager::checkAvailability(const InstrDesc &Desc) const {
286 uint64_t BusyResourceMask = 0;
287 uint64_t ConsumedResourceMask = 0;
288 DenseMap<uint64_t, unsigned> AvailableUnits;
289
290 for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {
291 unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits;
292 const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];
293 if (!RS.isReady(NumUnits)) {
294 BusyResourceMask |= E.first;
295 continue;
296 }
297
298 if (Desc.HasPartiallyOverlappingGroups && !RS.isAResourceGroup()) {
299 unsigned NumAvailableUnits = llvm::popcount(RS.getReadyMask());
300 NumAvailableUnits -= NumUnits;
301 AvailableUnits[E.first] = NumAvailableUnits;
302 if (!NumAvailableUnits)
303 ConsumedResourceMask |= E.first;
304 }
305 }
306
307 BusyResourceMask &= ProcResUnitMask;
308 if (BusyResourceMask)
309 return BusyResourceMask;
310
311 BusyResourceMask = Desc.UsedProcResGroups & ReservedResourceGroups;
312 if (!Desc.HasPartiallyOverlappingGroups || BusyResourceMask)
313 return BusyResourceMask;
314
315 // If this instruction has overlapping groups, make sure that we can
316 // select at least one unit per group.
317 for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {
318 const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];
319 if (!E.second.isReserved() && RS.isAResourceGroup()) {
320 uint64_t ReadyMask = RS.getReadyMask() & ~ConsumedResourceMask;
321 if (!ReadyMask) {
322 BusyResourceMask |= RS.getReadyMask();
323 continue;
324 }
325
326 uint64_t ResourceMask = llvm::bit_floor(ReadyMask);
327
328 auto [it, Inserted] = AvailableUnits.try_emplace(ResourceMask);
329 if (Inserted) {
330 unsigned Index = getResourceStateIndex(ResourceMask);
331 unsigned NumUnits = llvm::popcount(Resources[Index]->getReadyMask());
332 it->second = NumUnits;
333 }
334
335 if (!it->second) {
336 BusyResourceMask |= it->first;
337 continue;
338 }
339
340 it->second--;
341 if (!it->second)
342 ConsumedResourceMask |= it->first;
343 }
344 }
345
346 return BusyResourceMask;
347}
348
349void ResourceManager::issueInstructionImpl(
350 const InstrDesc &Desc, SmallVectorImpl<ResourceWithCycles> &Pipes) {
351
352 // Step 1.
353 // - Issue writes to non-group resources.
354 // - Issue writes to groups with only a single resource unit available.
355 // - Update reserved groups (if any)
356 // - Add any remaining resource usage requests to a Worklist.
358
359 using ResourceWithUsage = std::pair<uint64_t, ResourceUsage>;
360
361 for (const ResourceWithUsage &R : Desc.Resources) {
362 const CycleSegment &CS = R.second.CS;
363 if (!CS.size()) {
364 releaseResource(R.first);
365 continue;
366 }
367
368 assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
369 if (R.second.isReserved()) {
370 assert((llvm::popcount(R.first) > 1) && "Expected a group!");
371 // Mark this group as reserved.
372 assert(R.second.isReserved());
373 reserveResource(R.first);
374 BusyResources[ResourceRef(R.first, R.first)] += CS.size();
375 continue;
376 }
377
378 const ResourceState &RS = *Resources[getResourceStateIndex(R.first)];
379 if (RS.isAResourceGroup() && RS.getNumReadyUnits() > 1) {
380 Worklist.push_back(R);
381 continue;
382 }
383
384 ResourceRef Pipe = selectPipe(R.first);
385 use(Pipe);
386 BusyResources[Pipe] += CS.size();
387 Pipes.emplace_back(std::make_pair(Pipe, ReleaseAtCycles(CS.size())));
388 }
389
390 // Step 2.
391 // Prioritize writes to groups with less available resources.
392 // NOTE: this algorithm has quadratic complexity in the worst case scenario.
393 // On average, this algorithm is expected to perform quite well and always
394 // converge in very few iterations. That is mainly because instructions rarely
395 // consume more than two or three resource groups.
396
397 while (!Worklist.empty()) {
398 sort(Worklist, [&](const ResourceWithUsage &Lhs,
399 const ResourceWithUsage &Rhs) {
400 const ResourceState &LhsRS = *Resources[getResourceStateIndex(Lhs.first)];
401 const ResourceState &RhsRS = *Resources[getResourceStateIndex(Rhs.first)];
402 uint64_t LhsReadyUnits = LhsRS.getNumReadyUnits();
403 uint64_t RhsReadyUnits = RhsRS.getNumReadyUnits();
404 if (LhsReadyUnits == RhsReadyUnits)
405 return Lhs.first < Rhs.first;
406 return LhsReadyUnits < RhsReadyUnits;
407 });
408
410
411 for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
412 const auto &Elt = Worklist[I];
413 const ResourceState &RS = *Resources[getResourceStateIndex(Elt.first)];
414
415 if (I == 0 || RS.getNumReadyUnits() == 1) {
416 ResourceRef Pipe = selectPipe(Elt.first);
417 use(Pipe);
418 const CycleSegment &CS = Elt.second.CS;
419 BusyResources[Pipe] += CS.size();
420 Pipes.emplace_back(std::make_pair(Pipe, ReleaseAtCycles(CS.size())));
421 continue;
422 }
423
424 NewWorklist.push_back(Elt);
425 }
426
427 swap(NewWorklist, Worklist);
428 };
429}
430
431void ResourceManager::fastIssueInstruction(
432 const InstrDesc &Desc, SmallVectorImpl<ResourceWithCycles> &Pipes) {
433 for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
434 const CycleSegment &CS = R.second.CS;
435 if (!CS.size()) {
436 releaseResource(R.first);
437 continue;
438 }
439
440 assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
441 if (!R.second.isReserved()) {
442 ResourceRef Pipe = selectPipe(R.first);
443 use(Pipe);
444 BusyResources[Pipe] += CS.size();
445 Pipes.emplace_back(std::pair<ResourceRef, ReleaseAtCycles>(
446 Pipe, ReleaseAtCycles(CS.size())));
447 } else {
448 assert((llvm::popcount(R.first) > 1) && "Expected a group!");
449 // Mark this group as reserved.
450 assert(R.second.isReserved());
451 reserveResource(R.first);
452 BusyResources[ResourceRef(R.first, R.first)] += CS.size();
453 }
454 }
455}
456
457void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
458 for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
459 if (BR.second)
460 BR.second--;
461 if (!BR.second) {
462 // Release this resource.
463 const ResourceRef &RR = BR.first;
464
465 if (llvm::popcount(RR.first) == 1)
466 release(RR);
467 releaseResource(RR.first);
468 ResourcesFreed.push_back(RR);
469 }
470 }
471
472 for (const ResourceRef &RF : ResourcesFreed)
473 BusyResources.erase(RF);
474}
475
476void ResourceManager::reserveResource(uint64_t ResourceID) {
477 const unsigned Index = getResourceStateIndex(ResourceID);
478 ResourceState &Resource = *Resources[Index];
479 assert(Resource.isAResourceGroup() && !Resource.isReserved() &&
480 "Unexpected resource state found!");
481 Resource.setReserved();
482 ReservedResourceGroups ^= 1ULL << Index;
483}
484
485void ResourceManager::releaseResource(uint64_t ResourceID) {
486 const unsigned Index = getResourceStateIndex(ResourceID);
487 ResourceState &Resource = *Resources[Index];
488 Resource.clearReserved();
489 if (Resource.isAResourceGroup())
490 ReservedResourceGroups ^= 1ULL << Index;
491 // Now it is safe to release dispatch/issue resources.
492 if (Resource.isADispatchHazard())
493 ReservedBuffers ^= 1ULL << Index;
494}
495
496} // namespace mca
497} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
@ Unavailable
We know the block is not fully available. This is a fixpoint.
Definition GVN.cpp:949
iv Induction Variable Users
Definition IVUsers.cpp:48
Move duplicate certain instructions close to their use
Definition Localizer.cpp:33
#define I(x, y, z)
Definition MD5.cpp:57
static constexpr unsigned SM(unsigned Version)
The classes here represent processor resource units and their management strategy.
ResourceManager(const TargetSubtargetInfo *ST, ScheduleDAGInstrs *DAG)
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void used(uint64_t Mask) override
Called by the ResourceManager when a processor resource group, or a processor resource with multiple ...
uint64_t select(uint64_t ReadyMask) override
Selects a processor resource unit from a ReadyMask.
A processor resource descriptor.
Helper functions used by various pipeline components.
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
@ BR
Control flow instructions. These all have token chains.
static uint64_t computeResourceSizeMask(uint64_t Mask, bool IsAGroup, unsigned NumUnits)
static std::unique_ptr< ResourceStrategy > getStrategyFor(const ResourceState &RS)
ResourceStateEvent
Used to notify the internal state of a processor resource.
std::pair< uint64_t, uint64_t > ResourceRef
static uint64_t selectImpl(uint64_t CandidateMask, uint64_t &NextInSequenceMask)
LLVM_ABI void computeProcResourceMasks(const MCSchedModel &SM, MutableArrayRef< uint64_t > Masks)
Populates vector Masks with processor resource masks.
Definition Support.cpp:40
unsigned getResourceStateIndex(uint64_t Mask)
Definition Support.h:101
This is an optimization pass for GlobalISel generic memory operations.
Op::Description Desc
constexpr int popcount(T Value) noexcept
Count the number of set bits in a value.
Definition bit.h:154
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
FormattedNumber format_hex(uint64_t N, unsigned Width, bool Upper=false)
format_hex - Output N as a fixed width hexadecimal.
Definition Format.h:191
T bit_floor(T Value)
Returns the largest integral power of two no greater than Value if Value is nonzero.
Definition bit.h:345
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:872
Define a kind of processor resource that will be modeled by the scheduler.
Definition MCSchedule.h:36
Machine model for scheduling, bundling, and heuristics.
Definition MCSchedule.h:258