Skip to content

Commit

Permalink
Implement LocalBPU and IndirectBPU (#33)
Browse files Browse the repository at this point in the history
They are modeled after LocalBP and IndirectBP from gem5. One is N-bit local branch predictor and the other is a set-associative indirect branch predictor.
  • Loading branch information
tjhu authored Dec 6, 2024
1 parent d4f8d82 commit 21470e8
Show file tree
Hide file tree
Showing 8 changed files with 813 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ set(_CUSTOMHW_SOURCE_FILES
CustomHWUnits/CustomSourceMgr.cpp
CustomHWUnits/MCADLSUnit.cpp
CustomHWUnits/NaiveBranchPredictorUnit.cpp
CustomHWUnits/LocalBPU.cpp
CustomHWUnits/IndirectBPU.cpp
)

set(_CUSTOM_STAGES_SOURCE_FILES
Expand Down
11 changes: 11 additions & 0 deletions CustomHWUnits/AbstractBranchPredictorUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ class AbstractBranchPredictorUnit : public llvm::mca::HardwareUnit {

};

/* Similar to the AbstractBranchPredictorUnit, but it precdicts the branch
* target address instead of the direction.
*/
class AbstractIndirectBranchPredictorUnit : public llvm::mca::HardwareUnit {
public:
~AbstractIndirectBranchPredictorUnit() {}
virtual void recordTakenBranch(MDInstrAddr IA, MDInstrAddr destAddr) = 0;
virtual MDInstrAddr predictBranch(MDInstrAddr IA) = 0;
virtual unsigned getMispredictionPenalty() = 0;
};

}
}

Expand Down
41 changes: 41 additions & 0 deletions CustomHWUnits/IndirectBPU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <map>
#include "CustomHWUnits/IndirectBPU.h"

namespace llvm {
namespace mcad {

void IndirectBPU::recordTakenBranch(MDInstrAddr IA, MDInstrAddr destAddr) {
// Look for the entry in the table.
auto& set = indirectBranchTable[getSetIndex(IA)];
for (auto &way : set) {
if (way.tag == IA) {
way.target = destAddr;
return;
}
}

// If we didn't find an entry, we need to evict one.
// We will choose a random entry to evict.
auto &way = set[rand() % numWays];
way.tag = IA;
way.target = destAddr;
}

MDInstrAddr IndirectBPU::predictBranch(MDInstrAddr IA) {
// Look for the entry in the table.
auto& set = indirectBranchTable[getSetIndex(IA)];
for (auto &way : set) {
if (way.tag == IA) {
return way.target;
}
}

// If we didn't find an entry, we will predict the next byte.
return MDInstrAddr { IA.addr + 1 };
}

unsigned IndirectBPU::getSetIndex(MDInstrAddr IA) const {
return IA.addr % numSets;
}
} // namespace mcad
} // namespace llvm
44 changes: 44 additions & 0 deletions CustomHWUnits/IndirectBPU.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef LLVM_MCAD_INDIRECT_BPU_H
#define LLVM_MCAD_INDIRECT_BPU_H

#include "CustomHWUnits/AbstractBranchPredictorUnit.h"

#include <map>

#include "lib/gem5/sat_counter.h"
#include "lib/gem5/intmath.h"

namespace llvm {
namespace mcad {

/// @brief A simple indirect branch predictor.
///
/// This branch predictor is based off of the `IndirectBP` in gem5.
/// It maintains a set-associative table of indirect branch targets.
class IndirectBPU : public AbstractIndirectBranchPredictorUnit {
private:
const unsigned numSets;
const unsigned numWays;

struct IndirectBranchEntry {
MDInstrAddr tag = {0};
MDInstrAddr target = {0};
};

std::vector<std::vector<IndirectBranchEntry>> indirectBranchTable;

unsigned getSetIndex(MDInstrAddr IA) const;


public:
IndirectBPU(unsigned numSets = 256, unsigned numWays = 2)
: numSets(numSets), numWays(numWays), indirectBranchTable(numSets, std::vector<IndirectBranchEntry>(numWays)) {}

void recordTakenBranch(MDInstrAddr IA, MDInstrAddr destAddr) override;
MDInstrAddr predictBranch(MDInstrAddr IA) override;
};

} // namespace mcad
} // namespace llvm

#endif /* LLVM_MCAD_INDIRECT_BPU_H */
28 changes: 28 additions & 0 deletions CustomHWUnits/LocalBPU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <map>
#include "CustomHWUnits/LocalBPU.h"

namespace llvm {
namespace mcad {

void LocalBPU::recordTakenBranch(MDInstrAddr IA, BranchDirection nextInstrDirection) {
bool isTaken = nextInstrDirection == BranchDirection::TAKEN;
unsigned idx = getPredictorIndex(IA);
predictorTable[idx] += isTaken;
}

AbstractBranchPredictorUnit::BranchDirection LocalBPU::predictBranch(MDInstrAddr IA) {
return getPrediction(IA) ? BranchDirection::TAKEN : BranchDirection::NOT_TAKEN;
}

unsigned LocalBPU::getPredictorIndex(MDInstrAddr IA) const {
// TODO: this could probably be improved. gem5 shifts it by 2 then mask it.
return IA.addr % numPredictorSets;
}

bool LocalBPU::getPrediction(MDInstrAddr IA) const {
unsigned idx = getPredictorIndex(IA);
// Return the MSB of the counter.
return predictorTable[idx] >> (numCtrlBits - 1);
}
} // namespace mcad
} // namespace llvm
58 changes: 58 additions & 0 deletions CustomHWUnits/LocalBPU.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#ifndef LLVM_MCAD_TWO_BIT_LOCAL_BPU_H
#define LLVM_MCAD_TWO_BIT_LOCAL_BPU_H

#include "CustomHWUnits/AbstractBranchPredictorUnit.h"

#include <map>

#include "lib/gem5/sat_counter.h"
#include "lib/gem5/intmath.h"

namespace llvm {
namespace mcad {

/// @brief A simple two-bit local branch predictor.
///
/// This branch predictor is based off of the `LocalBP` in gem5.
/// It uses a table of n-bit saturating counters to predict whether a branch
/// will be taken or not.
class LocalBPU : public AbstractBranchPredictorUnit {
private:
/// @brief The penalty for a misprediction.
const unsigned mispredictionPenalty;
/// @brief The number of control bits per entry in the predictor table.
const unsigned numCtrlBits;
/// @brief The size of the predictor table in bits.
const unsigned predictorSize;
/// @brief The number of entries in the predictor table.
const unsigned numPredictorSets;
/// @brief The branch predictor table with n-bit saturating counters as entries.
std::vector<gem5::SatCounter8> predictorTable;

public:
LocalBPU(unsigned mispredictionPenalty = 20, unsigned numCtrlBits = 2,
unsigned predictorSize = 2048)
: mispredictionPenalty(mispredictionPenalty),
numCtrlBits(numCtrlBits), predictorSize(predictorSize)
,numPredictorSets(predictorSize / numCtrlBits)
,predictorTable(numPredictorSets, gem5::SatCounter8(numCtrlBits))
{
assert(gem5::isPowerOf2(predictorSize));
assert(numCtrlBits > 0);
};

void recordTakenBranch(MDInstrAddr IA, BranchDirection nextInstrDirection) override;
BranchDirection predictBranch(MDInstrAddr IA) override;
unsigned getMispredictionPenalty() override { return mispredictionPenalty; }

private:
/// @brief Get the index of the predictor table for the given instruction address.
unsigned getPredictorIndex(MDInstrAddr IA) const;
/// @brief Get the prediction for the given instruction address.
bool getPrediction(MDInstrAddr IA) const;
};

} // namespace mcad
} // namespace llvm

#endif /* LLVM_MCAD_TWO_BIT_LOCAL_BPU_H */
Loading

0 comments on commit 21470e8

Please sign in to comment.