//===---- ScheduleDAGList.cpp - Implement a list scheduler for isel DAG ---===// // // The LLVM Compiler Infrastructure // // This file was developed by Evan Cheng and is distributed under the // University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This implements a simple two pass scheduler. The first pass attempts to push // backward any lengthy instructions and critical paths. The second pass packs // instructions into semi-optimal time slots. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "sched" #include "llvm/CodeGen/ScheduleDAG.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetInstrInfo.h" #include "llvm/Support/Debug.h" #include #include #include #include using namespace llvm; namespace { /// SUnit - Scheduling unit. It's an wrapper around either a single SDNode or a /// group of nodes flagged together. struct SUnit { SDNode *Node; // Representative node. std::vector FlaggedNodes; // All nodes flagged to Node. std::vector Preds; // All real predecessors. std::vector ChainPreds; // All chain predecessors. std::vector Succs; // All real successors. std::vector ChainSuccs; // All chain successors. int NumPredsLeft; // # of preds not scheduled. int NumSuccsLeft; // # of succs not scheduled. int Priority1; // Scheduling priority 1. int Priority2; // Scheduling priority 2. unsigned Latency; // Node latency. unsigned CycleBound; // Upper/lower cycle to be scheduled at. unsigned Slot; // Cycle node is scheduled at. SUnit *Next; SUnit(SDNode *node) : Node(node), NumPredsLeft(0), NumSuccsLeft(0), Priority1(INT_MIN), Priority2(INT_MIN), Latency(0), CycleBound(0), Slot(0), Next(NULL) {} void dump(const SelectionDAG *G, bool All=true) const; }; void SUnit::dump(const SelectionDAG *G, bool All) const { std::cerr << "SU: "; Node->dump(G); std::cerr << "\n"; if (FlaggedNodes.size() != 0) { for (unsigned i = 0, e = FlaggedNodes.size(); i != e; i++) { std::cerr << " "; FlaggedNodes[i]->dump(G); std::cerr << "\n"; } } if (All) { std::cerr << "# preds left : " << NumPredsLeft << "\n"; std::cerr << "# succs left : " << NumSuccsLeft << "\n"; std::cerr << "Latency : " << Latency << "\n"; std::cerr << "Priority : " << Priority1 << " , " << Priority2 << "\n"; if (Preds.size() != 0) { std::cerr << "Predecessors :\n"; for (unsigned i = 0, e = Preds.size(); i != e; i++) { std::cerr << " "; Preds[i]->dump(G, false); } } if (ChainPreds.size() != 0) { std::cerr << "Chained Preds :\n"; for (unsigned i = 0, e = ChainPreds.size(); i != e; i++) { std::cerr << " "; ChainPreds[i]->dump(G, false); } } if (Succs.size() != 0) { std::cerr << "Successors :\n"; for (unsigned i = 0, e = Succs.size(); i != e; i++) { std::cerr << " "; Succs[i]->dump(G, false); } } if (ChainSuccs.size() != 0) { std::cerr << "Chained succs :\n"; for (unsigned i = 0, e = ChainSuccs.size(); i != e; i++) { std::cerr << " "; ChainSuccs[i]->dump(G, false); } } } } /// Sorting functions for the Available queue. struct ls_rr_sort : public std::binary_function { bool operator()(const SUnit* left, const SUnit* right) const { if (left->Priority1 > right->Priority1) { return true; } else if (left->Priority1 == right->Priority1) { unsigned lf = left->FlaggedNodes.size(); unsigned rf = right->FlaggedNodes.size(); if (lf > rf) return true; else if (lf == rf) { if (left->Priority2 > right->Priority2) return true; else if (left->Priority2 == right->Priority2) { if (left->CycleBound > right->CycleBound) return true; else return left->Node->getNodeDepth() < right->Node->getNodeDepth(); } } } return false; } }; /// ScheduleDAGList - List scheduler. class ScheduleDAGList : public ScheduleDAG { private: // SDNode to SUnit mapping (many to one). std::map SUnitMap; // Available queue. std::priority_queue, ls_rr_sort> Available; // The schedule. std::vector Sequence; // Current scheduling cycle. unsigned CurrCycle; // First and last SUnit created. SUnit *HeadSUnit, *TailSUnit; public: ScheduleDAGList(SelectionDAG &dag, MachineBasicBlock *bb, const TargetMachine &tm) : ScheduleDAG(listSchedulingBURR, dag, bb, tm), CurrCycle(0), HeadSUnit(NULL), TailSUnit(NULL) {}; ~ScheduleDAGList() { SUnit *SU = HeadSUnit; while (SU) { SUnit *NextSU = SU->Next; delete SU; SU = NextSU; } } void Schedule(); void dump() const; private: SUnit *NewSUnit(SDNode *N); void ReleasePred(SUnit *PredSU); void ScheduleNode(SUnit *SU); int CalcNodePriority(SUnit *SU); void CalculatePriorities(); void ListSchedule(); void BuildSchedUnits(); void EmitSchedule(); }; } // end namespace /// NewSUnit - Creates a new SUnit and return a ptr to it. SUnit *ScheduleDAGList::NewSUnit(SDNode *N) { SUnit *CurrSUnit = new SUnit(N); if (HeadSUnit == NULL) HeadSUnit = CurrSUnit; if (TailSUnit != NULL) TailSUnit->Next = CurrSUnit; TailSUnit = CurrSUnit; return CurrSUnit; } /// ReleasePred - Decrement the NumSuccsLeft count of a predecessor. Add it to /// the Available queue is the count reaches zero. Also update its cycle bound. void ScheduleDAGList::ReleasePred(SUnit *PredSU) { SDNode *PredNode = PredSU->Node; PredSU->NumSuccsLeft--; if (PredSU->NumSuccsLeft == 0) { // EntryToken has to go last! if (PredNode->getOpcode() != ISD::EntryToken) Available.push(PredSU); } else if (PredSU->NumSuccsLeft < 0) { #ifndef NDEBUG std::cerr << "*** List scheduling failed! ***\n"; PredSU->dump(&DAG); std::cerr << " has been released too many times!\n"; assert(0); #endif } // FIXME: the distance between two nodes is not always == the predecessor's // latency. For example, the reader can very well read the register written // by the predecessor later than the issue cycle. It also depends on the // interrupt model (drain vs. freeze). PredSU->CycleBound = std::max(PredSU->CycleBound, CurrCycle + PredSU->Latency); } /// ScheduleNode - Add the node to the schedule. Decrement the pending count of /// its predecessors. If a predecessor pending count is zero, add it to the /// Available queue. void ScheduleDAGList::ScheduleNode(SUnit *SU) { Sequence.push_back(SU); SU->Slot = CurrCycle; // Bottom up: release predecessors for (unsigned i = 0, e = SU->Preds.size(); i != e; i++) ReleasePred(SU->Preds[i]); for (unsigned i = 0, e = SU->ChainPreds.size(); i != e; i++) ReleasePred(SU->ChainPreds[i]); CurrCycle++; } /// isReady - True if node's lower cycle bound is less or equal to the current /// scheduling cycle. Always true if all nodes have uniform latency 1. static inline bool isReady(SUnit *SU, unsigned CurrCycle) { return SU->CycleBound <= CurrCycle; } /// ListSchedule - The main loop of list scheduling. void ScheduleDAGList::ListSchedule() { // Add root to Available queue SUnit *Root = SUnitMap[DAG.getRoot().Val]; Available.push(Root); // While Available queue is not empty, grab the node with the highest // priority. If it is not ready put it back. Schedule the node. std::vector NotReady; while (!Available.empty()) { SUnit *CurrNode = Available.top(); Available.pop(); NotReady.clear(); while (!isReady(CurrNode, CurrCycle)) { NotReady.push_back(CurrNode); CurrNode = Available.top(); Available.pop(); } for (unsigned i = 0, e = NotReady.size(); i != e; ++i) Available.push(NotReady[i]); DEBUG(std::cerr << "*** Scheduling: "); DEBUG(CurrNode->dump(&DAG, false)); ScheduleNode(CurrNode); } // Add entry node last if (DAG.getEntryNode().Val != DAG.getRoot().Val) { SUnit *Entry = SUnitMap[DAG.getEntryNode().Val]; Entry->Slot = CurrCycle; Sequence.push_back(Entry); } #ifndef NDEBUG bool AnyNotSched = false; for (SUnit *SU = HeadSUnit; SU != NULL; SU = SU->Next) { if (SU->NumSuccsLeft != 0) { if (!AnyNotSched) std::cerr << "*** List scheduling failed! ***\n"; SU->dump(&DAG); std::cerr << "has not been scheduled!\n"; AnyNotSched = true; } } assert(!AnyNotSched); #endif // Reverse the order if it is bottom up. std::reverse(Sequence.begin(), Sequence.end()); DEBUG(std::cerr << "*** Final schedule ***\n"); DEBUG(dump()); DEBUG(std::cerr << "\n"); } /// CalcNodePriority - Priority 1 is just the number of live range genned - number /// of live range killed. Priority 2 is the Sethi Ullman number. It returns /// priority 2 since it is calculated recursively. /// Smaller number is the higher priority in both cases. int ScheduleDAGList::CalcNodePriority(SUnit *SU) { if (SU->Priority2 != INT_MIN) return SU->Priority2; SU->Priority1 = SU->Preds.size() - SU->Succs.size(); if (SU->Preds.size() == 0) { SU->Priority2 = 1; } else { int Extra = 0; for (unsigned i = 0, e = SU->Preds.size(); i != e; i++) { SUnit *PredSU = SU->Preds[i]; int PredPriority = CalcNodePriority(PredSU); if (PredPriority > SU->Priority2) { SU->Priority2 = PredPriority; Extra = 0; } else if (PredPriority == SU->Priority2) Extra++; } if (SU->Node->getOpcode() != ISD::TokenFactor) SU->Priority2 += Extra; else SU->Priority2 = (Extra == 1) ? 0 : Extra-1; } return SU->Priority2; } /// CalculatePriorities - Calculate priorities of all scheduling units. void ScheduleDAGList::CalculatePriorities() { for (SUnit *SU = HeadSUnit; SU != NULL; SU = SU->Next) { // FIXME: assumes uniform latency for now. SU->Latency = 1; (void)CalcNodePriority(SU); DEBUG(SU->dump(&DAG)); DEBUG(std::cerr << "\n"); } } void ScheduleDAGList::BuildSchedUnits() { // Pass 1: create the SUnit's. for (unsigned i = 0, NC = NodeCount; i < NC; i++) { NodeInfo *NI = &Info[i]; SDNode *N = NI->Node; if (isPassiveNode(N)) continue; SUnit *SU; if (NI->isInGroup()) { if (NI != NI->Group->getBottom()) // Bottom up, so only look at bottom continue; // node of the NodeGroup SU = NewSUnit(N); // Find the flagged nodes. SDOperand FlagOp = N->getOperand(N->getNumOperands() - 1); SDNode *Flag = FlagOp.Val; unsigned ResNo = FlagOp.ResNo; while (Flag->getValueType(ResNo) == MVT::Flag) { NodeInfo *FNI = getNI(Flag); assert(FNI->Group == NI->Group); SU->FlaggedNodes.insert(SU->FlaggedNodes.begin(), Flag); SUnitMap[Flag] = SU; FlagOp = Flag->getOperand(Flag->getNumOperands() - 1); Flag = FlagOp.Val; ResNo = FlagOp.ResNo; } } else { SU = NewSUnit(N); } SUnitMap[N] = SU; } // Pass 2: add the preds, succs, etc. for (SUnit *SU = HeadSUnit; SU != NULL; SU = SU->Next) { SDNode *N = SU->Node; NodeInfo *NI = getNI(N); if (NI->isInGroup()) { // Find all predecessors (of the group). NodeGroupOpIterator NGOI(NI); while (!NGOI.isEnd()) { SDOperand Op = NGOI.next(); SDNode *OpN = Op.Val; MVT::ValueType VT = OpN->getValueType(Op.ResNo); NodeInfo *OpNI = getNI(OpN); if (OpNI->Group != NI->Group && !isPassiveNode(OpN)) { assert(VT != MVT::Flag); SUnit *OpSU = SUnitMap[OpN]; if (VT == MVT::Other) { SU ->ChainPreds.push_back(OpSU); OpSU->ChainSuccs.push_back(SU); } else { SU ->Preds.push_back(OpSU); OpSU->Succs.push_back(SU); } SU->NumPredsLeft++; OpSU->NumSuccsLeft++; } } } else { // Find node predecessors. for (unsigned j = 0, e = N->getNumOperands(); j != e; j++) { SDOperand Op = N->getOperand(j); SDNode *OpN = Op.Val; MVT::ValueType VT = OpN->getValueType(Op.ResNo); if (!isPassiveNode(OpN)) { assert(VT != MVT::Flag); SUnit *OpSU = SUnitMap[OpN]; if (VT == MVT::Other) { SU ->ChainPreds.push_back(OpSU); OpSU->ChainSuccs.push_back(SU); } else { SU ->Preds.push_back(OpSU); OpSU->Succs.push_back(SU); } SU->NumPredsLeft++; OpSU->NumSuccsLeft++; } } } } } /// EmitSchedule - Emit the machine code in scheduled order. void ScheduleDAGList::EmitSchedule() { for (unsigned i = 0, e = Sequence.size(); i != e; i++) { SDNode *N; SUnit *SU = Sequence[i]; for (unsigned j = 0, ee = SU->FlaggedNodes.size(); j != ee; j++) { N = SU->FlaggedNodes[j]; EmitNode(getNI(N)); } EmitNode(getNI(SU->Node)); } } /// dump - dump the schedule. void ScheduleDAGList::dump() const { for (unsigned i = 0, e = Sequence.size(); i != e; i++) { SUnit *SU = Sequence[i]; SU->dump(&DAG, false); } } /// Schedule - Schedule the DAG using list scheduling. /// FIXME: Right now it only supports the burr (bottom up register reducing) /// heuristic. void ScheduleDAGList::Schedule() { DEBUG(std::cerr << "********** List Scheduling **********\n"); // Build scheduling units. BuildSchedUnits(); // Calculate node prirorities. CalculatePriorities(); // Execute the actual scheduling loop. ListSchedule(); // Emit in scheduled order EmitSchedule(); } llvm::ScheduleDAG* llvm::createBURRListDAGScheduler(SelectionDAG &DAG, MachineBasicBlock *BB) { return new ScheduleDAGList(DAG, BB, DAG.getTarget()); }