1 //===--------------------- Scheduler.cpp ------------------------*- C++ -*-===// 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 // A scheduler for processor resource units and processor resource groups. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/MCA/HardwareUnits/Scheduler.h" 14 #include "llvm/Support/Debug.h" 15 #include "llvm/Support/raw_ostream.h" 16 17 namespace llvm { 18 namespace mca { 19 20 #define DEBUG_TYPE "llvm-mca" 21 22 void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) { 23 // Ensure we have a valid (non-null) strategy object. 24 Strategy = S ? std::move(S) : llvm::make_unique<DefaultSchedulerStrategy>(); 25 } 26 27 // Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy. 28 SchedulerStrategy::~SchedulerStrategy() = default; 29 DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default; 30 31 #ifndef NDEBUG 32 void Scheduler::dump() const { 33 dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n'; 34 dbgs() << "[SCHEDULER]: ReadySet size is: " << ReadySet.size() << '\n'; 35 dbgs() << "[SCHEDULER]: IssuedSet size is: " << IssuedSet.size() << '\n'; 36 Resources->dump(); 37 } 38 #endif 39 40 Scheduler::Status Scheduler::isAvailable(const InstRef &IR) { 41 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 42 43 ResourceStateEvent RSE = Resources->canBeDispatched(Desc.Buffers); 44 HadTokenStall = RSE != RS_BUFFER_AVAILABLE; 45 46 switch (RSE) { 47 case ResourceStateEvent::RS_BUFFER_UNAVAILABLE: 48 return Scheduler::SC_BUFFERS_FULL; 49 case ResourceStateEvent::RS_RESERVED: 50 return Scheduler::SC_DISPATCH_GROUP_STALL; 51 case ResourceStateEvent::RS_BUFFER_AVAILABLE: 52 break; 53 } 54 55 // Give lower priority to LSUnit stall events. 56 LSUnit::Status LSS = LSU.isAvailable(IR); 57 HadTokenStall = LSS != LSUnit::LSU_AVAILABLE; 58 59 switch (LSS) { 60 case LSUnit::LSU_LQUEUE_FULL: 61 return Scheduler::SC_LOAD_QUEUE_FULL; 62 case LSUnit::LSU_SQUEUE_FULL: 63 return Scheduler::SC_STORE_QUEUE_FULL; 64 case LSUnit::LSU_AVAILABLE: 65 return Scheduler::SC_AVAILABLE; 66 } 67 68 llvm_unreachable("Don't know how to process this LSU state result!"); 69 } 70 71 void Scheduler::issueInstructionImpl( 72 InstRef &IR, 73 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources) { 74 Instruction *IS = IR.getInstruction(); 75 const InstrDesc &D = IS->getDesc(); 76 77 // Issue the instruction and collect all the consumed resources 78 // into a vector. That vector is then used to notify the listener. 79 Resources->issueInstruction(D, UsedResources); 80 81 // Notify the instruction that it started executing. 82 // This updates the internal state of each write. 83 IS->execute(IR.getSourceIndex()); 84 85 IS->computeCriticalRegDep(); 86 87 if (IS->isExecuting()) 88 IssuedSet.emplace_back(IR); 89 else if (IS->isExecuted()) 90 LSU.onInstructionExecuted(IR); 91 } 92 93 // Release the buffered resources and issue the instruction. 94 void Scheduler::issueInstruction( 95 InstRef &IR, 96 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources, 97 SmallVectorImpl<InstRef> &PendingInstructions, 98 SmallVectorImpl<InstRef> &ReadyInstructions) { 99 const Instruction &Inst = *IR.getInstruction(); 100 bool HasDependentUsers = Inst.hasDependentUsers(); 101 102 Resources->releaseBuffers(Inst.getDesc().Buffers); 103 issueInstructionImpl(IR, UsedResources); 104 // Instructions that have been issued during this cycle might have unblocked 105 // other dependent instructions. Dependent instructions may be issued during 106 // this same cycle if operands have ReadAdvance entries. Promote those 107 // instructions to the ReadySet and notify the caller that those are ready. 108 if (HasDependentUsers && promoteToPendingSet(PendingInstructions)) 109 promoteToReadySet(ReadyInstructions); 110 } 111 112 static bool initializeCriticalMemDepInfo(InstRef &IR, const LSUnit &LSU) { 113 Instruction &IS = *IR.getInstruction(); 114 assert(IS.isMemOp() && "Not a memory operation!"); 115 116 // Check if this instruction depends on another memory operation. 117 InstRef DependentMemOp = LSU.isReady(IR); 118 const Instruction *MemOp = DependentMemOp.getInstruction(); 119 IS.setCurrentMemDep(MemOp); 120 121 // Initialize the CriticalMemDep structure. 122 unsigned Cycles = 0; 123 if (MemOp->isExecuting()) 124 Cycles = static_cast<unsigned>(MemOp->getCyclesLeft()); 125 IS.setCriticalMemDep(DependentMemOp.getSourceIndex(), Cycles); 126 return IR.getSourceIndex() == DependentMemOp.getSourceIndex(); 127 } 128 129 static bool updateMemoryDependencyInfo(InstRef &IR, const LSUnit &LSU) { 130 Instruction &IS = *IR.getInstruction(); 131 assert(IS.isMemOp() && "Not a memory operation!"); 132 133 const Instruction *MemOp = IS.getCurrentMemDep(); 134 if (!MemOp && initializeCriticalMemDepInfo(IR, LSU)) 135 return true; 136 137 MemOp = IS.getCurrentMemDep(); 138 if (MemOp == IR.getInstruction()) 139 return true; 140 141 const CriticalDependency &CMD = IS.getCriticalMemDep(); 142 if (MemOp->isExecuting() && !CMD.Cycles) { 143 // Update the critical memory dependency info. 144 IS.setCriticalMemDep(CMD.IID, MemOp->getCyclesLeft()); 145 return false; 146 } 147 148 if (!MemOp->isExecuted() && !MemOp->isRetired()) 149 return false; 150 151 // Check if there are still unsolved memory dependencies. 152 InstRef DependentMemOp = LSU.isReady(IR); 153 MemOp = DependentMemOp.getInstruction(); 154 IS.setCurrentMemDep(MemOp); 155 if (DependentMemOp == IR) 156 return true; 157 158 unsigned Cycles = 0; 159 if (MemOp->isExecuting()) 160 Cycles = static_cast<unsigned>(MemOp->getCyclesLeft()); 161 IS.setCriticalMemDep(DependentMemOp.getSourceIndex(), Cycles); 162 return false; 163 } 164 165 bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { 166 // Scan the set of waiting instructions and promote them to the 167 // ready set if operands are all ready. 168 unsigned PromotedElements = 0; 169 for (auto I = PendingSet.begin(), E = PendingSet.end(); I != E;) { 170 InstRef &IR = *I; 171 if (!IR) 172 break; 173 174 // Check if there are unsolved memory dependencies. 175 Instruction &IS = *IR.getInstruction(); 176 if (IS.isMemOp() && !updateMemoryDependencyInfo(IR, LSU)) { 177 ++I; 178 continue; 179 } 180 181 // Check if there are unsolved register dependencies. 182 if (!IS.isReady() && !IS.updatePending()) { 183 ++I; 184 continue; 185 } 186 LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR 187 << " promoted to the READY set.\n"); 188 189 Ready.emplace_back(IR); 190 ReadySet.emplace_back(IR); 191 192 IR.invalidate(); 193 ++PromotedElements; 194 std::iter_swap(I, E - PromotedElements); 195 } 196 197 PendingSet.resize(PendingSet.size() - PromotedElements); 198 return PromotedElements; 199 } 200 201 bool Scheduler::promoteToPendingSet(SmallVectorImpl<InstRef> &Pending) { 202 // Scan the set of waiting instructions and promote them to the 203 // pending set if operands are all ready. 204 unsigned RemovedElements = 0; 205 for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) { 206 InstRef &IR = *I; 207 if (!IR) 208 break; 209 210 // Check if this instruction is now ready. In case, force 211 // a transition in state using method 'updateDispatched()'. 212 Instruction &IS = *IR.getInstruction(); 213 if (IS.isDispatched() && !IS.updateDispatched()) { 214 ++I; 215 continue; 216 } 217 LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR 218 << " promoted to the PENDING set.\n"); 219 220 Pending.emplace_back(IR); 221 PendingSet.emplace_back(IR); 222 223 IR.invalidate(); 224 ++RemovedElements; 225 std::iter_swap(I, E - RemovedElements); 226 } 227 228 WaitSet.resize(WaitSet.size() - RemovedElements); 229 return RemovedElements; 230 } 231 232 InstRef Scheduler::select() { 233 unsigned QueueIndex = ReadySet.size(); 234 for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) { 235 InstRef &IR = ReadySet[I]; 236 if (QueueIndex == ReadySet.size() || 237 Strategy->compare(IR, ReadySet[QueueIndex])) { 238 Instruction &IS = *IR.getInstruction(); 239 uint64_t BusyResourceMask = Resources->checkAvailability(IS.getDesc()); 240 IS.setCriticalResourceMask(BusyResourceMask); 241 BusyResourceUnits |= BusyResourceMask; 242 if (!BusyResourceMask) 243 QueueIndex = I; 244 } 245 } 246 247 if (QueueIndex == ReadySet.size()) 248 return InstRef(); 249 250 // We found an instruction to issue. 251 InstRef IR = ReadySet[QueueIndex]; 252 std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]); 253 ReadySet.pop_back(); 254 return IR; 255 } 256 257 void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) { 258 unsigned RemovedElements = 0; 259 for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) { 260 InstRef &IR = *I; 261 if (!IR) 262 break; 263 Instruction &IS = *IR.getInstruction(); 264 if (!IS.isExecuted()) { 265 LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR 266 << " is still executing.\n"); 267 ++I; 268 continue; 269 } 270 271 // Instruction IR has completed execution. 272 LSU.onInstructionExecuted(IR); 273 Executed.emplace_back(IR); 274 ++RemovedElements; 275 IR.invalidate(); 276 std::iter_swap(I, E - RemovedElements); 277 } 278 279 IssuedSet.resize(IssuedSet.size() - RemovedElements); 280 } 281 282 uint64_t Scheduler::analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts) { 283 Insts.insert(Insts.end(), ReadySet.begin(), ReadySet.end()); 284 return BusyResourceUnits; 285 } 286 287 void Scheduler::analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps, 288 SmallVectorImpl<InstRef> &MemDeps) { 289 const auto EndIt = PendingSet.end() - NumDispatchedToThePendingSet; 290 for (InstRef &IR : make_range(PendingSet.begin(), EndIt)) { 291 Instruction &IS = *IR.getInstruction(); 292 if (Resources->checkAvailability(IS.getDesc())) 293 continue; 294 295 if (IS.isReady() || (IS.isMemOp() && LSU.isReady(IR) != IR)) 296 MemDeps.emplace_back(IR); 297 else 298 RegDeps.emplace_back(IR); 299 } 300 } 301 302 void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed, 303 SmallVectorImpl<InstRef> &Executed, 304 SmallVectorImpl<InstRef> &Pending, 305 SmallVectorImpl<InstRef> &Ready) { 306 // Release consumed resources. 307 Resources->cycleEvent(Freed); 308 309 for (InstRef &IR : IssuedSet) 310 IR.getInstruction()->cycleEvent(); 311 updateIssuedSet(Executed); 312 313 for (InstRef &IR : PendingSet) 314 IR.getInstruction()->cycleEvent(); 315 316 for (InstRef &IR : WaitSet) 317 IR.getInstruction()->cycleEvent(); 318 319 promoteToPendingSet(Pending); 320 promoteToReadySet(Ready); 321 322 NumDispatchedToThePendingSet = 0; 323 BusyResourceUnits = 0; 324 } 325 326 bool Scheduler::mustIssueImmediately(const InstRef &IR) const { 327 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 328 if (Desc.isZeroLatency()) 329 return true; 330 // Instructions that use an in-order dispatch/issue processor resource must be 331 // issued immediately to the pipeline(s). Any other in-order buffered 332 // resources (i.e. BufferSize=1) is consumed. 333 return Desc.MustIssueImmediately; 334 } 335 336 bool Scheduler::dispatch(const InstRef &IR) { 337 const Instruction &IS = *IR.getInstruction(); 338 const InstrDesc &Desc = IS.getDesc(); 339 Resources->reserveBuffers(Desc.Buffers); 340 341 // If necessary, reserve queue entries in the load-store unit (LSU). 342 if (IS.isMemOp()) 343 LSU.dispatch(IR); 344 345 if (IS.isPending()) { 346 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR 347 << " to the PendingSet\n"); 348 PendingSet.push_back(IR); 349 ++NumDispatchedToThePendingSet; 350 return false; 351 } 352 353 // Memory operations that are not in a ready state are initially assigned to 354 // the WaitSet. 355 if (!IS.isReady() || (IS.isMemOp() && LSU.isReady(IR) != IR)) { 356 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n"); 357 WaitSet.push_back(IR); 358 return false; 359 } 360 361 // Don't add a zero-latency instruction to the Ready queue. 362 // A zero-latency instruction doesn't consume any scheduler resources. That is 363 // because it doesn't need to be executed, and it is often removed at register 364 // renaming stage. For example, register-register moves are often optimized at 365 // register renaming stage by simply updating register aliases. On some 366 // targets, zero-idiom instructions (for example: a xor that clears the value 367 // of a register) are treated specially, and are often eliminated at register 368 // renaming stage. 369 if (!mustIssueImmediately(IR)) { 370 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n"); 371 ReadySet.push_back(IR); 372 } 373 374 return true; 375 } 376 377 } // namespace mca 378 } // namespace llvm 379