123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #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<SchedulerStrategy> Strategy;
- // Hardware resources that are managed by this scheduler.
- std::unique_ptr<ResourceManager> 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<InstRef> WaitSet;
- std::vector<InstRef> PendingSet;
- std::vector<InstRef> ReadySet;
- std::vector<InstRef> 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<SchedulerStrategy> S);
- /// Issue an instruction without updating the ready queue.
- void issueInstructionImpl(
- InstRef &IR,
- SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &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<InstRef> &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<InstRef> &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<InstRef> &Pending);
- public:
- Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu)
- : Scheduler(Model, Lsu, nullptr) {}
- Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu,
- std::unique_ptr<SchedulerStrategy> SelectStrategy)
- : Scheduler(std::make_unique<ResourceManager>(Model), Lsu,
- std::move(SelectStrategy)) {}
- Scheduler(std::unique_ptr<ResourceManager> RM, LSUnitBase &Lsu,
- std::unique_ptr<SchedulerStrategy> 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<std::pair<ResourceRef, ResourceCycles>> &Used,
- SmallVectorImpl<InstRef> &Pending,
- SmallVectorImpl<InstRef> &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<ResourceRef> &Freed,
- SmallVectorImpl<InstRef> &Executed,
- SmallVectorImpl<InstRef> &Pending,
- SmallVectorImpl<InstRef> &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<InstRef> &RegDeps,
- SmallVectorImpl<InstRef> &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<InstRef> &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
|