1 //===- VLIWMachineScheduler.h - VLIW-Focused Scheduling Pass ----*- 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 //===----------------------------------------------------------------------===//
10 
11 #ifndef LLVM_CODEGEN_VLIWMACHINESCHEDULER_H
12 #define LLVM_CODEGEN_VLIWMACHINESCHEDULER_H
13 
14 #include "llvm/ADT/SmallVector.h"
15 #include "llvm/ADT/Twine.h"
16 #include "llvm/CodeGen/MachineScheduler.h"
17 #include "llvm/CodeGen/TargetSchedule.h"
18 #include <limits>
19 #include <memory>
20 #include <utility>
21 
22 namespace llvm {
23 
24 class DFAPacketizer;
25 class RegisterClassInfo;
26 class ScheduleHazardRecognizer;
27 class SUnit;
28 class TargetInstrInfo;
29 class TargetSubtargetInfo;
30 
31 class VLIWResourceModel {
32 protected:
33   const TargetInstrInfo *TII;
34 
35   /// ResourcesModel - Represents VLIW state.
36   /// Not limited to VLIW targets per se, but assumes definition of resource
37   /// model by a target.
38   DFAPacketizer *ResourcesModel;
39 
40   const TargetSchedModel *SchedModel;
41 
42   /// Local packet/bundle model. Purely
43   /// internal to the MI scheduler at the time.
44   SmallVector<SUnit *> Packet;
45 
46   /// Total packets created.
47   unsigned TotalPackets = 0;
48 
49 public:
50   VLIWResourceModel(const TargetSubtargetInfo &STI, const TargetSchedModel *SM);
51 
52   virtual ~VLIWResourceModel();
53 
54   virtual void reset();
55 
56   virtual bool hasDependence(const SUnit *SUd, const SUnit *SUu);
57   virtual bool isResourceAvailable(SUnit *SU, bool IsTop);
58   virtual bool reserveResources(SUnit *SU, bool IsTop);
59   unsigned getTotalPackets() const { return TotalPackets; }
60   size_t getPacketInstCount() const { return Packet.size(); }
61   bool isInPacket(SUnit *SU) const { return is_contained(Packet, SU); }
62 
63 protected:
64   virtual DFAPacketizer *createPacketizer(const TargetSubtargetInfo &STI) const;
65 };
66 
67 /// Extend the standard ScheduleDAGMILive to provide more context and override
68 /// the top-level schedule() driver.
69 class VLIWMachineScheduler : public ScheduleDAGMILive {
70 public:
71   VLIWMachineScheduler(MachineSchedContext *C,
72                        std::unique_ptr<MachineSchedStrategy> S)
73       : ScheduleDAGMILive(C, std::move(S)) {}
74 
75   /// Schedule - This is called back from ScheduleDAGInstrs::Run() when it's
76   /// time to do some work.
77   void schedule() override;
78 
79   RegisterClassInfo *getRegClassInfo() { return RegClassInfo; }
80   int getBBSize() { return BB->size(); }
81 };
82 
83 //===----------------------------------------------------------------------===//
84 // ConvergingVLIWScheduler - Implementation of a VLIW-aware
85 // MachineSchedStrategy.
86 //===----------------------------------------------------------------------===//
87 
88 class ConvergingVLIWScheduler : public MachineSchedStrategy {
89 protected:
90   /// Store the state used by ConvergingVLIWScheduler heuristics, required
91   ///  for the lifetime of one invocation of pickNode().
92   struct SchedCandidate {
93     // The best SUnit candidate.
94     SUnit *SU = nullptr;
95 
96     // Register pressure values for the best candidate.
97     RegPressureDelta RPDelta;
98 
99     // Best scheduling cost.
100     int SCost = 0;
101 
102     SchedCandidate() = default;
103   };
104   /// Represent the type of SchedCandidate found within a single queue.
105   enum CandResult {
106     NoCand,
107     NodeOrder,
108     SingleExcess,
109     SingleCritical,
110     SingleMax,
111     MultiPressure,
112     BestCost,
113     Weak
114   };
115 
116   // Constants used to denote relative importance of
117   // heuristic components for cost computation.
118   static constexpr unsigned PriorityOne = 200;
119   static constexpr unsigned PriorityTwo = 50;
120   static constexpr unsigned PriorityThree = 75;
121   static constexpr unsigned ScaleTwo = 10;
122 
123   /// Each Scheduling boundary is associated with ready queues. It tracks the
124   /// current cycle in whichever direction at has moved, and maintains the state
125   /// of "hazards" and other interlocks at the current cycle.
126   struct VLIWSchedBoundary {
127     VLIWMachineScheduler *DAG = nullptr;
128     const TargetSchedModel *SchedModel = nullptr;
129 
130     ReadyQueue Available;
131     ReadyQueue Pending;
132     bool CheckPending = false;
133 
134     ScheduleHazardRecognizer *HazardRec = nullptr;
135     VLIWResourceModel *ResourceModel = nullptr;
136 
137     unsigned CurrCycle = 0;
138     unsigned IssueCount = 0;
139     unsigned CriticalPathLength = 0;
140 
141     /// MinReadyCycle - Cycle of the soonest available instruction.
142     unsigned MinReadyCycle = std::numeric_limits<unsigned>::max();
143 
144     // Remember the greatest min operand latency.
145     unsigned MaxMinLatency = 0;
146 
147     /// Pending queues extend the ready queues with the same ID and the
148     /// PendingFlag set.
149     VLIWSchedBoundary(unsigned ID, const Twine &Name)
150         : Available(ID, Name + ".A"),
151           Pending(ID << ConvergingVLIWScheduler::LogMaxQID, Name + ".P") {}
152 
153     ~VLIWSchedBoundary();
154 
155     void init(VLIWMachineScheduler *dag, const TargetSchedModel *smodel) {
156       DAG = dag;
157       SchedModel = smodel;
158       CurrCycle = 0;
159       IssueCount = 0;
160       // Initialize the critical path length limit, which used by the scheduling
161       // cost model to determine the value for scheduling an instruction. We use
162       // a slightly different heuristic for small and large functions. For small
163       // functions, it's important to use the height/depth of the instruction.
164       // For large functions, prioritizing by height or depth increases spills.
165       CriticalPathLength = DAG->getBBSize() / SchedModel->getIssueWidth();
166       if (DAG->getBBSize() < 50)
167         // We divide by two as a cheap and simple heuristic to reduce the
168         // critcal path length, which increases the priority of using the graph
169         // height/depth in the scheduler's cost computation.
170         CriticalPathLength >>= 1;
171       else {
172         // For large basic blocks, we prefer a larger critical path length to
173         // decrease the priority of using the graph height/depth.
174         unsigned MaxPath = 0;
175         for (auto &SU : DAG->SUnits)
176           MaxPath = std::max(MaxPath, isTop() ? SU.getHeight() : SU.getDepth());
177         CriticalPathLength = std::max(CriticalPathLength, MaxPath) + 1;
178       }
179     }
180 
181     bool isTop() const {
182       return Available.getID() == ConvergingVLIWScheduler::TopQID;
183     }
184 
185     bool checkHazard(SUnit *SU);
186 
187     void releaseNode(SUnit *SU, unsigned ReadyCycle);
188 
189     void bumpCycle();
190 
191     void bumpNode(SUnit *SU);
192 
193     void releasePending();
194 
195     void removeReady(SUnit *SU);
196 
197     SUnit *pickOnlyChoice();
198 
199     bool isLatencyBound(SUnit *SU) {
200       if (CurrCycle >= CriticalPathLength)
201         return true;
202       unsigned PathLength = isTop() ? SU->getHeight() : SU->getDepth();
203       return CriticalPathLength - CurrCycle <= PathLength;
204     }
205   };
206 
207   VLIWMachineScheduler *DAG = nullptr;
208   const TargetSchedModel *SchedModel = nullptr;
209 
210   // State of the top and bottom scheduled instruction boundaries.
211   VLIWSchedBoundary Top;
212   VLIWSchedBoundary Bot;
213 
214   /// List of pressure sets that have a high pressure level in the region.
215   SmallVector<bool> HighPressureSets;
216 
217 public:
218   /// SUnit::NodeQueueId: 0 (none), 1 (top), 2 (bot), 3 (both)
219   enum { TopQID = 1, BotQID = 2, LogMaxQID = 2 };
220 
221   ConvergingVLIWScheduler() : Top(TopQID, "TopQ"), Bot(BotQID, "BotQ") {}
222   virtual ~ConvergingVLIWScheduler() = default;
223 
224   void initialize(ScheduleDAGMI *dag) override;
225 
226   SUnit *pickNode(bool &IsTopNode) override;
227 
228   void schedNode(SUnit *SU, bool IsTopNode) override;
229 
230   void releaseTopNode(SUnit *SU) override;
231 
232   void releaseBottomNode(SUnit *SU) override;
233 
234   unsigned reportPackets() {
235     return Top.ResourceModel->getTotalPackets() +
236            Bot.ResourceModel->getTotalPackets();
237   }
238 
239 protected:
240   virtual VLIWResourceModel *
241   createVLIWResourceModel(const TargetSubtargetInfo &STI,
242                           const TargetSchedModel *SchedModel) const;
243 
244   SUnit *pickNodeBidrectional(bool &IsTopNode);
245 
246   int pressureChange(const SUnit *SU, bool isBotUp);
247 
248   virtual int SchedulingCost(ReadyQueue &Q, SUnit *SU,
249                              SchedCandidate &Candidate, RegPressureDelta &Delta,
250                              bool verbose);
251 
252   CandResult pickNodeFromQueue(VLIWSchedBoundary &Zone,
253                                const RegPressureTracker &RPTracker,
254                                SchedCandidate &Candidate);
255 #ifndef NDEBUG
256   void traceCandidate(const char *Label, const ReadyQueue &Q, SUnit *SU,
257                       int Cost, PressureChange P = PressureChange());
258 
259   void readyQueueVerboseDump(const RegPressureTracker &RPTracker,
260                              SchedCandidate &Candidate, ReadyQueue &Q);
261 #endif
262 };
263 
264 ScheduleDAGMILive *createVLIWSched(MachineSchedContext *C);
265 
266 } // end namespace llvm
267 
268 #endif // LLVM_CODEGEN_VLIWMACHINESCHEDULER_H
269