1 //===- Scheduler.cpp ------------------------------------------------------===// 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 #include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h" 10 #include "llvm/Transforms/Vectorize/SandboxVectorizer/VecUtils.h" 11 12 namespace llvm::sandboxir { 13 14 // TODO: Check if we can cache top/bottom to reduce compile-time. 15 DGNode *SchedBundle::getTop() const { 16 DGNode *TopN = Nodes.front(); 17 for (auto *N : drop_begin(Nodes)) { 18 if (N->getInstruction()->comesBefore(TopN->getInstruction())) 19 TopN = N; 20 } 21 return TopN; 22 } 23 24 DGNode *SchedBundle::getBot() const { 25 DGNode *BotN = Nodes.front(); 26 for (auto *N : drop_begin(Nodes)) { 27 if (BotN->getInstruction()->comesBefore(N->getInstruction())) 28 BotN = N; 29 } 30 return BotN; 31 } 32 33 void SchedBundle::cluster(BasicBlock::iterator Where) { 34 for (auto *N : Nodes) { 35 auto *I = N->getInstruction(); 36 if (I->getIterator() == Where) 37 ++Where; // Try to maintain bundle order. 38 I->moveBefore(*Where.getNodeParent(), Where); 39 } 40 } 41 42 #ifndef NDEBUG 43 void SchedBundle::dump(raw_ostream &OS) const { 44 for (auto *N : Nodes) 45 OS << *N; 46 } 47 48 void SchedBundle::dump() const { 49 dump(dbgs()); 50 dbgs() << "\n"; 51 } 52 #endif // NDEBUG 53 54 #ifndef NDEBUG 55 void ReadyListContainer::dump(raw_ostream &OS) const { 56 auto ListCopy = List; 57 while (!ListCopy.empty()) { 58 OS << *ListCopy.top() << "\n"; 59 ListCopy.pop(); 60 } 61 } 62 63 void ReadyListContainer::dump() const { 64 dump(dbgs()); 65 dbgs() << "\n"; 66 } 67 #endif // NDEBUG 68 69 void Scheduler::scheduleAndUpdateReadyList(SchedBundle &Bndl) { 70 // Find where we should schedule the instructions. 71 assert(ScheduleTopItOpt && "Should have been set by now!"); 72 auto Where = *ScheduleTopItOpt; 73 // Move all instructions in `Bndl` to `Where`. 74 Bndl.cluster(Where); 75 // Update the last scheduled bundle. 76 ScheduleTopItOpt = Bndl.getTop()->getInstruction()->getIterator(); 77 // Set nodes as "scheduled" and decrement the UnsceduledSuccs counter of all 78 // dependency predecessors. 79 for (DGNode *N : Bndl) { 80 N->setScheduled(true); 81 for (auto *DepN : N->preds(DAG)) { 82 // TODO: preds() should not return nullptr. 83 if (DepN == nullptr) 84 continue; 85 DepN->decrUnscheduledSuccs(); 86 if (DepN->ready()) 87 ReadyList.insert(DepN); 88 } 89 } 90 } 91 92 SchedBundle *Scheduler::createBundle(ArrayRef<Instruction *> Instrs) { 93 SchedBundle::ContainerTy Nodes; 94 Nodes.reserve(Instrs.size()); 95 for (auto *I : Instrs) 96 Nodes.push_back(DAG.getNode(I)); 97 auto BndlPtr = std::make_unique<SchedBundle>(std::move(Nodes)); 98 auto *Bndl = BndlPtr.get(); 99 Bndls[Bndl] = std::move(BndlPtr); 100 return Bndl; 101 } 102 103 void Scheduler::eraseBundle(SchedBundle *SB) { Bndls.erase(SB); } 104 105 bool Scheduler::tryScheduleUntil(ArrayRef<Instruction *> Instrs) { 106 // Use a set of instructions, instead of `Instrs` for fast lookups. 107 DenseSet<Instruction *> InstrsToDefer(Instrs.begin(), Instrs.end()); 108 // This collects the nodes that correspond to instructions found in `Instrs` 109 // that have just become ready. These nodes won't be scheduled right away. 110 SmallVector<DGNode *, 8> DeferredNodes; 111 112 // Keep scheduling ready nodes until we either run out of ready nodes (i.e., 113 // ReadyList is empty), or all nodes that correspond to `Instrs` (the nodes of 114 // which are collected in DeferredNodes) are all ready to schedule. 115 while (!ReadyList.empty()) { 116 auto *ReadyN = ReadyList.pop(); 117 if (InstrsToDefer.contains(ReadyN->getInstruction())) { 118 // If the ready instruction is one of those in `Instrs`, then we don't 119 // schedule it right away. Instead we defer it until we can schedule it 120 // along with the rest of the instructions in `Instrs`, at the same 121 // time in a single scheduling bundle. 122 DeferredNodes.push_back(ReadyN); 123 bool ReadyToScheduleDeferred = DeferredNodes.size() == Instrs.size(); 124 if (ReadyToScheduleDeferred) { 125 scheduleAndUpdateReadyList(*createBundle(Instrs)); 126 return true; 127 } 128 } else { 129 // If the ready instruction is not found in `Instrs`, then we wrap it in a 130 // scheduling bundle and schedule it right away. 131 scheduleAndUpdateReadyList(*createBundle({ReadyN->getInstruction()})); 132 } 133 } 134 assert(DeferredNodes.size() != Instrs.size() && 135 "We should have succesfully scheduled and early-returned!"); 136 return false; 137 } 138 139 Scheduler::BndlSchedState 140 Scheduler::getBndlSchedState(ArrayRef<Instruction *> Instrs) const { 141 assert(!Instrs.empty() && "Expected non-empty bundle"); 142 bool PartiallyScheduled = false; 143 bool FullyScheduled = true; 144 for (auto *I : Instrs) { 145 auto *N = DAG.getNode(I); 146 if (N != nullptr && N->scheduled()) 147 PartiallyScheduled = true; 148 else 149 FullyScheduled = false; 150 } 151 if (FullyScheduled) { 152 // If not all instrs in the bundle are in the same SchedBundle then this 153 // should be considered as partially-scheduled, because we will need to 154 // re-schedule. 155 SchedBundle *SB = DAG.getNode(Instrs[0])->getSchedBundle(); 156 assert(SB != nullptr && "FullyScheduled assumes that there is an SB!"); 157 if (any_of(drop_begin(Instrs), [this, SB](sandboxir::Value *SBV) { 158 return DAG.getNode(cast<sandboxir::Instruction>(SBV)) 159 ->getSchedBundle() != SB; 160 })) 161 FullyScheduled = false; 162 } 163 return FullyScheduled ? BndlSchedState::FullyScheduled 164 : PartiallyScheduled ? BndlSchedState::PartiallyOrDifferentlyScheduled 165 : BndlSchedState::NoneScheduled; 166 } 167 168 void Scheduler::trimSchedule(ArrayRef<Instruction *> Instrs) { 169 Instruction *TopI = &*ScheduleTopItOpt.value(); 170 Instruction *LowestI = VecUtils::getLowest(Instrs); 171 // Destroy the schedule bundles from LowestI all the way to the top. 172 for (auto *I = LowestI, *E = TopI->getPrevNode(); I != E; 173 I = I->getPrevNode()) { 174 auto *N = DAG.getNode(I); 175 if (auto *SB = N->getSchedBundle()) 176 eraseBundle(SB); 177 } 178 // TODO: For now we clear the DAG. Trim view once it gets implemented. 179 Bndls.clear(); 180 DAG.clear(); 181 182 // Since we are scheduling NewRegion from scratch, we clear the ready lists. 183 // The nodes currently in the list may not be ready after clearing the View. 184 ReadyList.clear(); 185 } 186 187 bool Scheduler::trySchedule(ArrayRef<Instruction *> Instrs) { 188 assert(all_of(drop_begin(Instrs), 189 [Instrs](Instruction *I) { 190 return I->getParent() == (*Instrs.begin())->getParent(); 191 }) && 192 "Instrs not in the same BB!"); 193 auto SchedState = getBndlSchedState(Instrs); 194 switch (SchedState) { 195 case BndlSchedState::FullyScheduled: 196 // Nothing to do. 197 return true; 198 case BndlSchedState::PartiallyOrDifferentlyScheduled: 199 // If one or more instrs are already scheduled we need to destroy the 200 // top-most part of the schedule that includes the instrs in the bundle and 201 // re-schedule. 202 trimSchedule(Instrs); 203 [[fallthrough]]; 204 case BndlSchedState::NoneScheduled: { 205 // TODO: Set the window of the DAG that we are interested in. 206 // We start scheduling at the bottom instr of Instrs. 207 ScheduleTopItOpt = std::next(VecUtils::getLowest(Instrs)->getIterator()); 208 209 // Extend the DAG to include Instrs. 210 Interval<Instruction> Extension = DAG.extend(Instrs); 211 // Add nodes to ready list. 212 for (auto &I : Extension) { 213 auto *N = DAG.getNode(&I); 214 if (N->ready()) 215 ReadyList.insert(N); 216 } 217 // Try schedule all nodes until we can schedule Instrs back-to-back. 218 return tryScheduleUntil(Instrs); 219 } 220 } 221 llvm_unreachable("Unhandled BndlSchedState enum"); 222 } 223 224 #ifndef NDEBUG 225 void Scheduler::dump(raw_ostream &OS) const { 226 OS << "ReadyList:\n"; 227 ReadyList.dump(OS); 228 } 229 void Scheduler::dump() const { dump(dbgs()); } 230 #endif // NDEBUG 231 232 } // namespace llvm::sandboxir 233