Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Branch Prediction API #139

Merged
merged 12 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions core/BranchPred.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "BranchPred.hpp"

/*
* The algorithm used for prediction / update is as follows:
* Prediction:
* - look up BHT to determine if the branch is predicted taken or not
* - look up BTB to see if an entry exists for the input fetch pc
* - if present in BTB and predicted taken, BTB entry is used to determine
* prediction branch idx and predictedPC
* - if present in BTB but predicted not taken, BTB entry is used to determine
* prediction branch idx, while predictedPC is the fall through addr
* - if not present in BTB entry, prediction branch idx is the last instr of
* the FetchPacket, while predicted PC is the fall through addr. Also, create
* a new BTB entry
* Update:
* - a valid BTB entry must be present for fetch PC
* - TBD
*
*/

#define BYTES_PER_INST 4
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved

void SimpleBranchPredictor::updatePredictor(DefaultUpdate & update) {
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved

sparta_assert(branch_target_buffer_.find(update.FetchPC) != branch_target_buffer_.end());
branch_target_buffer_[update.FetchPC].branch_idx = update.branch_idx;
if (update.actuallyTaken) {
branch_history_table_[update.FetchPC] =
(branch_history_table_[update.FetchPC] == 3) ? 3 :
branch_history_table_[update.FetchPC] + 1;
branch_target_buffer_[update.FetchPC].predictedPC = update.correctedPC;
} else {
branch_history_table_[update.FetchPC] =
(branch_history_table_[update.FetchPC] == 0) ? 0 :
branch_history_table_[update.FetchPC] - 1;
}
}

DefaultPrediction SimpleBranchPredictor::getPrediction(const DefaultInput & input) {
bool predictTaken;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
if (branch_history_table_.find(input.FetchPC) != branch_history_table_.end()) {
predictTaken = (branch_history_table_[input.FetchPC] > 1);
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
} else {
predictTaken = false;
// add a new entry to BHT, biased towards not taken
branch_history_table_.insert(std::pair<uint64_t, uint8_t>(input.FetchPC, 1));
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
}

DefaultPrediction prediction;
if (branch_target_buffer_.find(input.FetchPC) != branch_target_buffer_.end()) {
// BTB hit
BTBEntry btb_entry = branch_target_buffer_[input.FetchPC];
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
prediction.branch_idx = btb_entry.branch_idx;
if (predictTaken) {
prediction.predictedPC = btb_entry.predictedPC;
} else {
// fall through address
prediction.predictedPC = input.FetchPC + prediction.branch_idx + BYTES_PER_INST;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
// BTB miss
prediction.branch_idx = max_fetch_insts_;
prediction.predictedPC = input.FetchPC + max_fetch_insts_ * BYTES_PER_INST;
// add new entry to BTB
branch_target_buffer_.insert(std::pair<uint64_t,BTBEntry>(
input.FetchPC, BTBEntry(prediction.branch_idx, prediction.predictedPC)));
}

return prediction;
}
96 changes: 96 additions & 0 deletions core/BranchPred.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// <Branch.hpp> -*- C++ -*-

//!
//! \file BranchPred.hpp
//! \brief Definition of Branch Prediction API
//!

/*
* This file defines the Branch Prediction API.
* The goal is to define an API that is generic and yet flexible enough to support various
* branch prediction microarchitecture.
* To the end, we envision a generic branch predictor as a black box with following inputs
* and outputs:
* * A generic Prediction output
* * A generic Prediction input
* * A generic Update input
*
* The generic branch predictor may have two operations:
* * getPrediction: produces Prediction output based on the Prediction input.
* * updatePredictor: updates Predictor with Update input.
*
* It is intended that an implementation of branch predictor must also specify
* implementations of Prediction output, Prediction input and Update input, along with
* implementations of getPrediction and updatePredictor operations.
* */
#pragma once

#include <cstdint>
#include <map>
#include "sparta/utils/SpartaAssert.hpp"

template <class PredictionT, class UpdateT, class InputT>
class BranchPredictorIF
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
{
public:
virtual PredictionT getPrediction(const InputT &) = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a const return type, to allow compiler to do move optimization
use: virtual const PredictionT getPrediction(const InputT &) = 0;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain further why this is needed / recommended ?

This https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f49-dont-return-const-t , seem to suggest that const return type would suppress use of move semantics and is not advisable.

virtual void updatePredictor(UpdateT &) = 0;
};

// following class definitions are example inputs & outputs for a very simple branch
// predictor
class DefaultPrediction
{
public:
// index of branch instruction in the fetch packet
// branch_idx can vary from 0 to (FETCH_WIDTH - 1)
uint32_t branch_idx;
// predicted target PC
uint64_t predictedPC;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
};

class DefaultUpdate
{
public:
uint64_t FetchPC;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
uint32_t branch_idx;
uint64_t correctedPC;
bool actuallyTaken;
};

class DefaultInput
{
public:
// PC of first instruction of fetch packet
uint64_t FetchPC;
};

class BTBEntry
{
public:
// use of BTBEntry in std:map operator [] requires default constructor
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
BTBEntry(uint32_t bidx=0, uint64_t predPC=0) :
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
branch_idx(bidx),
predictedPC(predPC)
{}
uint32_t branch_idx;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
uint64_t predictedPC;
};

class SimpleBranchPredictor : public BranchPredictorIF<DefaultPrediction, DefaultUpdate, DefaultInput>
{
public:
SimpleBranchPredictor(uint32_t max_fetch_insts) :
max_fetch_insts_(max_fetch_insts)
{}
DefaultPrediction getPrediction(const DefaultInput &);
void updatePredictor(DefaultUpdate &);
private:
// maximum number of instructions in a FetchPacket
uint32_t max_fetch_insts_;
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
// a map of branch PC to 2 bit staurating counter tracking branch history
std::map <uint64_t, uint8_t> branch_history_table_; // BHT
arupc-vmicro marked this conversation as resolved.
Show resolved Hide resolved
// a map of branch PC to target of the branch
std::map <uint64_t, BTBEntry> branch_target_buffer_; // BTB
//
};
1 change: 1 addition & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
project (core)
add_library(core
Core.cpp
BranchPred.cpp
Fetch.cpp
Decode.cpp
Rename.cpp
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ add_subdirectory(core/dispatch)
add_subdirectory(core/l2cache)
add_subdirectory(core/rename)
add_subdirectory(core/lsu)
add_subdirectory(core/branch_pred)
43 changes: 43 additions & 0 deletions test/core/branch_pred/BranchPred_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "BranchPred.hpp"
#include "sparta/utils/SpartaTester.hpp"

TEST_INIT

void runTest(int argc, char **argv)
{
SimpleBranchPredictor predictor(4); //specify max num insts to fetch

DefaultInput input;
input.FetchPC = 0x0;

// BTB miss
DefaultPrediction prediction = predictor.getPrediction(input);

EXPECT_EQUAL(prediction.branch_idx, 4);
EXPECT_EQUAL(prediction.predictedPC, 16);

// there was a taken branch at the 3rd instruction from fetchPC, with target 0x100
DefaultUpdate update;
update.FetchPC = 0x0;
update.branch_idx = 2;
update.correctedPC = 0x100;
update.actuallyTaken = true;
predictor.updatePredictor(update);

// try the same input with fetchPC 0x0 again
prediction = predictor.getPrediction(input);

EXPECT_EQUAL(prediction.branch_idx, 2);
EXPECT_EQUAL(prediction.predictedPC, 0x100);

// TODO: add more tests

}

int main(int argc, char **argv)
{
runTest(argc, argv);

REPORT_ERROR;
return (int)ERROR_CODE;
}
6 changes: 6 additions & 0 deletions test/core/branch_pred/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
project(BranchPred_test)

add_executable(BranchPred_test BranchPred_test.cpp)
target_link_libraries(BranchPred_test core common_test SPARTA::sparta)

sparta_named_test(BranchPred_test_Run BranchPred_test)
Loading