Path: blob/main/contrib/llvm-project/llvm/lib/MCA/HardwareUnits/Scheduler.cpp
35292 views
//===--------------------- Scheduler.cpp ------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// A scheduler for processor resource units and processor resource groups.9//10//===----------------------------------------------------------------------===//1112#include "llvm/MCA/HardwareUnits/Scheduler.h"13#include "llvm/Support/Debug.h"14#include "llvm/Support/raw_ostream.h"1516namespace llvm {17namespace mca {1819#define DEBUG_TYPE "llvm-mca"2021void Scheduler::initializeStrategy(std::unique_ptr<SchedulerStrategy> S) {22// Ensure we have a valid (non-null) strategy object.23Strategy = S ? std::move(S) : std::make_unique<DefaultSchedulerStrategy>();24}2526// Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy.27SchedulerStrategy::~SchedulerStrategy() = default;28DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default;2930#ifndef NDEBUG31void Scheduler::dump() const {32dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n';33dbgs() << "[SCHEDULER]: ReadySet size is: " << ReadySet.size() << '\n';34dbgs() << "[SCHEDULER]: IssuedSet size is: " << IssuedSet.size() << '\n';35Resources->dump();36}37#endif3839Scheduler::Status Scheduler::isAvailable(const InstRef &IR) {40ResourceStateEvent RSE =41Resources->canBeDispatched(IR.getInstruction()->getUsedBuffers());42HadTokenStall = RSE != RS_BUFFER_AVAILABLE;4344switch (RSE) {45case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:46return Scheduler::SC_BUFFERS_FULL;47case ResourceStateEvent::RS_RESERVED:48return Scheduler::SC_DISPATCH_GROUP_STALL;49case ResourceStateEvent::RS_BUFFER_AVAILABLE:50break;51}5253// Give lower priority to LSUnit stall events.54LSUnit::Status LSS = LSU.isAvailable(IR);55HadTokenStall = LSS != LSUnit::LSU_AVAILABLE;5657switch (LSS) {58case LSUnit::LSU_LQUEUE_FULL:59return Scheduler::SC_LOAD_QUEUE_FULL;60case LSUnit::LSU_SQUEUE_FULL:61return Scheduler::SC_STORE_QUEUE_FULL;62case LSUnit::LSU_AVAILABLE:63return Scheduler::SC_AVAILABLE;64}6566llvm_unreachable("Don't know how to process this LSU state result!");67}6869void Scheduler::issueInstructionImpl(70InstRef &IR,71SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &UsedResources) {72Instruction *IS = IR.getInstruction();73const InstrDesc &D = IS->getDesc();7475// Issue the instruction and collect all the consumed resources76// into a vector. That vector is then used to notify the listener.77Resources->issueInstruction(D, UsedResources);7879// Notify the instruction that it started executing.80// This updates the internal state of each write.81IS->execute(IR.getSourceIndex());8283IS->computeCriticalRegDep();8485if (IS->isMemOp()) {86LSU.onInstructionIssued(IR);87const MemoryGroup &Group = LSU.getGroup(IS->getLSUTokenID());88IS->setCriticalMemDep(Group.getCriticalPredecessor());89}9091if (IS->isExecuting())92IssuedSet.emplace_back(IR);93else if (IS->isExecuted())94LSU.onInstructionExecuted(IR);95}9697// Release the buffered resources and issue the instruction.98void Scheduler::issueInstruction(99InstRef &IR,100SmallVectorImpl<std::pair<ResourceRef, ReleaseAtCycles>> &UsedResources,101SmallVectorImpl<InstRef> &PendingInstructions,102SmallVectorImpl<InstRef> &ReadyInstructions) {103const Instruction &Inst = *IR.getInstruction();104bool HasDependentUsers = Inst.hasDependentUsers();105HasDependentUsers |= Inst.isMemOp() && LSU.hasDependentUsers(IR);106107Resources->releaseBuffers(Inst.getUsedBuffers());108issueInstructionImpl(IR, UsedResources);109// Instructions that have been issued during this cycle might have unblocked110// other dependent instructions. Dependent instructions may be issued during111// this same cycle if operands have ReadAdvance entries. Promote those112// instructions to the ReadySet and notify the caller that those are ready.113if (HasDependentUsers)114if (promoteToPendingSet(PendingInstructions))115promoteToReadySet(ReadyInstructions);116}117118bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {119// Scan the set of waiting instructions and promote them to the120// ready set if operands are all ready.121unsigned PromotedElements = 0;122for (auto I = PendingSet.begin(), E = PendingSet.end(); I != E;) {123InstRef &IR = *I;124if (!IR)125break;126127// Check if there are unsolved register dependencies.128Instruction &IS = *IR.getInstruction();129if (!IS.isReady() && !IS.updatePending()) {130++I;131continue;132}133// Check if there are unsolved memory dependencies.134if (IS.isMemOp() && !LSU.isReady(IR)) {135++I;136continue;137}138139LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR140<< " promoted to the READY set.\n");141142Ready.emplace_back(IR);143ReadySet.emplace_back(IR);144145IR.invalidate();146++PromotedElements;147std::iter_swap(I, E - PromotedElements);148}149150PendingSet.resize(PendingSet.size() - PromotedElements);151return PromotedElements;152}153154bool Scheduler::promoteToPendingSet(SmallVectorImpl<InstRef> &Pending) {155// Scan the set of waiting instructions and promote them to the156// pending set if operands are all ready.157unsigned RemovedElements = 0;158for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) {159InstRef &IR = *I;160if (!IR)161break;162163// Check if this instruction is now ready. In case, force164// a transition in state using method 'updateDispatched()'.165Instruction &IS = *IR.getInstruction();166if (IS.isDispatched() && !IS.updateDispatched()) {167++I;168continue;169}170171if (IS.isMemOp() && LSU.isWaiting(IR)) {172++I;173continue;174}175176LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR177<< " promoted to the PENDING set.\n");178179Pending.emplace_back(IR);180PendingSet.emplace_back(IR);181182IR.invalidate();183++RemovedElements;184std::iter_swap(I, E - RemovedElements);185}186187WaitSet.resize(WaitSet.size() - RemovedElements);188return RemovedElements;189}190191InstRef Scheduler::select() {192unsigned QueueIndex = ReadySet.size();193for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) {194InstRef &IR = ReadySet[I];195if (QueueIndex == ReadySet.size() ||196Strategy->compare(IR, ReadySet[QueueIndex])) {197Instruction &IS = *IR.getInstruction();198uint64_t BusyResourceMask = Resources->checkAvailability(IS.getDesc());199if (BusyResourceMask)200IS.setCriticalResourceMask(BusyResourceMask);201BusyResourceUnits |= BusyResourceMask;202if (!BusyResourceMask)203QueueIndex = I;204}205}206207if (QueueIndex == ReadySet.size())208return InstRef();209210// We found an instruction to issue.211InstRef IR = ReadySet[QueueIndex];212std::swap(ReadySet[QueueIndex], ReadySet[ReadySet.size() - 1]);213ReadySet.pop_back();214return IR;215}216217void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) {218unsigned RemovedElements = 0;219for (auto I = IssuedSet.begin(), E = IssuedSet.end(); I != E;) {220InstRef &IR = *I;221if (!IR)222break;223Instruction &IS = *IR.getInstruction();224if (!IS.isExecuted()) {225LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR226<< " is still executing.\n");227++I;228continue;229}230231// Instruction IR has completed execution.232LSU.onInstructionExecuted(IR);233Executed.emplace_back(IR);234++RemovedElements;235IR.invalidate();236std::iter_swap(I, E - RemovedElements);237}238239IssuedSet.resize(IssuedSet.size() - RemovedElements);240}241242uint64_t Scheduler::analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts) {243llvm::append_range(Insts, ReadySet);244return BusyResourceUnits;245}246247void Scheduler::analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps,248SmallVectorImpl<InstRef> &MemDeps) {249const auto EndIt = PendingSet.end() - NumDispatchedToThePendingSet;250for (const InstRef &IR : make_range(PendingSet.begin(), EndIt)) {251const Instruction &IS = *IR.getInstruction();252if (Resources->checkAvailability(IS.getDesc()))253continue;254255if (IS.isMemOp() && LSU.isPending(IR))256MemDeps.emplace_back(IR);257258if (IS.isPending())259RegDeps.emplace_back(IR);260}261}262263void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed,264SmallVectorImpl<InstRef> &Executed,265SmallVectorImpl<InstRef> &Pending,266SmallVectorImpl<InstRef> &Ready) {267LSU.cycleEvent();268269// Release consumed resources.270Resources->cycleEvent(Freed);271272for (InstRef &IR : IssuedSet)273IR.getInstruction()->cycleEvent();274updateIssuedSet(Executed);275276for (InstRef &IR : PendingSet)277IR.getInstruction()->cycleEvent();278279for (InstRef &IR : WaitSet)280IR.getInstruction()->cycleEvent();281282promoteToPendingSet(Pending);283promoteToReadySet(Ready);284285NumDispatchedToThePendingSet = 0;286BusyResourceUnits = 0;287}288289bool Scheduler::mustIssueImmediately(const InstRef &IR) const {290const InstrDesc &Desc = IR.getInstruction()->getDesc();291if (Desc.isZeroLatency())292return true;293// Instructions that use an in-order dispatch/issue processor resource must be294// issued immediately to the pipeline(s). Any other in-order buffered295// resources (i.e. BufferSize=1) is consumed.296return Desc.MustIssueImmediately;297}298299bool Scheduler::dispatch(InstRef &IR) {300Instruction &IS = *IR.getInstruction();301Resources->reserveBuffers(IS.getUsedBuffers());302303// If necessary, reserve queue entries in the load-store unit (LSU).304if (IS.isMemOp())305IS.setLSUTokenID(LSU.dispatch(IR));306307if (IS.isDispatched() || (IS.isMemOp() && LSU.isWaiting(IR))) {308LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n");309WaitSet.push_back(IR);310return false;311}312313if (IS.isPending() || (IS.isMemOp() && LSU.isPending(IR))) {314LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR315<< " to the PendingSet\n");316PendingSet.push_back(IR);317++NumDispatchedToThePendingSet;318return false;319}320321assert(IS.isReady() && (!IS.isMemOp() || LSU.isReady(IR)) &&322"Unexpected internal state found!");323// Don't add a zero-latency instruction to the Ready queue.324// A zero-latency instruction doesn't consume any scheduler resources. That is325// because it doesn't need to be executed, and it is often removed at register326// renaming stage. For example, register-register moves are often optimized at327// register renaming stage by simply updating register aliases. On some328// targets, zero-idiom instructions (for example: a xor that clears the value329// of a register) are treated specially, and are often eliminated at register330// renaming stage.331if (!mustIssueImmediately(IR)) {332LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n");333ReadySet.push_back(IR);334}335336return true;337}338339} // namespace mca340} // namespace llvm341342343