1 //===--------------------- Scheduler.h ------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// \file 9 /// 10 /// A scheduler for Processor Resource Units and Processor Resource Groups. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_MCA_HARDWAREUNITS_SCHEDULER_H 15 #define LLVM_MCA_HARDWAREUNITS_SCHEDULER_H 16 17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/MC/MCSchedule.h" 19 #include "llvm/MCA/HardwareUnits/HardwareUnit.h" 20 #include "llvm/MCA/HardwareUnits/LSUnit.h" 21 #include "llvm/MCA/HardwareUnits/ResourceManager.h" 22 #include "llvm/MCA/Support.h" 23 #include "llvm/Support/Compiler.h" 24 25 namespace llvm { 26 namespace mca { 27 28 class LLVM_ABI SchedulerStrategy { 29 public: 30 SchedulerStrategy() = default; 31 virtual ~SchedulerStrategy(); 32 33 /// Returns true if Lhs should take priority over Rhs. 34 /// 35 /// This method is used by class Scheduler to select the "best" ready 36 /// instruction to issue to the underlying pipelines. 37 virtual bool compare(const InstRef &Lhs, const InstRef &Rhs) const = 0; 38 }; 39 40 /// Default instruction selection strategy used by class Scheduler. 41 class LLVM_ABI DefaultSchedulerStrategy : public SchedulerStrategy { 42 /// This method ranks instructions based on their age, and the number of known 43 /// users. The lower the rank value, the better. computeRank(const InstRef & Lhs)44 int computeRank(const InstRef &Lhs) const { 45 return Lhs.getSourceIndex() - Lhs.getInstruction()->getNumUsers(); 46 } 47 48 public: 49 DefaultSchedulerStrategy() = default; 50 virtual ~DefaultSchedulerStrategy(); 51 compare(const InstRef & Lhs,const InstRef & Rhs)52 bool compare(const InstRef &Lhs, const InstRef &Rhs) const override { 53 int LhsRank = computeRank(Lhs); 54 int RhsRank = computeRank(Rhs); 55 56 /// Prioritize older instructions over younger instructions to minimize the 57 /// pressure on the reorder buffer. 58 if (LhsRank == RhsRank) 59 return Lhs.getSourceIndex() < Rhs.getSourceIndex(); 60 return LhsRank < RhsRank; 61 } 62 }; 63 64 /// Class Scheduler is responsible for issuing instructions to pipeline 65 /// resources. 66 /// 67 /// Internally, it delegates to a ResourceManager the management of processor 68 /// resources. This class is also responsible for tracking the progress of 69 /// instructions from the dispatch stage, until the write-back stage. 70 /// 71 class Scheduler : public HardwareUnit { 72 LSUnitBase &LSU; 73 74 // Instruction selection strategy for this Scheduler. 75 std::unique_ptr<SchedulerStrategy> Strategy; 76 77 // Hardware resources that are managed by this scheduler. 78 std::unique_ptr<ResourceManager> Resources; 79 80 // Instructions dispatched to the Scheduler are internally classified based on 81 // the instruction stage (see Instruction::InstrStage). 82 // 83 // An Instruction dispatched to the Scheduler is added to the WaitSet if not 84 // all its register operands are available, and at least one latency is 85 // unknown. By construction, the WaitSet only contains instructions that are 86 // in the IS_DISPATCHED stage. 87 // 88 // An Instruction transitions from the WaitSet to the PendingSet if the 89 // instruction is not ready yet, but the latency of every register read is 90 // known. Instructions in the PendingSet can only be in the IS_PENDING or 91 // IS_READY stage. Only IS_READY instructions that are waiting on memory 92 // dependencies can be added to the PendingSet. 93 // 94 // Instructions in the PendingSet are immediately dominated only by 95 // instructions that have already been issued to the underlying pipelines. In 96 // the presence of bottlenecks caused by data dependencies, the PendingSet can 97 // be inspected to identify problematic data dependencies between 98 // instructions. 99 // 100 // An instruction is moved to the ReadySet when all register operands become 101 // available, and all memory dependencies are met. Instructions that are 102 // moved from the PendingSet to the ReadySet must transition to the 'IS_READY' 103 // stage. 104 // 105 // On every cycle, the Scheduler checks if it can promote instructions from the 106 // PendingSet to the ReadySet. 107 // 108 // An Instruction is moved from the ReadySet to the `IssuedSet` when it starts 109 // exection. This event also causes an instruction state transition (i.e. from 110 // state IS_READY, to state IS_EXECUTING). An Instruction leaves the IssuedSet 111 // only when it reaches the write-back stage. 112 std::vector<InstRef> WaitSet; 113 std::vector<InstRef> PendingSet; 114 std::vector<InstRef> ReadySet; 115 std::vector<InstRef> IssuedSet; 116 117 // A mask of busy resource units. It defaults to the empty set (i.e. a zero 118 // mask), and it is cleared at the beginning of every cycle. 119 // It is updated every time the scheduler fails to issue an instruction from 120 // the ready set due to unavailable pipeline resources. 121 // Each bit of the mask represents an unavailable resource. 122 uint64_t BusyResourceUnits; 123 124 // Counts the number of instructions in the pending set that were dispatched 125 // during this cycle. 126 unsigned NumDispatchedToThePendingSet; 127 128 // True if the previous pipeline Stage was unable to dispatch a full group of 129 // opcodes because scheduler buffers (or LS queues) were unavailable. 130 bool HadTokenStall; 131 132 /// Verify the given selection strategy and set the Strategy member 133 /// accordingly. If no strategy is provided, the DefaultSchedulerStrategy is 134 /// used. 135 LLVM_ABI void initializeStrategy(std::unique_ptr<SchedulerStrategy> S); 136 137 /// Issue an instruction without updating the ready queue. 138 void issueInstructionImpl( 139 InstRef &IR, 140 SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &Pipes); 141 142 // Identify instructions that have finished executing, and remove them from 143 // the IssuedSet. References to executed instructions are added to input 144 // vector 'Executed'. 145 void updateIssuedSet(SmallVectorImpl<InstRef> &Executed); 146 147 // Try to promote instructions from the PendingSet to the ReadySet. 148 // Add promoted instructions to the 'Ready' vector in input. 149 // Returns true if at least one instruction was promoted. 150 bool promoteToReadySet(SmallVectorImpl<InstRef> &Ready); 151 152 // Try to promote instructions from the WaitSet to the PendingSet. 153 // Add promoted instructions to the 'Pending' vector in input. 154 // Returns true if at least one instruction was promoted. 155 bool promoteToPendingSet(SmallVectorImpl<InstRef> &Pending); 156 157 public: Scheduler(const MCSchedModel & Model,LSUnitBase & Lsu)158 Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu) 159 : Scheduler(Model, Lsu, nullptr) {} 160 Scheduler(const MCSchedModel & Model,LSUnitBase & Lsu,std::unique_ptr<SchedulerStrategy> SelectStrategy)161 Scheduler(const MCSchedModel &Model, LSUnitBase &Lsu, 162 std::unique_ptr<SchedulerStrategy> SelectStrategy) 163 : Scheduler(std::make_unique<ResourceManager>(Model), Lsu, 164 std::move(SelectStrategy)) {} 165 Scheduler(std::unique_ptr<ResourceManager> RM,LSUnitBase & Lsu,std::unique_ptr<SchedulerStrategy> SelectStrategy)166 Scheduler(std::unique_ptr<ResourceManager> RM, LSUnitBase &Lsu, 167 std::unique_ptr<SchedulerStrategy> SelectStrategy) 168 : LSU(Lsu), Resources(std::move(RM)), BusyResourceUnits(0), 169 NumDispatchedToThePendingSet(0), HadTokenStall(false) { 170 initializeStrategy(std::move(SelectStrategy)); 171 } 172 173 // Stalls generated by the scheduler. 174 enum Status { 175 SC_AVAILABLE, 176 SC_LOAD_QUEUE_FULL, 177 SC_STORE_QUEUE_FULL, 178 SC_BUFFERS_FULL, 179 SC_DISPATCH_GROUP_STALL, 180 }; 181 182 /// Check if the instruction in 'IR' can be dispatched during this cycle. 183 /// Return SC_AVAILABLE if both scheduler and LS resources are available. 184 /// 185 /// This method is also responsible for setting field HadTokenStall if 186 /// IR cannot be dispatched to the Scheduler due to unavailable resources. 187 LLVM_ABI Status isAvailable(const InstRef &IR); 188 189 /// Reserves buffer and LSUnit queue resources that are necessary to issue 190 /// this instruction. 191 /// 192 /// Returns true if instruction IR is ready to be issued to the underlying 193 /// pipelines. Note that this operation cannot fail; it assumes that a 194 /// previous call to method `isAvailable(IR)` returned `SC_AVAILABLE`. 195 /// 196 /// If IR is a memory operation, then the Scheduler queries the LS unit to 197 /// obtain a LS token. An LS token is used internally to track memory 198 /// dependencies. 199 LLVM_ABI bool dispatch(InstRef &IR); 200 201 /// Issue an instruction and populates a vector of used pipeline resources, 202 /// and a vector of instructions that transitioned to the ready state as a 203 /// result of this event. 204 LLVM_ABI void issueInstruction( 205 InstRef &IR, 206 SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &Used, 207 SmallVectorImpl<InstRef> &Pending, SmallVectorImpl<InstRef> &Ready); 208 209 /// Returns true if IR has to be issued immediately, or if IR is a zero 210 /// latency instruction. 211 LLVM_ABI bool mustIssueImmediately(const InstRef &IR) const; 212 213 /// This routine notifies the Scheduler that a new cycle just started. 214 /// 215 /// It notifies the underlying ResourceManager that a new cycle just started. 216 /// Vector `Freed` is populated with resourceRef related to resources that 217 /// have changed in state, and that are now available to new instructions. 218 /// Instructions executed are added to vector Executed, while vector Ready is 219 /// populated with instructions that have become ready in this new cycle. 220 /// Vector Pending is popluated by instructions that have transitioned through 221 /// the pending stat during this cycle. The Pending and Ready sets may not be 222 /// disjoint. An instruction is allowed to transition from the WAIT state to 223 /// the READY state (going through the PENDING state) within a single cycle. 224 /// That means, instructions may appear in both the Pending and Ready set. 225 LLVM_ABI void cycleEvent(SmallVectorImpl<ResourceRef> &Freed, 226 SmallVectorImpl<InstRef> &Executed, 227 SmallVectorImpl<InstRef> &Pending, 228 SmallVectorImpl<InstRef> &Ready); 229 230 /// Convert a resource mask into a valid llvm processor resource identifier. 231 /// 232 /// Only the most significant bit of the Mask is used by this method to 233 /// identify the processor resource. getResourceID(uint64_t Mask)234 unsigned getResourceID(uint64_t Mask) const { 235 return Resources->resolveResourceMask(Mask); 236 } 237 238 /// Select the next instruction to issue from the ReadySet. Returns an invalid 239 /// instruction reference if there are no ready instructions, or if processor 240 /// resources are not available. 241 LLVM_ABI InstRef select(); 242 isReadySetEmpty()243 bool isReadySetEmpty() const { return ReadySet.empty(); } isWaitSetEmpty()244 bool isWaitSetEmpty() const { return WaitSet.empty(); } 245 246 /// This method is called by the ExecuteStage at the end of each cycle to 247 /// identify bottlenecks caused by data dependencies. Vector RegDeps is 248 /// populated by instructions that were not issued because of unsolved 249 /// register dependencies. Vector MemDeps is populated by instructions that 250 /// were not issued because of unsolved memory dependencies. 251 LLVM_ABI void analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps, 252 SmallVectorImpl<InstRef> &MemDeps); 253 254 /// Returns a mask of busy resources, and populates vector Insts with 255 /// instructions that could not be issued to the underlying pipelines because 256 /// not all pipeline resources were available. 257 LLVM_ABI uint64_t analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts); 258 259 // Returns true if the dispatch logic couldn't dispatch a full group due to 260 // unavailable scheduler and/or LS resources. hadTokenStall()261 bool hadTokenStall() const { return HadTokenStall; } 262 263 #ifndef NDEBUG 264 // Update the ready queues. 265 void dump() const; 266 267 // This routine performs a basic correctness check. This routine should only 268 // be called when we know that 'IR' is not in the scheduler's instruction 269 // queues. instructionCheck(const InstRef & IR)270 void instructionCheck(const InstRef &IR) const { 271 assert(!is_contained(WaitSet, IR) && "Already in the wait set!"); 272 assert(!is_contained(ReadySet, IR) && "Already in the ready set!"); 273 assert(!is_contained(IssuedSet, IR) && "Already executing!"); 274 } 275 #endif // !NDEBUG 276 }; 277 } // namespace mca 278 } // namespace llvm 279 280 #endif // LLVM_MCA_HARDWAREUNITS_SCHEDULER_H 281