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) const { 41 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 42 43 switch (Resources->canBeDispatched(Desc.Buffers)) { 44 case ResourceStateEvent::RS_BUFFER_UNAVAILABLE: 45 return Scheduler::SC_BUFFERS_FULL; 46 case ResourceStateEvent::RS_RESERVED: 47 return Scheduler::SC_DISPATCH_GROUP_STALL; 48 case ResourceStateEvent::RS_BUFFER_AVAILABLE: 49 break; 50 } 51 52 // Give lower priority to LSUnit stall events. 53 switch (LSU.isAvailable(IR)) { 54 case LSUnit::LSU_LQUEUE_FULL: 55 return Scheduler::SC_LOAD_QUEUE_FULL; 56 case LSUnit::LSU_SQUEUE_FULL: 57 return Scheduler::SC_STORE_QUEUE_FULL; 58 case LSUnit::LSU_AVAILABLE: 59 return Scheduler::SC_AVAILABLE; 60 } 61 62 llvm_unreachable("Don't know how to process this LSU state result!"); 63 } 64 65 void Scheduler::issueInstructionImpl( 66 InstRef &IR, 67 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources) { 68 Instruction *IS = IR.getInstruction(); 69 const InstrDesc &D = IS->getDesc(); 70 71 // Issue the instruction and collect all the consumed resources 72 // into a vector. That vector is then used to notify the listener. 73 Resources->issueInstruction(D, UsedResources); 74 75 // Notify the instruction that it started executing. 76 // This updates the internal state of each write. 77 IS->execute(); 78 79 if (IS->isExecuting()) 80 IssuedSet.emplace_back(IR); 81 else if (IS->isExecuted()) 82 LSU.onInstructionExecuted(IR); 83 } 84 85 // Release the buffered resources and issue the instruction. 86 void Scheduler::issueInstruction( 87 InstRef &IR, 88 SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources, 89 SmallVectorImpl<InstRef> &ReadyInstructions) { 90 const Instruction &Inst = *IR.getInstruction(); 91 bool HasDependentUsers = Inst.hasDependentUsers(); 92 93 Resources->releaseBuffers(Inst.getDesc().Buffers); 94 issueInstructionImpl(IR, UsedResources); 95 // Instructions that have been issued during this cycle might have unblocked 96 // other dependent instructions. Dependent instructions may be issued during 97 // this same cycle if operands have ReadAdvance entries. Promote those 98 // instructions to the ReadySet and notify the caller that those are ready. 99 if (HasDependentUsers) 100 promoteToReadySet(ReadyInstructions); 101 } 102 103 void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { 104 // Scan the set of waiting instructions and promote them to the 105 // ready queue if operands are all ready. 106 unsigned RemovedElements = 0; 107 for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) { 108 InstRef &IR = *I; 109 if (!IR) 110 break; 111 112 // Check if this instruction is now ready. In case, force 113 // a transition in state using method 'update()'. 114 Instruction &IS = *IR.getInstruction(); 115 if (!IS.isReady()) 116 IS.update(); 117 118 // Check if there are still unsolved data dependencies. 119 if (!isReady(IR)) { 120 ++I; 121 continue; 122 } 123 124 Ready.emplace_back(IR); 125 ReadySet.emplace_back(IR); 126 127 IR.invalidate(); 128 ++RemovedElements; 129 std::iter_swap(I, E - RemovedElements); 130 } 131 132 WaitSet.resize(WaitSet.size() - RemovedElements); 133 } 134 135 InstRef Scheduler::select() { 136 unsigned QueueIndex = ReadySet.size(); 137 for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) { 138 const InstRef &IR = ReadySet[I]; 139 if (QueueIndex == ReadySet.size() || 140 Strategy->compare(IR, ReadySet[QueueIndex])) { 141 const InstrDesc &D = IR.getInstruction()->getDesc(); 142 uint64_t BusyResourceMask = Resources->checkAvailability(D); 143 BusyResourceUnits |= BusyResourceMask; 144 if (!BusyResourceMask) 145 QueueIndex = I; 146 } 147 } 148 149 if (QueueIndex == ReadySet.size()) 150 return InstRef(); 151 152 // We found an instruction to issue. 153 InstRef IR = ReadySet[QueueIndex]; 154 std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]); 155 ReadySet.pop_back(); 156 return IR; 157 } 158 159 void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) { 160 unsigned RemovedElements = 0; 161 for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) { 162 InstRef &IR = *I; 163 if (!IR) 164 break; 165 Instruction &IS = *IR.getInstruction(); 166 if (!IS.isExecuted()) { 167 LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR 168 << " is still executing.\n"); 169 ++I; 170 continue; 171 } 172 173 // Instruction IR has completed execution. 174 LSU.onInstructionExecuted(IR); 175 Executed.emplace_back(IR); 176 ++RemovedElements; 177 IR.invalidate(); 178 std::iter_swap(I, E - RemovedElements); 179 } 180 181 IssuedSet.resize(IssuedSet.size() - RemovedElements); 182 } 183 184 void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed, 185 SmallVectorImpl<InstRef> &Executed, 186 SmallVectorImpl<InstRef> &Ready) { 187 // Release consumed resources. 188 Resources->cycleEvent(Freed); 189 190 // Propagate the cycle event to the 'Issued' and 'Wait' sets. 191 for (InstRef &IR : IssuedSet) 192 IR.getInstruction()->cycleEvent(); 193 194 updateIssuedSet(Executed); 195 196 for (InstRef &IR : WaitSet) 197 IR.getInstruction()->cycleEvent(); 198 199 promoteToReadySet(Ready); 200 201 BusyResourceUnits = 0; 202 } 203 204 bool Scheduler::mustIssueImmediately(const InstRef &IR) const { 205 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 206 if (Desc.isZeroLatency()) 207 return true; 208 // Instructions that use an in-order dispatch/issue processor resource must be 209 // issued immediately to the pipeline(s). Any other in-order buffered 210 // resources (i.e. BufferSize=1) is consumed. 211 return Desc.MustIssueImmediately; 212 } 213 214 void Scheduler::dispatch(const InstRef &IR) { 215 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 216 Resources->reserveBuffers(Desc.Buffers); 217 218 // If necessary, reserve queue entries in the load-store unit (LSU). 219 bool IsMemOp = Desc.MayLoad || Desc.MayStore; 220 if (IsMemOp) 221 LSU.dispatch(IR); 222 223 if (!isReady(IR)) { 224 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n"); 225 WaitSet.push_back(IR); 226 return; 227 } 228 229 // Don't add a zero-latency instruction to the Ready queue. 230 // A zero-latency instruction doesn't consume any scheduler resources. That is 231 // because it doesn't need to be executed, and it is often removed at register 232 // renaming stage. For example, register-register moves are often optimized at 233 // register renaming stage by simply updating register aliases. On some 234 // targets, zero-idiom instructions (for example: a xor that clears the value 235 // of a register) are treated specially, and are often eliminated at register 236 // renaming stage. 237 if (!mustIssueImmediately(IR)) { 238 LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n"); 239 ReadySet.push_back(IR); 240 } 241 } 242 243 bool Scheduler::isReady(const InstRef &IR) const { 244 const InstrDesc &Desc = IR.getInstruction()->getDesc(); 245 bool IsMemOp = Desc.MayLoad || Desc.MayStore; 246 return IR.getInstruction()->isReady() && (!IsMemOp || LSU.isReady(IR)); 247 } 248 249 } // namespace mca 250 } // namespace llvm 251