11d09925bSvporpo //===- Scheduler.cpp ------------------------------------------------------===// 21d09925bSvporpo // 31d09925bSvporpo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41d09925bSvporpo // See https://llvm.org/LICENSE.txt for license information. 51d09925bSvporpo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61d09925bSvporpo // 71d09925bSvporpo //===----------------------------------------------------------------------===// 81d09925bSvporpo 91d09925bSvporpo #include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h" 10f7ef7b2fSvporpo #include "llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h" 111d09925bSvporpo 121d09925bSvporpo namespace llvm::sandboxir { 131d09925bSvporpo 141d09925bSvporpo // TODO: Check if we can cache top/bottom to reduce compile-time. 151d09925bSvporpo DGNode *SchedBundle::getTop() const { 161d09925bSvporpo DGNode *TopN = Nodes.front(); 171d09925bSvporpo for (auto *N : drop_begin(Nodes)) { 181d09925bSvporpo if (N->getInstruction()->comesBefore(TopN->getInstruction())) 191d09925bSvporpo TopN = N; 201d09925bSvporpo } 211d09925bSvporpo return TopN; 221d09925bSvporpo } 231d09925bSvporpo 241d09925bSvporpo DGNode *SchedBundle::getBot() const { 251d09925bSvporpo DGNode *BotN = Nodes.front(); 261d09925bSvporpo for (auto *N : drop_begin(Nodes)) { 271d09925bSvporpo if (BotN->getInstruction()->comesBefore(N->getInstruction())) 281d09925bSvporpo BotN = N; 291d09925bSvporpo } 301d09925bSvporpo return BotN; 311d09925bSvporpo } 321d09925bSvporpo 331d09925bSvporpo void SchedBundle::cluster(BasicBlock::iterator Where) { 341d09925bSvporpo for (auto *N : Nodes) { 351d09925bSvporpo auto *I = N->getInstruction(); 361d09925bSvporpo if (I->getIterator() == Where) 371d09925bSvporpo ++Where; // Try to maintain bundle order. 381d09925bSvporpo I->moveBefore(*Where.getNodeParent(), Where); 391d09925bSvporpo } 401d09925bSvporpo } 411d09925bSvporpo 421d09925bSvporpo #ifndef NDEBUG 431d09925bSvporpo void SchedBundle::dump(raw_ostream &OS) const { 441d09925bSvporpo for (auto *N : Nodes) 451d09925bSvporpo OS << *N; 461d09925bSvporpo } 471d09925bSvporpo 481d09925bSvporpo void SchedBundle::dump() const { 491d09925bSvporpo dump(dbgs()); 501d09925bSvporpo dbgs() << "\n"; 511d09925bSvporpo } 521d09925bSvporpo #endif // NDEBUG 531d09925bSvporpo 541d09925bSvporpo #ifndef NDEBUG 551d09925bSvporpo void ReadyListContainer::dump(raw_ostream &OS) const { 561d09925bSvporpo auto ListCopy = List; 571d09925bSvporpo while (!ListCopy.empty()) { 581d09925bSvporpo OS << *ListCopy.top() << "\n"; 591d09925bSvporpo ListCopy.pop(); 601d09925bSvporpo } 611d09925bSvporpo } 621d09925bSvporpo 631d09925bSvporpo void ReadyListContainer::dump() const { 641d09925bSvporpo dump(dbgs()); 651d09925bSvporpo dbgs() << "\n"; 661d09925bSvporpo } 671d09925bSvporpo #endif // NDEBUG 681d09925bSvporpo 691d09925bSvporpo void Scheduler::scheduleAndUpdateReadyList(SchedBundle &Bndl) { 701d09925bSvporpo // Find where we should schedule the instructions. 711d09925bSvporpo assert(ScheduleTopItOpt && "Should have been set by now!"); 721d09925bSvporpo auto Where = *ScheduleTopItOpt; 731d09925bSvporpo // Move all instructions in `Bndl` to `Where`. 741d09925bSvporpo Bndl.cluster(Where); 751d09925bSvporpo // Update the last scheduled bundle. 761d09925bSvporpo ScheduleTopItOpt = Bndl.getTop()->getInstruction()->getIterator(); 771d09925bSvporpo // Set nodes as "scheduled" and decrement the UnsceduledSuccs counter of all 781d09925bSvporpo // dependency predecessors. 791d09925bSvporpo for (DGNode *N : Bndl) { 801d09925bSvporpo N->setScheduled(true); 811d09925bSvporpo for (auto *DepN : N->preds(DAG)) { 821d09925bSvporpo // TODO: preds() should not return nullptr. 831d09925bSvporpo if (DepN == nullptr) 841d09925bSvporpo continue; 851d09925bSvporpo DepN->decrUnscheduledSuccs(); 861d09925bSvporpo if (DepN->ready()) 871d09925bSvporpo ReadyList.insert(DepN); 881d09925bSvporpo } 891d09925bSvporpo } 901d09925bSvporpo } 911d09925bSvporpo 921d09925bSvporpo SchedBundle *Scheduler::createBundle(ArrayRef<Instruction *> Instrs) { 931d09925bSvporpo SchedBundle::ContainerTy Nodes; 941d09925bSvporpo Nodes.reserve(Instrs.size()); 951d09925bSvporpo for (auto *I : Instrs) 961d09925bSvporpo Nodes.push_back(DAG.getNode(I)); 971d09925bSvporpo auto BndlPtr = std::make_unique<SchedBundle>(std::move(Nodes)); 981d09925bSvporpo auto *Bndl = BndlPtr.get(); 99f7ef7b2fSvporpo Bndls[Bndl] = std::move(BndlPtr); 1001d09925bSvporpo return Bndl; 1011d09925bSvporpo } 1021d09925bSvporpo 103f7ef7b2fSvporpo void Scheduler::eraseBundle(SchedBundle *SB) { Bndls.erase(SB); } 104f7ef7b2fSvporpo 1051d09925bSvporpo bool Scheduler::tryScheduleUntil(ArrayRef<Instruction *> Instrs) { 1061d09925bSvporpo // Use a set of instructions, instead of `Instrs` for fast lookups. 1071d09925bSvporpo DenseSet<Instruction *> InstrsToDefer(Instrs.begin(), Instrs.end()); 1081d09925bSvporpo // This collects the nodes that correspond to instructions found in `Instrs` 1091d09925bSvporpo // that have just become ready. These nodes won't be scheduled right away. 1101d09925bSvporpo SmallVector<DGNode *, 8> DeferredNodes; 1111d09925bSvporpo 1121d09925bSvporpo // Keep scheduling ready nodes until we either run out of ready nodes (i.e., 1131d09925bSvporpo // ReadyList is empty), or all nodes that correspond to `Instrs` (the nodes of 1141d09925bSvporpo // which are collected in DeferredNodes) are all ready to schedule. 1151d09925bSvporpo while (!ReadyList.empty()) { 1161d09925bSvporpo auto *ReadyN = ReadyList.pop(); 1171d09925bSvporpo if (InstrsToDefer.contains(ReadyN->getInstruction())) { 1181d09925bSvporpo // If the ready instruction is one of those in `Instrs`, then we don't 1191d09925bSvporpo // schedule it right away. Instead we defer it until we can schedule it 1201d09925bSvporpo // along with the rest of the instructions in `Instrs`, at the same 1211d09925bSvporpo // time in a single scheduling bundle. 1221d09925bSvporpo DeferredNodes.push_back(ReadyN); 1231d09925bSvporpo bool ReadyToScheduleDeferred = DeferredNodes.size() == Instrs.size(); 1241d09925bSvporpo if (ReadyToScheduleDeferred) { 1251d09925bSvporpo scheduleAndUpdateReadyList(*createBundle(Instrs)); 1261d09925bSvporpo return true; 1271d09925bSvporpo } 1281d09925bSvporpo } else { 1291d09925bSvporpo // If the ready instruction is not found in `Instrs`, then we wrap it in a 1301d09925bSvporpo // scheduling bundle and schedule it right away. 1311d09925bSvporpo scheduleAndUpdateReadyList(*createBundle({ReadyN->getInstruction()})); 1321d09925bSvporpo } 1331d09925bSvporpo } 1341d09925bSvporpo assert(DeferredNodes.size() != Instrs.size() && 1351d09925bSvporpo "We should have succesfully scheduled and early-returned!"); 1361d09925bSvporpo return false; 1371d09925bSvporpo } 1381d09925bSvporpo 139f7ef7b2fSvporpo Scheduler::BndlSchedState 140f7ef7b2fSvporpo Scheduler::getBndlSchedState(ArrayRef<Instruction *> Instrs) const { 141f7ef7b2fSvporpo assert(!Instrs.empty() && "Expected non-empty bundle"); 142f7ef7b2fSvporpo bool PartiallyScheduled = false; 143f7ef7b2fSvporpo bool FullyScheduled = true; 144f7ef7b2fSvporpo for (auto *I : Instrs) { 145f7ef7b2fSvporpo auto *N = DAG.getNode(I); 146f7ef7b2fSvporpo if (N != nullptr && N->scheduled()) 147f7ef7b2fSvporpo PartiallyScheduled = true; 148f7ef7b2fSvporpo else 149f7ef7b2fSvporpo FullyScheduled = false; 150f7ef7b2fSvporpo } 151f7ef7b2fSvporpo if (FullyScheduled) { 152f7ef7b2fSvporpo // If not all instrs in the bundle are in the same SchedBundle then this 153f7ef7b2fSvporpo // should be considered as partially-scheduled, because we will need to 154f7ef7b2fSvporpo // re-schedule. 155f7ef7b2fSvporpo SchedBundle *SB = DAG.getNode(Instrs[0])->getSchedBundle(); 156f7ef7b2fSvporpo assert(SB != nullptr && "FullyScheduled assumes that there is an SB!"); 157f7ef7b2fSvporpo if (any_of(drop_begin(Instrs), [this, SB](sandboxir::Value *SBV) { 158f7ef7b2fSvporpo return DAG.getNode(cast<sandboxir::Instruction>(SBV)) 159f7ef7b2fSvporpo ->getSchedBundle() != SB; 160f7ef7b2fSvporpo })) 161f7ef7b2fSvporpo FullyScheduled = false; 162f7ef7b2fSvporpo } 163f7ef7b2fSvporpo return FullyScheduled ? BndlSchedState::FullyScheduled 164f7ef7b2fSvporpo : PartiallyScheduled ? BndlSchedState::PartiallyOrDifferentlyScheduled 165f7ef7b2fSvporpo : BndlSchedState::NoneScheduled; 166f7ef7b2fSvporpo } 167f7ef7b2fSvporpo 168f7ef7b2fSvporpo void Scheduler::trimSchedule(ArrayRef<Instruction *> Instrs) { 169f7ef7b2fSvporpo Instruction *TopI = &*ScheduleTopItOpt.value(); 170f7ef7b2fSvporpo Instruction *LowestI = VecUtils::getLowest(Instrs); 171f7ef7b2fSvporpo // Destroy the schedule bundles from LowestI all the way to the top. 172f7ef7b2fSvporpo for (auto *I = LowestI, *E = TopI->getPrevNode(); I != E; 173f7ef7b2fSvporpo I = I->getPrevNode()) { 174f7ef7b2fSvporpo auto *N = DAG.getNode(I); 175*b178c2d6SVasileios Porpodas if (N == nullptr) 176*b178c2d6SVasileios Porpodas continue; 177f7ef7b2fSvporpo if (auto *SB = N->getSchedBundle()) 178f7ef7b2fSvporpo eraseBundle(SB); 179f7ef7b2fSvporpo } 180f7ef7b2fSvporpo // TODO: For now we clear the DAG. Trim view once it gets implemented. 181f7ef7b2fSvporpo Bndls.clear(); 182f7ef7b2fSvporpo DAG.clear(); 183f7ef7b2fSvporpo 184f7ef7b2fSvporpo // Since we are scheduling NewRegion from scratch, we clear the ready lists. 185f7ef7b2fSvporpo // The nodes currently in the list may not be ready after clearing the View. 186f7ef7b2fSvporpo ReadyList.clear(); 187f7ef7b2fSvporpo } 188f7ef7b2fSvporpo 1891d09925bSvporpo bool Scheduler::trySchedule(ArrayRef<Instruction *> Instrs) { 1901d09925bSvporpo assert(all_of(drop_begin(Instrs), 1911d09925bSvporpo [Instrs](Instruction *I) { 1921d09925bSvporpo return I->getParent() == (*Instrs.begin())->getParent(); 1931d09925bSvporpo }) && 1945cb2db3bSvporpo "Instrs not in the same BB, should have been rejected by Legality!"); 1955cb2db3bSvporpo if (ScheduledBB == nullptr) 1965cb2db3bSvporpo ScheduledBB = Instrs[0]->getParent(); 1975cb2db3bSvporpo // We don't support crossing BBs for now. 1985cb2db3bSvporpo if (any_of(Instrs, 1995cb2db3bSvporpo [this](Instruction *I) { return I->getParent() != ScheduledBB; })) 2005cb2db3bSvporpo return false; 201f7ef7b2fSvporpo auto SchedState = getBndlSchedState(Instrs); 202f7ef7b2fSvporpo switch (SchedState) { 203f7ef7b2fSvporpo case BndlSchedState::FullyScheduled: 204f7ef7b2fSvporpo // Nothing to do. 205f7ef7b2fSvporpo return true; 206f7ef7b2fSvporpo case BndlSchedState::PartiallyOrDifferentlyScheduled: 207f7ef7b2fSvporpo // If one or more instrs are already scheduled we need to destroy the 208f7ef7b2fSvporpo // top-most part of the schedule that includes the instrs in the bundle and 209f7ef7b2fSvporpo // re-schedule. 210f7ef7b2fSvporpo trimSchedule(Instrs); 211f7ef7b2fSvporpo [[fallthrough]]; 212f7ef7b2fSvporpo case BndlSchedState::NoneScheduled: { 2131d09925bSvporpo // TODO: Set the window of the DAG that we are interested in. 2141d09925bSvporpo // We start scheduling at the bottom instr of Instrs. 215f7ef7b2fSvporpo ScheduleTopItOpt = std::next(VecUtils::getLowest(Instrs)->getIterator()); 216f7ef7b2fSvporpo 217c7053ac2Svporpo // TODO: For now don't cross BBs. 218c7053ac2Svporpo if (!DAG.getInterval().empty()) { 219c7053ac2Svporpo auto *BB = DAG.getInterval().top()->getParent(); 220c7053ac2Svporpo if (any_of(Instrs, [BB](auto *I) { return I->getParent() != BB; })) 221c7053ac2Svporpo return false; 222c7053ac2Svporpo } 223c7053ac2Svporpo 224f7ef7b2fSvporpo // Extend the DAG to include Instrs. 225f7ef7b2fSvporpo Interval<Instruction> Extension = DAG.extend(Instrs); 2261d09925bSvporpo // Add nodes to ready list. 2271d09925bSvporpo for (auto &I : Extension) { 2281d09925bSvporpo auto *N = DAG.getNode(&I); 2291d09925bSvporpo if (N->ready()) 2301d09925bSvporpo ReadyList.insert(N); 2311d09925bSvporpo } 2321d09925bSvporpo // Try schedule all nodes until we can schedule Instrs back-to-back. 2331d09925bSvporpo return tryScheduleUntil(Instrs); 2341d09925bSvporpo } 235f7ef7b2fSvporpo } 236490e58a9SSimon Pilgrim llvm_unreachable("Unhandled BndlSchedState enum"); 237f7ef7b2fSvporpo } 2381d09925bSvporpo 2391d09925bSvporpo #ifndef NDEBUG 2401d09925bSvporpo void Scheduler::dump(raw_ostream &OS) const { 2411d09925bSvporpo OS << "ReadyList:\n"; 2421d09925bSvporpo ReadyList.dump(OS); 2431d09925bSvporpo } 2441d09925bSvporpo void Scheduler::dump() const { dump(dbgs()); } 2451d09925bSvporpo #endif // NDEBUG 2461d09925bSvporpo 2471d09925bSvporpo } // namespace llvm::sandboxir 248