diff options
| author | Matt Davis <Matthew.Davis@sony.com> | 2018-08-24 22:05:14 +0000 | 
|---|---|---|
| committer | Matt Davis <Matthew.Davis@sony.com> | 2018-08-24 22:05:14 +0000 | 
| commit | e442a8701f7b493dc9e529fa0f491e94f7d67374 (patch) | |
| tree | 959ee5dde5a2fe95b608695f1a3f8a5e2d1776d1 /llvm/tools/llvm-mca | |
| parent | 006bdad69240670717d0257ef5b7123b65fb12c4 (diff) | |
| download | bcm5719-llvm-e442a8701f7b493dc9e529fa0f491e94f7d67374.tar.gz bcm5719-llvm-e442a8701f7b493dc9e529fa0f491e94f7d67374.zip  | |
[llvm-mca] Revert r340659. NFC.
Choosing to revert the change and do it again, hopefully preserving the history
of the changes by using svn copy instead of simply creating a new file from the
contents within Scheduler.
llvm-svn: 340661
Diffstat (limited to 'llvm/tools/llvm-mca')
| -rw-r--r-- | llvm/tools/llvm-mca/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/ResourceManager.cpp | 309 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/ResourceManager.h | 360 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Scheduler.cpp | 284 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Scheduler.h | 339 | 
5 files changed, 621 insertions, 672 deletions
diff --git a/llvm/tools/llvm-mca/CMakeLists.txt b/llvm/tools/llvm-mca/CMakeLists.txt index 63cbda5b242..a1bc11740d1 100644 --- a/llvm/tools/llvm-mca/CMakeLists.txt +++ b/llvm/tools/llvm-mca/CMakeLists.txt @@ -25,7 +25,6 @@ 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 deleted file mode 100644 index 80ea73e631d..00000000000 --- a/llvm/tools/llvm-mca/ResourceManager.cpp +++ /dev/null @@ -1,309 +0,0 @@ -//===--------------------- 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<unsigned>(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<uint64_t>::digits - llvm::countLeadingZeros(Mask); -} - -static std::unique_ptr<ResourceStrategy> -getStrategyFor(const ResourceState &RS) { -  if (RS.isAResourceGroup() || RS.getNumUnits() > 1) -    return llvm::make_unique<DefaultResourceStrategy>(RS.getReadyMask()); -  return std::unique_ptr<ResourceStrategy>(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<ResourceState>(*SM.getProcResource(I), I, Mask); -    Strategies[Index] = getStrategyFor(*Resources[Index]); -  } -} - -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 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<ResourceState> &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<ResourceState> &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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t, const ResourceUsage> &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<std::pair<ResourceRef, double>> &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(); -      // 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<ResourceRef, double>(Pipe, static_cast<double>(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) { -  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 deleted file mode 100644 index c3f893660eb..00000000000 --- a/llvm/tools/llvm-mca/ResourceManager.h +++ /dev/null @@ -1,360 +0,0 @@ -//===--------------------- 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<unsigned>(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<uint64_t, uint64_t> ResourceRef; - -// First: a MCProcResourceDesc index identifying a buffered resource. -// Second: max number of buffer entries used in this resource. -typedef std::pair<unsigned, unsigned> 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<std::unique_ptr<ResourceState>> Resources; -  std::vector<std::unique_ptr<ResourceStrategy>> Strategies; - -  // Keeps track of which resources are busy, and how many cycles are left -  // before those become usable again. -  llvm::SmallDenseMap<ResourceRef, unsigned> BusyResources; - -  // A table to map processor resource IDs to processor resource masks. -  llvm::SmallVector<uint64_t, 8> 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<ResourceStrategy> 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<ResourceStrategy> 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<uint64_t> 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<uint64_t> Buffers); - -  // Release buffer entries previously allocated by method reserveBuffers. -  void releaseBuffers(llvm::ArrayRef<uint64_t> 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<std::pair<ResourceRef, double>> &Pipes); - -  void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed); - -#ifndef NDEBUG -  void dump() const { -    for (const std::unique_ptr<ResourceState> &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 b0b0d71d400..0fd720d6f03 100644 --- a/llvm/tools/llvm-mca/Scheduler.cpp +++ b/llvm/tools/llvm-mca/Scheduler.cpp @@ -12,6 +12,7 @@  //===----------------------------------------------------------------------===//  #include "Scheduler.h" +#include "Support.h"  #include "llvm/Support/Debug.h"  #include "llvm/Support/raw_ostream.h" @@ -21,11 +22,294 @@ using namespace llvm;  #define DEBUG_TYPE "llvm-mca" +ResourceStrategy::~ResourceStrategy() = default; +  void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) {    // Ensure we have a valid (non-null) strategy object.    Strategy = S ? std::move(S) : llvm::make_unique<DefaultSchedulerStrategy>();  } +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<unsigned>(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<uint64_t>::digits - llvm::countLeadingZeros(Mask); +} + +static std::unique_ptr<ResourceStrategy> +getStrategyFor(const ResourceState &RS) { +  if (RS.isAResourceGroup() || RS.getNumUnits() > 1) +    return llvm::make_unique<DefaultResourceStrategy>(RS.getReadyMask()); +  return std::unique_ptr<ResourceStrategy>(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<ResourceState>(*SM.getProcResource(I), I, Mask); +    Strategies[Index] = getStrategyFor(*Resources[Index]); +  } +} + +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 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<ResourceState> &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<ResourceState> &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<uint64_t> 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<uint64_t> 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<uint64_t> 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<uint64_t, const ResourceUsage> &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<std::pair<ResourceRef, double>> &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(); +      // 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<ResourceRef, double>(Pipe, static_cast<double>(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) { +  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 70f90c5989f..ed138fd7180 100644 --- a/llvm/tools/llvm-mca/Scheduler.h +++ b/llvm/tools/llvm-mca/Scheduler.h @@ -16,13 +16,348 @@  #define LLVM_TOOLS_LLVM_MCA_SCHEDULER_H  #include "HardwareUnit.h" +#include "Instruction.h"  #include "LSUnit.h" -#include "ResourceManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h"  #include "llvm/ADT/SmallVector.h" -#include "llvm/MC/MCSchedule.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include <map>  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<unsigned>(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<uint64_t, uint64_t> ResourceRef; + +// First: a MCProcResourceDesc index identifying a buffered resource. +// Second: max number of buffer entries used in this resource. +typedef std::pair<unsigned, unsigned> 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<std::unique_ptr<ResourceState>> Resources; +  std::vector<std::unique_ptr<ResourceStrategy>> Strategies; + +  // Keeps track of which resources are busy, and how many cycles are left +  // before those become usable again. +  llvm::SmallDenseMap<ResourceRef, unsigned> BusyResources; + +  // A table to map processor resource IDs to processor resource masks. +  llvm::SmallVector<uint64_t, 8> 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<ResourceStrategy> 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<ResourceStrategy> 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<uint64_t> 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<uint64_t> Buffers); + +  // Release buffer entries previously allocated by method reserveBuffers. +  void releaseBuffers(llvm::ArrayRef<uint64_t> 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<std::pair<ResourceRef, double>> &Pipes); + +  void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed); + +#ifndef NDEBUG +  void dump() const { +    for (const std::unique_ptr<ResourceState> &Resource : Resources) +      Resource->dump(); +  } +#endif +}; // namespace mca +  class SchedulerStrategy {  public:    SchedulerStrategy() = default;  | 

