Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:16

0001 //===--------------------- ResourceManager.h --------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 /// \file
0009 ///
0010 /// The classes here represent processor resource units and their management
0011 /// strategy.  These classes are managed by the Scheduler.
0012 ///
0013 //===----------------------------------------------------------------------===//
0014 
0015 #ifndef LLVM_MCA_HARDWAREUNITS_RESOURCEMANAGER_H
0016 #define LLVM_MCA_HARDWAREUNITS_RESOURCEMANAGER_H
0017 
0018 #include "llvm/ADT/DenseMap.h"
0019 #include "llvm/ADT/SmallVector.h"
0020 #include "llvm/MC/MCSchedule.h"
0021 #include "llvm/MCA/Instruction.h"
0022 #include "llvm/MCA/Support.h"
0023 
0024 namespace llvm {
0025 namespace mca {
0026 
0027 /// Used to notify the internal state of a processor resource.
0028 ///
0029 /// A processor resource is available if it is not reserved, and there are
0030 /// available slots in the buffer.  A processor resource is unavailable if it
0031 /// is either reserved, or the associated buffer is full. A processor resource
0032 /// with a buffer size of -1 is always available if it is not reserved.
0033 ///
0034 /// Values of type ResourceStateEvent are returned by method
0035 /// ResourceManager::canBeDispatched()
0036 ///
0037 /// The naming convention for resource state events is:
0038 ///  * Event names start with prefix RS_
0039 ///  * Prefix RS_ is followed by a string describing the actual resource state.
0040 enum ResourceStateEvent {
0041   RS_BUFFER_AVAILABLE,
0042   RS_BUFFER_UNAVAILABLE,
0043   RS_RESERVED
0044 };
0045 
0046 /// Resource allocation strategy used by hardware scheduler resources.
0047 class ResourceStrategy {
0048   ResourceStrategy(const ResourceStrategy &) = delete;
0049   ResourceStrategy &operator=(const ResourceStrategy &) = delete;
0050 
0051 public:
0052   ResourceStrategy() = default;
0053   virtual ~ResourceStrategy();
0054 
0055   /// Selects a processor resource unit from a ReadyMask.
0056   virtual uint64_t select(uint64_t ReadyMask) = 0;
0057 
0058   /// Called by the ResourceManager when a processor resource group, or a
0059   /// processor resource with multiple units has become unavailable.
0060   ///
0061   /// The default strategy uses this information to bias its selection logic.
0062   virtual void used(uint64_t ResourceMask) {}
0063 };
0064 
0065 /// Default resource allocation strategy used by processor resource groups and
0066 /// processor resources with multiple units.
0067 class DefaultResourceStrategy final : public ResourceStrategy {
0068   /// A Mask of resource unit identifiers.
0069   ///
0070   /// There is one bit set for every available resource unit.
0071   /// It defaults to the value of field ResourceSizeMask in ResourceState.
0072   const uint64_t ResourceUnitMask;
0073 
0074   /// A simple round-robin selector for processor resource units.
0075   /// Each bit of this mask identifies a sub resource within a group.
0076   ///
0077   /// As an example, lets assume that this is a default policy for a
0078   /// processor resource group composed by the following three units:
0079   ///   ResourceA -- 0b001
0080   ///   ResourceB -- 0b010
0081   ///   ResourceC -- 0b100
0082   ///
0083   /// Field NextInSequenceMask is used to select the next unit from the set of
0084   /// resource units. It defaults to the value of field `ResourceUnitMasks` (in
0085   /// this example, it defaults to mask '0b111').
0086   ///
0087   /// The round-robin selector would firstly select 'ResourceC', then
0088   /// 'ResourceB', and eventually 'ResourceA'.  When a resource R is used, the
0089   /// corresponding bit in NextInSequenceMask is cleared.  For example, if
0090   /// 'ResourceC' is selected, then the new value of NextInSequenceMask becomes
0091   /// 0xb011.
0092   ///
0093   /// When NextInSequenceMask becomes zero, it is automatically reset to the
0094   /// default value (i.e. ResourceUnitMask).
0095   uint64_t NextInSequenceMask;
0096 
0097   /// This field is used to track resource units that are used (i.e. selected)
0098   /// by other groups other than the one associated with this strategy object.
0099   ///
0100   /// In LLVM processor resource groups are allowed to partially (or fully)
0101   /// overlap. That means, a same unit may be visible to multiple groups.
0102   /// This field keeps track of uses that have originated from outside of
0103   /// this group. The idea is to bias the selection strategy, so that resources
0104   /// that haven't been used by other groups get prioritized.
0105   ///
0106   /// The end goal is to (try to) keep the resource distribution as much uniform
0107   /// as possible. By construction, this mask only tracks one-level of resource
0108   /// usage. Therefore, this strategy is expected to be less accurate when same
0109   /// units are used multiple times by other groups within a single round of
0110   /// select.
0111   ///
0112   /// Note: an LRU selector would have a better accuracy at the cost of being
0113   /// slightly more expensive (mostly in terms of runtime cost). Methods
0114   /// 'select' and 'used', are always in the hot execution path of llvm-mca.
0115   /// Therefore, a slow implementation of 'select' would have a negative impact
0116   /// on the overall performance of the tool.
0117   uint64_t RemovedFromNextInSequence;
0118 
0119 public:
0120   DefaultResourceStrategy(uint64_t UnitMask)
0121       : ResourceUnitMask(UnitMask), NextInSequenceMask(UnitMask),
0122         RemovedFromNextInSequence(0) {}
0123   virtual ~DefaultResourceStrategy() = default;
0124 
0125   uint64_t select(uint64_t ReadyMask) override;
0126   void used(uint64_t Mask) override;
0127 };
0128 
0129 /// A processor resource descriptor.
0130 ///
0131 /// There is an instance of this class for every processor resource defined by
0132 /// the machine scheduling model.
0133 /// Objects of class ResourceState dynamically track the usage of processor
0134 /// resource units.
0135 class ResourceState {
0136   /// An index to the MCProcResourceDesc entry in the processor model.
0137   const unsigned ProcResourceDescIndex;
0138   /// A resource mask. This is generated by the tool with the help of
0139   /// function `mca::computeProcResourceMasks' (see Support.h).
0140   ///
0141   /// Field ResourceMask only has one bit set if this resource state describes a
0142   /// processor resource unit (i.e. this is not a group). That means, we can
0143   /// quickly check if a resource is a group by simply counting the number of
0144   /// bits that are set in the mask.
0145   ///
0146   /// The most significant bit of a mask (MSB) uniquely identifies a resource.
0147   /// Remaining bits are used to describe the composition of a group (Group).
0148   ///
0149   /// Example (little endian):
0150   ///            Resource |  Mask      |  MSB       |  Group
0151   ///            ---------+------------+------------+------------
0152   ///            A        |  0b000001  |  0b000001  |  0b000000
0153   ///                     |            |            |
0154   ///            B        |  0b000010  |  0b000010  |  0b000000
0155   ///                     |            |            |
0156   ///            C        |  0b010000  |  0b010000  |  0b000000
0157   ///                     |            |            |
0158   ///            D        |  0b110010  |  0b100000  |  0b010010
0159   ///
0160   /// In this example, resources A, B and C are processor resource units.
0161   /// Only resource D is a group resource, and it contains resources B and C.
0162   /// That is because MSB(B) and MSB(C) are both contained within Group(D).
0163   const uint64_t ResourceMask;
0164 
0165   /// A ProcResource can have multiple units.
0166   ///
0167   /// For processor resource groups this field is a mask of contained resource
0168   /// units. It is obtained from ResourceMask by clearing the highest set bit.
0169   /// The number of resource units in a group can be simply computed as the
0170   /// population count of this field.
0171   ///
0172   /// For normal (i.e. non-group) resources, the number of bits set in this mask
0173   /// is equivalent to the number of units declared by the processor model (see
0174   /// field 'NumUnits' in 'ProcResourceUnits').
0175   uint64_t ResourceSizeMask;
0176 
0177   /// A mask of ready units.
0178   uint64_t ReadyMask;
0179 
0180   /// Buffered resources will have this field set to a positive number different
0181   /// than zero. A buffered resource behaves like a reservation station
0182   /// implementing its own buffer for out-of-order execution.
0183   ///
0184   /// A BufferSize of 1 is used by scheduler resources that force in-order
0185   /// execution.
0186   ///
0187   /// A BufferSize of 0 is used to model in-order issue/dispatch resources.
0188   /// Since in-order issue/dispatch resources don't implement buffers, dispatch
0189   /// events coincide with issue events.
0190   /// Also, no other instruction ca be dispatched/issue while this resource is
0191   /// in use. Only when all the "resource cycles" are consumed (after the issue
0192   /// event), a new instruction ca be dispatched.
0193   const int BufferSize;
0194 
0195   /// Available slots in the buffer (zero, if this is not a buffered resource).
0196   unsigned AvailableSlots;
0197 
0198   /// This field is set if this resource is currently reserved.
0199   ///
0200   /// Resources can be reserved for a number of cycles.
0201   /// Instructions can still be dispatched to reserved resources. However,
0202   /// istructions dispatched to a reserved resource cannot be issued to the
0203   /// underlying units (i.e. pipelines) until the resource is released.
0204   bool Unavailable;
0205 
0206   const bool IsAGroup;
0207 
0208   /// Checks for the availability of unit 'SubResMask' in the group.
0209   bool isSubResourceReady(uint64_t SubResMask) const {
0210     return ReadyMask & SubResMask;
0211   }
0212 
0213 public:
0214   ResourceState(const MCProcResourceDesc &Desc, unsigned Index, uint64_t Mask);
0215 
0216   unsigned getProcResourceID() const { return ProcResourceDescIndex; }
0217   uint64_t getResourceMask() const { return ResourceMask; }
0218   uint64_t getReadyMask() const { return ReadyMask; }
0219   int getBufferSize() const { return BufferSize; }
0220 
0221   bool isBuffered() const { return BufferSize > 0; }
0222   bool isInOrder() const { return BufferSize == 1; }
0223 
0224   /// Returns true if this is an in-order dispatch/issue resource.
0225   bool isADispatchHazard() const { return BufferSize == 0; }
0226   bool isReserved() const { return Unavailable; }
0227 
0228   void setReserved() { Unavailable = true; }
0229   void clearReserved() { Unavailable = false; }
0230 
0231   /// Returs true if this resource is not reserved, and if there are at least
0232   /// `NumUnits` available units.
0233   bool isReady(unsigned NumUnits = 1) const;
0234 
0235   uint64_t getNumReadyUnits() const { return llvm::popcount(ReadyMask); }
0236 
0237   bool isAResourceGroup() const { return IsAGroup; }
0238 
0239   bool containsResource(uint64_t ID) const { return ResourceMask & ID; }
0240 
0241   void markSubResourceAsUsed(uint64_t ID) {
0242     assert(isSubResourceReady(ID));
0243     ReadyMask ^= ID;
0244   }
0245 
0246   void releaseSubResource(uint64_t ID) {
0247     assert(!isSubResourceReady(ID));
0248     ReadyMask ^= ID;
0249   }
0250 
0251   unsigned getNumUnits() const {
0252     return isAResourceGroup() ? 1U : llvm::popcount(ResourceSizeMask);
0253   }
0254 
0255   /// Checks if there is an available slot in the resource buffer.
0256   ///
0257   /// Returns RS_BUFFER_AVAILABLE if this is not a buffered resource, or if
0258   /// there is a slot available.
0259   ///
0260   /// Returns RS_RESERVED if this buffered resource is a dispatch hazard, and it
0261   /// is reserved.
0262   ///
0263   /// Returns RS_BUFFER_UNAVAILABLE if there are no available slots.
0264   ResourceStateEvent isBufferAvailable() const;
0265 
0266   /// Reserve a buffer slot.
0267   ///
0268   /// Returns true if the buffer is not full.
0269   /// It always returns true if BufferSize is set to zero.
0270   bool reserveBuffer() {
0271     if (BufferSize <= 0)
0272       return true;
0273 
0274     --AvailableSlots;
0275     assert(AvailableSlots <= static_cast<unsigned>(BufferSize));
0276     return AvailableSlots;
0277   }
0278 
0279   /// Releases a slot in the buffer.
0280   void releaseBuffer() {
0281     // Ignore dispatch hazards or invalid buffer sizes.
0282     if (BufferSize <= 0)
0283       return;
0284 
0285     ++AvailableSlots;
0286     assert(AvailableSlots <= static_cast<unsigned>(BufferSize));
0287   }
0288 
0289 #ifndef NDEBUG
0290   void dump() const;
0291 #endif
0292 };
0293 
0294 /// A resource unit identifier.
0295 ///
0296 /// This is used to identify a specific processor resource unit using a pair
0297 /// of indices where the 'first' index is a processor resource mask, and the
0298 /// 'second' index is an index for a "sub-resource" (i.e. unit).
0299 typedef std::pair<uint64_t, uint64_t> ResourceRef;
0300 
0301 // First: a MCProcResourceDesc index identifying a buffered resource.
0302 // Second: max number of buffer entries used in this resource.
0303 typedef std::pair<unsigned, unsigned> BufferUsageEntry;
0304 
0305 /// A resource manager for processor resource units and groups.
0306 ///
0307 /// This class owns all the ResourceState objects, and it is responsible for
0308 /// acting on requests from a Scheduler by updating the internal state of
0309 /// ResourceState objects.
0310 /// This class doesn't know about instruction itineraries and functional units.
0311 /// In future, it can be extended to support itineraries too through the same
0312 /// public interface.
0313 class ResourceManager {
0314   // Set of resources available on the subtarget.
0315   //
0316   // There is an instance of ResourceState for every resource declared by the
0317   // target scheduling model.
0318   //
0319   // Elements of this vector are ordered by resource kind. In particular,
0320   // resource units take precedence over resource groups.
0321   //
0322   // The index of a processor resource in this vector depends on the value of
0323   // its mask (see the description of field ResourceState::ResourceMask).  In
0324   // particular, it is computed as the position of the most significant bit set
0325   // (MSB) in the mask plus one (since we want to ignore the invalid resource
0326   // descriptor at index zero).
0327   //
0328   // Example (little endian):
0329   //
0330   //             Resource | Mask    |  MSB    | Index
0331   //             ---------+---------+---------+-------
0332   //                 A    | 0b00001 | 0b00001 |   1
0333   //                      |         |         |
0334   //                 B    | 0b00100 | 0b00100 |   3
0335   //                      |         |         |
0336   //                 C    | 0b10010 | 0b10000 |   5
0337   //
0338   //
0339   // The same index is also used to address elements within vector `Strategies`
0340   // and vector `Resource2Groups`.
0341   std::vector<std::unique_ptr<ResourceState>> Resources;
0342   std::vector<std::unique_ptr<ResourceStrategy>> Strategies;
0343 
0344   // Used to quickly identify groups that own a particular resource unit.
0345   std::vector<uint64_t> Resource2Groups;
0346 
0347   // A table that maps processor resource IDs to processor resource masks.
0348   SmallVector<uint64_t, 8> ProcResID2Mask;
0349 
0350   // A table that maps resource indices to actual processor resource IDs in the
0351   // scheduling model.
0352   SmallVector<unsigned, 8> ResIndex2ProcResID;
0353 
0354   // Keeps track of which resources are busy, and how many cycles are left
0355   // before those become usable again.
0356   SmallDenseMap<ResourceRef, unsigned> BusyResources;
0357 
0358   // Set of processor resource units available on the target.
0359   uint64_t ProcResUnitMask;
0360 
0361   // Set of processor resource units that are available during this cycle.
0362   uint64_t AvailableProcResUnits;
0363 
0364   // Set of processor resources that are currently reserved.
0365   uint64_t ReservedResourceGroups;
0366 
0367   // Set of unavailable scheduler buffer resources. This is used internally to
0368   // speedup `canBeDispatched()` queries.
0369   uint64_t AvailableBuffers;
0370 
0371   // Set of dispatch hazard buffer resources that are currently unavailable.
0372   uint64_t ReservedBuffers;
0373 
0374   // Returns the actual resource unit that will be used.
0375   ResourceRef selectPipe(uint64_t ResourceID);
0376 
0377   void use(const ResourceRef &RR);
0378   void release(const ResourceRef &RR);
0379 
0380   unsigned getNumUnits(uint64_t ResourceID) const;
0381 
0382   // Overrides the selection strategy for the processor resource with the given
0383   // mask.
0384   void setCustomStrategyImpl(std::unique_ptr<ResourceStrategy> S,
0385                              uint64_t ResourceMask);
0386 
0387 public:
0388   ResourceManager(const MCSchedModel &SM);
0389   virtual ~ResourceManager() = default;
0390 
0391   // Overrides the selection strategy for the resource at index ResourceID in
0392   // the MCProcResourceDesc table.
0393   void setCustomStrategy(std::unique_ptr<ResourceStrategy> S,
0394                          unsigned ResourceID) {
0395     assert(ResourceID < ProcResID2Mask.size() &&
0396            "Invalid resource index in input!");
0397     return setCustomStrategyImpl(std::move(S), ProcResID2Mask[ResourceID]);
0398   }
0399 
0400   // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if
0401   // there are enough available slots in the buffers.
0402   ResourceStateEvent canBeDispatched(uint64_t ConsumedBuffers) const;
0403 
0404   // Return the processor resource identifier associated to this Mask.
0405   unsigned resolveResourceMask(uint64_t Mask) const;
0406 
0407   // Acquires a slot from every buffered resource in mask `ConsumedBuffers`.
0408   // Units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved.
0409   void reserveBuffers(uint64_t ConsumedBuffers);
0410 
0411   // Releases a slot from every buffered resource in mask `ConsumedBuffers`.
0412   // ConsumedBuffers is a bitmask of previously acquired buffers (using method
0413   // `reserveBuffers`). Units that are dispatch hazards (i.e. BufferSize=0) are
0414   // not automatically unreserved by this method.
0415   void releaseBuffers(uint64_t ConsumedBuffers);
0416 
0417   // Reserve a processor resource. A reserved resource is not available for
0418   // instruction issue until it is released.
0419   void reserveResource(uint64_t ResourceID);
0420 
0421   // Release a previously reserved processor resource.
0422   void releaseResource(uint64_t ResourceID);
0423 
0424   // Returns a zero mask if resources requested by Desc are all available during
0425   // this cycle. It returns a non-zero mask value only if there are unavailable
0426   // processor resources; each bit set in the mask represents a busy processor
0427   // resource unit or a reserved processor resource group.
0428   uint64_t checkAvailability(const InstrDesc &Desc) const;
0429 
0430   uint64_t getProcResUnitMask() const { return ProcResUnitMask; }
0431   uint64_t getAvailableProcResUnits() const { return AvailableProcResUnits; }
0432 
0433   using ResourceWithCycles = std::pair<ResourceRef, ReleaseAtCycles>;
0434 
0435   void issueInstruction(const InstrDesc &Desc,
0436                         SmallVectorImpl<ResourceWithCycles> &Pipes) {
0437     if (Desc.HasPartiallyOverlappingGroups)
0438       return issueInstructionImpl(Desc, Pipes);
0439 
0440     return fastIssueInstruction(Desc, Pipes);
0441   }
0442 
0443   // Selects pipeline resources consumed by an instruction.
0444   // This method works under the assumption that used group resources don't
0445   // partially overlap. The logic is guaranteed to find a valid resource unit
0446   // schedule, no matter in which order individual uses are processed. For that
0447   // reason, the vector of resource uses is simply (and quickly) processed in
0448   // sequence. The resulting schedule is eventually stored into vector `Pipes`.
0449   void fastIssueInstruction(const InstrDesc &Desc,
0450                             SmallVectorImpl<ResourceWithCycles> &Pipes);
0451 
0452   // Selects pipeline resources consumed by an instruction.
0453   // This method works under the assumption that used resource groups may
0454   // partially overlap. This complicates the selection process, because the
0455   // order in which uses are processed matters. The logic internally prioritizes
0456   // groups which are more constrained than others.
0457   void issueInstructionImpl(const InstrDesc &Desc,
0458                             SmallVectorImpl<ResourceWithCycles> &Pipes);
0459 
0460   void cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed);
0461 
0462 #ifndef NDEBUG
0463   void dump() const {
0464     for (const std::unique_ptr<ResourceState> &Resource : Resources)
0465       Resource->dump();
0466   }
0467 #endif
0468 };
0469 } // namespace mca
0470 } // namespace llvm
0471 
0472 #endif // LLVM_MCA_HARDWAREUNITS_RESOURCEMANAGER_H