From 220271acde05dcd60cf23267b359b5b97d2dcd72 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 24 Aug 2018 21:53:12 +0000 Subject: [PATCH] [llvm-mca] Move the ResourceManger from the Scheduler into its own file. NFC. llvm-svn: 340659 --- llvm/tools/llvm-mca/CMakeLists.txt | 1 + llvm/tools/llvm-mca/ResourceManager.cpp | 309 +++++++++++++++++++++++++++ llvm/tools/llvm-mca/ResourceManager.h | 360 ++++++++++++++++++++++++++++++++ llvm/tools/llvm-mca/Scheduler.cpp | 284 ------------------------- llvm/tools/llvm-mca/Scheduler.h | 339 +----------------------------- 5 files changed, 672 insertions(+), 621 deletions(-) create mode 100644 llvm/tools/llvm-mca/ResourceManager.cpp create mode 100644 llvm/tools/llvm-mca/ResourceManager.h diff --git a/llvm/tools/llvm-mca/CMakeLists.txt b/llvm/tools/llvm-mca/CMakeLists.txt index a1bc117..63cbda5 100644 --- a/llvm/tools/llvm-mca/CMakeLists.txt +++ b/llvm/tools/llvm-mca/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_tool(llvm-mca Pipeline.cpp PipelinePrinter.cpp RegisterFile.cpp + ResourceManager.cpp RetireControlUnit.cpp RetireStage.cpp Scheduler.cpp diff --git a/llvm/tools/llvm-mca/ResourceManager.cpp b/llvm/tools/llvm-mca/ResourceManager.cpp new file mode 100644 index 0000000..80ea73e --- /dev/null +++ b/llvm/tools/llvm-mca/ResourceManager.cpp @@ -0,0 +1,309 @@ +//===--------------------- 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 "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 = llvm::PowerOf2Floor(NextInSequenceMask); + while (!(ReadyMask & CandidateMask)) { + skipMask(CandidateMask); + CandidateMask = llvm::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 (llvm::countPopulation(ResourceMask) > 1) + ResourceSizeMask = ResourceMask ^ llvm::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()) && + llvm::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 - llvm::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, static_cast(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 diff --git a/llvm/tools/llvm-mca/ResourceManager.h b/llvm/tools/llvm-mca/ResourceManager.h new file mode 100644 index 0000000..c3f8936 --- /dev/null +++ b/llvm/tools/llvm-mca/ResourceManager.h @@ -0,0 +1,360 @@ +//===--------------------- ResourceManager.h --------------------*- 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. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H +#define LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H + +#include "Instruction.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCSchedule.h" + +namespace mca { + +/// Used to notify the internal state of a processor resource. +/// +/// A processor resource is available if it is not reserved, and there are +/// available slots in the buffer. A processor resource is unavailable if it +/// is either reserved, or the associated buffer is full. A processor resource +/// with a buffer size of -1 is always available if it is not reserved. +/// +/// Values of type ResourceStateEvent are returned by method +/// ResourceState::isBufferAvailable(), which is used to query the internal +/// state of a resource. +/// +/// The naming convention for resource state events is: +/// * Event names start with prefix RS_ +/// * Prefix RS_ is followed by a string describing the actual resource state. +enum ResourceStateEvent { + RS_BUFFER_AVAILABLE, + RS_BUFFER_UNAVAILABLE, + RS_RESERVED +}; + +/// Resource allocation strategy used by hardware scheduler resources. +class ResourceStrategy { + ResourceStrategy(const ResourceStrategy &) = delete; + ResourceStrategy &operator=(const ResourceStrategy &) = delete; + +public: + ResourceStrategy() {} + virtual ~ResourceStrategy(); + + /// Selects a processor resource unit from a ReadyMask. + virtual uint64_t select(uint64_t ReadyMask) = 0; + + /// Called by the ResourceManager when a processor resource group, or a + /// processor resource with multiple units has become unavailable. + /// + /// The default strategy uses this information to bias its selection logic. + virtual void used(uint64_t ResourceMask) {} +}; + +/// Default resource allocation strategy used by processor resource groups and +/// processor resources with multiple units. +class DefaultResourceStrategy final : public ResourceStrategy { + /// A Mask of resource unit identifiers. + /// + /// There is one bit set for every available resource unit. + /// It defaults to the value of field ResourceSizeMask in ResourceState. + const unsigned ResourceUnitMask; + + /// A simple round-robin selector for processor resource units. + /// Each bit of this mask identifies a sub resource within a group. + /// + /// As an example, lets assume that this is a default policy for a + /// processor resource group composed by the following three units: + /// ResourceA -- 0b001 + /// ResourceB -- 0b010 + /// ResourceC -- 0b100 + /// + /// Field NextInSequenceMask is used to select the next unit from the set of + /// resource units. It defaults to the value of field `ResourceUnitMasks` (in + /// this example, it defaults to mask '0b111'). + /// + /// The round-robin selector would firstly select 'ResourceC', then + /// 'ResourceB', and eventually 'ResourceA'. When a resource R is used, the + /// corresponding bit in NextInSequenceMask is cleared. For example, if + /// 'ResourceC' is selected, then the new value of NextInSequenceMask becomes + /// 0xb011. + /// + /// When NextInSequenceMask becomes zero, it is automatically reset to the + /// default value (i.e. ResourceUnitMask). + uint64_t NextInSequenceMask; + + /// This field is used to track resource units that are used (i.e. selected) + /// by other groups other than the one associated with this strategy object. + /// + /// In LLVM processor resource groups are allowed to partially (or fully) + /// overlap. That means, a same unit may be visible to multiple groups. + /// This field keeps track of uses that have originated from outside of + /// this group. The idea is to bias the selection strategy, so that resources + /// that haven't been used by other groups get prioritized. + /// + /// The end goal is to (try to) keep the resource distribution as much uniform + /// as possible. By construction, this mask only tracks one-level of resource + /// usage. Therefore, this strategy is expected to be less accurate when same + /// units are used multiple times by other groups within a single round of + /// select. + /// + /// Note: an LRU selector would have a better accuracy at the cost of being + /// slightly more expensive (mostly in terms of runtime cost). Methods + /// 'select' and 'used', are always in the hot execution path of llvm-mca. + /// Therefore, a slow implementation of 'select' would have a negative impact + /// on the overall performance of the tool. + uint64_t RemovedFromNextInSequence; + + void skipMask(uint64_t Mask); + +public: + DefaultResourceStrategy(uint64_t UnitMask) + : ResourceStrategy(), ResourceUnitMask(UnitMask), + NextInSequenceMask(UnitMask), RemovedFromNextInSequence(0) {} + virtual ~DefaultResourceStrategy() = default; + + uint64_t select(uint64_t ReadyMask) override; + void used(uint64_t Mask) override; +}; + +/// A processor resource descriptor. +/// +/// There is an instance of this class for every processor resource defined by +/// the machine scheduling model. +/// Objects of class ResourceState dynamically track the usage of processor +/// resource units. +class ResourceState { + /// An index to the MCProcResourceDesc entry in the processor model. + const unsigned ProcResourceDescIndex; + /// A resource mask. This is generated by the tool with the help of + /// function `mca::createProcResourceMasks' (see Support.h). + const uint64_t ResourceMask; + + /// A ProcResource can have multiple units. + /// + /// For processor resource groups, + /// this field default to the value of field `ResourceMask`; the number of + /// bits set is equal to the cardinality of the group. For normal (i.e. + /// non-group) resources, the number of bits set in this mask is equivalent + /// to the number of units declared by the processor model (see field + /// 'NumUnits' in 'ProcResourceUnits'). + uint64_t ResourceSizeMask; + + /// A mask of ready units. + uint64_t ReadyMask; + + /// Buffered resources will have this field set to a positive number different + /// than zero. A buffered resource behaves like a reservation station + /// implementing its own buffer for out-of-order execution. + /// + /// A BufferSize of 1 is used by scheduler resources that force in-order + /// execution. + /// + /// A BufferSize of 0 is used to model in-order issue/dispatch resources. + /// Since in-order issue/dispatch resources don't implement buffers, dispatch + /// events coincide with issue events. + /// Also, no other instruction ca be dispatched/issue while this resource is + /// in use. Only when all the "resource cycles" are consumed (after the issue + /// event), a new instruction ca be dispatched. + const int BufferSize; + + /// Available slots in the buffer (zero, if this is not a buffered resource). + unsigned AvailableSlots; + + /// This field is set if this resource is currently reserved. + /// + /// Resources can be reserved for a number of cycles. + /// Instructions can still be dispatched to reserved resources. However, + /// istructions dispatched to a reserved resource cannot be issued to the + /// underlying units (i.e. pipelines) until the resource is released. + bool Unavailable; + + /// Checks for the availability of unit 'SubResMask' in the group. + bool isSubResourceReady(uint64_t SubResMask) const { + return ReadyMask & SubResMask; + } + +public: + ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index, + uint64_t Mask); + + unsigned getProcResourceID() const { return ProcResourceDescIndex; } + uint64_t getResourceMask() const { return ResourceMask; } + uint64_t getReadyMask() const { return ReadyMask; } + int getBufferSize() const { return BufferSize; } + + bool isBuffered() const { return BufferSize > 0; } + bool isInOrder() const { return BufferSize == 1; } + + /// Returns true if this is an in-order dispatch/issue resource. + bool isADispatchHazard() const { return BufferSize == 0; } + bool isReserved() const { return Unavailable; } + + void setReserved() { Unavailable = true; } + void clearReserved() { Unavailable = false; } + + /// Returs true if this resource is not reserved, and if there are at least + /// `NumUnits` available units. + bool isReady(unsigned NumUnits = 1) const; + + bool isAResourceGroup() const { + return llvm::countPopulation(ResourceMask) > 1; + } + + bool containsResource(uint64_t ID) const { return ResourceMask & ID; } + + void markSubResourceAsUsed(uint64_t ID) { + assert(isSubResourceReady(ID)); + ReadyMask ^= ID; + } + + void releaseSubResource(uint64_t ID) { + assert(!isSubResourceReady(ID)); + ReadyMask ^= ID; + } + + unsigned getNumUnits() const { + return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask); + } + + /// Checks if there is an available slot in the resource buffer. + /// + /// Returns RS_BUFFER_AVAILABLE if this is not a buffered resource, or if + /// there is a slot available. + /// + /// Returns RS_RESERVED if this buffered resource is a dispatch hazard, and it + /// is reserved. + /// + /// Returns RS_BUFFER_UNAVAILABLE if there are no available slots. + ResourceStateEvent isBufferAvailable() const; + + /// Reserve a slot in the buffer. + void reserveBuffer() { + if (AvailableSlots) + AvailableSlots--; + } + + /// Release a slot in the buffer. + void releaseBuffer() { + if (BufferSize > 0) + AvailableSlots++; + assert(AvailableSlots <= static_cast(BufferSize)); + } + +#ifndef NDEBUG + void dump() const; +#endif +}; + +/// A resource unit identifier. +/// +/// This is used to identify a specific processor resource unit using a pair +/// of indices where the 'first' index is a processor resource mask, and the +/// 'second' index is an index for a "sub-resource" (i.e. unit). +typedef std::pair ResourceRef; + +// First: a MCProcResourceDesc index identifying a buffered resource. +// Second: max number of buffer entries used in this resource. +typedef std::pair BufferUsageEntry; + +/// A resource manager for processor resource units and groups. +/// +/// This class owns all the ResourceState objects, and it is responsible for +/// acting on requests from a Scheduler by updating the internal state of +/// ResourceState objects. +/// This class doesn't know about instruction itineraries and functional units. +/// In future, it can be extended to support itineraries too through the same +/// public interface. +class ResourceManager { + // The resource manager owns all the ResourceState. + std::vector> Resources; + std::vector> Strategies; + + // Keeps track of which resources are busy, and how many cycles are left + // before those become usable again. + llvm::SmallDenseMap BusyResources; + + // A table to map processor resource IDs to processor resource masks. + llvm::SmallVector ProcResID2Mask; + + // Returns the actual resource unit that will be used. + ResourceRef selectPipe(uint64_t ResourceID); + + void use(const ResourceRef &RR); + void release(const ResourceRef &RR); + + unsigned getNumUnits(uint64_t ResourceID) const; + + // Overrides the selection strategy for the processor resource with the given + // mask. + void setCustomStrategyImpl(std::unique_ptr S, + uint64_t ResourceMask); + +public: + ResourceManager(const llvm::MCSchedModel &SM); + virtual ~ResourceManager() = default; + + // Overrides the selection strategy for the resource at index ResourceID in + // the MCProcResourceDesc table. + void setCustomStrategy(std::unique_ptr S, + unsigned ResourceID) { + assert(ResourceID < ProcResID2Mask.size() && + "Invalid resource index in input!"); + return setCustomStrategyImpl(std::move(S), ProcResID2Mask[ResourceID]); + } + + // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if + // there are enough available slots in the buffers. + ResourceStateEvent canBeDispatched(llvm::ArrayRef Buffers) const; + + // Return the processor resource identifier associated to this Mask. + unsigned resolveResourceMask(uint64_t Mask) const; + + // Consume a slot in every buffered resource from array 'Buffers'. Resource + // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved. + void reserveBuffers(llvm::ArrayRef Buffers); + + // Release buffer entries previously allocated by method reserveBuffers. + void releaseBuffers(llvm::ArrayRef Buffers); + + // Reserve a processor resource. A reserved resource is not available for + // instruction issue until it is released. + void reserveResource(uint64_t ResourceID); + + // Release a previously reserved processor resource. + void releaseResource(uint64_t ResourceID); + + // Returns true if all resources are in-order, and there is at least one + // resource which is a dispatch hazard (BufferSize = 0). + bool mustIssueImmediately(const InstrDesc &Desc) const; + + bool canBeIssued(const InstrDesc &Desc) const; + + void issueInstruction( + const InstrDesc &Desc, + llvm::SmallVectorImpl> &Pipes); + + void cycleEvent(llvm::SmallVectorImpl &ResourcesFreed); + +#ifndef NDEBUG + void dump() const { + for (const std::unique_ptr &Resource : Resources) + Resource->dump(); + } +#endif +}; +} // namespace mca + +#endif // LLVM_TOOLS_LLVM_MCA_RESOURCE_MANAGER_H diff --git a/llvm/tools/llvm-mca/Scheduler.cpp b/llvm/tools/llvm-mca/Scheduler.cpp index 0fd720d..b0b0d71 100644 --- a/llvm/tools/llvm-mca/Scheduler.cpp +++ b/llvm/tools/llvm-mca/Scheduler.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "Scheduler.h" -#include "Support.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -22,294 +21,11 @@ using namespace llvm; #define DEBUG_TYPE "llvm-mca" -ResourceStrategy::~ResourceStrategy() = default; - void Scheduler::initializeStrategy(std::unique_ptr S) { // Ensure we have a valid (non-null) strategy object. Strategy = S ? std::move(S) : llvm::make_unique(); } -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 = llvm::PowerOf2Floor(NextInSequenceMask); - while (!(ReadyMask & CandidateMask)) { - skipMask(CandidateMask); - CandidateMask = llvm::PowerOf2Floor(NextInSequenceMask); - } - return CandidateMask; -} - -void DefaultResourceStrategy::used(uint64_t Mask) { - if (Mask > NextInSequenceMask) { - RemovedFromNextInSequence |= Mask; - return; - } - skipMask(Mask); -} - -ResourceState::ResourceState(const llvm::MCProcResourceDesc &Desc, - unsigned Index, uint64_t Mask) - : ProcResourceDescIndex(Index), ResourceMask(Mask), - BufferSize(Desc.BufferSize) { - if (llvm::countPopulation(ResourceMask) > 1) - ResourceSizeMask = ResourceMask ^ llvm::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()) && - llvm::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 - llvm::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 llvm::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, static_cast(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(); -} - // Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy. SchedulerStrategy::~SchedulerStrategy() = default; DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default; diff --git a/llvm/tools/llvm-mca/Scheduler.h b/llvm/tools/llvm-mca/Scheduler.h index ed138fd..70f90c5 100644 --- a/llvm/tools/llvm-mca/Scheduler.h +++ b/llvm/tools/llvm-mca/Scheduler.h @@ -16,348 +16,13 @@ #define LLVM_TOOLS_LLVM_MCA_SCHEDULER_H #include "HardwareUnit.h" -#include "Instruction.h" #include "LSUnit.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" +#include "ResourceManager.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include +#include "llvm/MC/MCSchedule.h" namespace mca { -/// Used to notify the internal state of a processor resource. -/// -/// A processor resource is available if it is not reserved, and there are -/// available slots in the buffer. A processor resource is unavailable if it -/// is either reserved, or the associated buffer is full. A processor resource -/// with a buffer size of -1 is always available if it is not reserved. -/// -/// Values of type ResourceStateEvent are returned by method -/// ResourceState::isBufferAvailable(), which is used to query the internal -/// state of a resource. -/// -/// The naming convention for resource state events is: -/// * Event names start with prefix RS_ -/// * Prefix RS_ is followed by a string describing the actual resource state. -enum ResourceStateEvent { - RS_BUFFER_AVAILABLE, - RS_BUFFER_UNAVAILABLE, - RS_RESERVED -}; - -/// Resource allocation strategy used by hardware scheduler resources. -class ResourceStrategy { - ResourceStrategy(const ResourceStrategy &) = delete; - ResourceStrategy &operator=(const ResourceStrategy &) = delete; - -public: - ResourceStrategy() {} - virtual ~ResourceStrategy(); - - /// Selects a processor resource unit from a ReadyMask. - virtual uint64_t select(uint64_t ReadyMask) = 0; - - /// Called by the ResourceManager when a processor resource group, or a - /// processor resource with multiple units has become unavailable. - /// - /// The default strategy uses this information to bias its selection logic. - virtual void used(uint64_t ResourceMask) {} -}; - -/// Default resource allocation strategy used by processor resource groups and -/// processor resources with multiple units. -class DefaultResourceStrategy final : public ResourceStrategy { - /// A Mask of resource unit identifiers. - /// - /// There is one bit set for every available resource unit. - /// It defaults to the value of field ResourceSizeMask in ResourceState. - const unsigned ResourceUnitMask; - - /// A simple round-robin selector for processor resource units. - /// Each bit of this mask identifies a sub resource within a group. - /// - /// As an example, lets assume that this is a default policy for a - /// processor resource group composed by the following three units: - /// ResourceA -- 0b001 - /// ResourceB -- 0b010 - /// ResourceC -- 0b100 - /// - /// Field NextInSequenceMask is used to select the next unit from the set of - /// resource units. It defaults to the value of field `ResourceUnitMasks` (in - /// this example, it defaults to mask '0b111'). - /// - /// The round-robin selector would firstly select 'ResourceC', then - /// 'ResourceB', and eventually 'ResourceA'. When a resource R is used, the - /// corresponding bit in NextInSequenceMask is cleared. For example, if - /// 'ResourceC' is selected, then the new value of NextInSequenceMask becomes - /// 0xb011. - /// - /// When NextInSequenceMask becomes zero, it is automatically reset to the - /// default value (i.e. ResourceUnitMask). - uint64_t NextInSequenceMask; - - /// This field is used to track resource units that are used (i.e. selected) - /// by other groups other than the one associated with this strategy object. - /// - /// In LLVM processor resource groups are allowed to partially (or fully) - /// overlap. That means, a same unit may be visible to multiple groups. - /// This field keeps track of uses that have originated from outside of - /// this group. The idea is to bias the selection strategy, so that resources - /// that haven't been used by other groups get prioritized. - /// - /// The end goal is to (try to) keep the resource distribution as much uniform - /// as possible. By construction, this mask only tracks one-level of resource - /// usage. Therefore, this strategy is expected to be less accurate when same - /// units are used multiple times by other groups within a single round of - /// select. - /// - /// Note: an LRU selector would have a better accuracy at the cost of being - /// slightly more expensive (mostly in terms of runtime cost). Methods - /// 'select' and 'used', are always in the hot execution path of llvm-mca. - /// Therefore, a slow implementation of 'select' would have a negative impact - /// on the overall performance of the tool. - uint64_t RemovedFromNextInSequence; - - void skipMask(uint64_t Mask); - -public: - DefaultResourceStrategy(uint64_t UnitMask) - : ResourceStrategy(), ResourceUnitMask(UnitMask), - NextInSequenceMask(UnitMask), RemovedFromNextInSequence(0) {} - virtual ~DefaultResourceStrategy() = default; - - uint64_t select(uint64_t ReadyMask) override; - void used(uint64_t Mask) override; -}; - -/// A processor resource descriptor. -/// -/// There is an instance of this class for every processor resource defined by -/// the machine scheduling model. -/// Objects of class ResourceState dynamically track the usage of processor -/// resource units. -class ResourceState { - /// An index to the MCProcResourceDesc entry in the processor model. - const unsigned ProcResourceDescIndex; - /// A resource mask. This is generated by the tool with the help of - /// function `mca::createProcResourceMasks' (see Support.h). - const uint64_t ResourceMask; - - /// A ProcResource can have multiple units. - /// - /// For processor resource groups, - /// this field default to the value of field `ResourceMask`; the number of - /// bits set is equal to the cardinality of the group. For normal (i.e. - /// non-group) resources, the number of bits set in this mask is equivalent - /// to the number of units declared by the processor model (see field - /// 'NumUnits' in 'ProcResourceUnits'). - uint64_t ResourceSizeMask; - - /// A mask of ready units. - uint64_t ReadyMask; - - /// Buffered resources will have this field set to a positive number different - /// than zero. A buffered resource behaves like a reservation station - /// implementing its own buffer for out-of-order execution. - /// - /// A BufferSize of 1 is used by scheduler resources that force in-order - /// execution. - /// - /// A BufferSize of 0 is used to model in-order issue/dispatch resources. - /// Since in-order issue/dispatch resources don't implement buffers, dispatch - /// events coincide with issue events. - /// Also, no other instruction ca be dispatched/issue while this resource is - /// in use. Only when all the "resource cycles" are consumed (after the issue - /// event), a new instruction ca be dispatched. - const int BufferSize; - - /// Available slots in the buffer (zero, if this is not a buffered resource). - unsigned AvailableSlots; - - /// This field is set if this resource is currently reserved. - /// - /// Resources can be reserved for a number of cycles. - /// Instructions can still be dispatched to reserved resources. However, - /// istructions dispatched to a reserved resource cannot be issued to the - /// underlying units (i.e. pipelines) until the resource is released. - bool Unavailable; - - /// Checks for the availability of unit 'SubResMask' in the group. - bool isSubResourceReady(uint64_t SubResMask) const { - return ReadyMask & SubResMask; - } - -public: - ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index, - uint64_t Mask); - - unsigned getProcResourceID() const { return ProcResourceDescIndex; } - uint64_t getResourceMask() const { return ResourceMask; } - uint64_t getReadyMask() const { return ReadyMask; } - int getBufferSize() const { return BufferSize; } - - bool isBuffered() const { return BufferSize > 0; } - bool isInOrder() const { return BufferSize == 1; } - - /// Returns true if this is an in-order dispatch/issue resource. - bool isADispatchHazard() const { return BufferSize == 0; } - bool isReserved() const { return Unavailable; } - - void setReserved() { Unavailable = true; } - void clearReserved() { Unavailable = false; } - - /// Returs true if this resource is not reserved, and if there are at least - /// `NumUnits` available units. - bool isReady(unsigned NumUnits = 1) const; - - bool isAResourceGroup() const { - return llvm::countPopulation(ResourceMask) > 1; - } - - bool containsResource(uint64_t ID) const { return ResourceMask & ID; } - - void markSubResourceAsUsed(uint64_t ID) { - assert(isSubResourceReady(ID)); - ReadyMask ^= ID; - } - - void releaseSubResource(uint64_t ID) { - assert(!isSubResourceReady(ID)); - ReadyMask ^= ID; - } - - unsigned getNumUnits() const { - return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask); - } - - /// Checks if there is an available slot in the resource buffer. - /// - /// Returns RS_BUFFER_AVAILABLE if this is not a buffered resource, or if - /// there is a slot available. - /// - /// Returns RS_RESERVED if this buffered resource is a dispatch hazard, and it - /// is reserved. - /// - /// Returns RS_BUFFER_UNAVAILABLE if there are no available slots. - ResourceStateEvent isBufferAvailable() const; - - /// Reserve a slot in the buffer. - void reserveBuffer() { - if (AvailableSlots) - AvailableSlots--; - } - - /// Release a slot in the buffer. - void releaseBuffer() { - if (BufferSize > 0) - AvailableSlots++; - assert(AvailableSlots <= static_cast(BufferSize)); - } - -#ifndef NDEBUG - void dump() const; -#endif -}; - -/// A resource unit identifier. -/// -/// This is used to identify a specific processor resource unit using a pair -/// of indices where the 'first' index is a processor resource mask, and the -/// 'second' index is an index for a "sub-resource" (i.e. unit). -typedef std::pair ResourceRef; - -// First: a MCProcResourceDesc index identifying a buffered resource. -// Second: max number of buffer entries used in this resource. -typedef std::pair BufferUsageEntry; - -/// A resource manager for processor resource units and groups. -/// -/// This class owns all the ResourceState objects, and it is responsible for -/// acting on requests from a Scheduler by updating the internal state of -/// ResourceState objects. -/// This class doesn't know about instruction itineraries and functional units. -/// In future, it can be extended to support itineraries too through the same -/// public interface. -class ResourceManager { - // The resource manager owns all the ResourceState. - std::vector> Resources; - std::vector> Strategies; - - // Keeps track of which resources are busy, and how many cycles are left - // before those become usable again. - llvm::SmallDenseMap BusyResources; - - // A table to map processor resource IDs to processor resource masks. - llvm::SmallVector ProcResID2Mask; - - // Returns the actual resource unit that will be used. - ResourceRef selectPipe(uint64_t ResourceID); - - void use(const ResourceRef &RR); - void release(const ResourceRef &RR); - - unsigned getNumUnits(uint64_t ResourceID) const; - - // Overrides the selection strategy for the processor resource with the given - // mask. - void setCustomStrategyImpl(std::unique_ptr S, - uint64_t ResourceMask); - -public: - ResourceManager(const llvm::MCSchedModel &SM); - virtual ~ResourceManager() = default; - - // Overrides the selection strategy for the resource at index ResourceID in - // the MCProcResourceDesc table. - void setCustomStrategy(std::unique_ptr S, - unsigned ResourceID) { - assert(ResourceID < ProcResID2Mask.size() && - "Invalid resource index in input!"); - return setCustomStrategyImpl(std::move(S), ProcResID2Mask[ResourceID]); - } - - // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if - // there are enough available slots in the buffers. - ResourceStateEvent canBeDispatched(llvm::ArrayRef Buffers) const; - - // Return the processor resource identifier associated to this Mask. - unsigned resolveResourceMask(uint64_t Mask) const; - - // Consume a slot in every buffered resource from array 'Buffers'. Resource - // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved. - void reserveBuffers(llvm::ArrayRef Buffers); - - // Release buffer entries previously allocated by method reserveBuffers. - void releaseBuffers(llvm::ArrayRef Buffers); - - // Reserve a processor resource. A reserved resource is not available for - // instruction issue until it is released. - void reserveResource(uint64_t ResourceID); - - // Release a previously reserved processor resource. - void releaseResource(uint64_t ResourceID); - - // Returns true if all resources are in-order, and there is at least one - // resource which is a dispatch hazard (BufferSize = 0). - bool mustIssueImmediately(const InstrDesc &Desc) const; - - bool canBeIssued(const InstrDesc &Desc) const; - - void issueInstruction( - const InstrDesc &Desc, - llvm::SmallVectorImpl> &Pipes); - - void cycleEvent(llvm::SmallVectorImpl &ResourcesFreed); - -#ifndef NDEBUG - void dump() const { - for (const std::unique_ptr &Resource : Resources) - Resource->dump(); - } -#endif -}; // namespace mca - class SchedulerStrategy { public: SchedulerStrategy() = default; -- 2.7.4