[llvm-mca] Add the RetireStage.
Summary:
This class maintains the same logic as the original RetireControlUnit.
This is just an intermediate patch to make the RCU a Stage. Future patches will remove the dependency on the DispatchStage, and then more properly populate the pre/execute/post Stage interface.
Reviewers: andreadb, RKSimon, courbet
Reviewed By: andreadb, courbet
Subscribers: javed.absar, mgorny, tschuett, gbedwell, llvm-commits
Differential Revision: https://reviews.llvm.org/D47244
llvm-svn: 333292
diff --git a/llvm/tools/llvm-mca/Backend.cpp b/llvm/tools/llvm-mca/Backend.cpp
index 9077b6e..dc2b1de 100644
--- a/llvm/tools/llvm-mca/Backend.cpp
+++ b/llvm/tools/llvm-mca/Backend.cpp
@@ -37,10 +37,15 @@
void Backend::runCycle(unsigned Cycle) {
notifyCycleBegin(Cycle);
+ // Update the stages before we do any processing for this cycle.
InstRef IR;
+ Retire->preExecute(IR);
Dispatch->preExecute(IR);
+
+ // This will execute scheduled instructions.
HWS->cycleEvent(); // TODO: This will eventually be stage-ified.
+ // Fetch instructions and dispatch them to the hardware.
while (Fetch->execute(IR)) {
if (!Dispatch->execute(IR))
break;
diff --git a/llvm/tools/llvm-mca/Backend.h b/llvm/tools/llvm-mca/Backend.h
index 72ae46c..f38902b 100644
--- a/llvm/tools/llvm-mca/Backend.h
+++ b/llvm/tools/llvm-mca/Backend.h
@@ -18,6 +18,9 @@
#include "DispatchStage.h"
#include "FetchStage.h"
#include "InstrBuilder.h"
+#include "RegisterFile.h"
+#include "RetireControlUnit.h"
+#include "RetireStage.h"
#include "Scheduler.h"
namespace mca {
@@ -51,12 +54,17 @@
/// histograms. For example, it tracks how the dispatch group size changes
/// over time.
class Backend {
- /// This is the initial stage of the pipeline.
+ // The following are the simulated hardware components of the backend.
+ RetireControlUnit RCU;
+ RegisterFile PRF;
+
/// TODO: Eventually this will become a list of unique Stage* that this
/// backend pipeline executes.
std::unique_ptr<FetchStage> Fetch;
std::unique_ptr<Scheduler> HWS;
std::unique_ptr<DispatchStage> Dispatch;
+ std::unique_ptr<RetireStage> Retire;
+
std::set<HWEventListener *> Listeners;
unsigned Cycles;
@@ -68,15 +76,16 @@
std::unique_ptr<FetchStage> InitialStage, unsigned DispatchWidth = 0,
unsigned RegisterFileSize = 0, unsigned LoadQueueSize = 0,
unsigned StoreQueueSize = 0, bool AssumeNoAlias = false)
- : Fetch(std::move(InitialStage)),
- HWS(llvm::make_unique<Scheduler>(this, Subtarget.getSchedModel(),
+ : RCU(Subtarget.getSchedModel()),
+ PRF(Subtarget.getSchedModel(), MRI, RegisterFileSize),
+ Fetch(std::move(InitialStage)),
+ HWS(llvm::make_unique<Scheduler>(this, Subtarget.getSchedModel(), RCU,
LoadQueueSize, StoreQueueSize,
AssumeNoAlias)),
Dispatch(llvm::make_unique<DispatchStage>(
- this, Subtarget, MRI, RegisterFileSize, DispatchWidth, HWS.get())),
- Cycles(0) {
- HWS->setDispatchStage(Dispatch.get());
- }
+ this, Subtarget, MRI, RegisterFileSize, DispatchWidth, RCU, PRF,
+ HWS.get())),
+ Retire(llvm::make_unique<RetireStage>(this, RCU, PRF)), Cycles(0) {}
void run();
void addEventListener(HWEventListener *Listener);
diff --git a/llvm/tools/llvm-mca/CMakeLists.txt b/llvm/tools/llvm-mca/CMakeLists.txt
index fd83ff3..166641a 100644
--- a/llvm/tools/llvm-mca/CMakeLists.txt
+++ b/llvm/tools/llvm-mca/CMakeLists.txt
@@ -28,6 +28,7 @@
ResourcePressureView.cpp
RetireControlUnit.cpp
RetireControlUnitStatistics.cpp
+ RetireStage.cpp
Scheduler.cpp
SchedulerStatistics.cpp
Stage.cpp
diff --git a/llvm/tools/llvm-mca/DispatchStage.cpp b/llvm/tools/llvm-mca/DispatchStage.cpp
index 8a907af..baffb16 100644
--- a/llvm/tools/llvm-mca/DispatchStage.cpp
+++ b/llvm/tools/llvm-mca/DispatchStage.cpp
@@ -30,23 +30,13 @@
Owner->notifyInstructionEvent(HWInstructionDispatchedEvent(IR, UsedRegs));
}
-void DispatchStage::notifyInstructionRetired(const InstRef &IR) {
- LLVM_DEBUG(dbgs() << "[E] Instruction Retired: " << IR << '\n');
- SmallVector<unsigned, 4> FreedRegs(RAT->getNumRegisterFiles());
- const InstrDesc &Desc = IR.getInstruction()->getDesc();
-
- for (const std::unique_ptr<WriteState> &WS : IR.getInstruction()->getDefs())
- RAT->removeRegisterWrite(*WS.get(), FreedRegs, !Desc.isZeroLatency());
- Owner->notifyInstructionEvent(HWInstructionRetiredEvent(IR, FreedRegs));
-}
-
-bool DispatchStage::checkRAT(const InstRef &IR) {
+bool DispatchStage::checkPRF(const InstRef &IR) {
SmallVector<unsigned, 4> RegDefs;
for (const std::unique_ptr<WriteState> &RegDef :
IR.getInstruction()->getDefs())
RegDefs.emplace_back(RegDef->getRegisterID());
- unsigned RegisterMask = RAT->isAvailable(RegDefs);
+ const unsigned RegisterMask = PRF.isAvailable(RegDefs);
// A mask with all zeroes means: register files are available.
if (RegisterMask) {
Owner->notifyStallEvent(HWStallEvent(HWStallEvent::RegisterFileStall, IR));
@@ -58,7 +48,7 @@
bool DispatchStage::checkRCU(const InstRef &IR) {
const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
- if (RCU->isAvailable(NumMicroOps))
+ if (RCU.isAvailable(NumMicroOps))
return true;
Owner->notifyStallEvent(
HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
@@ -125,13 +115,13 @@
// By default, a dependency-breaking zero-latency instruction is expected to
// be optimized at register renaming stage. That means, no physical register
// is allocated to the instruction.
- SmallVector<unsigned, 4> RegisterFiles(RAT->getNumRegisterFiles());
+ SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
for (std::unique_ptr<WriteState> &WS : IS.getDefs())
- RAT->addRegisterWrite(*WS, RegisterFiles, !Desc.isZeroLatency());
+ PRF.addRegisterWrite(*WS, RegisterFiles, !Desc.isZeroLatency());
// Reserve slots in the RCU, and notify the instruction that it has been
// dispatched to the schedulers for execution.
- IS.dispatch(RCU->reserveSlot(IR, NumMicroOps));
+ IS.dispatch(RCU.reserveSlot(IR, NumMicroOps));
// Notify listeners of the "instruction dispatched" event.
notifyInstructionDispatched(IR, RegisterFiles);
@@ -143,7 +133,6 @@
}
void DispatchStage::preExecute(const InstRef &IR) {
- RCU->cycleEvent();
AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
}
@@ -158,8 +147,8 @@
#ifndef NDEBUG
void DispatchStage::dump() const {
- RAT->dump();
- RCU->dump();
+ PRF.dump();
+ RCU.dump();
}
#endif
} // namespace mca
diff --git a/llvm/tools/llvm-mca/DispatchStage.h b/llvm/tools/llvm-mca/DispatchStage.h
index 0d62333..c2fbc53 100644
--- a/llvm/tools/llvm-mca/DispatchStage.h
+++ b/llvm/tools/llvm-mca/DispatchStage.h
@@ -57,16 +57,16 @@
unsigned AvailableEntries;
unsigned CarryOver;
Scheduler *SC;
- std::unique_ptr<RegisterFile> RAT;
- std::unique_ptr<RetireControlUnit> RCU;
Backend *Owner;
const llvm::MCSubtargetInfo &STI;
+ RetireControlUnit &RCU;
+ RegisterFile &PRF;
- bool checkRAT(const InstRef &IR);
bool checkRCU(const InstRef &IR);
+ bool checkPRF(const InstRef &IR);
bool checkScheduler(const InstRef &IR);
void dispatch(InstRef IR);
- bool isRCUEmpty() const { return RCU->isEmpty(); }
+ bool isRCUEmpty() const { return RCU.isEmpty(); }
void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
void notifyInstructionDispatched(const InstRef &IR,
@@ -78,36 +78,27 @@
bool canDispatch(const InstRef &IR) {
assert(isAvailable(IR.getInstruction()->getDesc().NumMicroOps));
- return checkRCU(IR) && checkRAT(IR) && checkScheduler(IR);
+ return checkRCU(IR) && checkPRF(IR) && checkScheduler(IR);
}
void collectWrites(llvm::SmallVectorImpl<WriteState *> &Vec,
unsigned RegID) const {
- return RAT->collectWrites(Vec, RegID);
+ return PRF.collectWrites(Vec, RegID);
}
public:
DispatchStage(Backend *B, const llvm::MCSubtargetInfo &Subtarget,
const llvm::MCRegisterInfo &MRI, unsigned RegisterFileSize,
- unsigned MaxDispatchWidth, Scheduler *Sched)
+ unsigned MaxDispatchWidth, RetireControlUnit &R,
+ RegisterFile &F, Scheduler *Sched)
: DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth),
- CarryOver(0U), SC(Sched),
- RAT(llvm::make_unique<RegisterFile>(Subtarget.getSchedModel(), MRI,
- RegisterFileSize)),
- RCU(llvm::make_unique<RetireControlUnit>(Subtarget.getSchedModel(),
- this)),
- Owner(B), STI(Subtarget) {}
+ CarryOver(0U), SC(Sched), Owner(B), STI(Subtarget), RCU(R), PRF(F) {}
virtual bool isReady() const override final { return isRCUEmpty(); }
virtual void preExecute(const InstRef &IR) override final;
virtual bool execute(InstRef &IR) override final;
- void notifyInstructionRetired(const InstRef &IR);
void notifyDispatchStall(const InstRef &IR, unsigned EventType);
- void onInstructionExecuted(unsigned TokenID) {
- RCU->onInstructionExecuted(TokenID);
- }
-
#ifndef NDEBUG
void dump() const;
#endif
diff --git a/llvm/tools/llvm-mca/RetireControlUnit.cpp b/llvm/tools/llvm-mca/RetireControlUnit.cpp
index 3ce7e3e..d4c4274 100644
--- a/llvm/tools/llvm-mca/RetireControlUnit.cpp
+++ b/llvm/tools/llvm-mca/RetireControlUnit.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
/// \file
///
-/// This file implements methods declared by the RetireControlUnit interface.
+/// This file simulates the hardware responsible for retiring instructions.
///
//===----------------------------------------------------------------------===//
@@ -22,10 +22,9 @@
namespace mca {
-RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM,
- DispatchStage *DS)
+RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM)
: NextAvailableSlotIdx(0), CurrentInstructionSlotIdx(0),
- AvailableSlots(SM.MicroOpBufferSize), MaxRetirePerCycle(0), Owner(DS) {
+ AvailableSlots(SM.MicroOpBufferSize), MaxRetirePerCycle(0) {
// Check if the scheduling model provides extra information about the machine
// processor. If so, then use that information to set the reorder buffer size
// and the maximum number of instructions retired per cycle.
@@ -58,25 +57,19 @@
return TokenID;
}
-void RetireControlUnit::cycleEvent() {
- if (isEmpty())
- return;
+const RetireControlUnit::RUToken &RetireControlUnit::peekCurrentToken() const {
+ return Queue[CurrentInstructionSlotIdx];
+}
- unsigned NumRetired = 0;
- while (!isEmpty()) {
- if (MaxRetirePerCycle != 0 && NumRetired == MaxRetirePerCycle)
- break;
- RUToken &Current = Queue[CurrentInstructionSlotIdx];
- assert(Current.NumSlots && "Reserved zero slots?");
- assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
- if (!Current.Executed)
- break;
- Owner->notifyInstructionRetired(Current.IR);
- CurrentInstructionSlotIdx += Current.NumSlots;
- CurrentInstructionSlotIdx %= Queue.size();
- AvailableSlots += Current.NumSlots;
- NumRetired++;
- }
+void RetireControlUnit::consumeCurrentToken() {
+ const RetireControlUnit::RUToken &Current = peekCurrentToken();
+ assert(Current.NumSlots && "Reserved zero slots?");
+ assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
+
+ // Update the slot index to be the next item in the circular queue.
+ CurrentInstructionSlotIdx += Current.NumSlots;
+ CurrentInstructionSlotIdx %= Queue.size();
+ AvailableSlots += Current.NumSlots;
}
void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
diff --git a/llvm/tools/llvm-mca/RetireControlUnit.h b/llvm/tools/llvm-mca/RetireControlUnit.h
index 497abbc..eb2593d 100644
--- a/llvm/tools/llvm-mca/RetireControlUnit.h
+++ b/llvm/tools/llvm-mca/RetireControlUnit.h
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
/// \file
///
-/// This file implements the logic for retiring instructions.
+/// This file simulates the hardware responsible for retiring instructions.
///
//===----------------------------------------------------------------------===//
@@ -62,27 +62,32 @@
unsigned AvailableSlots;
unsigned MaxRetirePerCycle; // 0 means no limit.
std::vector<RUToken> Queue;
- DispatchStage *Owner;
public:
- RetireControlUnit(const llvm::MCSchedModel &SM, DispatchStage *DU);
+ RetireControlUnit(const llvm::MCSchedModel &SM);
bool isFull() const { return !AvailableSlots; }
bool isEmpty() const { return AvailableSlots == Queue.size(); }
bool isAvailable(unsigned Quantity = 1) const {
- // Some instructions may declare a number of uOps which exceedes the size
+ // Some instructions may declare a number of uOps which exceeds the size
// of the reorder buffer. To avoid problems, cap the amount of slots to
// the size of the reorder buffer.
Quantity = std::min(Quantity, static_cast<unsigned>(Queue.size()));
return AvailableSlots >= Quantity;
}
+ unsigned getMaxRetirePerCycle() const { return MaxRetirePerCycle; }
+
// Reserves a number of slots, and returns a new token.
unsigned reserveSlot(const InstRef &IS, unsigned NumMicroOps);
- /// Retires instructions in program order.
- void cycleEvent();
+ // Return the current token from the RCU's circular token queue.
+ const RUToken &peekCurrentToken() const;
+ // Advance the pointer to the next token in the circular token queue.
+ void consumeCurrentToken();
+
+ // Update the RCU token to represent the executed state.
void onInstructionExecuted(unsigned TokenID);
#ifndef NDEBUG
diff --git a/llvm/tools/llvm-mca/RetireStage.cpp b/llvm/tools/llvm-mca/RetireStage.cpp
new file mode 100644
index 0000000..a38c758
--- /dev/null
+++ b/llvm/tools/llvm-mca/RetireStage.cpp
@@ -0,0 +1,56 @@
+//===---------------------- RetireStage.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the retire stage of an instruction pipeline.
+/// The RetireStage represents the process logic that interacts with the
+/// simulated RetireControlUnit hardware.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RetireStage.h"
+#include "Backend.h"
+#include "HWEventListener.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+void RetireStage::preExecute(const InstRef &IR) {
+ if (RCU.isEmpty())
+ return;
+
+ const unsigned MaxRetirePerCycle = RCU.getMaxRetirePerCycle();
+ unsigned NumRetired = 0;
+ while (!RCU.isEmpty()) {
+ if (MaxRetirePerCycle != 0 && NumRetired == MaxRetirePerCycle)
+ break;
+ const RetireControlUnit::RUToken &Current = RCU.peekCurrentToken();
+ if (!Current.Executed)
+ break;
+ RCU.consumeCurrentToken();
+ notifyInstructionRetired(Current.IR);
+ NumRetired++;
+ }
+}
+
+void RetireStage::notifyInstructionRetired(const InstRef &IR) {
+ LLVM_DEBUG(dbgs() << "[E] Instruction Retired: " << IR << '\n');
+ SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles());
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+
+ for (const std::unique_ptr<WriteState> &WS : IR.getInstruction()->getDefs())
+ PRF.removeRegisterWrite(*WS.get(), FreedRegs, !Desc.isZeroLatency());
+ Owner->notifyInstructionEvent(HWInstructionRetiredEvent(IR, FreedRegs));
+}
+
+} // namespace mca
diff --git a/llvm/tools/llvm-mca/RetireStage.h b/llvm/tools/llvm-mca/RetireStage.h
new file mode 100644
index 0000000..a40e403
--- /dev/null
+++ b/llvm/tools/llvm-mca/RetireStage.h
@@ -0,0 +1,48 @@
+//===---------------------- RetireStage.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the retire stage of an instruction pipeline.
+/// The RetireStage represents the process logic that interacts with the
+/// simulated RetireControlUnit hardware.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
+
+#include "RegisterFile.h"
+#include "RetireControlUnit.h"
+#include "Stage.h"
+
+namespace mca {
+
+class Backend;
+
+class RetireStage : public Stage {
+ // Owner will go away when we move listeners/eventing to the stages.
+ Backend *Owner;
+ RetireControlUnit &RCU;
+ RegisterFile &PRF;
+
+public:
+ RetireStage(Backend *B, RetireControlUnit &R, RegisterFile &F)
+ : Stage(), Owner(B), RCU(R), PRF(F) {}
+ RetireStage(const RetireStage &Other) = delete;
+ RetireStage &operator=(const RetireStage &Other) = delete;
+
+ virtual void preExecute(const InstRef &IR) override final;
+ virtual bool execute(InstRef &IR) override final { return true; }
+ void notifyInstructionRetired(const InstRef &IR);
+ void onInstructionExecuted(unsigned TokenID);
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
diff --git a/llvm/tools/llvm-mca/Scheduler.cpp b/llvm/tools/llvm-mca/Scheduler.cpp
index 04899c3..56dc746 100644
--- a/llvm/tools/llvm-mca/Scheduler.cpp
+++ b/llvm/tools/llvm-mca/Scheduler.cpp
@@ -468,7 +468,7 @@
LLVM_DEBUG(dbgs() << "[E] Instruction Executed: " << IR << '\n');
Owner->notifyInstructionEvent(
HWInstructionEvent(HWInstructionEvent::Executed, IR));
- DS->onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
+ RCU.onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
}
void Scheduler::notifyInstructionReady(const InstRef &IR) {
diff --git a/llvm/tools/llvm-mca/Scheduler.h b/llvm/tools/llvm-mca/Scheduler.h
index 337737a..f8dce3c 100644
--- a/llvm/tools/llvm-mca/Scheduler.h
+++ b/llvm/tools/llvm-mca/Scheduler.h
@@ -17,6 +17,7 @@
#include "Instruction.h"
#include "LSUnit.h"
+#include "RetireControlUnit.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include <map>
@@ -24,7 +25,6 @@
namespace mca {
class Backend;
-class DispatchStage;
/// Used to notify the internal state of a processor resource.
///
@@ -402,6 +402,7 @@
/// An Instruction leaves the IssuedQueue when it reaches the write-back stage.
class Scheduler {
const llvm::MCSchedModel &SM;
+ RetireControlUnit &RCU;
// Hardware resources that are managed by this scheduler.
std::unique_ptr<ResourceManager> Resources;
@@ -410,9 +411,6 @@
// The Backend gets notified when instructions are ready/issued/executed.
Backend *const Owner;
- // The dispatch unit gets notified when instructions are executed.
- DispatchStage *DS;
-
using QueueEntryTy = std::pair<unsigned, Instruction *>;
std::map<unsigned, Instruction *> WaitQueue;
std::map<unsigned, Instruction *> ReadyQueue;
@@ -447,15 +445,13 @@
void updateIssuedQueue(llvm::SmallVectorImpl<InstRef> &Executed);
public:
- Scheduler(Backend *B, const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
- unsigned StoreQueueSize, bool AssumeNoAlias)
- : SM(Model), Resources(llvm::make_unique<ResourceManager>(SM)),
+ Scheduler(Backend *B, const llvm::MCSchedModel &Model, RetireControlUnit &R,
+ unsigned LoadQueueSize, unsigned StoreQueueSize, bool AssumeNoAlias)
+ : SM(Model), RCU(R), Resources(llvm::make_unique<ResourceManager>(SM)),
LSU(llvm::make_unique<LSUnit>(LoadQueueSize, StoreQueueSize,
AssumeNoAlias)),
Owner(B) {}
- void setDispatchStage(DispatchStage *DispStage) { DS = DispStage; }
-
/// Check if the instruction in 'IR' can be dispatched.
///
/// The DispatchStage is responsible for querying the Scheduler before