Skip to content

Commit 09a0bc2

Browse files
authored
Add FetchDelay stage (#27)
2 parents ae912fe + aa8b54e commit 09a0bc2

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ set(_CUSTOMHW_SOURCE_FILES
9999
CustomHWUnits/MCADLSUnit.cpp
100100
)
101101

102+
set(_CUSTOM_STAGES_SOURCE_FILES
103+
CustomStages/MCADFetchDelayStage.cpp
104+
)
105+
102106
set(_BROKERS_SOURCE_FILES
103107
Brokers/BrokerPlugin.cpp
104108
Brokers/RawBytesBroker.cpp
@@ -111,6 +115,7 @@ set(_SOURCE_FILES
111115
llvm-mcad.cpp
112116
${_MCAVIEWS_SOURCE_FILES}
113117
${_CUSTOMHW_SOURCE_FILES}
118+
${_CUSTOM_STAGES_SOURCE_FILES}
114119
${_BROKERS_SOURCE_FILES}
115120
MCAWorker.cpp
116121
PipelinePrinter.cpp
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include <iostream>
2+
#include "CustomStages/MCADFetchDelayStage.h"
3+
4+
namespace llvm {
5+
namespace mcad {
6+
7+
struct MCADInstructionFetchedEvent {};
8+
9+
bool MCADFetchDelayStage::hasWorkToComplete() const {
10+
return !instrQueue.empty();
11+
}
12+
13+
bool MCADFetchDelayStage::isAvailable(const llvm::mca::InstRef &IR) const {
14+
return checkNextStage(IR);
15+
}
16+
17+
llvm::Error MCADFetchDelayStage::forwardDueInstrs() {
18+
while(!instrQueue.empty() && instrQueue.front().delayCyclesLeft == 0) {
19+
llvm::mca::InstRef IR = instrQueue.front().IR;
20+
if (llvm::Error Val = moveToTheNextStage(IR)) {
21+
return Val;
22+
}
23+
instrQueue.pop_front();
24+
}
25+
return llvm::ErrorSuccess();
26+
}
27+
28+
llvm::Error MCADFetchDelayStage::execute(llvm::mca::InstRef &IR) {
29+
// We (ab-)use the LastGenericEventType to create a notification when the instruction first enters this stage.
30+
// We use this elsewhere to calculate the number of cycles between when an instruction first enters the pipeline and the end of its execution.
31+
notifyEvent<llvm::mca::HWInstructionEvent>(llvm::mca::HWInstructionEvent(llvm::mca::HWInstructionEvent::LastGenericEventType, IR));
32+
const llvm::mca::Instruction *I = IR.getInstruction();
33+
const llvm::mca::InstrDesc &ID = I->getDesc();
34+
const llvm::MCInstrDesc &MCID = MCII.get(I->getOpcode());
35+
bool immediatelyExecute = true;
36+
unsigned delayCyclesLeft = 0;
37+
if(MCID.isBranch()) {
38+
// delayed, will have to wait
39+
delayCyclesLeft = 100;
40+
}
41+
instrQueue.emplace_back(DelayedInstr { delayCyclesLeft, IR });
42+
// if the instruction is not delayed, execute it immediately (it will
43+
// have a delayCyclesLeft of 0 and be at the top of the queue)
44+
return forwardDueInstrs();
45+
}
46+
47+
llvm::Error MCADFetchDelayStage::cycleStart() {
48+
if(!instrQueue.empty()) {
49+
instrQueue.front().delayCyclesLeft--;
50+
}
51+
return forwardDueInstrs();
52+
}
53+
54+
}
55+
}

CustomStages/MCADFetchDelayStage.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// This class does not model a real hardware stage. It is used to block the
2+
// pipeline for a number of cycles to prevent further instructions from being
3+
// fetched. We use this to model the cost of branch mispredictions.
4+
5+
#ifndef LLVM_MCAD_FETCH_DELAY_STAGE_H
6+
#define LLVM_MCAD_FETCH_DELAY_STAGE_H
7+
8+
#include "llvm/MC/MCInstrInfo.h"
9+
#include "llvm/MCA/SourceMgr.h"
10+
#include "llvm/MCA/Stages/Stage.h"
11+
12+
#include <vector>
13+
#include <queue>
14+
15+
namespace llvm {
16+
namespace mcad {
17+
18+
class MCADFetchDelayStage : public llvm::mca::Stage {
19+
20+
struct DelayedInstr {
21+
unsigned delayCyclesLeft;
22+
llvm::mca::InstRef IR;
23+
};
24+
25+
const llvm::MCInstrInfo &MCII;
26+
std::deque<DelayedInstr> instrQueue = {};
27+
28+
public:
29+
MCADFetchDelayStage(const llvm::MCInstrInfo &MCII) : MCII(MCII) {}
30+
31+
bool hasWorkToComplete() const override;
32+
bool isAvailable(const llvm::mca::InstRef &IR) const override;
33+
llvm::Error execute(llvm::mca::InstRef &IR) override;
34+
35+
//llvm::Error cycleStart() override;
36+
llvm::Error cycleStart() override;
37+
38+
llvm::Error forwardDueInstrs();
39+
40+
///// Called after the pipeline is resumed from pausing state.
41+
//virtual Error cycleResume() { return ErrorSuccess(); }
42+
43+
///// Called once at the end of each cycle.
44+
45+
};
46+
47+
}
48+
}
49+
50+
#endif

MCAWorker.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <unistd.h>
3838

3939
#include "CustomHWUnits/MCADLSUnit.h"
40+
#include "CustomStages/MCADFetchDelayStage.h"
4041
#include "MCAViews/SummaryView.h"
4142
#include "MCAViews/TimelineView.h"
4243
#include "MCAWorker.h"
@@ -183,6 +184,7 @@ std::unique_ptr<mca::Pipeline> MCAWorker::createDefaultPipeline() {
183184

184185
// Create the pipeline stages.
185186
auto Fetch = std::make_unique<EntryStage>(SrcMgr);
187+
auto FetchDelay = std::make_unique<MCADFetchDelayStage>(MCII);
186188
auto Dispatch = std::make_unique<DispatchStage>(STI, MRI, MCAPO.DispatchWidth,
187189
*RCU, *PRF);
188190
auto Execute =
@@ -198,6 +200,7 @@ std::unique_ptr<mca::Pipeline> MCAWorker::createDefaultPipeline() {
198200
// Build the pipeline.
199201
auto StagePipeline = std::make_unique<Pipeline>();
200202
StagePipeline->appendStage(std::move(Fetch));
203+
StagePipeline->appendStage(std::move(FetchDelay));
201204
if (MCAPO.MicroOpQueueSize)
202205
StagePipeline->appendStage(std::make_unique<MicroOpQueueStage>(
203206
MCAPO.MicroOpQueueSize, MCAPO.DecodersThroughput));
@@ -224,6 +227,7 @@ std::unique_ptr<mca::Pipeline> MCAWorker::createInOrderPipeline() {
224227

225228
// Create the pipeline stages.
226229
auto Entry = std::make_unique<EntryStage>(SrcMgr);
230+
auto FetchDelay = std::make_unique<MCADFetchDelayStage>(MCII);
227231
auto InOrderIssue = std::make_unique<InOrderIssueStage>(STI, *PRF, *CB, *LSU);
228232
auto StagePipeline = std::make_unique<Pipeline>();
229233

@@ -233,6 +237,7 @@ std::unique_ptr<mca::Pipeline> MCAWorker::createInOrderPipeline() {
233237

234238
// Build the pipeline.
235239
StagePipeline->appendStage(std::move(Entry));
240+
StagePipeline->appendStage(std::move(FetchDelay));
236241
StagePipeline->appendStage(std::move(InOrderIssue));
237242

238243
for (auto *listener : Listeners) {
@@ -420,6 +425,9 @@ Error MCAWorker::run() {
420425
<< " has Token " << MDTok << "\n");
421426
NewInst->setIdentifier(MDTok);
422427
}
428+
429+
// Add this instruction to be processed by the pipeline
430+
// (Entry stage will consume from source manager.)
423431
SrcMgr.addInst(std::move(NewInst));
424432
}
425433
}

plugins/binja-broker/Broker.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ class RawListener : public mca::HWEventListener {
150150
}
151151

152152
switch (Event.Type) {
153-
case mca::HWInstructionEvent::GenericEventType::Ready: {
153+
//case mca::HWInstructionEvent::GenericEventType::Dispatched: {
154+
// We (ab-)use the LastGenericEventType in the FetchDelay stage to
155+
// notify of an event whenever the instruction is fetch. This way, the
156+
// cycle count in Binja shows the total instruction cycle count
157+
// including the fetch and dispatch cost.
158+
case mca::HWInstructionEvent::GenericEventType::LastGenericEventType : {
154159
BridgeRef.CountStore[index].CycleReady = CurrentCycle;
155160
}
156161
case mca::HWInstructionEvent::GenericEventType::Executed: {

0 commit comments

Comments
 (0)