|  | //===--------------------- 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(countPopulation(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()) && | 
|  | countPopulation(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; | 
|  | for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) { | 
|  | unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits; | 
|  | unsigned Index = getResourceStateIndex(E.first); | 
|  | if (!Resources[Index]->isReady(NumUnits)) | 
|  | BusyResourceMask |= E.first; | 
|  | } | 
|  |  | 
|  | BusyResourceMask &= ProcResUnitMask; | 
|  | if (BusyResourceMask) | 
|  | return BusyResourceMask; | 
|  | return Desc.UsedProcResGroups & ReservedResourceGroups; | 
|  | } | 
|  |  | 
|  | 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((countPopulation(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 (countPopulation(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 |