Path: blob/main/contrib/llvm-project/llvm/lib/MCA/HardwareUnits/ResourceManager.cpp
35291 views
//===--------------------- ResourceManager.cpp ------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7/// \file8///9/// The classes here represent processor resource units and their management10/// strategy. These classes are managed by the Scheduler.11///12//===----------------------------------------------------------------------===//1314#include "llvm/MCA/HardwareUnits/ResourceManager.h"15#include "llvm/MCA/Support.h"16#include "llvm/Support/Debug.h"17#include "llvm/Support/raw_ostream.h"1819namespace llvm {20namespace mca {2122#define DEBUG_TYPE "llvm-mca"23ResourceStrategy::~ResourceStrategy() = default;2425static uint64_t selectImpl(uint64_t CandidateMask,26uint64_t &NextInSequenceMask) {27// The upper bit set in CandidateMask identifies our next candidate resource.28CandidateMask = 1ULL << getResourceStateIndex(CandidateMask);29NextInSequenceMask &= (CandidateMask | (CandidateMask - 1));30return CandidateMask;31}3233uint64_t DefaultResourceStrategy::select(uint64_t ReadyMask) {34// This method assumes that ReadyMask cannot be zero.35uint64_t CandidateMask = ReadyMask & NextInSequenceMask;36if (CandidateMask)37return selectImpl(CandidateMask, NextInSequenceMask);3839NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;40RemovedFromNextInSequence = 0;41CandidateMask = ReadyMask & NextInSequenceMask;42if (CandidateMask)43return selectImpl(CandidateMask, NextInSequenceMask);4445NextInSequenceMask = ResourceUnitMask;46CandidateMask = ReadyMask & NextInSequenceMask;47return selectImpl(CandidateMask, NextInSequenceMask);48}4950void DefaultResourceStrategy::used(uint64_t Mask) {51if (Mask > NextInSequenceMask) {52RemovedFromNextInSequence |= Mask;53return;54}5556NextInSequenceMask &= (~Mask);57if (NextInSequenceMask)58return;5960NextInSequenceMask = ResourceUnitMask ^ RemovedFromNextInSequence;61RemovedFromNextInSequence = 0;62}6364ResourceState::ResourceState(const MCProcResourceDesc &Desc, unsigned Index,65uint64_t Mask)66: ProcResourceDescIndex(Index), ResourceMask(Mask),67BufferSize(Desc.BufferSize), IsAGroup(llvm::popcount(ResourceMask) > 1) {68if (IsAGroup) {69ResourceSizeMask =70ResourceMask ^ 1ULL << getResourceStateIndex(ResourceMask);71} else {72ResourceSizeMask = (1ULL << Desc.NumUnits) - 1;73}74ReadyMask = ResourceSizeMask;75AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);76Unavailable = false;77}7879bool ResourceState::isReady(unsigned NumUnits) const {80return (!isReserved() || isADispatchHazard()) &&81(unsigned)llvm::popcount(ReadyMask) >= NumUnits;82}8384ResourceStateEvent ResourceState::isBufferAvailable() const {85if (isADispatchHazard() && isReserved())86return RS_RESERVED;87if (!isBuffered() || AvailableSlots)88return RS_BUFFER_AVAILABLE;89return RS_BUFFER_UNAVAILABLE;90}9192#ifndef NDEBUG93void ResourceState::dump() const {94dbgs() << "MASK=" << format_hex(ResourceMask, 16)95<< ", SZMASK=" << format_hex(ResourceSizeMask, 16)96<< ", RDYMASK=" << format_hex(ReadyMask, 16)97<< ", BufferSize=" << BufferSize98<< ", AvailableSlots=" << AvailableSlots99<< ", Reserved=" << Unavailable << '\n';100}101#endif102103static std::unique_ptr<ResourceStrategy>104getStrategyFor(const ResourceState &RS) {105if (RS.isAResourceGroup() || RS.getNumUnits() > 1)106return std::make_unique<DefaultResourceStrategy>(RS.getReadyMask());107return std::unique_ptr<ResourceStrategy>(nullptr);108}109110ResourceManager::ResourceManager(const MCSchedModel &SM)111: Resources(SM.getNumProcResourceKinds() - 1),112Strategies(SM.getNumProcResourceKinds() - 1),113Resource2Groups(SM.getNumProcResourceKinds() - 1, 0),114ProcResID2Mask(SM.getNumProcResourceKinds(), 0),115ResIndex2ProcResID(SM.getNumProcResourceKinds() - 1, 0),116ProcResUnitMask(0), ReservedResourceGroups(0), AvailableBuffers(~0ULL),117ReservedBuffers(0) {118computeProcResourceMasks(SM, ProcResID2Mask);119120// initialize vector ResIndex2ProcResID.121for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {122unsigned Index = getResourceStateIndex(ProcResID2Mask[I]);123ResIndex2ProcResID[Index] = I;124}125126for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {127uint64_t Mask = ProcResID2Mask[I];128unsigned Index = getResourceStateIndex(Mask);129Resources[Index] =130std::make_unique<ResourceState>(*SM.getProcResource(I), I, Mask);131Strategies[Index] = getStrategyFor(*Resources[Index]);132}133134for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {135uint64_t Mask = ProcResID2Mask[I];136unsigned Index = getResourceStateIndex(Mask);137const ResourceState &RS = *Resources[Index];138if (!RS.isAResourceGroup()) {139ProcResUnitMask |= Mask;140continue;141}142143uint64_t GroupMaskIdx = 1ULL << Index;144Mask -= GroupMaskIdx;145while (Mask) {146// Extract lowest set isolated bit.147uint64_t Unit = Mask & (-Mask);148unsigned IndexUnit = getResourceStateIndex(Unit);149Resource2Groups[IndexUnit] |= GroupMaskIdx;150Mask ^= Unit;151}152}153154AvailableProcResUnits = ProcResUnitMask;155}156157void ResourceManager::setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,158uint64_t ResourceMask) {159unsigned Index = getResourceStateIndex(ResourceMask);160assert(Index < Resources.size() && "Invalid processor resource index!");161assert(S && "Unexpected null strategy in input!");162Strategies[Index] = std::move(S);163}164165unsigned ResourceManager::resolveResourceMask(uint64_t Mask) const {166return ResIndex2ProcResID[getResourceStateIndex(Mask)];167}168169unsigned ResourceManager::getNumUnits(uint64_t ResourceID) const {170return Resources[getResourceStateIndex(ResourceID)]->getNumUnits();171}172173// Returns the actual resource consumed by this Use.174// First, is the primary resource ID.175// Second, is the specific sub-resource ID.176ResourceRef ResourceManager::selectPipe(uint64_t ResourceID) {177unsigned Index = getResourceStateIndex(ResourceID);178assert(Index < Resources.size() && "Invalid resource use!");179ResourceState &RS = *Resources[Index];180assert(RS.isReady() && "No available units to select!");181182// Special case where RS is not a group, and it only declares a single183// resource unit.184if (!RS.isAResourceGroup() && RS.getNumUnits() == 1)185return std::make_pair(ResourceID, RS.getReadyMask());186187uint64_t SubResourceID = Strategies[Index]->select(RS.getReadyMask());188if (RS.isAResourceGroup())189return selectPipe(SubResourceID);190return std::make_pair(ResourceID, SubResourceID);191}192193void ResourceManager::use(const ResourceRef &RR) {194// Mark the sub-resource referenced by RR as used.195unsigned RSID = getResourceStateIndex(RR.first);196ResourceState &RS = *Resources[RSID];197RS.markSubResourceAsUsed(RR.second);198// Remember to update the resource strategy for non-group resources with199// multiple units.200if (RS.getNumUnits() > 1)201Strategies[RSID]->used(RR.second);202203// If there are still available units in RR.first,204// then we are done.205if (RS.isReady())206return;207208AvailableProcResUnits ^= RR.first;209210// Notify groups that RR.first is no longer available.211uint64_t Users = Resource2Groups[RSID];212while (Users) {213// Extract lowest set isolated bit.214unsigned GroupIndex = getResourceStateIndex(Users & (-Users));215ResourceState &CurrentUser = *Resources[GroupIndex];216CurrentUser.markSubResourceAsUsed(RR.first);217Strategies[GroupIndex]->used(RR.first);218// Reset lowest set bit.219Users &= Users - 1;220}221}222223void ResourceManager::release(const ResourceRef &RR) {224unsigned RSID = getResourceStateIndex(RR.first);225ResourceState &RS = *Resources[RSID];226bool WasFullyUsed = !RS.isReady();227RS.releaseSubResource(RR.second);228if (!WasFullyUsed)229return;230231AvailableProcResUnits ^= RR.first;232233// Notify groups that RR.first is now available again.234uint64_t Users = Resource2Groups[RSID];235while (Users) {236unsigned GroupIndex = getResourceStateIndex(Users & (-Users));237ResourceState &CurrentUser = *Resources[GroupIndex];238CurrentUser.releaseSubResource(RR.first);239Users &= Users - 1;240}241}242243ResourceStateEvent244ResourceManager::canBeDispatched(uint64_t ConsumedBuffers) const {245if (ConsumedBuffers & ReservedBuffers)246return ResourceStateEvent::RS_RESERVED;247if (ConsumedBuffers & (~AvailableBuffers))248return ResourceStateEvent::RS_BUFFER_UNAVAILABLE;249return ResourceStateEvent::RS_BUFFER_AVAILABLE;250}251252void ResourceManager::reserveBuffers(uint64_t ConsumedBuffers) {253while (ConsumedBuffers) {254uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);255ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];256ConsumedBuffers ^= CurrentBuffer;257assert(RS.isBufferAvailable() == ResourceStateEvent::RS_BUFFER_AVAILABLE);258if (!RS.reserveBuffer())259AvailableBuffers ^= CurrentBuffer;260if (RS.isADispatchHazard()) {261// Reserve this buffer now, and release it once pipeline resources262// consumed by the instruction become available again.263// We do this to simulate an in-order dispatch/issue of instructions.264ReservedBuffers ^= CurrentBuffer;265}266}267}268269void ResourceManager::releaseBuffers(uint64_t ConsumedBuffers) {270AvailableBuffers |= ConsumedBuffers;271while (ConsumedBuffers) {272uint64_t CurrentBuffer = ConsumedBuffers & (-ConsumedBuffers);273ResourceState &RS = *Resources[getResourceStateIndex(CurrentBuffer)];274ConsumedBuffers ^= CurrentBuffer;275RS.releaseBuffer();276// Do not unreserve dispatch hazard resource buffers. Wait until all277// pipeline resources have been freed too.278}279}280281uint64_t ResourceManager::checkAvailability(const InstrDesc &Desc) const {282uint64_t BusyResourceMask = 0;283uint64_t ConsumedResourceMask = 0;284DenseMap<uint64_t, unsigned> AvailableUnits;285286for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {287unsigned NumUnits = E.second.isReserved() ? 0U : E.second.NumUnits;288const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];289if (!RS.isReady(NumUnits)) {290BusyResourceMask |= E.first;291continue;292}293294if (Desc.HasPartiallyOverlappingGroups && !RS.isAResourceGroup()) {295unsigned NumAvailableUnits = llvm::popcount(RS.getReadyMask());296NumAvailableUnits -= NumUnits;297AvailableUnits[E.first] = NumAvailableUnits;298if (!NumAvailableUnits)299ConsumedResourceMask |= E.first;300}301}302303BusyResourceMask &= ProcResUnitMask;304if (BusyResourceMask)305return BusyResourceMask;306307BusyResourceMask = Desc.UsedProcResGroups & ReservedResourceGroups;308if (!Desc.HasPartiallyOverlappingGroups || BusyResourceMask)309return BusyResourceMask;310311// If this instruction has overlapping groups, make sure that we can312// select at least one unit per group.313for (const std::pair<uint64_t, ResourceUsage> &E : Desc.Resources) {314const ResourceState &RS = *Resources[getResourceStateIndex(E.first)];315if (!E.second.isReserved() && RS.isAResourceGroup()) {316uint64_t ReadyMask = RS.getReadyMask() & ~ConsumedResourceMask;317if (!ReadyMask) {318BusyResourceMask |= RS.getReadyMask();319continue;320}321322uint64_t ResourceMask = llvm::bit_floor(ReadyMask);323324auto it = AvailableUnits.find(ResourceMask);325if (it == AvailableUnits.end()) {326unsigned Index = getResourceStateIndex(ResourceMask);327unsigned NumUnits = llvm::popcount(Resources[Index]->getReadyMask());328it =329AvailableUnits.insert(std::make_pair(ResourceMask, NumUnits)).first;330}331332if (!it->second) {333BusyResourceMask |= it->first;334continue;335}336337it->second--;338if (!it->second)339ConsumedResourceMask |= it->first;340}341}342343return BusyResourceMask;344}345346void ResourceManager::issueInstruction(347const InstrDesc &Desc,348SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &Pipes) {349for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {350const CycleSegment &CS = R.second.CS;351if (!CS.size()) {352releaseResource(R.first);353continue;354}355356assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");357if (!R.second.isReserved()) {358ResourceRef Pipe = selectPipe(R.first);359use(Pipe);360BusyResources[Pipe] += CS.size();361Pipes.emplace_back(std::pair<ResourceRef, ReleaseAtCycles>(362Pipe, ReleaseAtCycles(CS.size())));363} else {364assert((llvm::popcount(R.first) > 1) && "Expected a group!");365// Mark this group as reserved.366assert(R.second.isReserved());367reserveResource(R.first);368BusyResources[ResourceRef(R.first, R.first)] += CS.size();369}370}371}372373void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {374for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {375if (BR.second)376BR.second--;377if (!BR.second) {378// Release this resource.379const ResourceRef &RR = BR.first;380381if (llvm::popcount(RR.first) == 1)382release(RR);383releaseResource(RR.first);384ResourcesFreed.push_back(RR);385}386}387388for (const ResourceRef &RF : ResourcesFreed)389BusyResources.erase(RF);390}391392void ResourceManager::reserveResource(uint64_t ResourceID) {393const unsigned Index = getResourceStateIndex(ResourceID);394ResourceState &Resource = *Resources[Index];395assert(Resource.isAResourceGroup() && !Resource.isReserved() &&396"Unexpected resource state found!");397Resource.setReserved();398ReservedResourceGroups ^= 1ULL << Index;399}400401void ResourceManager::releaseResource(uint64_t ResourceID) {402const unsigned Index = getResourceStateIndex(ResourceID);403ResourceState &Resource = *Resources[Index];404Resource.clearReserved();405if (Resource.isAResourceGroup())406ReservedResourceGroups ^= 1ULL << Index;407// Now it is safe to release dispatch/issue resources.408if (Resource.isADispatchHazard())409ReservedBuffers ^= 1ULL << Index;410}411412} // namespace mca413} // namespace llvm414415416