//===--------------------- ResourceManager.cpp ------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// \file /// /// The classes here represent processor resource units and their management /// strategy. These classes are managed by the Scheduler. /// //===----------------------------------------------------------------------===// #include "HardwareUnits/ResourceManager.h" #include "Support.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" namespace mca { using namespace llvm; #define DEBUG_TYPE "llvm-mca" ResourceStrategy::~ResourceStrategy() = default; void DefaultResourceStrategy::skipMask(uint64_t Mask) { NextInSequenceMask &= (~Mask); if (!NextInSequenceMask) { NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence; RemovedFromNextInSequence = 0; } } uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) { // This method assumes that ReadyMask cannot be zero. uint64_t CandidateMask = PowerOf2Floor(NextInSequenceMask); while (!(ReadyMask & CandidateMask)) { skipMask(CandidateMask); CandidateMask = PowerOf2Floor(NextInSequenceMask); } return CandidateMask; } void DefaultResourceStrategy::used(uint64_t Mask) { if (Mask > NextInSequenceMask) { RemovedFromNextInSequence |= Mask; return; } skipMask(Mask); } ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index, uint64_t Mask) : ProcResourceDescIndex(Index), ResourceMask(Mask), BufferSize(Desc.BufferSize) { if (countPopulation(ResourceMask) > 1) ResourceSizeMask = ResourceMask ^ PowerOf2Floor(ResourceMask); else ResourceSizeMask = (1ULL << Desc.NumUnits) - 1; ReadyMask = ResourceSizeMask; AvailableSlots = BufferSize == -1 ? 0U : static_cast(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: " << ResourceMask << ", SIZE_MASK: " << ResourceSizeMask << ", RDYMASK: " << ReadyMask << ", BufferSize=" << BufferSize << ", AvailableSlots=" << AvailableSlots << ", Reserved=" << Unavailable << '\n'; } #endif static unsigned getResourceStateIndex(uint64_t Mask) { return std::numeric_limits::digits - countLeadingZeros(Mask); } static std::unique_ptr getStrategyFor(const ResourceState &RS) { if (RS.isAResourceGroup() || RS.getNumUnits() > 1) return llvm::make_unique(RS.getReadyMask()); return std::unique_ptr(nullptr); } ResourceManager::ResourceManager(const MCSchedModel &SM) : ProcResID2Mask(SM.getNumProcResourceKinds()) { computeProcResourceMasks(SM, ProcResID2Mask); Resources.resize(SM.getNumProcResourceKinds()); Strategies.resize(SM.getNumProcResourceKinds()); for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { uint64_t Mask = ProcResID2Mask[I]; unsigned Index = getResourceStateIndex(Mask); Resources[Index] = llvm::make_unique(*SM.getProcResource(I), I, Mask); Strategies[Index] = getStrategyFor(*Resources[Index]); } } void ResourceManager::setCustomStrategyImpl(std::unique_ptr 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 Resources[getResourceStateIndex(Mask)]->getProcResourceID(); } 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); 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. ResourceState &RS = *Resources[getResourceStateIndex(RR.first)]; RS.markSubResourceAsUsed(RR.second); // If there are still available units in RR.first, // then we are done. if (RS.isReady()) return; // Notify to other resources that RR.first is no longer available. for (std::unique_ptr &Res : Resources) { ResourceState &Current = *Res; if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first) continue; if (Current.containsResource(RR.first)) { unsigned Index = getResourceStateIndex(Current.getResourceMask()); Current.markSubResourceAsUsed(RR.first); Strategies[Index]->used(RR.first); } } } void ResourceManager::release(const ResourceRef &RR) { ResourceState &RS = *Resources[getResourceStateIndex(RR.first)]; bool WasFullyUsed = !RS.isReady(); RS.releaseSubResource(RR.second); if (!WasFullyUsed) return; for (std::unique_ptr &Res : Resources) { ResourceState &Current = *Res; if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first) continue; if (Current.containsResource(RR.first)) Current.releaseSubResource(RR.first); } } ResourceStateEvent ResourceManager::canBeDispatched(ArrayRef Buffers) const { ResourceStateEvent Result = ResourceStateEvent::RS_BUFFER_AVAILABLE; for (uint64_t Buffer : Buffers) { ResourceState &RS = *Resources[getResourceStateIndex(Buffer)]; Result = RS.isBufferAvailable(); if (Result != ResourceStateEvent::RS_BUFFER_AVAILABLE) break; } return Result; } void ResourceManager::reserveBuffers(ArrayRef Buffers) { for (const uint64_t Buffer : Buffers) { ResourceState &RS = *Resources[getResourceStateIndex(Buffer)]; assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE); RS.reserveBuffer(); if (RS.isADispatchHazard()) { assert(!RS.isReserved()); RS.setReserved(); } } } void ResourceManager::releaseBuffers(ArrayRef Buffers) { for (const uint64_t R : Buffers) Resources[getResourceStateIndex(R)]->releaseBuffer(); } bool ResourceManager::canBeIssued(const InstrDesc &Desc) const { return std::all_of(Desc.Resources.begin(), Desc.Resources.end(), [&](const std::pair &E) { unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits; unsigned Index = getResourceStateIndex(E.first); return Resources[Index]->isReady(NumUnits); }); } // Returns true if all resources are in-order, and there is at least one // resource which is a dispatch hazard (BufferSize = 0). bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) const { if (!canBeIssued(Desc)) return false; bool AllInOrderResources = all_of(Desc.Buffers, [&](uint64_t BufferMask) { unsigned Index = getResourceStateIndex(BufferMask); const ResourceState &Resource = *Resources[Index]; return Resource.isInOrder() || Resource.isADispatchHazard(); }); if (!AllInOrderResources) return false; return any_of(Desc.Buffers, [&](uint64_t BufferMask) { return Resources[getResourceStateIndex(BufferMask)]->isADispatchHazard(); }); } void ResourceManager::issueInstruction( const InstrDesc &Desc, SmallVectorImpl> &Pipes) { for (const std::pair &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(); // Replace the resource mask with a valid processor resource index. const ResourceState &RS = *Resources[getResourceStateIndex(Pipe.first)]; Pipe.first = RS.getProcResourceID(); Pipes.emplace_back(std::pair( 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 &ResourcesFreed) { for (std::pair &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) { ResourceState &Resource = *Resources[getResourceStateIndex(ResourceID)]; assert(!Resource.isReserved()); Resource.setReserved(); } void ResourceManager::releaseResource(uint64_t ResourceID) { ResourceState &Resource = *Resources[getResourceStateIndex(ResourceID)]; Resource.clearReserved(); } } // namespace mca