aboutsummaryrefslogtreecommitdiff
path: root/lib/MCA/HardwareUnits/Scheduler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MCA/HardwareUnits/Scheduler.cpp')
-rw-r--r--lib/MCA/HardwareUnits/Scheduler.cpp166
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