Index: tools/llvm-mca/Backend.h =================================================================== --- tools/llvm-mca/Backend.h +++ tools/llvm-mca/Backend.h @@ -15,14 +15,13 @@ #ifndef LLVM_TOOLS_LLVM_MCA_BACKEND_H #define LLVM_TOOLS_LLVM_MCA_BACKEND_H -#include "DispatchStage.h" -#include "ExecuteStage.h" #include "FetchStage.h" #include "InstrBuilder.h" #include "RegisterFile.h" #include "RetireControlUnit.h" -#include "RetireStage.h" #include "Scheduler.h" +#include "Stage.h" +#include "llvm/ADT/SmallVector.h" namespace mca { @@ -60,35 +59,23 @@ RegisterFile PRF; Scheduler HWS; - /// TODO: Eventually this will become a list of unique Stage* that this - /// backend pipeline executes. - std::unique_ptr Fetch; - std::unique_ptr Dispatch; - std::unique_ptr Execute; - std::unique_ptr Retire; - + /// An ordered list of stages that define this backend's instruction pipeline. + llvm::SmallVector, 8> Stages; std::set Listeners; unsigned Cycles; + bool executeStages(InstRef &IR); + void postExecuteStages(const InstRef &IR); + bool hasWorkToProcess(); void runCycle(unsigned Cycle); + void appendStage(std::unique_ptr S) { Stages.push_back(std::move(S)); } public: Backend(const llvm::MCSubtargetInfo &Subtarget, const llvm::MCRegisterInfo &MRI, std::unique_ptr InitialStage, unsigned DispatchWidth = 0, unsigned RegisterFileSize = 0, unsigned LoadQueueSize = 0, - unsigned StoreQueueSize = 0, bool AssumeNoAlias = false) - : RCU(Subtarget.getSchedModel()), - PRF(Subtarget.getSchedModel(), MRI, RegisterFileSize), - HWS(Subtarget.getSchedModel(), LoadQueueSize, StoreQueueSize, - AssumeNoAlias), - Fetch(std::move(InitialStage)), - Dispatch(llvm::make_unique( - this, Subtarget, MRI, RegisterFileSize, DispatchWidth, RCU, PRF, - HWS)), - Execute(llvm::make_unique(this, RCU, HWS)), - Retire(llvm::make_unique(this, RCU, PRF)), Cycles(0) {} - + unsigned StoreQueueSize = 0, bool AssumeNoAlias = false); void run(); void addEventListener(HWEventListener *Listener); void notifyCycleBegin(unsigned Cycle); Index: tools/llvm-mca/Backend.cpp =================================================================== --- tools/llvm-mca/Backend.cpp +++ tools/llvm-mca/Backend.cpp @@ -13,8 +13,11 @@ //===----------------------------------------------------------------------===// #include "Backend.h" +#include "DispatchStage.h" +#include "ExecuteStage.h" #include "FetchStage.h" #include "HWEventListener.h" +#include "RetireStage.h" #include "llvm/CodeGen/TargetSchedule.h" #include "llvm/Support/Debug.h" @@ -24,13 +27,52 @@ using namespace llvm; +Backend::Backend(const llvm::MCSubtargetInfo &Subtarget, + const llvm::MCRegisterInfo &MRI, + std::unique_ptr InitialStage, + unsigned DispatchWidth, unsigned RegisterFileSize, + unsigned LoadQueueSize, unsigned StoreQueueSize, + bool AssumeNoAlias) + : RCU(Subtarget.getSchedModel()), + PRF(Subtarget.getSchedModel(), MRI, RegisterFileSize), + HWS(Subtarget.getSchedModel(), LoadQueueSize, StoreQueueSize, + AssumeNoAlias) { + Cycles = 0; + appendStage(std::move(InitialStage)); + appendStage(llvm::make_unique( + this, Subtarget, MRI, RegisterFileSize, DispatchWidth, RCU, PRF, HWS)); + appendStage(llvm::make_unique(this, RCU, PRF)); + appendStage(llvm::make_unique(this, RCU, HWS)); +} + void Backend::addEventListener(HWEventListener *Listener) { if (Listener) Listeners.insert(Listener); } +bool Backend::hasWorkToProcess() { + const auto It = llvm::find_if(Stages, [](const std::unique_ptr &S) { + return S->hasWorkToComplete(); + }); + return It != Stages.end(); +} + +// This routine returns early if any stage returns 'false' after execute() is +// called on it. +bool Backend::executeStages(InstRef &IR) { + for (const std::unique_ptr &S : Stages) + if (!S->execute(IR)) + return false; + return true; +} + +void Backend::postExecuteStages(const InstRef &IR) { + for (const std::unique_ptr &S : Stages) + S->postExecute(IR); +} + void Backend::run() { - while (Fetch->isReady() || !Dispatch->isReady()) + while (hasWorkToProcess()) runCycle(Cycles++); } @@ -39,17 +81,13 @@ // Update the stages before we do any processing for this cycle. InstRef IR; - Retire->preExecute(IR); - Dispatch->preExecute(IR); - Execute->preExecute(IR); - - // Fetch instructions and dispatch them to the hardware. - while (Fetch->execute(IR)) { - if (!Dispatch->execute(IR)) - break; - Execute->execute(IR); - Fetch->postExecute(IR); - } + for (auto &S : Stages) + S->preExecute(IR); + + // Continue executing this cycle until any stage claims it cannot make + // progress. + while (executeStages(IR)) + postExecuteStages(IR); notifyCycleEnd(Cycle); } Index: tools/llvm-mca/DispatchStage.h =================================================================== --- tools/llvm-mca/DispatchStage.h +++ tools/llvm-mca/DispatchStage.h @@ -69,7 +69,6 @@ bool checkPRF(const InstRef &IR); bool checkScheduler(const InstRef &IR); void dispatch(InstRef IR); - bool isRCUEmpty() const { return RCU.isEmpty(); } void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI); void notifyInstructionDispatched(const InstRef &IR, @@ -97,7 +96,10 @@ : DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth), CarryOver(0U), Owner(B), STI(Subtarget), RCU(R), PRF(F), SC(Sched) {} - virtual bool isReady() const override final { return isRCUEmpty(); } + // We can always try to dispatch, so returning false is okay in this case. + // The retire stage, which controls the RCU, might have items to complete but + // RetireStage::hasWorkToComplete will check for that case. + virtual bool hasWorkToComplete() const override final { return false; } virtual void preExecute(const InstRef &IR) override final; virtual bool execute(InstRef &IR) override final; void notifyDispatchStall(const InstRef &IR, unsigned EventType); Index: tools/llvm-mca/ExecuteStage.h =================================================================== --- tools/llvm-mca/ExecuteStage.h +++ tools/llvm-mca/ExecuteStage.h @@ -45,6 +45,10 @@ ExecuteStage(const ExecuteStage &Other) = delete; ExecuteStage &operator=(const ExecuteStage &Other) = delete; + // The ExecuteStage will always complete all of its work per call to + // execute(), so it is never left in a 'to-be-processed' state. + virtual bool hasWorkToComplete() const override final { return false; } + virtual void preExecute(const InstRef &IR) override final; virtual bool execute(InstRef &IR) override final; Index: tools/llvm-mca/ExecuteStage.cpp =================================================================== --- tools/llvm-mca/ExecuteStage.cpp +++ tools/llvm-mca/ExecuteStage.cpp @@ -110,9 +110,10 @@ HWS.reserveBuffers(Desc.Buffers); notifyReservedBuffers(Desc.Buffers); - // Obtain a slot in the LSU. + // Obtain a slot in the LSU. If we cannot reserve resources, return true, so + // that succeeding stages can make progress. if (!HWS.reserveResources(IR)) - return false; + return true; // If we did not return early, then the scheduler is ready for execution. notifyInstructionReady(IR); Index: tools/llvm-mca/FetchStage.h =================================================================== --- tools/llvm-mca/FetchStage.h +++ tools/llvm-mca/FetchStage.h @@ -34,7 +34,7 @@ FetchStage(const FetchStage &Other) = delete; FetchStage &operator=(const FetchStage &Other) = delete; - bool isReady() const override final; + bool hasWorkToComplete() const override final; bool execute(InstRef &IR) override final; void postExecute(const InstRef &IR) override final; }; Index: tools/llvm-mca/FetchStage.cpp =================================================================== --- tools/llvm-mca/FetchStage.cpp +++ tools/llvm-mca/FetchStage.cpp @@ -17,7 +17,7 @@ namespace mca { -bool FetchStage::isReady() const { return SM.hasNext(); } +bool FetchStage::hasWorkToComplete() const { return SM.hasNext(); } bool FetchStage::execute(InstRef &IR) { if (!SM.hasNext()) Index: tools/llvm-mca/RetireStage.h =================================================================== --- tools/llvm-mca/RetireStage.h +++ tools/llvm-mca/RetireStage.h @@ -37,6 +37,7 @@ RetireStage(const RetireStage &Other) = delete; RetireStage &operator=(const RetireStage &Other) = delete; + virtual bool hasWorkToComplete() const override final { return !RCU.isEmpty(); } virtual void preExecute(const InstRef &IR) override final; virtual bool execute(InstRef &IR) override final { return true; } void notifyInstructionRetired(const InstRef &IR); Index: tools/llvm-mca/Stage.h =================================================================== --- tools/llvm-mca/Stage.h +++ tools/llvm-mca/Stage.h @@ -32,10 +32,11 @@ Stage(); virtual ~Stage() = default; - /// Called prior to preExecute to ensure that the stage can operate. - /// TODO: Remove this logic once backend::run and backend::runCycle become - /// one routine. - virtual bool isReady() const { return true; } + /// Called prior to preExecute to ensure that the stage has items that it + /// is to process. For example, a FetchStage might have more instructions + /// that need to be processed, or a RCU might have items that have yet to + /// retire. + virtual bool hasWorkToComplete() const = 0; /// Called as a setup phase to prepare for the main stage execution. virtual void preExecute(const InstRef &IR) {}