Scheduler.h 12 KB

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