Skip to content

Commit

Permalink
[MCA] Refactor the InOrderIssueStage stage. NFCI
Browse files Browse the repository at this point in the history
Moved the logic that checks for RAW hazards from the InOrderIssueStage to the
RegisterFile.

Changed how the InOrderIssueStage keeps track of backend stalls. Stall events
are now generated from method notifyStallEvent().

No functional change intended.
  • Loading branch information
adibiagio committed May 27, 2021
1 parent 62b5df7 commit 50770d8
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 163 deletions.
10 changes: 5 additions & 5 deletions llvm/include/llvm/MCA/HWEventListener.h
Expand Up @@ -59,14 +59,15 @@ class HWInstructionEvent {
const InstRef &IR;
};

using ResourceRef = std::pair<uint64_t, uint64_t>;
using ResourceUse = std::pair<ResourceRef, ResourceCycles>;

class HWInstructionIssuedEvent : public HWInstructionEvent {
public:
using ResourceRef = std::pair<uint64_t, uint64_t>;
HWInstructionIssuedEvent(const InstRef &IR,
ArrayRef<std::pair<ResourceRef, ResourceCycles>> UR)
HWInstructionIssuedEvent(const InstRef &IR, ArrayRef<ResourceUse> UR)
: HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}

ArrayRef<std::pair<ResourceRef, ResourceCycles>> UsedResources;
ArrayRef<ResourceUse> UsedResources;
};

class HWInstructionDispatchedEvent : public HWInstructionEvent {
Expand Down Expand Up @@ -165,7 +166,6 @@ class HWEventListener {
virtual void onEvent(const HWStallEvent &Event) {}
virtual void onEvent(const HWPressureEvent &Event) {}

using ResourceRef = std::pair<uint64_t, uint64_t>;
virtual void onResourceAvailable(const ResourceRef &RRef) {}

// Events generated by the Scheduler when buffered resources are
Expand Down
11 changes: 11 additions & 0 deletions llvm/include/llvm/MCA/HardwareUnits/RegisterFile.h
Expand Up @@ -236,6 +236,17 @@ class RegisterFile : public HardwareUnit {
void collectWrites(const MCSubtargetInfo &STI, const ReadState &RS,
SmallVectorImpl<WriteRef> &Writes,
SmallVectorImpl<WriteRef> &CommittedWrites) const;
struct RAWHazard {
MCPhysReg RegisterID;
int CyclesLeft;

RAWHazard() : RegisterID(), CyclesLeft() {}
bool isValid() const { return RegisterID; }
bool hasUnknownCycles() const { return CyclesLeft < 0; }
};

RAWHazard checkRAWHazards(const MCSubtargetInfo &STI,
const ReadState &RS) const;

// This method updates the register mappings inserting a new register
// definition. This method is also responsible for updating the number of
Expand Down
5 changes: 2 additions & 3 deletions llvm/include/llvm/MCA/Stages/ExecuteStage.h
Expand Up @@ -72,9 +72,8 @@ class ExecuteStage final : public Stage {
Error cycleEnd() override;
Error execute(InstRef &IR) override;

void notifyInstructionIssued(
const InstRef &IR,
MutableArrayRef<std::pair<ResourceRef, ResourceCycles>> Used) const;
void notifyInstructionIssued(const InstRef &IR,
MutableArrayRef<ResourceUse> Used) const;
void notifyInstructionExecuted(const InstRef &IR) const;
void notifyInstructionPending(const InstRef &IR) const;
void notifyInstructionReady(const InstRef &IR) const;
Expand Down
73 changes: 59 additions & 14 deletions llvm/include/llvm/MCA/Stages/InOrderIssueStage.h
Expand Up @@ -18,8 +18,6 @@
#include "llvm/MCA/SourceMgr.h"
#include "llvm/MCA/Stages/Stage.h"

#include <queue>

namespace llvm {
struct MCSchedModel;
class MCSubtargetInfo;
Expand All @@ -28,6 +26,45 @@ namespace mca {
class RegisterFile;
class ResourceManager;

struct StallInfo {
enum class StallKind { DEFAULT, REGISTER_DEPS, DISPATCH, DELAY };

InstRef IR;
unsigned CyclesLeft;
StallKind Kind;

StallInfo() : IR(), CyclesLeft(), Kind(StallKind::DEFAULT) {}

bool isValid() const { return (bool)IR; }

StallKind getStallKind() const { return Kind; }
unsigned getCyclesLeft() const { return CyclesLeft; }
const InstRef &getInstruction() const { return IR; }
InstRef &getInstruction() { return IR; }

void clear() {
IR.invalidate();
CyclesLeft = 0;
Kind = StallKind::DEFAULT;
}

void update(const InstRef &Inst, unsigned Cycles, StallKind SK) {
IR = Inst;
CyclesLeft = Cycles;
Kind = SK;
}

void cycleEnd() {
if (!isValid())
return;

if (!CyclesLeft)
return;

--CyclesLeft;
}
};

class InOrderIssueStage final : public Stage {
const MCSchedModel &SM;
const MCSubtargetInfo &STI;
Expand All @@ -40,10 +77,7 @@ class InOrderIssueStage final : public Stage {
/// Number of instructions issued in the current cycle.
unsigned NumIssued;

/// If an instruction cannot execute due to an unmet register or resource
/// dependency, the it is stalled for StallCyclesLeft.
InstRef StalledInst;
unsigned StallCyclesLeft;
StallInfo SI;

/// Instruction that is issued in more than 1 cycle.
InstRef CarriedOver;
Expand All @@ -61,29 +95,40 @@ class InOrderIssueStage final : public Stage {
InOrderIssueStage(const InOrderIssueStage &Other) = delete;
InOrderIssueStage &operator=(const InOrderIssueStage &Other) = delete;

/// If IR has an unmet register or resource dependency, canExecute returns
/// false. StallCycles is set to the number of cycles left before the
/// instruction can be issued.
bool canExecute(const InstRef &IR, unsigned *StallCycles) const;
/// Returns true if IR can execute during this cycle.
/// In case of stall, it updates SI with information about the stalled
/// instruction and the stall reason.
bool canExecute(const InstRef &IR);

/// Issue the instruction, or update StallCycles if IR is stalled.
Error tryIssue(InstRef &IR, unsigned *StallCycles);
/// Issue the instruction, or update the StallInfo.
Error tryIssue(InstRef &IR);

/// Update status of instructions from IssuedInst.
void updateIssuedInst();

/// Continue to issue the CarriedOver instruction.
void updateCarriedOver();

/// Notifies a stall event to the Stage listener. Stall information is
/// obtained from the internal StallInfo field.
void notifyStallEvent();

void notifyInstructionIssued(const InstRef &IR,
ArrayRef<ResourceUse> UsedRes);
void notifyInstructionDispatched(const InstRef &IR, unsigned Ops,
ArrayRef<unsigned> UsedRegs);
void notifyInstructionExecuted(const InstRef &IR);
void notifyInstructionRetired(const InstRef &IR,
ArrayRef<unsigned> FreedRegs);

/// Retire instruction once it is executed.
void retireInstruction(InstRef &IR);

public:
InOrderIssueStage(RegisterFile &PRF, const MCSchedModel &SM,
const MCSubtargetInfo &STI)
: SM(SM), STI(STI), PRF(PRF), RM(std::make_unique<ResourceManager>(SM)),
NumIssued(0), StallCyclesLeft(0), CarryOver(0), Bandwidth(0),
LastWriteBackCycle(0) {}
NumIssued(), SI(), CarryOver(), Bandwidth(), LastWriteBackCycle() {}

bool isAvailable(const InstRef &) const override;
bool hasWorkToComplete() const override;
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/MCA/Stages/InstructionTables.h
Expand Up @@ -27,7 +27,7 @@ namespace mca {

class InstructionTables final : public Stage {
const MCSchedModel &SM;
SmallVector<std::pair<ResourceRef, ResourceCycles>, 4> UsedResources;
SmallVector<ResourceUse, 4> UsedResources;
SmallVector<uint64_t, 8> Masks;

public:
Expand Down
62 changes: 58 additions & 4 deletions llvm/lib/MCA/HardwareUnits/RegisterFile.cpp
Expand Up @@ -30,6 +30,8 @@ WriteRef::WriteRef(unsigned SourceIndex, WriteState *WS)

void WriteRef::commit() {
assert(Write && Write->isExecuted() && "Cannot commit before write back!");
RegisterID = Write->getRegisterID();
WriteResID = Write->getWriteResourceID();
Write = nullptr;
}

Expand Down Expand Up @@ -225,8 +227,8 @@ void RegisterFile::addRegisterWrite(WriteRef Write,
assert(RegID && "Adding an invalid register definition?");

LLVM_DEBUG({
dbgs() << "RegisterFile: addRegisterWrite [ " << Write.getSourceIndex()
<< ", " << MRI.getName(RegID) << "]\n";
dbgs() << "[PRF] addRegisterWrite [ " << Write.getSourceIndex() << ", "
<< MRI.getName(RegID) << "]\n";
});

// If RenameAs is equal to RegID, then RegID is subject to register renaming
Expand Down Expand Up @@ -480,7 +482,7 @@ void RegisterFile::collectWrites(
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
MCPhysReg RegID = RS.getRegisterID();
assert(RegID && RegID < RegisterMappings.size());
LLVM_DEBUG(dbgs() << "RegisterFile: collecting writes for register "
LLVM_DEBUG(dbgs() << "[PRF] collecting writes for register "
<< MRI.getName(RegID) << '\n');

// Check if this is an alias.
Expand Down Expand Up @@ -536,6 +538,57 @@ void RegisterFile::collectWrites(
});
}

RegisterFile::RAWHazard
RegisterFile::checkRAWHazards(const MCSubtargetInfo &STI,
const ReadState &RS) const {
RAWHazard Hazard;
SmallVector<WriteRef, 4> Writes;
SmallVector<WriteRef, 4> CommittedWrites;

const MCSchedModel &SM = STI.getSchedModel();
const ReadDescriptor &RD = RS.getDescriptor();
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);

collectWrites(STI, RS, Writes, CommittedWrites);
for (const WriteRef &WR : Writes) {
const WriteState *WS = WR.getWriteState();
unsigned WriteResID = WS->getWriteResourceID();
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);

if (WS->getCyclesLeft() == UNKNOWN_CYCLES) {
if (Hazard.isValid())
continue;

Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = UNKNOWN_CYCLES;
continue;
}

int CyclesLeft = WS->getCyclesLeft() - ReadAdvance;
if (CyclesLeft > 0) {
if (Hazard.CyclesLeft < CyclesLeft) {
Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = CyclesLeft;
}
}
}
Writes.clear();

for (const WriteRef &WR : CommittedWrites) {
unsigned WriteResID = WR.getWriteResourceID();
int NegReadAdvance = -STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
int Elapsed = static_cast<int>(getElapsedCyclesFromWriteBack(WR));
int CyclesLeft = NegReadAdvance - Elapsed;
assert(CyclesLeft > 0 && "Write should not be in the CommottedWrites set!");
if (Hazard.CyclesLeft < CyclesLeft) {
Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = CyclesLeft;
}
}

return Hazard;
}

void RegisterFile::addRegisterRead(ReadState &RS,
const MCSubtargetInfo &STI) const {
MCPhysReg RegID = RS.getRegisterID();
Expand Down Expand Up @@ -608,7 +661,8 @@ unsigned RegisterFile::isAvailable(ArrayRef<MCPhysReg> Regs) const {
// microarchitectural registers in register file #0 was changed by the
// users via flag -reg-file-size. Alternatively, the scheduling model
// specified a too small number of registers for this register file.
LLVM_DEBUG(dbgs() << "Not enough registers in the register file.\n");
LLVM_DEBUG(
dbgs() << "[PRF] Not enough registers in the register file.\n");

// FIXME: Normalize the instruction register count to match the
// NumPhysRegs value. This is a highly unusual case, and is not expected
Expand Down
19 changes: 9 additions & 10 deletions llvm/lib/MCA/Stages/ExecuteStage.cpp
Expand Up @@ -51,7 +51,7 @@ bool ExecuteStage::isAvailable(const InstRef &IR) const {
}

Error ExecuteStage::issueInstruction(InstRef &IR) {
SmallVector<std::pair<ResourceRef, ResourceCycles>, 4> Used;
SmallVector<ResourceUse, 4> Used;
SmallVector<InstRef, 4> Pending;
SmallVector<InstRef, 4> Ready;

Expand Down Expand Up @@ -203,7 +203,7 @@ Error ExecuteStage::execute(InstRef &IR) {
unsigned NumMicroOps = Inst.getNumMicroOps();
NumDispatchedOpcodes += NumMicroOps;
notifyReservedOrReleasedBuffers(IR, /* Reserved */ true);

if (!IsReadyInstruction) {
if (Inst.isPending())
notifyInstructionPending(IR);
Expand Down Expand Up @@ -250,20 +250,19 @@ void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) const {
}

void ExecuteStage::notifyInstructionIssued(
const InstRef &IR,
MutableArrayRef<std::pair<ResourceRef, ResourceCycles>> Used) const {
const InstRef &IR, MutableArrayRef<ResourceUse> Used) const {
LLVM_DEBUG({
dbgs() << "[E] Instruction Issued: #" << IR << '\n';
for (const std::pair<ResourceRef, ResourceCycles> &Resource : Used) {
assert(Resource.second.getDenominator() == 1 && "Invalid cycles!");
dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
<< Resource.first.second << "], ";
dbgs() << "cycles: " << Resource.second.getNumerator() << '\n';
for (const ResourceUse &Use : Used) {
assert(Use.second.getDenominator() == 1 && "Invalid cycles!");
dbgs() << "[E] Resource Used: [" << Use.first.first << '.'
<< Use.first.second << "], ";
dbgs() << "cycles: " << Use.second.getNumerator() << '\n';
}
});

// Replace resource masks with valid resource processor IDs.
for (std::pair<ResourceRef, ResourceCycles> &Use : Used)
for (ResourceUse &Use : Used)
Use.first.first = HWS.getResourceID(Use.first.first);

notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
Expand Down

0 comments on commit 50770d8

Please sign in to comment.