xref: /freebsd/contrib/llvm-project/llvm/include/llvm/MCA/HardwareUnits/Scheduler.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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