[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
This commit is contained in:
Matt Davis 2018-05-25 18:00:25 +00:00
parent 8697c9c142
commit 61885d41df
11 changed files with 175 additions and 82 deletions

View File

@ -37,10 +37,15 @@ void Backend::run() {
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;

View File

@ -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 @@ class HWStallEvent;
/// 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 @@ public:
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);

View File

@ -28,6 +28,7 @@ add_llvm_tool(llvm-mca
ResourcePressureView.cpp
RetireControlUnit.cpp
RetireControlUnitStatistics.cpp
RetireStage.cpp
Scheduler.cpp
SchedulerStatistics.cpp
Stage.cpp

View File

@ -30,23 +30,13 @@ void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
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::checkRAT(const InstRef &IR) {
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 @@ void DispatchStage::dispatch(InstRef IR) {
// 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::dispatch(InstRef IR) {
}
void DispatchStage::preExecute(const InstRef &IR) {
RCU->cycleEvent();
AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
}
@ -158,8 +147,8 @@ bool DispatchStage::execute(InstRef &IR) {
#ifndef NDEBUG
void DispatchStage::dump() const {
RAT->dump();
RCU->dump();
PRF.dump();
RCU.dump();
}
#endif
} // namespace mca

View File

@ -57,16 +57,16 @@ class DispatchStage : public Stage {
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 @@ class DispatchStage : public Stage {
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

View File

@ -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 @@ using namespace llvm;
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 @@ unsigned RetireControlUnit::reserveSlot(const InstRef &IR,
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) {

View File

@ -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 @@ private:
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

View File

@ -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

View File

@ -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

View File

@ -468,7 +468,7 @@ void Scheduler::notifyInstructionExecuted(const InstRef &IR) {
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) {

View File

@ -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 @@ public:
/// 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 @@ class Scheduler {
// 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 @@ class Scheduler {
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