123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- //===--------------------- ResourceManager.cpp ------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- /// \file
- ///
- /// The classes here represent processor resource units and their management
- /// strategy. These classes are managed by the Scheduler.
- ///
- //===----------------------------------------------------------------------===//
- #include "llvm/MCA/HardwareUnits/ResourceManager.h"
- #include "llvm/MCA/Support.h"
- #include "llvm/Support/Debug.h"
- #include "llvm/Support/raw_ostream.h"
- namespace llvm {
- namespace mca {
- #define DEBUG_TYPE "llvm-mca"
- ResourceStrategy::~ResourceStrategy() = default;
- static uint64_t selectImpl(uint64_t CandidateMask,
- uint64_t &NextInSequenceMask) {
- // The upper bit set in CandidateMask identifies our next candidate resource.
- CandidateMask = 1ULL << getResourceStateIndex(CandidateMask);
- NextInSequenceMask &= (CandidateMask | (CandidateMask - 1));
- return CandidateMask;
- }
- uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) {
- // This method assumes that ReadyMask cannot be zero.
- uint64_t CandidateMask = ReadyMask & NextInSequenceMask;
- if (CandidateMask)
- return selectImpl(CandidateMask, NextInSequenceMask);
- NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
- RemovedFromNextInSequence = 0;
- CandidateMask = ReadyMask & NextInSequenceMask;
- if (CandidateMask)
- return selectImpl(CandidateMask, NextInSequenceMask);
- NextInSequenceMask = ResourceUnitMask;
- CandidateMask = ReadyMask & NextInSequenceMask;
- return selectImpl(CandidateMask, NextInSequenceMask);
- }
- void DefaultResourceStrategy::used(uint64_t Mask) {
- if (Mask > NextInSequenceMask) {
- RemovedFromNextInSequence |= Mask;
- return;
- }
- NextInSequenceMask &= (~Mask);
- if (NextInSequenceMask)
- return;
- NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;
- RemovedFromNextInSequence = 0;
- }
- ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index,
- uint64_t Mask)
- : ProcResourceDescIndex(Index), ResourceMask(Mask),
- BufferSize(Desc.BufferSize), IsAGroup(llvm::popcount(ResourceMask) > 1) {
- if (IsAGroup) {
- ResourceSizeMask =
- ResourceMask ^ 1ULL << getResourceStateIndex(ResourceMask);
- } else {
- ResourceSizeMask = (1ULL << Desc.NumUnits) - 1;
- }
- ReadyMask = ResourceSizeMask;
- AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
- Unavailable = false;
- }
- bool ResourceState::isReady(unsigned NumUnits) const {
- return (!isReserved() || isADispatchHazard()) &&
- (unsigned)llvm::popcount(ReadyMask) >= NumUnits;
- }
- ResourceStateEvent ResourceState::isBufferAvailable() const {
- if (isADispatchHazard() && isReserved())
- return RS_RESERVED;
- if (!isBuffered() || AvailableSlots)
- return RS_BUFFER_AVAILABLE;
- return RS_BUFFER_UNAVAILABLE;
- }
- #ifndef NDEBUG
- void ResourceState::dump() const {
- dbgs() << "MASK=" << format_hex(ResourceMask, 16)
- << ", SZMASK=" << format_hex(ResourceSizeMask, 16)
- << ", RDYMASK=" << format_hex(ReadyMask, 16)
- << ", BufferSize=" << BufferSize
- << ", AvailableSlots=" << AvailableSlots
- << ", Reserved=" << Unavailable << '\n';
- }
- #endif
- static std::unique_ptr<ResourceStrategy>
- getStrategyFor(const ResourceState &RS) {
- if (RS.isAResourceGroup() || RS.getNumUnits() > 1)
- return std::make_unique<DefaultResourceStrategy>(RS.getReadyMask());
- return std::unique_ptr<ResourceStrategy>(nullptr);
- }
- ResourceManager::ResourceManager(const MCSchedModel &SM)
- : Resources(SM.getNumProcResourceKinds() - 1),
- Strategies(SM.getNumProcResourceKinds() - 1),
- Resource2Groups(SM.getNumProcResourceKinds() - 1, 0),
- ProcResID2Mask(SM.getNumProcResourceKinds(), 0),
- ResIndex2ProcResID(SM.getNumProcResourceKinds() - 1, 0),
- ProcResUnitMask(0), ReservedResourceGroups(0), AvailableBuffers(~0ULL),
- ReservedBuffers(0) {
- computeProcResourceMasks(SM, ProcResID2Mask);
- // initialize vector ResIndex2ProcResID.
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- unsigned Index = getResourceStateIndex(ProcResID2Mask[I]);
- ResIndex2ProcResID[Index] = I;
- }
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- uint64_t Mask = ProcResID2Mask[I];
- unsigned Index = getResourceStateIndex(Mask);
- Resources[Index] =
- std::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);
- Strategies[Index] = getStrategyFor(*Resources[Index]);
- }
- for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
- uint64_t Mask = ProcResID2Mask[I];
- unsigned Index = getResourceStateIndex(Mask);
- const ResourceState &RS = *Resources[Index];
- if (!RS.isAResourceGroup()) {
- ProcResUnitMask |= Mask;
- continue;
- }
- uint64_t GroupMaskIdx = 1ULL << Index;
- Mask -= GroupMaskIdx;
- while (Mask) {
- // Extract lowest set isolated bit.
- uint64_t Unit = Mask & (-Mask);
- unsigned IndexUnit = getResourceStateIndex(Unit);
- Resource2Groups[IndexUnit] |= GroupMaskIdx;
- Mask ^= Unit;
- }
- }
- AvailableProcResUnits = ProcResUnitMask;
- }
- void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
- uint64_t ResourceMask) {
- unsigned Index = getResourceStateIndex(ResourceMask);
- assert(Index < Resources.size() && "Invalid processor resource index!");
- assert(S && "Unexpected null strategy in input!");
- Strategies[Index] = std::move(S);
- }
- unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {
- return ResIndex2ProcResID[getResourceStateIndex(Mask)];
- }
- unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {
- return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();
- }
- // Returns the actual resource consumed by this Use.
- // First, is the primary resource ID.
- // Second, is the specific sub-resource ID.
- ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {
- unsigned Index = getResourceStateIndex(ResourceID);
- assert(Index < Resources.size() && "Invalid resource use!");
- ResourceState &RS = *Resources[Index];
- assert(RS.isReady() && "No available units to select!");
- // Special case where RS is not a group, and it only declares a single
- // resource unit.
- if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)
- return std::make_pair(ResourceID, RS.getReadyMask());
- uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());
- if (RS.isAResourceGroup())
- return selectPipe(SubResourceID);
- return std::make_pair(ResourceID, SubResourceID);
- }
- void ResourceManager::use(const ResourceRef &RR) {
- // Mark the sub-resource referenced by RR as used.
- unsigned RSID = getResourceStateIndex(RR.first);
- ResourceState &RS = *Resources[RSID];
- RS.markSubResourceAsUsed(RR.second);
- // Remember to update the resource strategy for non-group resources with
- // multiple units.
- if (RS.getNumUnits() > 1)
- Strategies[RSID]->used(RR.second);
- // If there are still available units in RR.first,
- // then we are done.
- if (RS.isReady())
- return;
- AvailableProcResUnits ^= RR.first;
- // Notify groups that RR.first is no longer available.
- uint64_t Users = Resource2Groups[RSID];
- while (Users) {
- // Extract lowest set isolated bit.
- unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
- ResourceState &CurrentUser = *Resources[GroupIndex];
- CurrentUser.markSubResourceAsUsed(RR.first);
- Strategies[GroupIndex]->used(RR.first);
- // Reset lowest set bit.
- Users &= Users - 1;
- }
- }
- void ResourceManager::release(const ResourceRef &RR) {
- unsigned RSID = getResourceStateIndex(RR.first);
- ResourceState &RS = *Resources[RSID];
- bool WasFullyUsed = !RS.isReady();
- RS.releaseSubResource(RR.second);
- if (!WasFullyUsed)
- return;
- AvailableProcResUnits ^= RR.first;
- // Notify groups that RR.first is now available again.
- uint64_t Users = Resource2Groups[RSID];
- while (Users) {
- unsigned GroupIndex = getResourceStateIndex(Users & (-Users));
- ResourceState &CurrentUser = *Resources[GroupIndex];
- CurrentUser.releaseSubResource(RR.first);
- Users &= Users - 1;
- }
- }
- ResourceStateEvent
- ResourceManager::canBeDispatched(uint64_t ConsumedBuffers) const {
- if (ConsumedBuffers & ReservedBuffers)
- return ResourceStateEvent::RS_RESERVED;
- if (ConsumedBuffers & (~AvailableBuffers))
- return ResourceStateEvent::RS_BUFFER_UNAVAILABLE;
- return ResourceStateEvent::RS_BUFFER_AVAILABLE;
- }
- void ResourceManager::reserveBuffers(uint64_t ConsumedBuffers) {
- while (ConsumedBuffers) {
- uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
- ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
- ConsumedBuffers ^= CurrentBuffer;
- assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE);
- if (!RS.reserveBuffer())
- AvailableBuffers ^= CurrentBuffer;
- if (RS.isADispatchHazard()) {
- // Reserve this buffer now, and release it once pipeline resources
- // consumed by the instruction become available again.
- // We do this to simulate an in-order dispatch/issue of instructions.
- ReservedBuffers ^= CurrentBuffer;
- }
- }
- }
- void ResourceManager::releaseBuffers(uint64_t ConsumedBuffers) {
- AvailableBuffers |= ConsumedBuffers;
- while (ConsumedBuffers) {
- uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);
- ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];
- ConsumedBuffers ^= CurrentBuffer;
- RS.releaseBuffer();
- // Do not unreserve dispatch hazard resource buffers. Wait until all
- // pipeline resources have been freed too.
- }
- }
- uint64_t ResourceManager::checkAvailability(const InstrDesc &Desc) const {
- uint64_t BusyResourceMask = 0;
- uint64_t ConsumedResourceMask = 0;
- DenseMap<uint64_t, unsigned> AvailableUnits;
- for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {
- unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits;
- const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];
- if (!RS.isReady(NumUnits)) {
- BusyResourceMask |= E.first;
- continue;
- }
- if (Desc.HasPartiallyOverlappingGroups && !RS.isAResourceGroup()) {
- unsigned NumAvailableUnits = llvm::popcount(RS.getReadyMask());
- NumAvailableUnits -= NumUnits;
- AvailableUnits[E.first] = NumAvailableUnits;
- if (!NumAvailableUnits)
- ConsumedResourceMask |= E.first;
- }
- }
- BusyResourceMask &= ProcResUnitMask;
- if (BusyResourceMask)
- return BusyResourceMask;
- BusyResourceMask = Desc.UsedProcResGroups & ReservedResourceGroups;
- if (!Desc.HasPartiallyOverlappingGroups || BusyResourceMask)
- return BusyResourceMask;
- // If this instruction has overlapping groups, make sure that we can
- // select at least one unit per group.
- for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {
- const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];
- if (!E.second.isReserved() && RS.isAResourceGroup()) {
- uint64_t ReadyMask = RS.getReadyMask() & ~ConsumedResourceMask;
- if (!ReadyMask) {
- BusyResourceMask |= RS.getReadyMask();
- continue;
- }
- uint64_t ResourceMask = PowerOf2Floor(ReadyMask);
- auto it = AvailableUnits.find(ResourceMask);
- if (it == AvailableUnits.end()) {
- unsigned Index = getResourceStateIndex(ResourceMask);
- unsigned NumUnits = llvm::popcount(Resources[Index]->getReadyMask());
- it =
- AvailableUnits.insert(std::make_pair(ResourceMask, NumUnits)).first;
- }
- if (!it->second) {
- BusyResourceMask |= it->first;
- continue;
- }
- it->second--;
- if (!it->second)
- ConsumedResourceMask |= it->first;
- }
- }
- return BusyResourceMask;
- }
- void ResourceManager::issueInstruction(
- const InstrDesc &Desc,
- SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &Pipes) {
- for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
- const CycleSegment &CS = R.second.CS;
- if (!CS.size()) {
- releaseResource(R.first);
- continue;
- }
- assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
- if (!R.second.isReserved()) {
- ResourceRef Pipe = selectPipe(R.first);
- use(Pipe);
- BusyResources[Pipe] += CS.size();
- Pipes.emplace_back(std::pair<ResourceRef, ResourceCycles>(
- Pipe, ResourceCycles(CS.size())));
- } else {
- assert((llvm::popcount(R.first) > 1) && "Expected a group!");
- // Mark this group as reserved.
- assert(R.second.isReserved());
- reserveResource(R.first);
- BusyResources[ResourceRef(R.first, R.first)] += CS.size();
- }
- }
- }
- void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
- for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
- if (BR.second)
- BR.second--;
- if (!BR.second) {
- // Release this resource.
- const ResourceRef &RR = BR.first;
- if (llvm::popcount(RR.first) == 1)
- release(RR);
- releaseResource(RR.first);
- ResourcesFreed.push_back(RR);
- }
- }
- for (const ResourceRef &RF : ResourcesFreed)
- BusyResources.erase(RF);
- }
- void ResourceManager::reserveResource(uint64_t ResourceID) {
- const unsigned Index = getResourceStateIndex(ResourceID);
- ResourceState &Resource = *Resources[Index];
- assert(Resource.isAResourceGroup() && !Resource.isReserved() &&
- "Unexpected resource state found!");
- Resource.setReserved();
- ReservedResourceGroups ^= 1ULL << Index;
- }
- void ResourceManager::releaseResource(uint64_t ResourceID) {
- const unsigned Index = getResourceStateIndex(ResourceID);
- ResourceState &Resource = *Resources[Index];
- Resource.clearReserved();
- if (Resource.isAResourceGroup())
- ReservedResourceGroups ^= 1ULL << Index;
- // Now it is safe to release dispatch/issue resources.
- if (Resource.isADispatchHazard())
- ReservedBuffers ^= 1ULL << Index;
- }
- } // namespace mca
- } // namespace llvm
|