109467b48Spatrick //===- ScheduleDAGVLIW.cpp - SelectionDAG list scheduler for VLIW -*- C++ -*-=//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This implements a top-down list scheduler, using standard algorithms.
1009467b48Spatrick // The basic approach uses a priority queue of available nodes to schedule.
1109467b48Spatrick // One at a time, nodes are taken from the priority queue (thus in priority
1209467b48Spatrick // order), checked for legality to schedule, and emitted if legal.
1309467b48Spatrick //
1409467b48Spatrick // Nodes may not be legal to schedule either due to structural hazards (e.g.
1509467b48Spatrick // pipeline or resource constraints) or because an input to the instruction has
1609467b48Spatrick // not completed execution.
1709467b48Spatrick //
1809467b48Spatrick //===----------------------------------------------------------------------===//
1909467b48Spatrick
2009467b48Spatrick #include "ScheduleDAGSDNodes.h"
2109467b48Spatrick #include "llvm/ADT/Statistic.h"
2209467b48Spatrick #include "llvm/CodeGen/ResourcePriorityQueue.h"
2309467b48Spatrick #include "llvm/CodeGen/ScheduleHazardRecognizer.h"
2409467b48Spatrick #include "llvm/CodeGen/SchedulerRegistry.h"
2509467b48Spatrick #include "llvm/CodeGen/SelectionDAGISel.h"
2609467b48Spatrick #include "llvm/CodeGen/TargetInstrInfo.h"
2709467b48Spatrick #include "llvm/CodeGen/TargetSubtargetInfo.h"
2809467b48Spatrick #include "llvm/Support/Debug.h"
2909467b48Spatrick #include "llvm/Support/ErrorHandling.h"
3009467b48Spatrick #include "llvm/Support/raw_ostream.h"
3109467b48Spatrick using namespace llvm;
3209467b48Spatrick
3309467b48Spatrick #define DEBUG_TYPE "pre-RA-sched"
3409467b48Spatrick
3509467b48Spatrick STATISTIC(NumNoops , "Number of noops inserted");
3609467b48Spatrick STATISTIC(NumStalls, "Number of pipeline stalls");
3709467b48Spatrick
3809467b48Spatrick static RegisterScheduler
3909467b48Spatrick VLIWScheduler("vliw-td", "VLIW scheduler",
4009467b48Spatrick createVLIWDAGScheduler);
4109467b48Spatrick
4209467b48Spatrick namespace {
4309467b48Spatrick //===----------------------------------------------------------------------===//
4409467b48Spatrick /// ScheduleDAGVLIW - The actual DFA list scheduler implementation. This
4509467b48Spatrick /// supports / top-down scheduling.
4609467b48Spatrick ///
4709467b48Spatrick class ScheduleDAGVLIW : public ScheduleDAGSDNodes {
4809467b48Spatrick private:
4909467b48Spatrick /// AvailableQueue - The priority queue to use for the available SUnits.
5009467b48Spatrick ///
5109467b48Spatrick SchedulingPriorityQueue *AvailableQueue;
5209467b48Spatrick
5309467b48Spatrick /// PendingQueue - This contains all of the instructions whose operands have
5409467b48Spatrick /// been issued, but their results are not ready yet (due to the latency of
5509467b48Spatrick /// the operation). Once the operands become available, the instruction is
5609467b48Spatrick /// added to the AvailableQueue.
5709467b48Spatrick std::vector<SUnit*> PendingQueue;
5809467b48Spatrick
5909467b48Spatrick /// HazardRec - The hazard recognizer to use.
6009467b48Spatrick ScheduleHazardRecognizer *HazardRec;
6109467b48Spatrick
6209467b48Spatrick /// AA - AAResults for making memory reference queries.
6309467b48Spatrick AAResults *AA;
6409467b48Spatrick
6509467b48Spatrick public:
ScheduleDAGVLIW(MachineFunction & mf,AAResults * aa,SchedulingPriorityQueue * availqueue)6609467b48Spatrick ScheduleDAGVLIW(MachineFunction &mf, AAResults *aa,
6709467b48Spatrick SchedulingPriorityQueue *availqueue)
6809467b48Spatrick : ScheduleDAGSDNodes(mf), AvailableQueue(availqueue), AA(aa) {
6909467b48Spatrick const TargetSubtargetInfo &STI = mf.getSubtarget();
7009467b48Spatrick HazardRec = STI.getInstrInfo()->CreateTargetHazardRecognizer(&STI, this);
7109467b48Spatrick }
7209467b48Spatrick
~ScheduleDAGVLIW()7309467b48Spatrick ~ScheduleDAGVLIW() override {
7409467b48Spatrick delete HazardRec;
7509467b48Spatrick delete AvailableQueue;
7609467b48Spatrick }
7709467b48Spatrick
7809467b48Spatrick void Schedule() override;
7909467b48Spatrick
8009467b48Spatrick private:
8109467b48Spatrick void releaseSucc(SUnit *SU, const SDep &D);
8209467b48Spatrick void releaseSuccessors(SUnit *SU);
8309467b48Spatrick void scheduleNodeTopDown(SUnit *SU, unsigned CurCycle);
8409467b48Spatrick void listScheduleTopDown();
8509467b48Spatrick };
8609467b48Spatrick } // end anonymous namespace
8709467b48Spatrick
8809467b48Spatrick /// Schedule - Schedule the DAG using list scheduling.
Schedule()8909467b48Spatrick void ScheduleDAGVLIW::Schedule() {
9009467b48Spatrick LLVM_DEBUG(dbgs() << "********** List Scheduling " << printMBBReference(*BB)
9109467b48Spatrick << " '" << BB->getName() << "' **********\n");
9209467b48Spatrick
9309467b48Spatrick // Build the scheduling graph.
9409467b48Spatrick BuildSchedGraph(AA);
9509467b48Spatrick
9609467b48Spatrick AvailableQueue->initNodes(SUnits);
9709467b48Spatrick
9809467b48Spatrick listScheduleTopDown();
9909467b48Spatrick
10009467b48Spatrick AvailableQueue->releaseState();
10109467b48Spatrick }
10209467b48Spatrick
10309467b48Spatrick //===----------------------------------------------------------------------===//
10409467b48Spatrick // Top-Down Scheduling
10509467b48Spatrick //===----------------------------------------------------------------------===//
10609467b48Spatrick
10709467b48Spatrick /// releaseSucc - Decrement the NumPredsLeft count of a successor. Add it to
10809467b48Spatrick /// the PendingQueue if the count reaches zero. Also update its cycle bound.
releaseSucc(SUnit * SU,const SDep & D)10909467b48Spatrick void ScheduleDAGVLIW::releaseSucc(SUnit *SU, const SDep &D) {
11009467b48Spatrick SUnit *SuccSU = D.getSUnit();
11109467b48Spatrick
11209467b48Spatrick #ifndef NDEBUG
11309467b48Spatrick if (SuccSU->NumPredsLeft == 0) {
11409467b48Spatrick dbgs() << "*** Scheduling failed! ***\n";
11509467b48Spatrick dumpNode(*SuccSU);
11609467b48Spatrick dbgs() << " has been released too many times!\n";
11709467b48Spatrick llvm_unreachable(nullptr);
11809467b48Spatrick }
11909467b48Spatrick #endif
12009467b48Spatrick assert(!D.isWeak() && "unexpected artificial DAG edge");
12109467b48Spatrick
12209467b48Spatrick --SuccSU->NumPredsLeft;
12309467b48Spatrick
12409467b48Spatrick SuccSU->setDepthToAtLeast(SU->getDepth() + D.getLatency());
12509467b48Spatrick
12609467b48Spatrick // If all the node's predecessors are scheduled, this node is ready
12709467b48Spatrick // to be scheduled. Ignore the special ExitSU node.
12809467b48Spatrick if (SuccSU->NumPredsLeft == 0 && SuccSU != &ExitSU) {
12909467b48Spatrick PendingQueue.push_back(SuccSU);
13009467b48Spatrick }
13109467b48Spatrick }
13209467b48Spatrick
releaseSuccessors(SUnit * SU)13309467b48Spatrick void ScheduleDAGVLIW::releaseSuccessors(SUnit *SU) {
13409467b48Spatrick // Top down: release successors.
13573471bf0Spatrick for (SDep &Succ : SU->Succs) {
13673471bf0Spatrick assert(!Succ.isAssignedRegDep() &&
13709467b48Spatrick "The list-td scheduler doesn't yet support physreg dependencies!");
13809467b48Spatrick
13973471bf0Spatrick releaseSucc(SU, Succ);
14009467b48Spatrick }
14109467b48Spatrick }
14209467b48Spatrick
14309467b48Spatrick /// scheduleNodeTopDown - Add the node to the schedule. Decrement the pending
14409467b48Spatrick /// count of its successors. If a successor pending count is zero, add it to
14509467b48Spatrick /// the Available queue.
scheduleNodeTopDown(SUnit * SU,unsigned CurCycle)14609467b48Spatrick void ScheduleDAGVLIW::scheduleNodeTopDown(SUnit *SU, unsigned CurCycle) {
14709467b48Spatrick LLVM_DEBUG(dbgs() << "*** Scheduling [" << CurCycle << "]: ");
14809467b48Spatrick LLVM_DEBUG(dumpNode(*SU));
14909467b48Spatrick
15009467b48Spatrick Sequence.push_back(SU);
15109467b48Spatrick assert(CurCycle >= SU->getDepth() && "Node scheduled above its depth!");
15209467b48Spatrick SU->setDepthToAtLeast(CurCycle);
15309467b48Spatrick
15409467b48Spatrick releaseSuccessors(SU);
15509467b48Spatrick SU->isScheduled = true;
15609467b48Spatrick AvailableQueue->scheduledNode(SU);
15709467b48Spatrick }
15809467b48Spatrick
15909467b48Spatrick /// listScheduleTopDown - The main loop of list scheduling for top-down
16009467b48Spatrick /// schedulers.
listScheduleTopDown()16109467b48Spatrick void ScheduleDAGVLIW::listScheduleTopDown() {
16209467b48Spatrick unsigned CurCycle = 0;
16309467b48Spatrick
16409467b48Spatrick // Release any successors of the special Entry node.
16509467b48Spatrick releaseSuccessors(&EntrySU);
16609467b48Spatrick
16709467b48Spatrick // All leaves to AvailableQueue.
168*d415bd75Srobert for (SUnit &SU : SUnits) {
16909467b48Spatrick // It is available if it has no predecessors.
170*d415bd75Srobert if (SU.Preds.empty()) {
171*d415bd75Srobert AvailableQueue->push(&SU);
172*d415bd75Srobert SU.isAvailable = true;
17309467b48Spatrick }
17409467b48Spatrick }
17509467b48Spatrick
17609467b48Spatrick // While AvailableQueue is not empty, grab the node with the highest
17709467b48Spatrick // priority. If it is not ready put it back. Schedule the node.
17809467b48Spatrick std::vector<SUnit*> NotReady;
17909467b48Spatrick Sequence.reserve(SUnits.size());
18009467b48Spatrick while (!AvailableQueue->empty() || !PendingQueue.empty()) {
18109467b48Spatrick // Check to see if any of the pending instructions are ready to issue. If
18209467b48Spatrick // so, add them to the available queue.
18309467b48Spatrick for (unsigned i = 0, e = PendingQueue.size(); i != e; ++i) {
18409467b48Spatrick if (PendingQueue[i]->getDepth() == CurCycle) {
18509467b48Spatrick AvailableQueue->push(PendingQueue[i]);
18609467b48Spatrick PendingQueue[i]->isAvailable = true;
18709467b48Spatrick PendingQueue[i] = PendingQueue.back();
18809467b48Spatrick PendingQueue.pop_back();
18909467b48Spatrick --i; --e;
19009467b48Spatrick }
19109467b48Spatrick else {
19209467b48Spatrick assert(PendingQueue[i]->getDepth() > CurCycle && "Negative latency?");
19309467b48Spatrick }
19409467b48Spatrick }
19509467b48Spatrick
19609467b48Spatrick // If there are no instructions available, don't try to issue anything, and
19709467b48Spatrick // don't advance the hazard recognizer.
19809467b48Spatrick if (AvailableQueue->empty()) {
19909467b48Spatrick // Reset DFA state.
20009467b48Spatrick AvailableQueue->scheduledNode(nullptr);
20109467b48Spatrick ++CurCycle;
20209467b48Spatrick continue;
20309467b48Spatrick }
20409467b48Spatrick
20509467b48Spatrick SUnit *FoundSUnit = nullptr;
20609467b48Spatrick
20709467b48Spatrick bool HasNoopHazards = false;
20809467b48Spatrick while (!AvailableQueue->empty()) {
20909467b48Spatrick SUnit *CurSUnit = AvailableQueue->pop();
21009467b48Spatrick
21109467b48Spatrick ScheduleHazardRecognizer::HazardType HT =
21209467b48Spatrick HazardRec->getHazardType(CurSUnit, 0/*no stalls*/);
21309467b48Spatrick if (HT == ScheduleHazardRecognizer::NoHazard) {
21409467b48Spatrick FoundSUnit = CurSUnit;
21509467b48Spatrick break;
21609467b48Spatrick }
21709467b48Spatrick
21809467b48Spatrick // Remember if this is a noop hazard.
21909467b48Spatrick HasNoopHazards |= HT == ScheduleHazardRecognizer::NoopHazard;
22009467b48Spatrick
22109467b48Spatrick NotReady.push_back(CurSUnit);
22209467b48Spatrick }
22309467b48Spatrick
22409467b48Spatrick // Add the nodes that aren't ready back onto the available list.
22509467b48Spatrick if (!NotReady.empty()) {
22609467b48Spatrick AvailableQueue->push_all(NotReady);
22709467b48Spatrick NotReady.clear();
22809467b48Spatrick }
22909467b48Spatrick
23009467b48Spatrick // If we found a node to schedule, do it now.
23109467b48Spatrick if (FoundSUnit) {
23209467b48Spatrick scheduleNodeTopDown(FoundSUnit, CurCycle);
23309467b48Spatrick HazardRec->EmitInstruction(FoundSUnit);
23409467b48Spatrick
23509467b48Spatrick // If this is a pseudo-op node, we don't want to increment the current
23609467b48Spatrick // cycle.
23709467b48Spatrick if (FoundSUnit->Latency) // Don't increment CurCycle for pseudo-ops!
23809467b48Spatrick ++CurCycle;
23909467b48Spatrick } else if (!HasNoopHazards) {
24009467b48Spatrick // Otherwise, we have a pipeline stall, but no other problem, just advance
24109467b48Spatrick // the current cycle and try again.
24209467b48Spatrick LLVM_DEBUG(dbgs() << "*** Advancing cycle, no work to do\n");
24309467b48Spatrick HazardRec->AdvanceCycle();
24409467b48Spatrick ++NumStalls;
24509467b48Spatrick ++CurCycle;
24609467b48Spatrick } else {
24709467b48Spatrick // Otherwise, we have no instructions to issue and we have instructions
24809467b48Spatrick // that will fault if we don't do this right. This is the case for
24909467b48Spatrick // processors without pipeline interlocks and other cases.
25009467b48Spatrick LLVM_DEBUG(dbgs() << "*** Emitting noop\n");
25109467b48Spatrick HazardRec->EmitNoop();
25209467b48Spatrick Sequence.push_back(nullptr); // NULL here means noop
25309467b48Spatrick ++NumNoops;
25409467b48Spatrick ++CurCycle;
25509467b48Spatrick }
25609467b48Spatrick }
25709467b48Spatrick
25809467b48Spatrick #ifndef NDEBUG
25909467b48Spatrick VerifyScheduledSequence(/*isBottomUp=*/false);
26009467b48Spatrick #endif
26109467b48Spatrick }
26209467b48Spatrick
26309467b48Spatrick //===----------------------------------------------------------------------===//
26409467b48Spatrick // Public Constructor Functions
26509467b48Spatrick //===----------------------------------------------------------------------===//
26609467b48Spatrick
26709467b48Spatrick /// createVLIWDAGScheduler - This creates a top-down list scheduler.
26809467b48Spatrick ScheduleDAGSDNodes *
createVLIWDAGScheduler(SelectionDAGISel * IS,CodeGenOpt::Level)26909467b48Spatrick llvm::createVLIWDAGScheduler(SelectionDAGISel *IS, CodeGenOpt::Level) {
27009467b48Spatrick return new ScheduleDAGVLIW(*IS->MF, IS->AA, new ResourcePriorityQueue(IS));
27109467b48Spatrick }
272