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