diff options
Diffstat (limited to 'lib/MCA/HardwareUnits/Scheduler.cpp')
-rw-r--r-- | lib/MCA/HardwareUnits/Scheduler.cpp | 166 |
1 files changed, 131 insertions, 35 deletions
diff --git a/lib/MCA/HardwareUnits/Scheduler.cpp b/lib/MCA/HardwareUnits/Scheduler.cpp index 355ef79d06a6..0f0f2ffb8325 100644 --- a/lib/MCA/HardwareUnits/Scheduler.cpp +++ b/lib/MCA/HardwareUnits/Scheduler.cpp @@ -1,9 +1,8 @@ //===--------------------- Scheduler.cpp ------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -38,10 +37,13 @@ void Scheduler::dump() const { } #endif -Scheduler::Status Scheduler::isAvailable(const InstRef &IR) const { +Scheduler::Status Scheduler::isAvailable(const InstRef &IR) { const InstrDesc &Desc = IR.getInstruction()->getDesc(); - switch (Resources->canBeDispatched(Desc.Buffers)) { + ResourceStateEvent RSE = Resources->canBeDispatched(Desc.Buffers); + HadTokenStall = RSE != RS_BUFFER_AVAILABLE; + + switch (RSE) { case ResourceStateEvent::RS_BUFFER_UNAVAILABLE: return Scheduler::SC_BUFFERS_FULL; case ResourceStateEvent::RS_RESERVED: @@ -51,7 +53,10 @@ Scheduler::Status Scheduler::isAvailable(const InstRef &IR) const { } // Give lower priority to LSUnit stall events. - switch (LSU.isAvailable(IR)) { + LSUnit::Status LSS = LSU.isAvailable(IR); + HadTokenStall = LSS != LSUnit::LSU_AVAILABLE; + + switch (LSS) { case LSUnit::LSU_LQUEUE_FULL: return Scheduler::SC_LOAD_QUEUE_FULL; case LSUnit::LSU_SQUEUE_FULL: @@ -75,7 +80,15 @@ void Scheduler::issueInstructionImpl( // Notify the instruction that it started executing. // This updates the internal state of each write. - IS->execute(); + IS->execute(IR.getSourceIndex()); + + IS->computeCriticalRegDep(); + + if (IS->isMemOp()) { + LSU.onInstructionIssued(IR); + const MemoryGroup &Group = LSU.getGroup(IS->getLSUTokenID()); + IS->setCriticalMemDep(Group.getCriticalPredecessor()); + } if (IS->isExecuting()) IssuedSet.emplace_back(IR); @@ -87,9 +100,11 @@ void Scheduler::issueInstructionImpl( void Scheduler::issueInstruction( InstRef &IR, SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &UsedResources, + SmallVectorImpl<InstRef> &PendingInstructions, SmallVectorImpl<InstRef> &ReadyInstructions) { const Instruction &Inst = *IR.getInstruction(); bool HasDependentUsers = Inst.hasDependentUsers(); + HasDependentUsers |= Inst.isMemOp() && LSU.hasDependentUsers(IR); Resources->releaseBuffers(Inst.getDesc().Buffers); issueInstructionImpl(IR, UsedResources); @@ -98,12 +113,49 @@ void Scheduler::issueInstruction( // this same cycle if operands have ReadAdvance entries. Promote those // instructions to the ReadySet and notify the caller that those are ready. if (HasDependentUsers) - promoteToReadySet(ReadyInstructions); + if (promoteToPendingSet(PendingInstructions)) + promoteToReadySet(ReadyInstructions); +} + +bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { + // Scan the set of waiting instructions and promote them to the + // ready set if operands are all ready. + unsigned PromotedElements = 0; + for (auto I = PendingSet.begin(), E = PendingSet.end(); I != E;) { + InstRef &IR = *I; + if (!IR) + break; + + // Check if there are unsolved register dependencies. + Instruction &IS = *IR.getInstruction(); + if (!IS.isReady() && !IS.updatePending()) { + ++I; + continue; + } + // Check if there are unsolved memory dependencies. + if (IS.isMemOp() && !LSU.isReady(IR)) { + ++I; + continue; + } + + LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR + << " promoted to the READY set.\n"); + + Ready.emplace_back(IR); + ReadySet.emplace_back(IR); + + IR.invalidate(); + ++PromotedElements; + std::iter_swap(I, E - PromotedElements); + } + + PendingSet.resize(PendingSet.size() - PromotedElements); + return PromotedElements; } -void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { +bool Scheduler::promoteToPendingSet(SmallVectorImpl<InstRef> &Pending) { // Scan the set of waiting instructions and promote them to the - // ready queue if operands are all ready. + // pending set if operands are all ready. unsigned RemovedElements = 0; for (auto I = WaitSet.begin(), E = WaitSet.end(); I != E;) { InstRef &IR = *I; @@ -111,19 +163,23 @@ void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { break; // Check if this instruction is now ready. In case, force - // a transition in state using method 'update()'. + // a transition in state using method 'updateDispatched()'. Instruction &IS = *IR.getInstruction(); - if (!IS.isReady()) - IS.update(); + if (IS.isDispatched() && !IS.updateDispatched()) { + ++I; + continue; + } - // Check if there are still unsolved data dependencies. - if (!isReady(IR)) { + if (IS.isMemOp() && LSU.isWaiting(IR)) { ++I; continue; } - Ready.emplace_back(IR); - ReadySet.emplace_back(IR); + LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << IR + << " promoted to the PENDING set.\n"); + + Pending.emplace_back(IR); + PendingSet.emplace_back(IR); IR.invalidate(); ++RemovedElements; @@ -131,16 +187,21 @@ void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) { } WaitSet.resize(WaitSet.size() - RemovedElements); + return RemovedElements; } InstRef Scheduler::select() { unsigned QueueIndex = ReadySet.size(); for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) { - const InstRef &IR = ReadySet[I]; + InstRef &IR = ReadySet[I]; if (QueueIndex == ReadySet.size() || Strategy->compare(IR, ReadySet[QueueIndex])) { - const InstrDesc &D = IR.getInstruction()->getDesc(); - if (Resources->canBeIssued(D)) + Instruction &IS = *IR.getInstruction(); + uint64_t BusyResourceMask = Resources->checkAvailability(IS.getDesc()); + if (BusyResourceMask) + IS.setCriticalResourceMask(BusyResourceMask); + BusyResourceUnits |= BusyResourceMask; + if (!BusyResourceMask) QueueIndex = I; } } @@ -180,22 +241,51 @@ void Scheduler::updateIssuedSet(SmallVectorImpl<InstRef> &Executed) { IssuedSet.resize(IssuedSet.size() - RemovedElements); } +uint64_t Scheduler::analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts) { + Insts.insert(Insts.end(), ReadySet.begin(), ReadySet.end()); + return BusyResourceUnits; +} + +void Scheduler::analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps, + SmallVectorImpl<InstRef> &MemDeps) { + const auto EndIt = PendingSet.end() - NumDispatchedToThePendingSet; + for (const InstRef &IR : make_range(PendingSet.begin(), EndIt)) { + const Instruction &IS = *IR.getInstruction(); + if (Resources->checkAvailability(IS.getDesc())) + continue; + + if (IS.isMemOp() && LSU.isPending(IR)) + MemDeps.emplace_back(IR); + + if (IS.isPending()) + RegDeps.emplace_back(IR); + } +} + void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed, SmallVectorImpl<InstRef> &Executed, + SmallVectorImpl<InstRef> &Pending, SmallVectorImpl<InstRef> &Ready) { + LSU.cycleEvent(); + // Release consumed resources. Resources->cycleEvent(Freed); - // Propagate the cycle event to the 'Issued' and 'Wait' sets. for (InstRef &IR : IssuedSet) IR.getInstruction()->cycleEvent(); - updateIssuedSet(Executed); + for (InstRef &IR : PendingSet) + IR.getInstruction()->cycleEvent(); + for (InstRef &IR : WaitSet) IR.getInstruction()->cycleEvent(); + promoteToPendingSet(Pending); promoteToReadySet(Ready); + + NumDispatchedToThePendingSet = 0; + BusyResourceUnits = 0; } bool Scheduler::mustIssueImmediately(const InstRef &IR) const { @@ -208,21 +298,31 @@ bool Scheduler::mustIssueImmediately(const InstRef &IR) const { return Desc.MustIssueImmediately; } -void Scheduler::dispatch(const InstRef &IR) { - const InstrDesc &Desc = IR.getInstruction()->getDesc(); +bool Scheduler::dispatch(InstRef &IR) { + Instruction &IS = *IR.getInstruction(); + const InstrDesc &Desc = IS.getDesc(); Resources->reserveBuffers(Desc.Buffers); // If necessary, reserve queue entries in the load-store unit (LSU). - bool IsMemOp = Desc.MayLoad || Desc.MayStore; - if (IsMemOp) - LSU.dispatch(IR); + if (IS.isMemOp()) + IS.setLSUTokenID(LSU.dispatch(IR)); - if (!isReady(IR)) { + if (IS.isDispatched() || (IS.isMemOp() && LSU.isWaiting(IR))) { LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n"); WaitSet.push_back(IR); - return; + return false; + } + + if (IS.isPending() || (IS.isMemOp() && LSU.isPending(IR))) { + LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR + << " to the PendingSet\n"); + PendingSet.push_back(IR); + ++NumDispatchedToThePendingSet; + return false; } + assert(IS.isReady() && (!IS.isMemOp() || LSU.isReady(IR)) && + "Unexpected internal state found!"); // Don't add a zero-latency instruction to the Ready queue. // A zero-latency instruction doesn't consume any scheduler resources. That is // because it doesn't need to be executed, and it is often removed at register @@ -235,12 +335,8 @@ void Scheduler::dispatch(const InstRef &IR) { LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the ReadySet\n"); ReadySet.push_back(IR); } -} -bool Scheduler::isReady(const InstRef &IR) const { - const InstrDesc &Desc = IR.getInstruction()->getDesc(); - bool IsMemOp = Desc.MayLoad || Desc.MayStore; - return IR.getInstruction()->isReady() && (!IsMemOp || LSU.isReady(IR)); + return true; } } // namespace mca |