1 //===--------------------- ResourceManager.cpp ------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// The classes here represent processor resource units and their management
11 /// strategy. These classes are managed by the Scheduler.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/MCA/HardwareUnits/ResourceManager.h"
16 #include "llvm/MCA/Support.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/raw_ostream.h"
23 #define DEBUG_TYPE "llvm-mca"
24 ResourceStrategy::~ResourceStrategy() = default;
26 static 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));
34 uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask
) {
35 // This method assumes that ReadyMask cannot be zero.
36 uint64_t CandidateMask
= ReadyMask
& NextInSequenceMask
;
38 return selectImpl(CandidateMask
, NextInSequenceMask
);
40 NextInSequenceMask
= ResourceUnitMask
^ RemovedFromNextInSequence
;
41 RemovedFromNextInSequence
= 0;
42 CandidateMask
= ReadyMask
& NextInSequenceMask
;
44 return selectImpl(CandidateMask
, NextInSequenceMask
);
46 NextInSequenceMask
= ResourceUnitMask
;
47 CandidateMask
= ReadyMask
& NextInSequenceMask
;
48 return selectImpl(CandidateMask
, NextInSequenceMask
);
51 void DefaultResourceStrategy::used(uint64_t Mask
) {
52 if (Mask
> NextInSequenceMask
) {
53 RemovedFromNextInSequence
|= Mask
;
57 NextInSequenceMask
&= (~Mask
);
58 if (NextInSequenceMask
)
61 NextInSequenceMask
= ResourceUnitMask
^ RemovedFromNextInSequence
;
62 RemovedFromNextInSequence
= 0;
65 ResourceState::ResourceState(const MCProcResourceDesc
&Desc
, unsigned Index
,
67 : ProcResourceDescIndex(Index
), ResourceMask(Mask
),
68 BufferSize(Desc
.BufferSize
), IsAGroup(llvm::popcount(ResourceMask
) > 1) {
71 ResourceMask
^ 1ULL << getResourceStateIndex(ResourceMask
);
73 ResourceSizeMask
= (1ULL << Desc
.NumUnits
) - 1;
75 ReadyMask
= ResourceSizeMask
;
76 AvailableSlots
= BufferSize
== -1 ? 0U : static_cast<unsigned>(BufferSize
);
80 bool ResourceState::isReady(unsigned NumUnits
) const {
81 return (!isReserved() || isADispatchHazard()) &&
82 (unsigned)llvm::popcount(ReadyMask
) >= NumUnits
;
85 ResourceStateEvent
ResourceState::isBufferAvailable() const {
86 if (isADispatchHazard() && isReserved())
88 if (!isBuffered() || AvailableSlots
)
89 return RS_BUFFER_AVAILABLE
;
90 return RS_BUFFER_UNAVAILABLE
;
94 void ResourceState::dump() const {
95 dbgs() << "MASK=" << format_hex(ResourceMask
, 16)
96 << ", SZMASK=" << format_hex(ResourceSizeMask
, 16)
97 << ", RDYMASK=" << format_hex(ReadyMask
, 16)
98 << ", BufferSize=" << BufferSize
99 << ", AvailableSlots=" << AvailableSlots
100 << ", Reserved=" << Unavailable
<< '\n';
104 static std::unique_ptr
<ResourceStrategy
>
105 getStrategyFor(const ResourceState
&RS
) {
106 if (RS
.isAResourceGroup() || RS
.getNumUnits() > 1)
107 return std::make_unique
<DefaultResourceStrategy
>(RS
.getReadyMask());
108 return std::unique_ptr
<ResourceStrategy
>(nullptr);
111 ResourceManager::ResourceManager(const MCSchedModel
&SM
)
112 : Resources(SM
.getNumProcResourceKinds() - 1),
113 Strategies(SM
.getNumProcResourceKinds() - 1),
114 Resource2Groups(SM
.getNumProcResourceKinds() - 1, 0),
115 ProcResID2Mask(SM
.getNumProcResourceKinds(), 0),
116 ResIndex2ProcResID(SM
.getNumProcResourceKinds() - 1, 0),
117 ProcResUnitMask(0), ReservedResourceGroups(0), AvailableBuffers(~0ULL),
119 computeProcResourceMasks(SM
, ProcResID2Mask
);
121 // initialize vector ResIndex2ProcResID.
122 for (unsigned I
= 1, E
= SM
.getNumProcResourceKinds(); I
< E
; ++I
) {
123 unsigned Index
= getResourceStateIndex(ProcResID2Mask
[I
]);
124 ResIndex2ProcResID
[Index
] = I
;
127 for (unsigned I
= 1, E
= SM
.getNumProcResourceKinds(); I
< E
; ++I
) {
128 uint64_t Mask
= ProcResID2Mask
[I
];
129 unsigned Index
= getResourceStateIndex(Mask
);
131 std::make_unique
<ResourceState
>(*SM
.getProcResource(I
), I
, Mask
);
132 Strategies
[Index
] = getStrategyFor(*Resources
[Index
]);
135 for (unsigned I
= 1, E
= SM
.getNumProcResourceKinds(); I
< E
; ++I
) {
136 uint64_t Mask
= ProcResID2Mask
[I
];
137 unsigned Index
= getResourceStateIndex(Mask
);
138 const ResourceState
&RS
= *Resources
[Index
];
139 if (!RS
.isAResourceGroup()) {
140 ProcResUnitMask
|= Mask
;
144 uint64_t GroupMaskIdx
= 1ULL << Index
;
145 Mask
-= GroupMaskIdx
;
147 // Extract lowest set isolated bit.
148 uint64_t Unit
= Mask
& (-Mask
);
149 unsigned IndexUnit
= getResourceStateIndex(Unit
);
150 Resource2Groups
[IndexUnit
] |= GroupMaskIdx
;
155 AvailableProcResUnits
= ProcResUnitMask
;
158 void ResourceManager::setCustomStrategyImpl(std::unique_ptr
<ResourceStrategy
> S
,
159 uint64_t ResourceMask
) {
160 unsigned Index
= getResourceStateIndex(ResourceMask
);
161 assert(Index
< Resources
.size() && "Invalid processor resource index!");
162 assert(S
&& "Unexpected null strategy in input!");
163 Strategies
[Index
] = std::move(S
);
166 unsigned ResourceManager::resolveResourceMask(uint64_t Mask
) const {
167 return ResIndex2ProcResID
[getResourceStateIndex(Mask
)];
170 unsigned ResourceManager::getNumUnits(uint64_t ResourceID
) const {
171 return Resources
[getResourceStateIndex(ResourceID
)]->getNumUnits();
174 // Returns the actual resource consumed by this Use.
175 // First, is the primary resource ID.
176 // Second, is the specific sub-resource ID.
177 ResourceRef
ResourceManager::selectPipe(uint64_t ResourceID
) {
178 unsigned Index
= getResourceStateIndex(ResourceID
);
179 assert(Index
< Resources
.size() && "Invalid resource use!");
180 ResourceState
&RS
= *Resources
[Index
];
181 assert(RS
.isReady() && "No available units to select!");
183 // Special case where RS is not a group, and it only declares a single
185 if (!RS
.isAResourceGroup() && RS
.getNumUnits() == 1)
186 return std::make_pair(ResourceID
, RS
.getReadyMask());
188 uint64_t SubResourceID
= Strategies
[Index
]->select(RS
.getReadyMask());
189 if (RS
.isAResourceGroup())
190 return selectPipe(SubResourceID
);
191 return std::make_pair(ResourceID
, SubResourceID
);
194 void ResourceManager::use(const ResourceRef
&RR
) {
195 // Mark the sub-resource referenced by RR as used.
196 unsigned RSID
= getResourceStateIndex(RR
.first
);
197 ResourceState
&RS
= *Resources
[RSID
];
198 RS
.markSubResourceAsUsed(RR
.second
);
199 // Remember to update the resource strategy for non-group resources with
201 if (RS
.getNumUnits() > 1)
202 Strategies
[RSID
]->used(RR
.second
);
204 // If there are still available units in RR.first,
209 AvailableProcResUnits
^= RR
.first
;
211 // Notify groups that RR.first is no longer available.
212 uint64_t Users
= Resource2Groups
[RSID
];
214 // Extract lowest set isolated bit.
215 unsigned GroupIndex
= getResourceStateIndex(Users
& (-Users
));
216 ResourceState
&CurrentUser
= *Resources
[GroupIndex
];
217 CurrentUser
.markSubResourceAsUsed(RR
.first
);
218 Strategies
[GroupIndex
]->used(RR
.first
);
219 // Reset lowest set bit.
224 void ResourceManager::release(const ResourceRef
&RR
) {
225 unsigned RSID
= getResourceStateIndex(RR
.first
);
226 ResourceState
&RS
= *Resources
[RSID
];
227 bool WasFullyUsed
= !RS
.isReady();
228 RS
.releaseSubResource(RR
.second
);
232 AvailableProcResUnits
^= RR
.first
;
234 // Notify groups that RR.first is now available again.
235 uint64_t Users
= Resource2Groups
[RSID
];
237 unsigned GroupIndex
= getResourceStateIndex(Users
& (-Users
));
238 ResourceState
&CurrentUser
= *Resources
[GroupIndex
];
239 CurrentUser
.releaseSubResource(RR
.first
);
245 ResourceManager::canBeDispatched(uint64_t ConsumedBuffers
) const {
246 if (ConsumedBuffers
& ReservedBuffers
)
247 return ResourceStateEvent::RS_RESERVED
;
248 if (ConsumedBuffers
& (~AvailableBuffers
))
249 return ResourceStateEvent::RS_BUFFER_UNAVAILABLE
;
250 return ResourceStateEvent::RS_BUFFER_AVAILABLE
;
253 void ResourceManager::reserveBuffers(uint64_t ConsumedBuffers
) {
254 while (ConsumedBuffers
) {
255 uint64_t CurrentBuffer
= ConsumedBuffers
& (-ConsumedBuffers
);
256 ResourceState
&RS
= *Resources
[getResourceStateIndex(CurrentBuffer
)];
257 ConsumedBuffers
^= CurrentBuffer
;
258 assert(RS
.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE
);
259 if (!RS
.reserveBuffer())
260 AvailableBuffers
^= CurrentBuffer
;
261 if (RS
.isADispatchHazard()) {
262 // Reserve this buffer now, and release it once pipeline resources
263 // consumed by the instruction become available again.
264 // We do this to simulate an in-order dispatch/issue of instructions.
265 ReservedBuffers
^= CurrentBuffer
;
270 void ResourceManager::releaseBuffers(uint64_t ConsumedBuffers
) {
271 AvailableBuffers
|= ConsumedBuffers
;
272 while (ConsumedBuffers
) {
273 uint64_t CurrentBuffer
= ConsumedBuffers
& (-ConsumedBuffers
);
274 ResourceState
&RS
= *Resources
[getResourceStateIndex(CurrentBuffer
)];
275 ConsumedBuffers
^= CurrentBuffer
;
277 // Do not unreserve dispatch hazard resource buffers. Wait until all
278 // pipeline resources have been freed too.
282 uint64_t ResourceManager::checkAvailability(const InstrDesc
&Desc
) const {
283 uint64_t BusyResourceMask
= 0;
284 uint64_t ConsumedResourceMask
= 0;
285 DenseMap
<uint64_t, unsigned> AvailableUnits
;
287 for (const std::pair
<uint64_t, ResourceUsage
> &E
: Desc
.Resources
) {
288 unsigned NumUnits
= E
.second
.isReserved() ? 0U : E
.second
.NumUnits
;
289 const ResourceState
&RS
= *Resources
[getResourceStateIndex(E
.first
)];
290 if (!RS
.isReady(NumUnits
)) {
291 BusyResourceMask
|= E
.first
;
295 if (Desc
.HasPartiallyOverlappingGroups
&& !RS
.isAResourceGroup()) {
296 unsigned NumAvailableUnits
= llvm::popcount(RS
.getReadyMask());
297 NumAvailableUnits
-= NumUnits
;
298 AvailableUnits
[E
.first
] = NumAvailableUnits
;
299 if (!NumAvailableUnits
)
300 ConsumedResourceMask
|= E
.first
;
304 BusyResourceMask
&= ProcResUnitMask
;
305 if (BusyResourceMask
)
306 return BusyResourceMask
;
308 BusyResourceMask
= Desc
.UsedProcResGroups
& ReservedResourceGroups
;
309 if (!Desc
.HasPartiallyOverlappingGroups
|| BusyResourceMask
)
310 return BusyResourceMask
;
312 // If this instruction has overlapping groups, make sure that we can
313 // select at least one unit per group.
314 for (const std::pair
<uint64_t, ResourceUsage
> &E
: Desc
.Resources
) {
315 const ResourceState
&RS
= *Resources
[getResourceStateIndex(E
.first
)];
316 if (!E
.second
.isReserved() && RS
.isAResourceGroup()) {
317 uint64_t ReadyMask
= RS
.getReadyMask() & ~ConsumedResourceMask
;
319 BusyResourceMask
|= RS
.getReadyMask();
323 uint64_t ResourceMask
= llvm::bit_floor(ReadyMask
);
325 auto it
= AvailableUnits
.find(ResourceMask
);
326 if (it
== AvailableUnits
.end()) {
327 unsigned Index
= getResourceStateIndex(ResourceMask
);
328 unsigned NumUnits
= llvm::popcount(Resources
[Index
]->getReadyMask());
330 AvailableUnits
.insert(std::make_pair(ResourceMask
, NumUnits
)).first
;
334 BusyResourceMask
|= it
->first
;
340 ConsumedResourceMask
|= it
->first
;
344 return BusyResourceMask
;
347 void ResourceManager::issueInstruction(
348 const InstrDesc
&Desc
,
349 SmallVectorImpl
<std::pair
<ResourceRef
, ReleaseAtCycles
>> &Pipes
) {
350 for (const std::pair
<uint64_t, ResourceUsage
> &R
: Desc
.Resources
) {
351 const CycleSegment
&CS
= R
.second
.CS
;
353 releaseResource(R
.first
);
357 assert(CS
.begin() == 0 && "Invalid {Start, End} cycles!");
358 if (!R
.second
.isReserved()) {
359 ResourceRef Pipe
= selectPipe(R
.first
);
361 BusyResources
[Pipe
] += CS
.size();
362 Pipes
.emplace_back(std::pair
<ResourceRef
, ReleaseAtCycles
>(
363 Pipe
, ReleaseAtCycles(CS
.size())));
365 assert((llvm::popcount(R
.first
) > 1) && "Expected a group!");
366 // Mark this group as reserved.
367 assert(R
.second
.isReserved());
368 reserveResource(R
.first
);
369 BusyResources
[ResourceRef(R
.first
, R
.first
)] += CS
.size();
374 void ResourceManager::cycleEvent(SmallVectorImpl
<ResourceRef
> &ResourcesFreed
) {
375 for (std::pair
<ResourceRef
, unsigned> &BR
: BusyResources
) {
379 // Release this resource.
380 const ResourceRef
&RR
= BR
.first
;
382 if (llvm::popcount(RR
.first
) == 1)
384 releaseResource(RR
.first
);
385 ResourcesFreed
.push_back(RR
);
389 for (const ResourceRef
&RF
: ResourcesFreed
)
390 BusyResources
.erase(RF
);
393 void ResourceManager::reserveResource(uint64_t ResourceID
) {
394 const unsigned Index
= getResourceStateIndex(ResourceID
);
395 ResourceState
&Resource
= *Resources
[Index
];
396 assert(Resource
.isAResourceGroup() && !Resource
.isReserved() &&
397 "Unexpected resource state found!");
398 Resource
.setReserved();
399 ReservedResourceGroups
^= 1ULL << Index
;
402 void ResourceManager::releaseResource(uint64_t ResourceID
) {
403 const unsigned Index
= getResourceStateIndex(ResourceID
);
404 ResourceState
&Resource
= *Resources
[Index
];
405 Resource
.clearReserved();
406 if (Resource
.isAResourceGroup())
407 ReservedResourceGroups
^= 1ULL << Index
;
408 // Now it is safe to release dispatch/issue resources.
409 if (Resource
.isADispatchHazard())
410 ReservedBuffers
^= 1ULL << Index
;