Back to home page

EIC code displayed by LXR

 
 

    


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

0001 //===--------------------- Scheduler.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 /// A scheduler for Processor Resource Units and Processor Resource Groups.
0011 ///
0012 //===----------------------------------------------------------------------===//
0013 
0014 #ifndef LLVM_MCA_HARDWAREUNITS_SCHEDULER_H
0015 #define LLVM_MCA_HARDWAREUNITS_SCHEDULER_H
0016 
0017 #include "llvm/ADT/SmallVector.h"
0018 #include "llvm/MC/MCSchedule.h"
0019 #include "llvm/MCA/HardwareUnits/HardwareUnit.h"
0020 #include "llvm/MCA/HardwareUnits/LSUnit.h"
0021 #include "llvm/MCA/HardwareUnits/ResourceManager.h"
0022 #include "llvm/MCA/Support.h"
0023 
0024 namespace llvm {
0025 namespace mca {
0026 
0027 class SchedulerStrategy {
0028 public:
0029   SchedulerStrategy() = default;
0030   virtual ~SchedulerStrategy();
0031 
0032   /// Returns true if Lhs should take priority over Rhs.
0033   ///
0034   /// This method is used by class Scheduler to select the "best" ready
0035   /// instruction to issue to the underlying pipelines.
0036   virtual bool compare(const InstRef &Lhs, const InstRef &Rhs) const = 0;
0037 };
0038 
0039 /// Default instruction selection strategy used by class Scheduler.
0040 class DefaultSchedulerStrategy : public SchedulerStrategy {
0041   /// This method ranks instructions based on their age, and the number of known
0042   /// users. The lower the rank value, the better.
0043   int computeRank(const InstRef &Lhs) const {
0044     return Lhs.getSourceIndex() - Lhs.getInstruction()->getNumUsers();
0045   }
0046 
0047 public:
0048   DefaultSchedulerStrategy() = default;
0049   virtual ~DefaultSchedulerStrategy();
0050 
0051   bool compare(const InstRef &Lhs, const InstRef &Rhs) const override {
0052     int LhsRank = computeRank(Lhs);
0053     int RhsRank = computeRank(Rhs);
0054 
0055     /// Prioritize older instructions over younger instructions to minimize the
0056     /// pressure on the reorder buffer.
0057     if (LhsRank == RhsRank)
0058       return Lhs.getSourceIndex() < Rhs.getSourceIndex();
0059     return LhsRank < RhsRank;
0060   }
0061 };
0062 
0063 /// Class Scheduler is responsible for issuing instructions to pipeline
0064 /// resources.
0065 ///
0066 /// Internally, it delegates to a ResourceManager the management of processor
0067 /// resources. This class is also responsible for tracking the progress of
0068 /// instructions from the dispatch stage, until the write-back stage.
0069 ///
0070 class Scheduler : public HardwareUnit {
0071   LSUnitBase &LSU;
0072 
0073   // Instruction selection strategy for this Scheduler.
0074   std::unique_ptr<SchedulerStrategy> Strategy;
0075 
0076   // Hardware resources that are managed by this scheduler.
0077   std::unique_ptr<ResourceManager> Resources;
0078 
0079   // Instructions dispatched to the Scheduler are internally classified based on
0080   // the instruction stage (see Instruction::InstrStage).
0081   //
0082   // An Instruction dispatched to the Scheduler is added to the WaitSet if not
0083   // all its register operands are available, and at least one latency is
0084   // unknown.  By construction, the WaitSet only contains instructions that are
0085   // in the IS_DISPATCHED stage.
0086   //
0087   // An Instruction transitions from the WaitSet to the PendingSet if the
0088   // instruction is not ready yet, but the latency of every register read is
0089   // known.  Instructions in the PendingSet can only be in the IS_PENDING or
0090   // IS_READY stage.  Only IS_READY instructions that are waiting on memory
0091   // dependencies can be added to the PendingSet.
0092   //
0093   // Instructions in the PendingSet are immediately dominated only by
0094   // instructions that have already been issued to the underlying pipelines.  In
0095   // the presence of bottlenecks caused by data dependencies, the PendingSet can
0096   // be inspected to identify problematic data dependencies between
0097   // instructions.
0098   //
0099   // An instruction is moved to the ReadySet when all register operands become
0100   // available, and all memory dependencies are met.  Instructions that are
0101   // moved from the PendingSet to the ReadySet must transition to the 'IS_READY'
0102   // stage.
0103   //
0104   // On every cycle, the Scheduler checks if it can promote instructions from the
0105   // PendingSet to the ReadySet.
0106   //
0107   // An Instruction is moved from the ReadySet to the `IssuedSet` when it starts
0108   // exection. This event also causes an instruction state transition (i.e. from
0109   // state IS_READY, to state IS_EXECUTING). An Instruction leaves the IssuedSet
0110   // only when it reaches the write-back stage.
0111   std::vector<InstRef> WaitSet;
0112   std::vector<InstRef> PendingSet;
0113   std::vector<InstRef> ReadySet;
0114   std::vector<InstRef> IssuedSet;
0115 
0116   // A mask of busy resource units. It defaults to the empty set (i.e. a zero
0117   // mask), and it is cleared at the beginning of every cycle.
0118   // It is updated every time the scheduler fails to issue an instruction from
0119   // the ready set due to unavailable pipeline resources.
0120   // Each bit of the mask represents an unavailable resource.
0121   uint64_t BusyResourceUnits;
0122 
0123   // Counts the number of instructions in the pending set that were dispatched
0124   // during this cycle.
0125   unsigned NumDispatchedToThePendingSet;
0126 
0127   // True if the previous pipeline Stage was unable to dispatch a full group of
0128   // opcodes because scheduler buffers (or LS queues) were unavailable.
0129   bool HadTokenStall;
0130 
0131   /// Verify the given selection strategy and set the Strategy member
0132   /// accordingly.  If no strategy is provided, the DefaultSchedulerStrategy is
0133   /// used.
0134   void initializeStrategy(std::unique_ptr<SchedulerStrategy> S);
0135 
0136   /// Issue an instruction without updating the ready queue.
0137   void issueInstructionImpl(
0138       InstRef &IR,
0139       SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &Pipes);
0140 
0141   // Identify instructions that have finished executing, and remove them from
0142   // the IssuedSet. References to executed instructions are added to input
0143   // vector 'Executed'.
0144   void updateIssuedSet(SmallVectorImpl<InstRef> &Executed);
0145 
0146   // Try to promote instructions from the PendingSet to the ReadySet.
0147   // Add promoted instructions to the 'Ready' vector in input.
0148   // Returns true if at least one instruction was promoted.
0149   bool promoteToReadySet(SmallVectorImpl<InstRef> &Ready);
0150 
0151   // Try to promote instructions from the WaitSet to the PendingSet.
0152   // Add promoted instructions to the 'Pending' vector in input.
0153   // Returns true if at least one instruction was promoted.
0154   bool promoteToPendingSet(SmallVectorImpl<InstRef> &Pending);
0155 
0156 public:
0157   Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu)
0158       : Scheduler(Model, Lsu, nullptr) {}
0159 
0160   Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu,
0161             std::unique_ptr<SchedulerStrategy> SelectStrategy)
0162       : Scheduler(std::make_unique<ResourceManager>(Model), Lsu,
0163                   std::move(SelectStrategy)) {}
0164 
0165   Scheduler(std::unique_ptr<ResourceManager> RM, LSUnitBase &Lsu,
0166             std::unique_ptr<SchedulerStrategy> SelectStrategy)
0167       : LSU(Lsu), Resources(std::move(RM)), BusyResourceUnits(0),
0168         NumDispatchedToThePendingSet(0), HadTokenStall(false) {
0169     initializeStrategy(std::move(SelectStrategy));
0170   }
0171 
0172   // Stalls generated by the scheduler.
0173   enum Status {
0174     SC_AVAILABLE,
0175     SC_LOAD_QUEUE_FULL,
0176     SC_STORE_QUEUE_FULL,
0177     SC_BUFFERS_FULL,
0178     SC_DISPATCH_GROUP_STALL,
0179   };
0180 
0181   /// Check if the instruction in 'IR' can be dispatched during this cycle.
0182   /// Return SC_AVAILABLE if both scheduler and LS resources are available.
0183   ///
0184   /// This method is also responsible for setting field HadTokenStall if
0185   /// IR cannot be dispatched to the Scheduler due to unavailable resources.
0186   Status isAvailable(const InstRef &IR);
0187 
0188   /// Reserves buffer and LSUnit queue resources that are necessary to issue
0189   /// this instruction.
0190   ///
0191   /// Returns true if instruction IR is ready to be issued to the underlying
0192   /// pipelines. Note that this operation cannot fail; it assumes that a
0193   /// previous call to method `isAvailable(IR)` returned `SC_AVAILABLE`.
0194   ///
0195   /// If IR is a memory operation, then the Scheduler queries the LS unit to
0196   /// obtain a LS token. An LS token is used internally to track memory
0197   /// dependencies.
0198   bool dispatch(InstRef &IR);
0199 
0200   /// Issue an instruction and populates a vector of used pipeline resources,
0201   /// and a vector of instructions that transitioned to the ready state as a
0202   /// result of this event.
0203   void issueInstruction(
0204       InstRef &IR,
0205       SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &Used,
0206       SmallVectorImpl<InstRef> &Pending,
0207       SmallVectorImpl<InstRef> &Ready);
0208 
0209   /// Returns true if IR has to be issued immediately, or if IR is a zero
0210   /// latency instruction.
0211   bool mustIssueImmediately(const InstRef &IR) const;
0212 
0213   /// This routine notifies the Scheduler that a new cycle just started.
0214   ///
0215   /// It notifies the underlying ResourceManager that a new cycle just started.
0216   /// Vector `Freed` is populated with resourceRef related to resources that
0217   /// have changed in state, and that are now available to new instructions.
0218   /// Instructions executed are added to vector Executed, while vector Ready is
0219   /// populated with instructions that have become ready in this new cycle.
0220   /// Vector Pending is popluated by instructions that have transitioned through
0221   /// the pending stat during this cycle. The Pending and Ready sets may not be
0222   /// disjoint. An instruction is allowed to transition from the WAIT state to
0223   /// the READY state (going through the PENDING state) within a single cycle.
0224   /// That means, instructions may appear in both the Pending and Ready set.
0225   void cycleEvent(SmallVectorImpl<ResourceRef> &Freed,
0226                   SmallVectorImpl<InstRef> &Executed,
0227                   SmallVectorImpl<InstRef> &Pending,
0228                   SmallVectorImpl<InstRef> &Ready);
0229 
0230   /// Convert a resource mask into a valid llvm processor resource identifier.
0231   ///
0232   /// Only the most significant bit of the Mask is used by this method to
0233   /// identify the processor resource.
0234   unsigned getResourceID(uint64_t Mask) const {
0235     return Resources->resolveResourceMask(Mask);
0236   }
0237 
0238   /// Select the next instruction to issue from the ReadySet. Returns an invalid
0239   /// instruction reference if there are no ready instructions, or if processor
0240   /// resources are not available.
0241   InstRef select();
0242 
0243   bool isReadySetEmpty() const { return ReadySet.empty(); }
0244   bool isWaitSetEmpty() const { return WaitSet.empty(); }
0245 
0246   /// This method is called by the ExecuteStage at the end of each cycle to
0247   /// identify bottlenecks caused by data dependencies. Vector RegDeps is
0248   /// populated by instructions that were not issued because of unsolved
0249   /// register dependencies.  Vector MemDeps is populated by instructions that
0250   /// were not issued because of unsolved memory dependencies.
0251   void analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps,
0252                                SmallVectorImpl<InstRef> &MemDeps);
0253 
0254   /// Returns a mask of busy resources, and populates vector Insts with
0255   /// instructions that could not be issued to the underlying pipelines because
0256   /// not all pipeline resources were available.
0257   uint64_t analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts);
0258 
0259   // Returns true if the dispatch logic couldn't dispatch a full group due to
0260   // unavailable scheduler and/or LS resources.
0261   bool hadTokenStall() const { return HadTokenStall; }
0262 
0263 #ifndef NDEBUG
0264   // Update the ready queues.
0265   void dump() const;
0266 
0267   // This routine performs a basic correctness check.  This routine should only
0268   // be called when we know that 'IR' is not in the scheduler's instruction
0269   // queues.
0270   void instructionCheck(const InstRef &IR) const {
0271     assert(!is_contained(WaitSet, IR) && "Already in the wait set!");
0272     assert(!is_contained(ReadySet, IR) && "Already in the ready set!");
0273     assert(!is_contained(IssuedSet, IR) && "Already executing!");
0274   }
0275 #endif // !NDEBUG
0276 };
0277 } // namespace mca
0278 } // namespace llvm
0279 
0280 #endif // LLVM_MCA_HARDWAREUNITS_SCHEDULER_H