10b57cec5SDimitry Andric //===---------------------------- GCNILPSched.cpp - -----------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "llvm/CodeGen/ScheduleDAG.h" 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric using namespace llvm; 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #define DEBUG_TYPE "machine-scheduler" 180b57cec5SDimitry Andric 190b57cec5SDimitry Andric namespace { 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric class GCNILPScheduler { 220b57cec5SDimitry Andric struct Candidate : ilist_node<Candidate> { 230b57cec5SDimitry Andric SUnit *SU; 240b57cec5SDimitry Andric 250b57cec5SDimitry Andric Candidate(SUnit *SU_) 260b57cec5SDimitry Andric : SU(SU_) {} 270b57cec5SDimitry Andric }; 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric SpecificBumpPtrAllocator<Candidate> Alloc; 300b57cec5SDimitry Andric typedef simple_ilist<Candidate> Queue; 310b57cec5SDimitry Andric Queue PendingQueue; 320b57cec5SDimitry Andric Queue AvailQueue; 330b57cec5SDimitry Andric unsigned CurQueueId = 0; 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric std::vector<unsigned> SUNumbers; 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric /// CurCycle - The current scheduler state corresponds to this cycle. 380b57cec5SDimitry Andric unsigned CurCycle = 0; 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric unsigned getNodePriority(const SUnit *SU) const; 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric const SUnit *pickBest(const SUnit *left, const SUnit *right); 430b57cec5SDimitry Andric Candidate* pickCandidate(); 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric void releasePending(); 460b57cec5SDimitry Andric void advanceToCycle(unsigned NextCycle); 470b57cec5SDimitry Andric void releasePredecessors(const SUnit* SU); 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric public: 500b57cec5SDimitry Andric std::vector<const SUnit*> schedule(ArrayRef<const SUnit*> TopRoots, 510b57cec5SDimitry Andric const ScheduleDAG &DAG); 520b57cec5SDimitry Andric }; 530b57cec5SDimitry Andric } // namespace 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric /// CalcNodeSethiUllmanNumber - Compute Sethi Ullman number. 560b57cec5SDimitry Andric /// Smaller number is the higher priority. 570b57cec5SDimitry Andric static unsigned 580b57cec5SDimitry Andric CalcNodeSethiUllmanNumber(const SUnit *SU, std::vector<unsigned> &SUNumbers) { 590b57cec5SDimitry Andric unsigned &SethiUllmanNumber = SUNumbers[SU->NodeNum]; 600b57cec5SDimitry Andric if (SethiUllmanNumber != 0) 610b57cec5SDimitry Andric return SethiUllmanNumber; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric unsigned Extra = 0; 640b57cec5SDimitry Andric for (const SDep &Pred : SU->Preds) { 650b57cec5SDimitry Andric if (Pred.isCtrl()) continue; // ignore chain preds 660b57cec5SDimitry Andric SUnit *PredSU = Pred.getSUnit(); 670b57cec5SDimitry Andric unsigned PredSethiUllman = CalcNodeSethiUllmanNumber(PredSU, SUNumbers); 680b57cec5SDimitry Andric if (PredSethiUllman > SethiUllmanNumber) { 690b57cec5SDimitry Andric SethiUllmanNumber = PredSethiUllman; 700b57cec5SDimitry Andric Extra = 0; 710b57cec5SDimitry Andric } 720b57cec5SDimitry Andric else if (PredSethiUllman == SethiUllmanNumber) 730b57cec5SDimitry Andric ++Extra; 740b57cec5SDimitry Andric } 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric SethiUllmanNumber += Extra; 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric if (SethiUllmanNumber == 0) 790b57cec5SDimitry Andric SethiUllmanNumber = 1; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric return SethiUllmanNumber; 820b57cec5SDimitry Andric } 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric // Lower priority means schedule further down. For bottom-up scheduling, lower 850b57cec5SDimitry Andric // priority SUs are scheduled before higher priority SUs. 860b57cec5SDimitry Andric unsigned GCNILPScheduler::getNodePriority(const SUnit *SU) const { 870b57cec5SDimitry Andric assert(SU->NodeNum < SUNumbers.size()); 880b57cec5SDimitry Andric if (SU->NumSuccs == 0 && SU->NumPreds != 0) 890b57cec5SDimitry Andric // If SU does not have a register use, i.e. it doesn't produce a value 900b57cec5SDimitry Andric // that would be consumed (e.g. store), then it terminates a chain of 910b57cec5SDimitry Andric // computation. Give it a large SethiUllman number so it will be 920b57cec5SDimitry Andric // scheduled right before its predecessors that it doesn't lengthen 930b57cec5SDimitry Andric // their live ranges. 940b57cec5SDimitry Andric return 0xffff; 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric if (SU->NumPreds == 0 && SU->NumSuccs != 0) 970b57cec5SDimitry Andric // If SU does not have a register def, schedule it close to its uses 980b57cec5SDimitry Andric // because it does not lengthen any live ranges. 990b57cec5SDimitry Andric return 0; 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric return SUNumbers[SU->NodeNum]; 1020b57cec5SDimitry Andric } 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric /// closestSucc - Returns the scheduled cycle of the successor which is 1050b57cec5SDimitry Andric /// closest to the current cycle. 1060b57cec5SDimitry Andric static unsigned closestSucc(const SUnit *SU) { 1070b57cec5SDimitry Andric unsigned MaxHeight = 0; 1080b57cec5SDimitry Andric for (const SDep &Succ : SU->Succs) { 1090b57cec5SDimitry Andric if (Succ.isCtrl()) continue; // ignore chain succs 1100b57cec5SDimitry Andric unsigned Height = Succ.getSUnit()->getHeight(); 1110b57cec5SDimitry Andric // If there are bunch of CopyToRegs stacked up, they should be considered 1120b57cec5SDimitry Andric // to be at the same position. 1130b57cec5SDimitry Andric if (Height > MaxHeight) 1140b57cec5SDimitry Andric MaxHeight = Height; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric return MaxHeight; 1170b57cec5SDimitry Andric } 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric /// calcMaxScratches - Returns an cost estimate of the worse case requirement 1200b57cec5SDimitry Andric /// for scratch registers, i.e. number of data dependencies. 1210b57cec5SDimitry Andric static unsigned calcMaxScratches(const SUnit *SU) { 1220b57cec5SDimitry Andric unsigned Scratches = 0; 1230b57cec5SDimitry Andric for (const SDep &Pred : SU->Preds) { 1240b57cec5SDimitry Andric if (Pred.isCtrl()) continue; // ignore chain preds 1250b57cec5SDimitry Andric Scratches++; 1260b57cec5SDimitry Andric } 1270b57cec5SDimitry Andric return Scratches; 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric // Return -1 if left has higher priority, 1 if right has higher priority. 1310b57cec5SDimitry Andric // Return 0 if latency-based priority is equivalent. 1320b57cec5SDimitry Andric static int BUCompareLatency(const SUnit *left, const SUnit *right) { 1330b57cec5SDimitry Andric // Scheduling an instruction that uses a VReg whose postincrement has not yet 1340b57cec5SDimitry Andric // been scheduled will induce a copy. Model this as an extra cycle of latency. 1350b57cec5SDimitry Andric int LHeight = (int)left->getHeight(); 1360b57cec5SDimitry Andric int RHeight = (int)right->getHeight(); 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric // If either node is scheduling for latency, sort them by height/depth 1390b57cec5SDimitry Andric // and latency. 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric // If neither instruction stalls (!LStall && !RStall) and HazardRecognizer 1420b57cec5SDimitry Andric // is enabled, grouping instructions by cycle, then its height is already 1430b57cec5SDimitry Andric // covered so only its depth matters. We also reach this point if both stall 1440b57cec5SDimitry Andric // but have the same height. 1450b57cec5SDimitry Andric if (LHeight != RHeight) 1460b57cec5SDimitry Andric return LHeight > RHeight ? 1 : -1; 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric int LDepth = left->getDepth(); 1490b57cec5SDimitry Andric int RDepth = right->getDepth(); 1500b57cec5SDimitry Andric if (LDepth != RDepth) { 1510b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << " Comparing latency of SU (" << left->NodeNum 1520b57cec5SDimitry Andric << ") depth " << LDepth << " vs SU (" << right->NodeNum 1530b57cec5SDimitry Andric << ") depth " << RDepth << "\n"); 1540b57cec5SDimitry Andric return LDepth < RDepth ? 1 : -1; 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric if (left->Latency != right->Latency) 1570b57cec5SDimitry Andric return left->Latency > right->Latency ? 1 : -1; 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric return 0; 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric 1620b57cec5SDimitry Andric const SUnit *GCNILPScheduler::pickBest(const SUnit *left, const SUnit *right) 1630b57cec5SDimitry Andric { 1640b57cec5SDimitry Andric // TODO: add register pressure lowering checks 1650b57cec5SDimitry Andric 1660b57cec5SDimitry Andric bool const DisableSchedCriticalPath = false; 1670b57cec5SDimitry Andric int MaxReorderWindow = 6; 1680b57cec5SDimitry Andric if (!DisableSchedCriticalPath) { 1690b57cec5SDimitry Andric int spread = (int)left->getDepth() - (int)right->getDepth(); 1700b57cec5SDimitry Andric if (std::abs(spread) > MaxReorderWindow) { 1710b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Depth of SU(" << left->NodeNum << "): " 1720b57cec5SDimitry Andric << left->getDepth() << " != SU(" << right->NodeNum 1730b57cec5SDimitry Andric << "): " << right->getDepth() << "\n"); 1740b57cec5SDimitry Andric return left->getDepth() < right->getDepth() ? right : left; 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric } 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric bool const DisableSchedHeight = false; 1790b57cec5SDimitry Andric if (!DisableSchedHeight && left->getHeight() != right->getHeight()) { 1800b57cec5SDimitry Andric int spread = (int)left->getHeight() - (int)right->getHeight(); 1810b57cec5SDimitry Andric if (std::abs(spread) > MaxReorderWindow) 1820b57cec5SDimitry Andric return left->getHeight() > right->getHeight() ? right : left; 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric // Prioritize by Sethi-Ulmann number and push CopyToReg nodes down. 1860b57cec5SDimitry Andric unsigned LPriority = getNodePriority(left); 1870b57cec5SDimitry Andric unsigned RPriority = getNodePriority(right); 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric if (LPriority != RPriority) 1900b57cec5SDimitry Andric return LPriority > RPriority ? right : left; 1910b57cec5SDimitry Andric 1920b57cec5SDimitry Andric // Try schedule def + use closer when Sethi-Ullman numbers are the same. 1930b57cec5SDimitry Andric // e.g. 1940b57cec5SDimitry Andric // t1 = op t2, c1 1950b57cec5SDimitry Andric // t3 = op t4, c2 1960b57cec5SDimitry Andric // 1970b57cec5SDimitry Andric // and the following instructions are both ready. 1980b57cec5SDimitry Andric // t2 = op c3 1990b57cec5SDimitry Andric // t4 = op c4 2000b57cec5SDimitry Andric // 2010b57cec5SDimitry Andric // Then schedule t2 = op first. 2020b57cec5SDimitry Andric // i.e. 2030b57cec5SDimitry Andric // t4 = op c4 2040b57cec5SDimitry Andric // t2 = op c3 2050b57cec5SDimitry Andric // t1 = op t2, c1 2060b57cec5SDimitry Andric // t3 = op t4, c2 2070b57cec5SDimitry Andric // 2080b57cec5SDimitry Andric // This creates more short live intervals. 2090b57cec5SDimitry Andric unsigned LDist = closestSucc(left); 2100b57cec5SDimitry Andric unsigned RDist = closestSucc(right); 2110b57cec5SDimitry Andric if (LDist != RDist) 2120b57cec5SDimitry Andric return LDist < RDist ? right : left; 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric // How many registers becomes live when the node is scheduled. 2150b57cec5SDimitry Andric unsigned LScratch = calcMaxScratches(left); 2160b57cec5SDimitry Andric unsigned RScratch = calcMaxScratches(right); 2170b57cec5SDimitry Andric if (LScratch != RScratch) 2180b57cec5SDimitry Andric return LScratch > RScratch ? right : left; 2190b57cec5SDimitry Andric 2200b57cec5SDimitry Andric bool const DisableSchedCycles = false; 2210b57cec5SDimitry Andric if (!DisableSchedCycles) { 2220b57cec5SDimitry Andric int result = BUCompareLatency(left, right); 2230b57cec5SDimitry Andric if (result != 0) 2240b57cec5SDimitry Andric return result > 0 ? right : left; 2250b57cec5SDimitry Andric return left; 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric else { 2280b57cec5SDimitry Andric if (left->getHeight() != right->getHeight()) 2290b57cec5SDimitry Andric return (left->getHeight() > right->getHeight()) ? right : left; 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric if (left->getDepth() != right->getDepth()) 2320b57cec5SDimitry Andric return (left->getDepth() < right->getDepth()) ? right : left; 2330b57cec5SDimitry Andric } 2340b57cec5SDimitry Andric 2350b57cec5SDimitry Andric assert(left->NodeQueueId && right->NodeQueueId && 2360b57cec5SDimitry Andric "NodeQueueId cannot be zero"); 2370b57cec5SDimitry Andric return (left->NodeQueueId > right->NodeQueueId) ? right : left; 2380b57cec5SDimitry Andric } 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric GCNILPScheduler::Candidate* GCNILPScheduler::pickCandidate() { 2410b57cec5SDimitry Andric if (AvailQueue.empty()) 2420b57cec5SDimitry Andric return nullptr; 2430b57cec5SDimitry Andric auto Best = AvailQueue.begin(); 2440b57cec5SDimitry Andric for (auto I = std::next(AvailQueue.begin()), E = AvailQueue.end(); I != E; ++I) { 2450b57cec5SDimitry Andric auto NewBestSU = pickBest(Best->SU, I->SU); 2460b57cec5SDimitry Andric if (NewBestSU != Best->SU) { 2470b57cec5SDimitry Andric assert(NewBestSU == I->SU); 2480b57cec5SDimitry Andric Best = I; 2490b57cec5SDimitry Andric } 2500b57cec5SDimitry Andric } 2510b57cec5SDimitry Andric return &*Best; 2520b57cec5SDimitry Andric } 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric void GCNILPScheduler::releasePending() { 2550b57cec5SDimitry Andric // Check to see if any of the pending instructions are ready to issue. If 2560b57cec5SDimitry Andric // so, add them to the available queue. 2570b57cec5SDimitry Andric for(auto I = PendingQueue.begin(), E = PendingQueue.end(); I != E;) { 2580b57cec5SDimitry Andric auto &C = *I++; 2590b57cec5SDimitry Andric if (C.SU->getHeight() <= CurCycle) { 2600b57cec5SDimitry Andric PendingQueue.remove(C); 2610b57cec5SDimitry Andric AvailQueue.push_back(C); 2620b57cec5SDimitry Andric C.SU->NodeQueueId = CurQueueId++; 2630b57cec5SDimitry Andric } 2640b57cec5SDimitry Andric } 2650b57cec5SDimitry Andric } 2660b57cec5SDimitry Andric 2670b57cec5SDimitry Andric /// Move the scheduler state forward by the specified number of Cycles. 2680b57cec5SDimitry Andric void GCNILPScheduler::advanceToCycle(unsigned NextCycle) { 2690b57cec5SDimitry Andric if (NextCycle <= CurCycle) 2700b57cec5SDimitry Andric return; 2710b57cec5SDimitry Andric CurCycle = NextCycle; 2720b57cec5SDimitry Andric releasePending(); 2730b57cec5SDimitry Andric } 2740b57cec5SDimitry Andric 2750b57cec5SDimitry Andric void GCNILPScheduler::releasePredecessors(const SUnit* SU) { 2760b57cec5SDimitry Andric for (const auto &PredEdge : SU->Preds) { 2770b57cec5SDimitry Andric auto PredSU = PredEdge.getSUnit(); 2780b57cec5SDimitry Andric if (PredEdge.isWeak()) 2790b57cec5SDimitry Andric continue; 2800b57cec5SDimitry Andric assert(PredSU->isBoundaryNode() || PredSU->NumSuccsLeft > 0); 2810b57cec5SDimitry Andric 2820b57cec5SDimitry Andric PredSU->setHeightToAtLeast(SU->getHeight() + PredEdge.getLatency()); 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric if (!PredSU->isBoundaryNode() && --PredSU->NumSuccsLeft == 0) 2850b57cec5SDimitry Andric PendingQueue.push_front(*new (Alloc.Allocate()) Candidate(PredSU)); 2860b57cec5SDimitry Andric } 2870b57cec5SDimitry Andric } 2880b57cec5SDimitry Andric 2890b57cec5SDimitry Andric std::vector<const SUnit*> 2900b57cec5SDimitry Andric GCNILPScheduler::schedule(ArrayRef<const SUnit*> BotRoots, 2910b57cec5SDimitry Andric const ScheduleDAG &DAG) { 2920b57cec5SDimitry Andric auto &SUnits = const_cast<ScheduleDAG&>(DAG).SUnits; 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric std::vector<SUnit> SUSavedCopy; 2950b57cec5SDimitry Andric SUSavedCopy.resize(SUnits.size()); 2960b57cec5SDimitry Andric 2970b57cec5SDimitry Andric // we cannot save only those fields we touch: some of them are private 2980b57cec5SDimitry Andric // so save units verbatim: this assumes SUnit should have value semantics 2990b57cec5SDimitry Andric for (const SUnit &SU : SUnits) 3000b57cec5SDimitry Andric SUSavedCopy[SU.NodeNum] = SU; 3010b57cec5SDimitry Andric 3020b57cec5SDimitry Andric SUNumbers.assign(SUnits.size(), 0); 3030b57cec5SDimitry Andric for (const SUnit &SU : SUnits) 3040b57cec5SDimitry Andric CalcNodeSethiUllmanNumber(&SU, SUNumbers); 3050b57cec5SDimitry Andric 306*bdd1243dSDimitry Andric for (const auto *SU : BotRoots) { 3070b57cec5SDimitry Andric AvailQueue.push_back( 3080b57cec5SDimitry Andric *new (Alloc.Allocate()) Candidate(const_cast<SUnit*>(SU))); 3090b57cec5SDimitry Andric } 3100b57cec5SDimitry Andric releasePredecessors(&DAG.ExitSU); 3110b57cec5SDimitry Andric 3120b57cec5SDimitry Andric std::vector<const SUnit*> Schedule; 3130b57cec5SDimitry Andric Schedule.reserve(SUnits.size()); 3140b57cec5SDimitry Andric while (true) { 3150b57cec5SDimitry Andric if (AvailQueue.empty() && !PendingQueue.empty()) { 3160b57cec5SDimitry Andric auto EarliestSU = std::min_element( 3170b57cec5SDimitry Andric PendingQueue.begin(), PendingQueue.end(), 3180b57cec5SDimitry Andric [=](const Candidate& C1, const Candidate& C2) { 3190b57cec5SDimitry Andric return C1.SU->getHeight() < C2.SU->getHeight(); 3200b57cec5SDimitry Andric })->SU; 3210b57cec5SDimitry Andric advanceToCycle(std::max(CurCycle + 1, EarliestSU->getHeight())); 3220b57cec5SDimitry Andric } 3230b57cec5SDimitry Andric if (AvailQueue.empty()) 3240b57cec5SDimitry Andric break; 3250b57cec5SDimitry Andric 3260b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\n=== Picking candidate\n" 3270b57cec5SDimitry Andric "Ready queue:"; 3280b57cec5SDimitry Andric for (auto &C 3290b57cec5SDimitry Andric : AvailQueue) dbgs() 3300b57cec5SDimitry Andric << ' ' << C.SU->NodeNum; 3310b57cec5SDimitry Andric dbgs() << '\n';); 3320b57cec5SDimitry Andric 3330b57cec5SDimitry Andric auto C = pickCandidate(); 3340b57cec5SDimitry Andric assert(C); 3350b57cec5SDimitry Andric AvailQueue.remove(*C); 3360b57cec5SDimitry Andric auto SU = C->SU; 3370b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Selected "; DAG.dumpNode(*SU)); 3380b57cec5SDimitry Andric 3390b57cec5SDimitry Andric advanceToCycle(SU->getHeight()); 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric releasePredecessors(SU); 3420b57cec5SDimitry Andric Schedule.push_back(SU); 3430b57cec5SDimitry Andric SU->isScheduled = true; 3440b57cec5SDimitry Andric } 3450b57cec5SDimitry Andric assert(SUnits.size() == Schedule.size()); 3460b57cec5SDimitry Andric 3470b57cec5SDimitry Andric std::reverse(Schedule.begin(), Schedule.end()); 3480b57cec5SDimitry Andric 3490b57cec5SDimitry Andric // restore units 3500b57cec5SDimitry Andric for (auto &SU : SUnits) 3510b57cec5SDimitry Andric SU = SUSavedCopy[SU.NodeNum]; 3520b57cec5SDimitry Andric 3530b57cec5SDimitry Andric return Schedule; 3540b57cec5SDimitry Andric } 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric namespace llvm { 3570b57cec5SDimitry Andric std::vector<const SUnit*> makeGCNILPScheduler(ArrayRef<const SUnit*> BotRoots, 3580b57cec5SDimitry Andric const ScheduleDAG &DAG) { 3590b57cec5SDimitry Andric GCNILPScheduler S; 3600b57cec5SDimitry Andric return S.schedule(BotRoots, DAG); 3610b57cec5SDimitry Andric } 3620b57cec5SDimitry Andric } 363