LLVM 22.0.0git
ScheduleDAGVLIW.cpp
Go to the documentation of this file.
1//===- ScheduleDAGVLIW.cpp - SelectionDAG list scheduler for VLIW -*- 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// This implements a top-down list scheduler, using standard algorithms.
10// The basic approach uses a priority queue of available nodes to schedule.
11// One at a time, nodes are taken from the priority queue (thus in priority
12// order), checked for legality to schedule, and emitted if legal.
13//
14// Nodes may not be legal to schedule either due to structural hazards (e.g.
15// pipeline or resource constraints) or because an input to the instruction has
16// not completed execution.
17//
18//===----------------------------------------------------------------------===//
19
20#include "ScheduleDAGSDNodes.h"
21#include "llvm/ADT/Statistic.h"
28#include "llvm/Support/Debug.h"
31using namespace llvm;
32
33#define DEBUG_TYPE "pre-RA-sched"
34
35STATISTIC(NumNoops , "Number of noops inserted");
36STATISTIC(NumStalls, "Number of pipeline stalls");
37
39 VLIWScheduler("vliw-td", "VLIW scheduler",
41
42namespace {
43//===----------------------------------------------------------------------===//
44/// ScheduleDAGVLIW - The actual DFA list scheduler implementation. This
45/// supports / top-down scheduling.
46///
47class ScheduleDAGVLIW : public ScheduleDAGSDNodes {
48private:
49 /// AvailableQueue - The priority queue to use for the available SUnits.
50 ///
51 SchedulingPriorityQueue *AvailableQueue;
52
53 /// PendingQueue - This contains all of the instructions whose operands have
54 /// been issued, but their results are not ready yet (due to the latency of
55 /// the operation). Once the operands become available, the instruction is
56 /// added to the AvailableQueue.
57 std::vector<SUnit*> PendingQueue;
58
59 /// HazardRec - The hazard recognizer to use.
60 ScheduleHazardRecognizer *HazardRec;
61
62public:
63 ScheduleDAGVLIW(MachineFunction &MF, SchedulingPriorityQueue *AvailableQueue)
64 : ScheduleDAGSDNodes(MF), AvailableQueue(AvailableQueue) {
65 const TargetSubtargetInfo &STI = MF.getSubtarget();
66 HazardRec = STI.getInstrInfo()->CreateTargetHazardRecognizer(&STI, this);
67 }
68
69 ~ScheduleDAGVLIW() override {
70 delete HazardRec;
71 delete AvailableQueue;
72 }
73
74 void Schedule() override;
75
76private:
77 void releaseSucc(SUnit *SU, const SDep &D);
78 void releaseSuccessors(SUnit *SU);
79 void scheduleNodeTopDown(SUnit *SU, unsigned CurCycle);
80 void listScheduleTopDown();
81};
82} // end anonymous namespace
83
84/// Schedule - Schedule the DAG using list scheduling.
85void ScheduleDAGVLIW::Schedule() {
86 LLVM_DEBUG(dbgs() << "********** List Scheduling " << printMBBReference(*BB)
87 << " '" << BB->getName() << "' **********\n");
88
89 // Build the scheduling graph.
90 BuildSchedGraph();
91
92 AvailableQueue->initNodes(SUnits);
93
94 listScheduleTopDown();
95
96 AvailableQueue->releaseState();
97}
98
99//===----------------------------------------------------------------------===//
100// Top-Down Scheduling
101//===----------------------------------------------------------------------===//
102
103/// releaseSucc - Decrement the NumPredsLeft count of a successor. Add it to
104/// the PendingQueue if the count reaches zero. Also update its cycle bound.
105void ScheduleDAGVLIW::releaseSucc(SUnit *SU, const SDep &D) {
106 SUnit *SuccSU = D.getSUnit();
107
108#ifndef NDEBUG
109 if (SuccSU->NumPredsLeft == 0) {
110 dbgs() << "*** Scheduling failed! ***\n";
111 dumpNode(*SuccSU);
112 dbgs() << " has been released too many times!\n";
113 llvm_unreachable(nullptr);
114 }
115#endif
116 assert(!D.isWeak() && "unexpected artificial DAG edge");
117
118 --SuccSU->NumPredsLeft;
119
120 SuccSU->setDepthToAtLeast(SU->getDepth() + D.getLatency());
121
122 // If all the node's predecessors are scheduled, this node is ready
123 // to be scheduled. Ignore the special ExitSU node.
124 if (SuccSU->NumPredsLeft == 0 && SuccSU != &ExitSU) {
125 PendingQueue.push_back(SuccSU);
126 }
127}
128
129void ScheduleDAGVLIW::releaseSuccessors(SUnit *SU) {
130 // Top down: release successors.
131 for (SDep &Succ : SU->Succs) {
132 assert(!Succ.isAssignedRegDep() &&
133 "The list-td scheduler doesn't yet support physreg dependencies!");
134
135 releaseSucc(SU, Succ);
136 }
137}
138
139/// scheduleNodeTopDown - Add the node to the schedule. Decrement the pending
140/// count of its successors. If a successor pending count is zero, add it to
141/// the Available queue.
142void ScheduleDAGVLIW::scheduleNodeTopDown(SUnit *SU, unsigned CurCycle) {
143 LLVM_DEBUG(dbgs() << "*** Scheduling [" << CurCycle << "]: ");
144 LLVM_DEBUG(dumpNode(*SU));
145
146 Sequence.push_back(SU);
147 assert(CurCycle >= SU->getDepth() && "Node scheduled above its depth!");
148 SU->setDepthToAtLeast(CurCycle);
149
150 releaseSuccessors(SU);
151 SU->isScheduled = true;
152 AvailableQueue->scheduledNode(SU);
153}
154
155/// listScheduleTopDown - The main loop of list scheduling for top-down
156/// schedulers.
157void ScheduleDAGVLIW::listScheduleTopDown() {
158 unsigned CurCycle = 0;
159
160 // Release any successors of the special Entry node.
161 releaseSuccessors(&EntrySU);
162
163 // All leaves to AvailableQueue.
164 for (SUnit &SU : SUnits) {
165 // It is available if it has no predecessors.
166 if (SU.Preds.empty()) {
167 AvailableQueue->push(&SU);
168 SU.isAvailable = true;
169 }
170 }
171
172 // While AvailableQueue is not empty, grab the node with the highest
173 // priority. If it is not ready put it back. Schedule the node.
174 std::vector<SUnit*> NotReady;
175 Sequence.reserve(SUnits.size());
176 while (!AvailableQueue->empty() || !PendingQueue.empty()) {
177 // Check to see if any of the pending instructions are ready to issue. If
178 // so, add them to the available queue.
179 for (unsigned i = 0, e = PendingQueue.size(); i != e; ++i) {
180 if (PendingQueue[i]->getDepth() == CurCycle) {
181 AvailableQueue->push(PendingQueue[i]);
182 PendingQueue[i]->isAvailable = true;
183 PendingQueue[i] = PendingQueue.back();
184 PendingQueue.pop_back();
185 --i; --e;
186 }
187 else {
188 assert(PendingQueue[i]->getDepth() > CurCycle && "Negative latency?");
189 }
190 }
191
192 // If there are no instructions available, don't try to issue anything, and
193 // don't advance the hazard recognizer.
194 if (AvailableQueue->empty()) {
195 // Reset DFA state.
196 AvailableQueue->scheduledNode(nullptr);
197 ++CurCycle;
198 continue;
199 }
200
201 SUnit *FoundSUnit = nullptr;
202
203 bool HasNoopHazards = false;
204 while (!AvailableQueue->empty()) {
205 SUnit *CurSUnit = AvailableQueue->pop();
206
208 HazardRec->getHazardType(CurSUnit, 0/*no stalls*/);
210 FoundSUnit = CurSUnit;
211 break;
212 }
213
214 // Remember if this is a noop hazard.
215 HasNoopHazards |= HT == ScheduleHazardRecognizer::NoopHazard;
216
217 NotReady.push_back(CurSUnit);
218 }
219
220 // Add the nodes that aren't ready back onto the available list.
221 if (!NotReady.empty()) {
222 AvailableQueue->push_all(NotReady);
223 NotReady.clear();
224 }
225
226 // If we found a node to schedule, do it now.
227 if (FoundSUnit) {
228 scheduleNodeTopDown(FoundSUnit, CurCycle);
229 HazardRec->EmitInstruction(FoundSUnit);
230
231 // If this is a pseudo-op node, we don't want to increment the current
232 // cycle.
233 if (FoundSUnit->Latency) // Don't increment CurCycle for pseudo-ops!
234 ++CurCycle;
235 } else if (!HasNoopHazards) {
236 // Otherwise, we have a pipeline stall, but no other problem, just advance
237 // the current cycle and try again.
238 LLVM_DEBUG(dbgs() << "*** Advancing cycle, no work to do\n");
239 HazardRec->AdvanceCycle();
240 ++NumStalls;
241 ++CurCycle;
242 } else {
243 // Otherwise, we have no instructions to issue and we have instructions
244 // that will fault if we don't do this right. This is the case for
245 // processors without pipeline interlocks and other cases.
246 LLVM_DEBUG(dbgs() << "*** Emitting noop\n");
247 HazardRec->EmitNoop();
248 Sequence.push_back(nullptr); // NULL here means noop
249 ++NumNoops;
250 ++CurCycle;
251 }
252 }
253
254#ifndef NDEBUG
255 VerifyScheduledSequence(/*isBottomUp=*/false);
256#endif
257}
258
259//===----------------------------------------------------------------------===//
260// Public Constructor Functions
261//===----------------------------------------------------------------------===//
262
263/// createVLIWDAGScheduler - This creates a top-down list scheduler.
266 return new ScheduleDAGVLIW(*IS->MF, new ResourcePriorityQueue(IS));
267}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static RegisterScheduler VLIWScheduler("vliw-td", "VLIW scheduler", createVLIWDAGScheduler)
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:114
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
bool isAssignedRegDep() const
Tests if this is a Data dependence that is associated with a register.
unsigned short Latency
Node latency.
unsigned getDepth() const
Returns the depth of this node, which is the length of the maximum path up to any node which has no p...
bool isScheduled
True once scheduled.
bool isAvailable
True once available.
unsigned NumPredsLeft
SmallVector< SDep, 4 > Succs
All sunit successors.
SmallVector< SDep, 4 > Preds
All sunit predecessors.
LLVM_ABI void setDepthToAtLeast(unsigned NewDepth)
If NewDepth is greater than this node's depth value, sets it to be the new depth value.
ScheduleDAGSDNodes - A ScheduleDAG for scheduling SDNode-based DAGs.
HazardRecognizer - This determines whether or not an instruction can be issued this cycle,...
virtual void EmitInstruction(SUnit *)
EmitInstruction - This callback is invoked when an instruction is emitted, to advance the hazard stat...
virtual void EmitNoop()
EmitNoop - This callback is invoked when a noop was added to the instruction stream.
virtual void AdvanceCycle()
AdvanceCycle - This callback is invoked whenever the next top-down instruction to be scheduled cannot...
virtual HazardType getHazardType(SUnit *, int Stalls=0)
getHazardType - Return the hazard type of emitting this node.
This interface is used to plug different priorities computation algorithms into the list scheduler.
virtual void releaseState()=0
virtual SUnit * pop()=0
virtual void scheduledNode(SUnit *)
As each node is scheduled, this method is invoked.
virtual void initNodes(std::vector< SUnit > &SUnits)=0
virtual bool empty() const =0
void push_all(const std::vector< SUnit * > &Nodes)
virtual void push(SUnit *U)=0
SelectionDAGISel - This is the common base class used for SelectionDAG-based pattern-matching instruc...
TargetSubtargetInfo - Generic base class for all target subtargets.
virtual const TargetInstrInfo * getInstrInfo() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr double e
Definition MathExtras.h:47
Sequence
A sequence of states that a pointer may go through in which an objc_retain and objc_release are actua...
Definition PtrState.h:41
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:207
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:82
LLVM_ABI ScheduleDAGSDNodes * createVLIWDAGScheduler(SelectionDAGISel *IS, CodeGenOptLevel OptLevel)
createVLIWDAGScheduler - Scheduler for VLIW targets.
LLVM_ABI Printable printMBBReference(const MachineBasicBlock &MBB)
Prints a machine basic block reference.