Skip to content

Commit

Permalink
[llvm-mca] Add the RetireStage.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Matt Davis authored and Matt Davis committed May 25, 2018
1 parent cb48efd commit 5b79ffc
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 82 deletions.
5 changes: 5 additions & 0 deletions llvm/tools/llvm-mca/Backend.cpp
Expand Up @@ -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;
Expand Down
23 changes: 16 additions & 7 deletions llvm/tools/llvm-mca/Backend.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand All @@ -68,15 +76,16 @@ class Backend {
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);
Expand Down
1 change: 1 addition & 0 deletions llvm/tools/llvm-mca/CMakeLists.txt
Expand Up @@ -28,6 +28,7 @@ add_llvm_tool(llvm-mca
ResourcePressureView.cpp
RetireControlUnit.cpp
RetireControlUnitStatistics.cpp
RetireStage.cpp
Scheduler.cpp
SchedulerStatistics.cpp
Stage.cpp
Expand Down
27 changes: 8 additions & 19 deletions llvm/tools/llvm-mca/DispatchStage.cpp
Expand Up @@ -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));
Expand All @@ -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));
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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
27 changes: 9 additions & 18 deletions llvm/tools/llvm-mca/DispatchStage.h
Expand Up @@ -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,
Expand All @@ -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
Expand Down
37 changes: 15 additions & 22 deletions llvm/tools/llvm-mca/RetireControlUnit.cpp
Expand Up @@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements methods declared by the RetireControlUnit interface.
/// This file simulates the hardware responsible for retiring instructions.
///
//===----------------------------------------------------------------------===//

Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down
17 changes: 11 additions & 6 deletions llvm/tools/llvm-mca/RetireControlUnit.h
Expand Up @@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements the logic for retiring instructions.
/// This file simulates the hardware responsible for retiring instructions.
///
//===----------------------------------------------------------------------===//

Expand Down Expand Up @@ -62,27 +62,32 @@ struct RetireControlUnit {
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
Expand Down
56 changes: 56 additions & 0 deletions 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

0 comments on commit 5b79ffc

Please sign in to comment.