[llvm-mca] Small refactoring in preparation for another patch that will improve the modularity of the Pipeline. NFCI

The main difference is that now `cycleStart()` and `cycleEnd()` return an
llvm::Error.

This patch implements a few minor style changes, and adds missing 'const' to
some methods.

llvm-svn: 339885
This commit is contained in:
Andrea Di Biagio 2018-08-16 15:43:09 +00:00
parent a9d64122c5
commit 492816d8e1
12 changed files with 67 additions and 45 deletions

View File

@ -33,7 +33,7 @@ void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
notifyEvent<HWInstructionEvent>(HWInstructionDispatchedEvent(IR, UsedRegs)); notifyEvent<HWInstructionEvent>(HWInstructionDispatchedEvent(IR, UsedRegs));
} }
bool DispatchStage::checkPRF(const InstRef &IR) { bool DispatchStage::checkPRF(const InstRef &IR) const {
SmallVector<unsigned, 4> RegDefs; SmallVector<unsigned, 4> RegDefs;
for (const std::unique_ptr<WriteState> &RegDef : for (const std::unique_ptr<WriteState> &RegDef :
IR.getInstruction()->getDefs()) IR.getInstruction()->getDefs())
@ -50,7 +50,7 @@ bool DispatchStage::checkPRF(const InstRef &IR) {
return true; return true;
} }
bool DispatchStage::checkRCU(const InstRef &IR) { bool DispatchStage::checkRCU(const InstRef &IR) const {
const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps; const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
if (RCU.isAvailable(NumMicroOps)) if (RCU.isAvailable(NumMicroOps))
return true; return true;
@ -59,7 +59,7 @@ bool DispatchStage::checkRCU(const InstRef &IR) {
return false; return false;
} }
bool DispatchStage::checkScheduler(const InstRef &IR) { bool DispatchStage::checkScheduler(const InstRef &IR) const {
HWStallEvent::GenericEventType Event; HWStallEvent::GenericEventType Event;
const bool Ready = SC.canBeDispatched(IR, Event); const bool Ready = SC.canBeDispatched(IR, Event);
if (!Ready) if (!Ready)
@ -131,9 +131,10 @@ void DispatchStage::dispatch(InstRef IR) {
notifyInstructionDispatched(IR, RegisterFiles); notifyInstructionDispatched(IR, RegisterFiles);
} }
void DispatchStage::cycleStart() { llvm::Error DispatchStage::cycleStart() {
AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver; AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U; CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
return llvm::ErrorSuccess();
} }
Stage::Status DispatchStage::execute(InstRef &IR) { Stage::Status DispatchStage::execute(InstRef &IR) {

View File

@ -58,9 +58,9 @@ class DispatchStage final : public Stage {
RegisterFile &PRF; RegisterFile &PRF;
Scheduler &SC; Scheduler &SC;
bool checkRCU(const InstRef &IR); bool checkRCU(const InstRef &IR) const;
bool checkPRF(const InstRef &IR); bool checkPRF(const InstRef &IR) const;
bool checkScheduler(const InstRef &IR); bool checkScheduler(const InstRef &IR) const;
void dispatch(InstRef IR); void dispatch(InstRef IR);
void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI); void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
@ -93,7 +93,7 @@ public:
// The retire stage, which controls the RCU, might have items to complete but // The retire stage, which controls the RCU, might have items to complete but
// RetireStage::hasWorkToComplete will check for that case. // RetireStage::hasWorkToComplete will check for that case.
bool hasWorkToComplete() const override { return false; } bool hasWorkToComplete() const override { return false; }
void cycleStart() override; llvm::Error cycleStart() override;
Status execute(InstRef &IR) override; Status execute(InstRef &IR) override;
void notifyDispatchStall(const InstRef &IR, unsigned EventType); void notifyDispatchStall(const InstRef &IR, unsigned EventType);

View File

@ -35,7 +35,7 @@ void ExecuteStage::reclaimSchedulerResources() {
} }
// Update the scheduler's instruction queues. // Update the scheduler's instruction queues.
void ExecuteStage::updateSchedulerQueues() { Error ExecuteStage::updateSchedulerQueues() {
SmallVector<InstRef, 4> InstructionIDs; SmallVector<InstRef, 4> InstructionIDs;
HWS.updateIssuedSet(InstructionIDs); HWS.updateIssuedSet(InstructionIDs);
for (const InstRef &IR : InstructionIDs) for (const InstRef &IR : InstructionIDs)
@ -45,10 +45,11 @@ void ExecuteStage::updateSchedulerQueues() {
HWS.updatePendingQueue(InstructionIDs); HWS.updatePendingQueue(InstructionIDs);
for (const InstRef &IR : InstructionIDs) for (const InstRef &IR : InstructionIDs)
notifyInstructionReady(IR); notifyInstructionReady(IR);
return ErrorSuccess();
} }
// Issue instructions that are waiting in the scheduler's ready queue. // Issue instructions that are waiting in the scheduler's ready queue.
void ExecuteStage::issueReadyInstructions() { Error ExecuteStage::issueReadyInstructions() {
SmallVector<InstRef, 4> InstructionIDs; SmallVector<InstRef, 4> InstructionIDs;
InstRef IR = HWS.select(); InstRef IR = HWS.select();
while (IR.isValid()) { while (IR.isValid()) {
@ -75,6 +76,8 @@ void ExecuteStage::issueReadyInstructions() {
// Select the next instruction to issue. // Select the next instruction to issue.
IR = HWS.select(); IR = HWS.select();
} }
return ErrorSuccess();
} }
// The following routine is the maintenance routine of the ExecuteStage. // The following routine is the maintenance routine of the ExecuteStage.
@ -89,10 +92,11 @@ void ExecuteStage::issueReadyInstructions() {
// Notifications are issued to this stage's listeners when instructions are // Notifications are issued to this stage's listeners when instructions are
// moved between the HWS's queues. In particular, when an instruction becomes // moved between the HWS's queues. In particular, when an instruction becomes
// ready or executed. // ready or executed.
void ExecuteStage::cycleStart() { Error ExecuteStage::cycleStart() {
reclaimSchedulerResources(); reclaimSchedulerResources();
updateSchedulerQueues(); if (Error S = updateSchedulerQueues())
issueReadyInstructions(); return S;
return issueReadyInstructions();
} }
// Schedule the instruction for execution on the hardware. // Schedule the instruction for execution on the hardware.

View File

@ -33,8 +33,8 @@ class ExecuteStage final : public Stage {
// The following routines are used to maintain the HWS. // The following routines are used to maintain the HWS.
void reclaimSchedulerResources(); void reclaimSchedulerResources();
void updateSchedulerQueues(); llvm::Error updateSchedulerQueues();
void issueReadyInstructions(); llvm::Error issueReadyInstructions();
ExecuteStage(const ExecuteStage &Other) = delete; ExecuteStage(const ExecuteStage &Other) = delete;
ExecuteStage &operator=(const ExecuteStage &Other) = delete; ExecuteStage &operator=(const ExecuteStage &Other) = delete;
@ -42,11 +42,15 @@ class ExecuteStage final : public Stage {
public: public:
ExecuteStage(RetireControlUnit &R, Scheduler &S) : Stage(), RCU(R), HWS(S) {} ExecuteStage(RetireControlUnit &R, Scheduler &S) : Stage(), RCU(R), HWS(S) {}
// The ExecuteStage will always complete all of its work per call to // This stage works under the assumption that the Pipeline will eventually
// execute(), so it is never left in a 'to-be-processed' state. // execute a retire stage. We don't need to check if pipelines and/or
// schedulers have instructions to process, because those instructions are
// also tracked by the retire control unit. That means,
// RetireControlUnit::hasWorkToComplete() is responsible for checking if there
// are still instructions in-flight in the out-of-order backend.
bool hasWorkToComplete() const override { return false; } bool hasWorkToComplete() const override { return false; }
void cycleStart() override; llvm::Error cycleStart() override;
Status execute(InstRef &IR) override; Status execute(InstRef &IR) override;
void void

View File

@ -35,7 +35,7 @@ Stage::Status FetchStage::execute(InstRef &IR) {
void FetchStage::postExecute() { SM.updateNext(); } void FetchStage::postExecute() { SM.updateNext(); }
void FetchStage::cycleEnd() { llvm::Error FetchStage::cycleEnd() {
// Find the first instruction which hasn't been retired. // Find the first instruction which hasn't been retired.
const InstMap::iterator It = const InstMap::iterator It =
llvm::find_if(Instructions, [](const InstMap::value_type &KeyValuePair) { llvm::find_if(Instructions, [](const InstMap::value_type &KeyValuePair) {
@ -45,6 +45,8 @@ void FetchStage::cycleEnd() {
// Erase instructions up to the first that hasn't been retired. // Erase instructions up to the first that hasn't been retired.
if (It != Instructions.begin()) if (It != Instructions.begin())
Instructions.erase(Instructions.begin(), It); Instructions.erase(Instructions.begin(), It);
return llvm::ErrorSuccess();
} }
} // namespace mca } // namespace mca

View File

@ -38,7 +38,7 @@ public:
bool hasWorkToComplete() const override; bool hasWorkToComplete() const override;
Status execute(InstRef &IR) override; Status execute(InstRef &IR) override;
void postExecute() override; void postExecute() override;
void cycleEnd() override; llvm::Error cycleEnd() override;
}; };
} // namespace mca } // namespace mca

View File

@ -32,10 +32,9 @@ void Pipeline::addEventListener(HWEventListener *Listener) {
} }
bool Pipeline::hasWorkToProcess() { bool Pipeline::hasWorkToProcess() {
const auto It = llvm::find_if(Stages, [](const std::unique_ptr<Stage> &S) { return llvm::any_of(Stages, [](const std::unique_ptr<Stage> &S) {
return S->hasWorkToComplete(); return S->hasWorkToComplete();
}); });
return It != Stages.end();
} }
// This routine returns early if any stage returns 'false' after execute() is // This routine returns early if any stage returns 'false' after execute() is
@ -62,6 +61,8 @@ void Pipeline::postExecuteStages() {
} }
llvm::Error Pipeline::run() { llvm::Error Pipeline::run() {
assert(!Stages.empty() && "Unexpected empty pipeline found!");
while (hasWorkToProcess()) { while (hasWorkToProcess()) {
notifyCycleBegin(); notifyCycleBegin();
if (llvm::Error Err = runCycle()) if (llvm::Error Err = runCycle())
@ -73,13 +74,18 @@ llvm::Error Pipeline::run() {
} }
llvm::Error Pipeline::runCycle() { llvm::Error Pipeline::runCycle() {
// Update the stages before we do any processing for this cycle. // Update stages before we start processing new instructions.
InstRef IR; llvm::Error Err = llvm::ErrorSuccess();
for (auto &S : Stages) for (auto I = Stages.begin(), E = Stages.end(); I != E && !Err; ++I) {
S->cycleStart(); const std::unique_ptr<Stage> &S = *I;
Err = S->cycleStart();
}
// Continue executing this cycle until any stage claims it cannot make if (Err)
// progress. return Err;
// Now fetch and execute new instructions.
InstRef IR;
while (true) { while (true) {
preExecuteStages(); preExecuteStages();
Stage::Status Val = executeStages(IR); Stage::Status Val = executeStages(IR);
@ -90,9 +96,12 @@ llvm::Error Pipeline::runCycle() {
postExecuteStages(); postExecuteStages();
} }
for (auto &S : Stages) // Update stages in preparation for a new cycle.
S->cycleEnd(); for (auto I = Stages.begin(), E = Stages.end(); I != E && !Err; ++I) {
return llvm::ErrorSuccess(); const std::unique_ptr<Stage> &S = *I;
Err = S->cycleEnd();
}
return Err;
} }
void Pipeline::notifyCycleBegin() { void Pipeline::notifyCycleBegin() {

View File

@ -18,15 +18,13 @@
#include "HWEventListener.h" #include "HWEventListener.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
using namespace llvm;
#define DEBUG_TYPE "llvm-mca" #define DEBUG_TYPE "llvm-mca"
namespace mca { namespace mca {
void RetireStage::cycleStart() { llvm::Error RetireStage::cycleStart() {
if (RCU.isEmpty()) if (RCU.isEmpty())
return; return llvm::ErrorSuccess();
const unsigned MaxRetirePerCycle = RCU.getMaxRetirePerCycle(); const unsigned MaxRetirePerCycle = RCU.getMaxRetirePerCycle();
unsigned NumRetired = 0; unsigned NumRetired = 0;
@ -40,11 +38,13 @@ void RetireStage::cycleStart() {
notifyInstructionRetired(Current.IR); notifyInstructionRetired(Current.IR);
NumRetired++; NumRetired++;
} }
return llvm::ErrorSuccess();
} }
void RetireStage::notifyInstructionRetired(const InstRef &IR) { void RetireStage::notifyInstructionRetired(const InstRef &IR) {
LLVM_DEBUG(dbgs() << "[E] Instruction Retired: #" << IR << '\n'); LLVM_DEBUG(llvm::dbgs() << "[E] Instruction Retired: #" << IR << '\n');
SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles()); llvm::SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles());
const Instruction &Inst = *IR.getInstruction(); const Instruction &Inst = *IR.getInstruction();
const InstrDesc &Desc = Inst.getDesc(); const InstrDesc &Desc = Inst.getDesc();

View File

@ -36,7 +36,7 @@ public:
: Stage(), RCU(R), PRF(F) {} : Stage(), RCU(R), PRF(F) {}
bool hasWorkToComplete() const override { return !RCU.isEmpty(); } bool hasWorkToComplete() const override { return !RCU.isEmpty(); }
void cycleStart() override; llvm::Error cycleStart() override;
Status execute(InstRef &IR) override { return Stage::Continue; } Status execute(InstRef &IR) override { return Stage::Continue; }
void notifyInstructionRetired(const InstRef &IR); void notifyInstructionRetired(const InstRef &IR);
void onInstructionExecuted(unsigned TokenID); void onInstructionExecuted(unsigned TokenID);

View File

@ -44,6 +44,7 @@ public:
void updateNext() { Current++; } void updateNext() { Current++; }
const SourceRef peekNext() const { const SourceRef peekNext() const {
assert(hasNext() && "Already at end of sequence!");
unsigned Index = getCurrentInstructionIndex(); unsigned Index = getCurrentInstructionIndex();
return SourceRef(Current, Sequence[Index].get()); return SourceRef(Current, Sequence[Index].get());
} }

View File

@ -18,7 +18,7 @@
namespace mca { namespace mca {
// Pin the vtable here in the implementation file. // Pin the vtable here in the implementation file.
Stage::Stage() {} Stage::~Stage() = default;
void Stage::addListener(HWEventListener *Listener) { void Stage::addListener(HWEventListener *Listener) {
Listeners.insert(Listener); Listeners.insert(Listener);

View File

@ -25,9 +25,10 @@ namespace mca {
class InstRef; class InstRef;
class Stage { class Stage {
std::set<HWEventListener *> Listeners;
Stage(const Stage &Other) = delete; Stage(const Stage &Other) = delete;
Stage &operator=(const Stage &Other) = delete; Stage &operator=(const Stage &Other) = delete;
std::set<HWEventListener *> Listeners;
public: public:
/// A Stage's execute() returns Continue, Stop, or an error. Returning /// A Stage's execute() returns Continue, Stop, or an error. Returning
@ -46,8 +47,8 @@ protected:
const std::set<HWEventListener *> &getListeners() const { return Listeners; } const std::set<HWEventListener *> &getListeners() const { return Listeners; }
public: public:
Stage(); Stage() {}
virtual ~Stage() = default; virtual ~Stage();
/// Called prior to preExecute to ensure that the stage has items that it /// Called prior to preExecute to ensure that the stage has items that it
/// is to process. For example, a FetchStage might have more instructions /// is to process. For example, a FetchStage might have more instructions
@ -57,10 +58,10 @@ public:
/// Called once at the start of each cycle. This can be used as a setup /// Called once at the start of each cycle. This can be used as a setup
/// phase to prepare for the executions during the cycle. /// phase to prepare for the executions during the cycle.
virtual void cycleStart() {} virtual llvm::Error cycleStart() { return llvm::ErrorSuccess(); }
/// Called once at the end of each cycle. /// Called once at the end of each cycle.
virtual void cycleEnd() {} virtual llvm::Error cycleEnd() { return llvm::ErrorSuccess(); }
/// Called prior to executing the list of stages. /// Called prior to executing the list of stages.
/// This can be called multiple times per cycle. /// This can be called multiple times per cycle.
@ -80,7 +81,7 @@ public:
void addListener(HWEventListener *Listener); void addListener(HWEventListener *Listener);
/// Notify listeners of a particular hardware event. /// Notify listeners of a particular hardware event.
template <typename EventT> void notifyEvent(const EventT &Event) { template <typename EventT> void notifyEvent(const EventT &Event) const {
for (HWEventListener *Listener : Listeners) for (HWEventListener *Listener : Listeners)
Listener->onEvent(Event); Listener->onEvent(Event);
} }