Scheduler.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //===--------------------- Scheduler.cpp ------------------------*- 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. //
  9. // A scheduler for processor resource units and processor resource groups.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "llvm/MCA/HardwareUnits/Scheduler.h"
  13. #include "llvm/Support/Debug.h"
  14. #include "llvm/Support/raw_ostream.h"
  15. namespace llvm {
  16. namespace mca {
  17. #define DEBUG_TYPE "llvm-mca"
  18. void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) {
  19. // Ensure we have a valid (non-null) strategy object.
  20. Strategy = S ? std::move(S) : std::make_unique<DefaultSchedulerStrategy>();
  21. }
  22. // Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy.
  23. SchedulerStrategy::~SchedulerStrategy() = default;
  24. DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default;
  25. #ifndef NDEBUG
  26. void Scheduler::dump() const {
  27. dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n';
  28. dbgs() << "[SCHEDULER]: ReadySet size is: " << ReadySet.size() << '\n';
  29. dbgs() << "[SCHEDULER]: IssuedSet size is: " << IssuedSet.size() << '\n';
  30. Resources->dump();
  31. }
  32. #endif
  33. Scheduler::Status Scheduler::isAvailable(const InstRef &IR) {
  34. ResourceStateEvent RSE =
  35. Resources->canBeDispatched(IR.getInstruction()->getUsedBuffers());
  36. HadTokenStall = RSE != RS_BUFFER_AVAILABLE;
  37. switch (RSE) {
  38. case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
  39. return Scheduler::SC_BUFFERS_FULL;
  40. case ResourceStateEvent::RS_RESERVED:
  41. return Scheduler::SC_DISPATCH_GROUP_STALL;
  42. case ResourceStateEvent::RS_BUFFER_AVAILABLE:
  43. break;
  44. }
  45. // Give lower priority to LSUnit stall events.
  46. LSUnit::Status LSS = LSU.isAvailable(IR);
  47. HadTokenStall = LSS != LSUnit::LSU_AVAILABLE;
  48. switch (LSS) {
  49. case LSUnit::LSU_LQUEUE_FULL:
  50. return Scheduler::SC_LOAD_QUEUE_FULL;
  51. case LSUnit::LSU_SQUEUE_FULL:
  52. return Scheduler::SC_STORE_QUEUE_FULL;
  53. case LSUnit::LSU_AVAILABLE:
  54. return Scheduler::SC_AVAILABLE;
  55. }
  56. llvm_unreachable("Don't know how to process this LSU state result!");
  57. }
  58. void Scheduler::issueInstructionImpl(
  59. InstRef &IR,
  60. SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources) {
  61. Instruction *IS = IR.getInstruction();
  62. const InstrDesc &D = IS->getDesc();
  63. // Issue the instruction and collect all the consumed resources
  64. // into a vector. That vector is then used to notify the listener.
  65. Resources->issueInstruction(D, UsedResources);
  66. // Notify the instruction that it started executing.
  67. // This updates the internal state of each write.
  68. IS->execute(IR.getSourceIndex());
  69. IS->computeCriticalRegDep();
  70. if (IS->isMemOp()) {
  71. LSU.onInstructionIssued(IR);
  72. const MemoryGroup &Group = LSU.getGroup(IS->getLSUTokenID());
  73. IS->setCriticalMemDep(Group.getCriticalPredecessor());
  74. }
  75. if (IS->isExecuting())
  76. IssuedSet.emplace_back(IR);
  77. else if (IS->isExecuted())
  78. LSU.onInstructionExecuted(IR);
  79. }
  80. // Release the buffered resources and issue the instruction.
  81. void Scheduler::issueInstruction(
  82. InstRef &IR,
  83. SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources,
  84. SmallVectorImpl<InstRef> &PendingInstructions,
  85. SmallVectorImpl<InstRef> &ReadyInstructions) {
  86. const Instruction &Inst = *IR.getInstruction();
  87. bool HasDependentUsers = Inst.hasDependentUsers();
  88. HasDependentUsers |= Inst.isMemOp() && LSU.hasDependentUsers(IR);
  89. Resources->releaseBuffers(Inst.getUsedBuffers());
  90. issueInstructionImpl(IR, UsedResources);
  91. // Instructions that have been issued during this cycle might have unblocked
  92. // other dependent instructions. Dependent instructions may be issued during
  93. // this same cycle if operands have ReadAdvance entries. Promote those
  94. // instructions to the ReadySet and notify the caller that those are ready.
  95. if (HasDependentUsers)
  96. if (promoteToPendingSet(PendingInstructions))
  97. promoteToReadySet(ReadyInstructions);
  98. }
  99. bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
  100. // Scan the set of waiting instructions and promote them to the
  101. // ready set if operands are all ready.
  102. unsigned PromotedElements = 0;
  103. for (auto I = PendingSet.begin(), E = PendingSet.end(); I != E;) {
  104. InstRef &IR = *I;
  105. if (!IR)
  106. break;
  107. // Check if there are unsolved register dependencies.
  108. Instruction &IS = *IR.getInstruction();
  109. if (!IS.isReady() && !IS.updatePending()) {
  110. ++I;
  111. continue;
  112. }
  113. // Check if there are unsolved memory dependencies.
  114. if (IS.isMemOp() && !LSU.isReady(IR)) {
  115. ++I;
  116. continue;
  117. }
  118. LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
  119. << " promoted to the READY set.\n");
  120. Ready.emplace_back(IR);
  121. ReadySet.emplace_back(IR);
  122. IR.invalidate();
  123. ++PromotedElements;
  124. std::iter_swap(I, E - PromotedElements);
  125. }
  126. PendingSet.resize(PendingSet.size() - PromotedElements);
  127. return PromotedElements;
  128. }
  129. bool Scheduler::promoteToPendingSet(SmallVectorImpl<InstRef> &Pending) {
  130. // Scan the set of waiting instructions and promote them to the
  131. // pending set if operands are all ready.
  132. unsigned RemovedElements = 0;
  133. for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) {
  134. InstRef &IR = *I;
  135. if (!IR)
  136. break;
  137. // Check if this instruction is now ready. In case, force
  138. // a transition in state using method 'updateDispatched()'.
  139. Instruction &IS = *IR.getInstruction();
  140. if (IS.isDispatched() && !IS.updateDispatched()) {
  141. ++I;
  142. continue;
  143. }
  144. if (IS.isMemOp() && LSU.isWaiting(IR)) {
  145. ++I;
  146. continue;
  147. }
  148. LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
  149. << " promoted to the PENDING set.\n");
  150. Pending.emplace_back(IR);
  151. PendingSet.emplace_back(IR);
  152. IR.invalidate();
  153. ++RemovedElements;
  154. std::iter_swap(I, E - RemovedElements);
  155. }
  156. WaitSet.resize(WaitSet.size() - RemovedElements);
  157. return RemovedElements;
  158. }
  159. InstRef Scheduler::select() {
  160. unsigned QueueIndex = ReadySet.size();
  161. for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) {
  162. InstRef &IR = ReadySet[I];
  163. if (QueueIndex == ReadySet.size() ||
  164. Strategy->compare(IR, ReadySet[QueueIndex])) {
  165. Instruction &IS = *IR.getInstruction();
  166. uint64_t BusyResourceMask = Resources->checkAvailability(IS.getDesc());
  167. if (BusyResourceMask)
  168. IS.setCriticalResourceMask(BusyResourceMask);
  169. BusyResourceUnits |= BusyResourceMask;
  170. if (!BusyResourceMask)
  171. QueueIndex = I;
  172. }
  173. }
  174. if (QueueIndex == ReadySet.size())
  175. return InstRef();
  176. // We found an instruction to issue.
  177. InstRef IR = ReadySet[QueueIndex];
  178. std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]);
  179. ReadySet.pop_back();
  180. return IR;
  181. }
  182. void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) {
  183. unsigned RemovedElements = 0;
  184. for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) {
  185. InstRef &IR = *I;
  186. if (!IR)
  187. break;
  188. Instruction &IS = *IR.getInstruction();
  189. if (!IS.isExecuted()) {
  190. LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR
  191. << " is still executing.\n");
  192. ++I;
  193. continue;
  194. }
  195. // Instruction IR has completed execution.
  196. LSU.onInstructionExecuted(IR);
  197. Executed.emplace_back(IR);
  198. ++RemovedElements;
  199. IR.invalidate();
  200. std::iter_swap(I, E - RemovedElements);
  201. }
  202. IssuedSet.resize(IssuedSet.size() - RemovedElements);
  203. }
  204. uint64_t Scheduler::analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts) {
  205. llvm::append_range(Insts, ReadySet);
  206. return BusyResourceUnits;
  207. }
  208. void Scheduler::analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps,
  209. SmallVectorImpl<InstRef> &MemDeps) {
  210. const auto EndIt = PendingSet.end() - NumDispatchedToThePendingSet;
  211. for (const InstRef &IR : make_range(PendingSet.begin(), EndIt)) {
  212. const Instruction &IS = *IR.getInstruction();
  213. if (Resources->checkAvailability(IS.getDesc()))
  214. continue;
  215. if (IS.isMemOp() && LSU.isPending(IR))
  216. MemDeps.emplace_back(IR);
  217. if (IS.isPending())
  218. RegDeps.emplace_back(IR);
  219. }
  220. }
  221. void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed,
  222. SmallVectorImpl<InstRef> &Executed,
  223. SmallVectorImpl<InstRef> &Pending,
  224. SmallVectorImpl<InstRef> &Ready) {
  225. LSU.cycleEvent();
  226. // Release consumed resources.
  227. Resources->cycleEvent(Freed);
  228. for (InstRef &IR : IssuedSet)
  229. IR.getInstruction()->cycleEvent();
  230. updateIssuedSet(Executed);
  231. for (InstRef &IR : PendingSet)
  232. IR.getInstruction()->cycleEvent();
  233. for (InstRef &IR : WaitSet)
  234. IR.getInstruction()->cycleEvent();
  235. promoteToPendingSet(Pending);
  236. promoteToReadySet(Ready);
  237. NumDispatchedToThePendingSet = 0;
  238. BusyResourceUnits = 0;
  239. }
  240. bool Scheduler::mustIssueImmediately(const InstRef &IR) const {
  241. const InstrDesc &Desc = IR.getInstruction()->getDesc();
  242. if (Desc.isZeroLatency())
  243. return true;
  244. // Instructions that use an in-order dispatch/issue processor resource must be
  245. // issued immediately to the pipeline(s). Any other in-order buffered
  246. // resources (i.e. BufferSize=1) is consumed.
  247. return Desc.MustIssueImmediately;
  248. }
  249. bool Scheduler::dispatch(InstRef &IR) {
  250. Instruction &IS = *IR.getInstruction();
  251. Resources->reserveBuffers(IS.getUsedBuffers());
  252. // If necessary, reserve queue entries in the load-store unit (LSU).
  253. if (IS.isMemOp())
  254. IS.setLSUTokenID(LSU.dispatch(IR));
  255. if (IS.isDispatched() || (IS.isMemOp() && LSU.isWaiting(IR))) {
  256. LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n");
  257. WaitSet.push_back(IR);
  258. return false;
  259. }
  260. if (IS.isPending() || (IS.isMemOp() && LSU.isPending(IR))) {
  261. LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR
  262. << " to the PendingSet\n");
  263. PendingSet.push_back(IR);
  264. ++NumDispatchedToThePendingSet;
  265. return false;
  266. }
  267. assert(IS.isReady() && (!IS.isMemOp() || LSU.isReady(IR)) &&
  268. "Unexpected internal state found!");
  269. // Don't add a zero-latency instruction to the Ready queue.
  270. // A zero-latency instruction doesn't consume any scheduler resources. That is
  271. // because it doesn't need to be executed, and it is often removed at register
  272. // renaming stage. For example, register-register moves are often optimized at
  273. // register renaming stage by simply updating register aliases. On some
  274. // targets, zero-idiom instructions (for example: a xor that clears the value
  275. // of a register) are treated specially, and are often eliminated at register
  276. // renaming stage.
  277. if (!mustIssueImmediately(IR)) {
  278. LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n");
  279. ReadySet.push_back(IR);
  280. }
  281. return true;
  282. }
  283. } // namespace mca
  284. } // namespace llvm