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 { @@ -55,40 +54,22 @@ /// histograms. For example, it tracks how the dispatch group size changes /// over time. class Backend { - // The following are the simulated hardware components of the backend. - RetireControlUnit RCU; - 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); 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) {} - + Backend(unsigned DispatchWidth = 0, unsigned RegisterFileSize = 0, + unsigned LoadQueueSize = 0, unsigned StoreQueueSize = 0, + bool AssumeNoAlias = false) + : Cycles(0) {} + void appendStage(std::unique_ptr S) { Stages.push_back(std::move(S)); } 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,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "Backend.h" -#include "FetchStage.h" #include "HWEventListener.h" #include "llvm/CodeGen/TargetSchedule.h" #include "llvm/Support/Debug.h" @@ -29,8 +28,29 @@ 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 +59,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 @@ -64,7 +64,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, @@ -92,7 +91,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) {} Index: tools/llvm-mca/llvm-mca.cpp =================================================================== --- tools/llvm-mca/llvm-mca.cpp +++ tools/llvm-mca/llvm-mca.cpp @@ -23,13 +23,19 @@ #include "BackendPrinter.h" #include "CodeRegion.h" +#include "DispatchStage.h" #include "DispatchStatistics.h" +#include "ExecuteStage.h" #include "FetchStage.h" #include "InstructionInfoView.h" #include "InstructionTables.h" +#include "RegisterFile.h" #include "RegisterFileStatistics.h" #include "ResourcePressureView.h" +#include "RetireControlUnit.h" #include "RetireControlUnitStatistics.h" +#include "RetireStage.h" +#include "Scheduler.h" #include "SchedulerStatistics.h" #include "SummaryView.h" #include "TimelineView.h" @@ -65,15 +71,13 @@ cl::value_desc("filename")); static cl::opt - ArchName("march", - cl::desc("Target arch to assemble for, " - "see -version for available targets"), + ArchName("march", cl::desc("Target arch to assemble for, " + "see -version for available targets"), cl::cat(ToolOptions)); static cl::opt - TripleName("mtriple", - cl::desc("Target triple to assemble for, " - "see -version for available targets"), + TripleName("mtriple", cl::desc("Target triple to assemble for, " + "see -version for available targets"), cl::cat(ToolOptions)); static cl::opt @@ -483,7 +487,7 @@ PrintInstructionTables ? 1 : Iterations); if (PrintInstructionTables) { - mca::InstructionTables IT(STI->getSchedModel(), IB, S); + mca::InstructionTables IT(SM, IB, S); if (PrintInstructionInfoView) { IT.addView( @@ -496,14 +500,20 @@ continue; } - // Ideally, I'd like to expose the pipeline building here, - // by registering all of the Stage instances. - // But for now, it's just this single puppy. - std::unique_ptr Fetch = - llvm::make_unique(IB, S); - mca::Backend B(*STI, *MRI, std::move(Fetch), Width, RegisterFileSize, - LoadQueueSize, StoreQueueSize, AssumeNoAlias); - mca::BackendPrinter Printer(B); + // Create the hardware components required for the pipeline. + mca::RetireControlUnit RCU(SM); + mca::RegisterFile PRF(SM, *MRI, RegisterFileSize); + mca::Scheduler HWS(SM, LoadQueueSize, StoreQueueSize, AssumeNoAlias); + + // Create the pipeline and add stages to it. + auto B = llvm::make_unique( + Width, RegisterFileSize, LoadQueueSize, StoreQueueSize, AssumeNoAlias); + B->appendStage(llvm::make_unique(IB, S)); + B->appendStage(llvm::make_unique( + B.get(), *STI, *MRI, RegisterFileSize, Width, RCU, PRF, HWS)); + B->appendStage(llvm::make_unique(B.get(), RCU, PRF)); + B->appendStage(llvm::make_unique(B.get(), RCU, HWS)); + mca::BackendPrinter Printer(*B); if (PrintSummaryView) Printer.addView(llvm::make_unique(SM, S, Width)); @@ -533,7 +543,7 @@ *STI, *IP, S, TimelineMaxIterations, TimelineMaxCycles)); } - B.run(); + B->run(); Printer.printReport(TOF->os()); }