#pragma once #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif //===--------------------- Scheduler.h ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// /// A scheduler for Processor Resource Units and Processor Resource Groups. /// //===----------------------------------------------------------------------===// #ifndef LLVM_MCA_HARDWAREUNITS_SCHEDULER_H #define LLVM_MCA_HARDWAREUNITS_SCHEDULER_H #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCSchedule.h" #include "llvm/MCA/HardwareUnits/HardwareUnit.h" #include "llvm/MCA/HardwareUnits/LSUnit.h" #include "llvm/MCA/HardwareUnits/ResourceManager.h" #include "llvm/MCA/Support.h" namespace llvm { namespace mca { class SchedulerStrategy { public: SchedulerStrategy() = default; virtual ~SchedulerStrategy(); /// Returns true if Lhs should take priority over Rhs. /// /// This method is used by class Scheduler to select the "best" ready /// instruction to issue to the underlying pipelines. virtual bool compare(const InstRef &Lhs, const InstRef &Rhs) const = 0; }; /// Default instruction selection strategy used by class Scheduler. class DefaultSchedulerStrategy : public SchedulerStrategy { /// This method ranks instructions based on their age, and the number of known /// users. The lower the rank value, the better. int computeRank(const InstRef &Lhs) const { return Lhs.getSourceIndex() - Lhs.getInstruction()->getNumUsers(); } public: DefaultSchedulerStrategy() = default; virtual ~DefaultSchedulerStrategy(); bool compare(const InstRef &Lhs, const InstRef &Rhs) const override { int LhsRank = computeRank(Lhs); int RhsRank = computeRank(Rhs); /// Prioritize older instructions over younger instructions to minimize the /// pressure on the reorder buffer. if (LhsRank == RhsRank) return Lhs.getSourceIndex() < Rhs.getSourceIndex(); return LhsRank < RhsRank; } }; /// Class Scheduler is responsible for issuing instructions to pipeline /// resources. /// /// Internally, it delegates to a ResourceManager the management of processor /// resources. This class is also responsible for tracking the progress of /// instructions from the dispatch stage, until the write-back stage. /// class Scheduler : public HardwareUnit { LSUnitBase &LSU; // Instruction selection strategy for this Scheduler. std::unique_ptr Strategy; // Hardware resources that are managed by this scheduler. std::unique_ptr Resources; // Instructions dispatched to the Scheduler are internally classified based on // the instruction stage (see Instruction::InstrStage). // // An Instruction dispatched to the Scheduler is added to the WaitSet if not // all its register operands are available, and at least one latency is // unknown. By construction, the WaitSet only contains instructions that are // in the IS_DISPATCHED stage. // // An Instruction transitions from the WaitSet to the PendingSet if the // instruction is not ready yet, but the latency of every register read is // known. Instructions in the PendingSet can only be in the IS_PENDING or // IS_READY stage. Only IS_READY instructions that are waiting on memory // dependencies can be added to the PendingSet. // // Instructions in the PendingSet are immediately dominated only by // instructions that have already been issued to the underlying pipelines. In // the presence of bottlenecks caused by data dependencies, the PendingSet can // be inspected to identify problematic data dependencies between // instructions. // // An instruction is moved to the ReadySet when all register operands become // available, and all memory dependencies are met. Instructions that are // moved from the PendingSet to the ReadySet must transition to the 'IS_READY' // stage. // // On every cycle, the Scheduler checks if it can promote instructions from the // PendingSet to the ReadySet. // // An Instruction is moved from the ReadySet to the `IssuedSet` when it starts // exection. This event also causes an instruction state transition (i.e. from // state IS_READY, to state IS_EXECUTING). An Instruction leaves the IssuedSet // only when it reaches the write-back stage. std::vector WaitSet; std::vector PendingSet; std::vector ReadySet; std::vector IssuedSet; // A mask of busy resource units. It defaults to the empty set (i.e. a zero // mask), and it is cleared at the beginning of every cycle. // It is updated every time the scheduler fails to issue an instruction from // the ready set due to unavailable pipeline resources. // Each bit of the mask represents an unavailable resource. uint64_t BusyResourceUnits; // Counts the number of instructions in the pending set that were dispatched // during this cycle. unsigned NumDispatchedToThePendingSet; // True if the previous pipeline Stage was unable to dispatch a full group of // opcodes because scheduler buffers (or LS queues) were unavailable. bool HadTokenStall; /// Verify the given selection strategy and set the Strategy member /// accordingly. If no strategy is provided, the DefaultSchedulerStrategy is /// used. void initializeStrategy(std::unique_ptr S); /// Issue an instruction without updating the ready queue. void issueInstructionImpl( InstRef &IR, SmallVectorImpl> &Pipes); // Identify instructions that have finished executing, and remove them from // the IssuedSet. References to executed instructions are added to input // vector 'Executed'. void updateIssuedSet(SmallVectorImpl &Executed); // Try to promote instructions from the PendingSet to the ReadySet. // Add promoted instructions to the 'Ready' vector in input. // Returns true if at least one instruction was promoted. bool promoteToReadySet(SmallVectorImpl &Ready); // Try to promote instructions from the WaitSet to the PendingSet. // Add promoted instructions to the 'Pending' vector in input. // Returns true if at least one instruction was promoted. bool promoteToPendingSet(SmallVectorImpl &Pending); public: Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu) : Scheduler(Model, Lsu, nullptr) {} Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu, std::unique_ptr SelectStrategy) : Scheduler(std::make_unique(Model), Lsu, std::move(SelectStrategy)) {} Scheduler(std::unique_ptr RM, LSUnitBase &Lsu, std::unique_ptr SelectStrategy) : LSU(Lsu), Resources(std::move(RM)), BusyResourceUnits(0), NumDispatchedToThePendingSet(0), HadTokenStall(false) { initializeStrategy(std::move(SelectStrategy)); } // Stalls generated by the scheduler. enum Status { SC_AVAILABLE, SC_LOAD_QUEUE_FULL, SC_STORE_QUEUE_FULL, SC_BUFFERS_FULL, SC_DISPATCH_GROUP_STALL, }; /// Check if the instruction in 'IR' can be dispatched during this cycle. /// Return SC_AVAILABLE if both scheduler and LS resources are available. /// /// This method is also responsible for setting field HadTokenStall if /// IR cannot be dispatched to the Scheduler due to unavailable resources. Status isAvailable(const InstRef &IR); /// Reserves buffer and LSUnit queue resources that are necessary to issue /// this instruction. /// /// Returns true if instruction IR is ready to be issued to the underlying /// pipelines. Note that this operation cannot fail; it assumes that a /// previous call to method `isAvailable(IR)` returned `SC_AVAILABLE`. /// /// If IR is a memory operation, then the Scheduler queries the LS unit to /// obtain a LS token. An LS token is used internally to track memory /// dependencies. bool dispatch(InstRef &IR); /// Issue an instruction and populates a vector of used pipeline resources, /// and a vector of instructions that transitioned to the ready state as a /// result of this event. void issueInstruction( InstRef &IR, SmallVectorImpl> &Used, SmallVectorImpl &Pending, SmallVectorImpl &Ready); /// Returns true if IR has to be issued immediately, or if IR is a zero /// latency instruction. bool mustIssueImmediately(const InstRef &IR) const; /// This routine notifies the Scheduler that a new cycle just started. /// /// It notifies the underlying ResourceManager that a new cycle just started. /// Vector `Freed` is populated with resourceRef related to resources that /// have changed in state, and that are now available to new instructions. /// Instructions executed are added to vector Executed, while vector Ready is /// populated with instructions that have become ready in this new cycle. /// Vector Pending is popluated by instructions that have transitioned through /// the pending stat during this cycle. The Pending and Ready sets may not be /// disjoint. An instruction is allowed to transition from the WAIT state to /// the READY state (going through the PENDING state) within a single cycle. /// That means, instructions may appear in both the Pending and Ready set. void cycleEvent(SmallVectorImpl &Freed, SmallVectorImpl &Executed, SmallVectorImpl &Pending, SmallVectorImpl &Ready); /// Convert a resource mask into a valid llvm processor resource identifier. /// /// Only the most significant bit of the Mask is used by this method to /// identify the processor resource. unsigned getResourceID(uint64_t Mask) const { return Resources->resolveResourceMask(Mask); } /// Select the next instruction to issue from the ReadySet. Returns an invalid /// instruction reference if there are no ready instructions, or if processor /// resources are not available. InstRef select(); bool isReadySetEmpty() const { return ReadySet.empty(); } bool isWaitSetEmpty() const { return WaitSet.empty(); } /// This method is called by the ExecuteStage at the end of each cycle to /// identify bottlenecks caused by data dependencies. Vector RegDeps is /// populated by instructions that were not issued because of unsolved /// register dependencies. Vector MemDeps is populated by instructions that /// were not issued because of unsolved memory dependencies. void analyzeDataDependencies(SmallVectorImpl &RegDeps, SmallVectorImpl &MemDeps); /// Returns a mask of busy resources, and populates vector Insts with /// instructions that could not be issued to the underlying pipelines because /// not all pipeline resources were available. uint64_t analyzeResourcePressure(SmallVectorImpl &Insts); // Returns true if the dispatch logic couldn't dispatch a full group due to // unavailable scheduler and/or LS resources. bool hadTokenStall() const { return HadTokenStall; } #ifndef NDEBUG // Update the ready queues. void dump() const; // This routine performs a basic correctness check. This routine should only // be called when we know that 'IR' is not in the scheduler's instruction // queues. void instructionCheck(const InstRef &IR) const { assert(!is_contained(WaitSet, IR) && "Already in the wait set!"); assert(!is_contained(ReadySet, IR) && "Already in the ready set!"); assert(!is_contained(IssuedSet, IR) && "Already executing!"); } #endif // !NDEBUG }; } // namespace mca } // namespace llvm #endif // LLVM_MCA_HARDWAREUNITS_SCHEDULER_H #ifdef __GNUC__ #pragma GCC diagnostic pop #endif