cpu: Restructure RAS (#428)

This commit is contained in:
Andreas Sandberg
2023-10-17 19:14:13 +01:00
committed by GitHub
10 changed files with 508 additions and 172 deletions

View File

@@ -57,6 +57,15 @@ class BranchType(Enum):
]
class ReturnAddrStack(SimObject):
type = "ReturnAddrStack"
cxx_class = "gem5::branch_prediction::ReturnAddrStack"
cxx_header = "cpu/pred/ras.hh"
numThreads = Param.Unsigned(Parent.numThreads, "Number of threads")
numEntries = Param.Unsigned(16, "Number of RAS entries")
class BranchTargetBuffer(ClockedObject):
type = "BranchTargetBuffer"
cxx_class = "gem5::branch_prediction::BranchTargetBuffer"
@@ -120,10 +129,10 @@ class BranchPredictor(SimObject):
numThreads = Param.Unsigned(Parent.numThreads, "Number of threads")
instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by")
RASSize = Param.Unsigned(16, "RAS size")
btb = Param.BranchTargetBuffer(SimpleBTB(), "Branch target buffer (BTB)")
ras = Param.ReturnAddrStack(
ReturnAddrStack(), "Return address stack, set to NULL to disable RAS."
)
indirectBranchPred = Param.IndirectPredictor(
SimpleIndirectPredictor(),
"Indirect branch predictor, set to NULL to disable "

View File

@@ -46,6 +46,7 @@ SimObject('BranchPredictor.py',
'BranchPredictor',
'IndirectPredictor', 'SimpleIndirectPredictor',
'BranchTargetBuffer', 'SimpleBTB',
'ReturnAddrStack',
'LocalBP', 'TournamentBP', 'BiModeBP', 'TAGEBase', 'TAGE', 'LoopPredictor',
'TAGE_SC_L_TAGE', 'TAGE_SC_L_TAGE_64KB', 'TAGE_SC_L_TAGE_8KB',
'LTAGE', 'TAGE_SC_L_LoopPredictor', 'StatisticalCorrector', 'TAGE_SC_L',
@@ -85,6 +86,7 @@ Source('btb.cc')
Source('simple_btb.cc')
DebugFlag('Indirect')
DebugFlag('BTB')
DebugFlag('RAS')
DebugFlag('FreeList')
DebugFlag('Branch')
DebugFlag('Tage')

View File

@@ -60,13 +60,11 @@ BPredUnit::BPredUnit(const Params &params)
numThreads(params.numThreads),
predHist(numThreads),
btb(params.btb),
RAS(numThreads),
ras(params.ras),
iPred(params.indirectBranchPred),
stats(this),
instShiftAmt(params.instShiftAmt)
{
for (auto& r : RAS)
r.init(params.RASSize);
}
BPredUnit::BPredUnitStats::BPredUnitStats(statistics::Group *parent)
@@ -172,19 +170,12 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
// support coroutines.
if (inst->isReturn()) {
++stats.RASUsed;
predict_record.wasReturn = true;
// If it's a function return call, then look up the address
// in the RAS.
const PCStateBase *ras_top = RAS[tid].top();
if (ras_top)
set(target, inst->buildRetPC(pc, *ras_top));
// Record the top entry of the RAS, and its index.
predict_record.usedRAS = true;
predict_record.RASIndex = RAS[tid].topIdx();
set(predict_record.RASTarget, ras_top);
RAS[tid].pop();
// If it's a return from a function call, then look up the
// RETURN address in the RAS.
const PCStateBase *return_addr = ras->pop(tid,
predict_record.rasHistory);
if (return_addr)
set(target, return_addr);
DPRINTF(Branch, "[tid:%i] [sn:%llu] Instruction %s is a return, "
"RAS predicted target: %s, RAS index: %i\n",
@@ -192,17 +183,17 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
}
if (inst->isCall()) {
RAS[tid].push(pc);
predict_record.pushedRAS = true;
// In case of a call build the return address and
// push it to the RAS.
auto return_addr = inst->buildRetPC(pc, pc);
ras->push(tid, *return_addr, predict_record.rasHistory);
// Record that it was a call so that the top RAS entry can
// be popped off if the speculation is incorrect.
predict_record.wasCall = true;
DPRINTF(Branch, "[tid:%i] [sn:%llu] Instr. %s was "
"a call, push return address %s onto the RAS\n",
tid, seqNum, pc, *return_addr);
DPRINTF(Branch,
"[tid:%i] [sn:%llu] Instruction %s was a call, adding "
"%s to the RAS index: %i\n",
tid, seqNum, pc, pc, RAS[tid].topIdx());
}
// The target address is not predicted by RAS.
@@ -237,7 +228,7 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
"called for %s\n",
tid, seqNum, pc);
} else if (inst->isCall() && !inst->isUncondCtrl()) {
RAS[tid].pop();
ras->squash(tid, predict_record.rasHistory);
predict_record.pushedRAS = false;
}
inst->advancePC(*target);
@@ -269,17 +260,13 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
if (!inst->isCall() && !inst->isReturn()) {
} else if (inst->isCall() && !inst->isUncondCtrl()) {
RAS[tid].pop();
predict_record.pushedRAS = false;
ras->squash(tid, predict_record.rasHistory);
}
inst->advancePC(*target);
}
}
}
} else {
if (inst->isReturn()) {
predict_record.wasReturn = true;
}
inst->advancePC(*target);
}
predict_record.target = target->instAddr();
@@ -325,6 +312,12 @@ BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid)
predHist[tid].back().indirectHistory);
}
if (ras) {
ras->commit(tid, predHist[tid].back().mispredict,
getBranchType(predHist[tid].back().inst),
predHist[tid].back().rasHistory);
}
predHist[tid].pop_back();
}
}
@@ -336,30 +329,15 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
while (!pred_hist.empty() &&
pred_hist.front().seqNum > squashed_sn) {
if (pred_hist.front().wasCall && pred_hist.front().pushedRAS) {
// Was a call but predicated false. Pop RAS here
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Squashing"
" Call [sn:%llu] PC: %s Popping RAS\n", tid, squashed_sn,
pred_hist.front().seqNum, pred_hist.front().pc);
RAS[tid].pop();
}
if (pred_hist.front().usedRAS) {
if (pred_hist.front().RASTarget != nullptr) {
DPRINTF(Branch, "[tid:%i] [squash sn:%llu]"
" Restoring top of RAS to: %i,"
" target: %s\n", tid, squashed_sn,
pred_hist.front().RASIndex,
*pred_hist.front().RASTarget);
}
else {
DPRINTF(Branch, "[tid:%i] [squash sn:%llu]"
" Restoring top of RAS to: %i,"
" target: INVALID_TARGET\n", tid, squashed_sn,
pred_hist.front().RASIndex);
}
RAS[tid].restore(pred_hist.front().RASIndex,
pred_hist.front().RASTarget.get());
if (pred_hist.front().rasHistory) {
assert(ras);
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] Incorrect call/return "
"PC %#x. Fix RAS.\n", tid, pred_hist.front().seqNum,
pred_hist.front().pc);
ras->squash(tid, pred_hist.front().rasHistory);
}
// This call should delete the bpHistory.
@@ -425,8 +403,7 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
assert(pred_hist.front().seqNum == squashed_sn);
}
if ((*hist_it).usedRAS) {
if ((*hist_it).rasHistory) {
++stats.RASIncorrect;
DPRINTF(Branch,
"[tid:%i] [squash sn:%llu] Incorrect RAS [sn:%llu]\n",
@@ -445,6 +422,7 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
// Remember the correct direction for the update at commit.
pred_hist.front().predTaken = actually_taken;
pred_hist.front().target = corr_target.instAddr();
pred_hist.front().mispredict = true;
update(tid, (*hist_it).pc, actually_taken,
pred_hist.front().bpHistory, true, pred_hist.front().inst,
@@ -459,16 +437,45 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
}
if (actually_taken) {
if (hist_it->wasReturn && !hist_it->usedRAS) {
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] "
"Incorrectly predicted "
"return [sn:%llu] PC: %#x\n", tid, squashed_sn,
hist_it->seqNum,
hist_it->pc);
RAS[tid].pop();
hist_it->usedRAS = true;
// Correct RAS ---------------------------------
if (ras) {
// The branch was taken and the RAS was not updated.
// In case of call or return that needs to be fixed.
if (actually_taken && (hist_it->rasHistory == nullptr)) {
// A return has not poped the RAS.
if (hist_it->inst->isReturn()) {
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] "
"Incorrectly predicted return [sn:%llu] PC: %#x\n",
tid, squashed_sn, hist_it->seqNum, hist_it->pc);
ras->pop(tid, hist_it->rasHistory);
}
// A call has not pushed a return address to the RAS.
if (hist_it->inst->isCall()) {
// In case of a call build the return address and
// push it to the RAS.
auto return_addr = hist_it->inst->buildRetPC(
corr_target, corr_target);
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] "
"Incorrectly predicted call: [sn:%llu,PC:%#x] "
" Push return address %s onto RAS\n", tid,
squashed_sn, hist_it->seqNum, hist_it->pc,
*return_addr);
ras->push(tid, *return_addr, hist_it->rasHistory);
}
// The branch was not taken but the RAS modified.
} else if (!actually_taken && (hist_it->rasHistory != nullptr)) {
// The branch was not taken but the RAS was modified.
// Needs to be fixed.
ras->squash(tid, hist_it->rasHistory);
}
}
if (actually_taken) {
if (hist_it->wasIndirect) {
++stats.indirectMispredicted;
} else {
@@ -481,42 +488,6 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
btb->update(tid, hist_it->pc, corr_target,
getBranchType(hist_it->inst));
}
} else {
//Actually not Taken
if (hist_it->wasCall && hist_it->pushedRAS) {
//Was a Call but predicated false. Pop RAS here
DPRINTF(Branch,
"[tid:%i] [squash sn:%llu] "
"Incorrectly predicted "
"Call [sn:%llu] PC: %s Popping RAS\n",
tid, squashed_sn,
hist_it->seqNum, hist_it->pc);
RAS[tid].pop();
hist_it->pushedRAS = false;
}
if (hist_it->usedRAS) {
std::string RASTarget;
DPRINTF(Branch,
"[tid:%i] [squash sn:%llu] Incorrectly predicted "
"return [sn:%llu] PC: %#x Restoring RAS\n", tid,
squashed_sn,
hist_it->seqNum, hist_it->pc);
if (hist_it->RASTarget) {
std::ostringstream os;
os << *hist_it->RASTarget.get();
RASTarget = os.str();
} else {
RASTarget = "no RAS";
}
DPRINTF(Branch,
"[tid:%i] [squash sn:%llu] Restoring top of RAS "
"to: %i, target: %s\n", tid, squashed_sn,
hist_it->RASIndex, RASTarget.c_str());
RAS[tid].restore(hist_it->RASIndex, hist_it->RASTarget.get());
hist_it->usedRAS = false;
}
}
} else {
DPRINTF(Branch, "[tid:%i] [sn:%llu] pred_hist empty, can't "

View File

@@ -218,17 +218,19 @@ class BPredUnit : public SimObject
void *indirect_history, ThreadID _tid,
const StaticInstPtr & inst)
: seqNum(seq_num), pc(instPC), bpHistory(bp_history),
indirectHistory(indirect_history), tid(_tid),
indirectHistory(indirect_history), rasHistory(nullptr),
tid(_tid),
predTaken(pred_taken), inst(inst)
{}
PredictorHistory(const PredictorHistory &other) :
seqNum(other.seqNum), pc(other.pc), bpHistory(other.bpHistory),
indirectHistory(other.indirectHistory), RASIndex(other.RASIndex),
indirectHistory(other.indirectHistory),
rasHistory(other.rasHistory), RASIndex(other.RASIndex),
tid(other.tid), predTaken(other.predTaken), usedRAS(other.usedRAS),
pushedRAS(other.pushedRAS), wasCall(other.wasCall),
wasReturn(other.wasReturn), wasIndirect(other.wasIndirect),
target(other.target), inst(other.inst)
pushedRAS(other.pushedRAS), wasIndirect(other.wasIndirect),
target(other.target), inst(other.inst),
mispredict(other.mispredict)
{
set(RASTarget, other.RASTarget);
}
@@ -253,6 +255,8 @@ class BPredUnit : public SimObject
void *indirectHistory = nullptr;
void *rasHistory = nullptr;
/** The RAS target (only valid if a return). */
std::unique_ptr<PCStateBase> RASTarget;
@@ -271,12 +275,6 @@ class BPredUnit : public SimObject
/* Whether or not the RAS was pushed */
bool pushedRAS = false;
/** Whether or not the instruction was a call. */
bool wasCall = false;
/** Whether or not the instruction was a return. */
bool wasReturn = false;
/** Wether this instruction was an indirect branch */
bool wasIndirect = false;
@@ -287,6 +285,9 @@ class BPredUnit : public SimObject
/** The branch instrction */
const StaticInstPtr inst;
/** Whether this branch was mispredicted */
bool mispredict = false;
};
typedef std::deque<PredictorHistory> History;
@@ -303,10 +304,10 @@ class BPredUnit : public SimObject
std::vector<History> predHist;
/** The BTB. */
BranchTargetBuffer* btb;
BranchTargetBuffer * btb;
/** The per-thread return address stack. */
std::vector<ReturnAddrStack> RAS;
/** The return address stack. */
ReturnAddrStack * ras;
/** The indirect target predictor. */
IndirectPredictor * iPred;

View File

@@ -1,4 +1,16 @@
/*
* Copyright (c) 2022-2023 The University of Edinburgh
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2004-2005 The Regents of The University of Michigan
* All rights reserved.
*
@@ -28,30 +40,46 @@
#include "cpu/pred/ras.hh"
#include <iomanip>
#include "debug/RAS.hh"
namespace gem5
{
namespace branch_prediction
{
void
ReturnAddrStack::init(unsigned _numEntries)
ReturnAddrStack::AddrStack::init(unsigned _numEntries)
{
numEntries = _numEntries;
addrStack.resize(numEntries);
for (unsigned i = 0; i < numEntries; ++i) {
addrStack[i] = nullptr;
}
reset();
}
void
ReturnAddrStack::reset()
ReturnAddrStack::AddrStack::reset()
{
usedEntries = 0;
tos = 0;
}
void
ReturnAddrStack::push(const PCStateBase &return_addr)
const PCStateBase *
ReturnAddrStack::AddrStack::top()
{
return addrStack[tos].get();
}
void
ReturnAddrStack::AddrStack::push(const PCStateBase &return_addr)
{
incrTos();
set(addrStack[tos], return_addr);
@@ -62,7 +90,7 @@ ReturnAddrStack::push(const PCStateBase &return_addr)
}
void
ReturnAddrStack::pop()
ReturnAddrStack::AddrStack::pop()
{
if (usedEntries > 0) {
--usedEntries;
@@ -72,9 +100,10 @@ ReturnAddrStack::pop()
}
void
ReturnAddrStack::restore(unsigned top_entry_idx, const PCStateBase *restored)
ReturnAddrStack::AddrStack::restore(unsigned _tos,
const PCStateBase *restored)
{
tos = top_entry_idx;
tos = _tos;
set(addrStack[tos], restored);
@@ -83,5 +112,218 @@ ReturnAddrStack::restore(unsigned top_entry_idx, const PCStateBase *restored)
}
}
std::string
ReturnAddrStack::AddrStack::toString(int n)
{
std::stringstream ss;
for (int i = 0; i < n; i++) {
int idx = int(tos)-i;
if (idx < 0 || addrStack[idx] == nullptr) {
break;
}
ss << std::dec << idx << ":0x" << std::setfill('0') << std::setw(16)
<< std::hex << addrStack[idx]->instAddr() << ";";
}
return ss.str();
}
// Return address stack class.
//
ReturnAddrStack::ReturnAddrStack(const Params &p)
: SimObject(p),
numEntries(p.numEntries),
numThreads(p.numThreads),
stats(this)
{
DPRINTF(RAS, "Create RAS stacks.\n");
for (unsigned i = 0; i < numThreads; ++i) {
addrStacks.emplace_back(*this);
addrStacks[i].init(numEntries);
}
}
void
ReturnAddrStack::reset()
{
DPRINTF(RAS, "RAS Reset.\n");
for (auto& r : addrStacks)
r.reset();
}
void
ReturnAddrStack::makeRASHistory(void* &ras_history)
{
RASHistory* history = new RASHistory;
history->pushed = false;
history->poped = false;
ras_history = static_cast<void*>(history);
}
void
ReturnAddrStack::push(ThreadID tid, const PCStateBase &pc,
void * &ras_history)
{
// Note: The RAS may be both popped and pushed to
// support coroutines.
if (ras_history == nullptr) {
makeRASHistory(ras_history);
}
RASHistory *history = static_cast<RASHistory*>(ras_history);
stats.pushes++;
history->pushed = true;
addrStacks[tid].push(pc);
DPRINTF(RAS, "%s: RAS[%i] <= %#x. Entries used: %i, tid:%i\n", __func__,
addrStacks[tid].tos, pc.instAddr(),
addrStacks[tid].usedEntries,tid);
// DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10));
}
const PCStateBase*
ReturnAddrStack::pop(ThreadID tid, void * &ras_history)
{
// Note: The RAS may be both popped and pushed to
// support coroutines.
if (ras_history == nullptr) {
makeRASHistory(ras_history);
}
RASHistory *history = static_cast<RASHistory*>(ras_history);
stats.pops++;
history->poped = true;
history->tos = addrStacks[tid].tos;
set(history->ras_entry, addrStacks[tid].top());
// Pop the top of stack
addrStacks[tid].pop();
DPRINTF(RAS, "%s: RAS[%i] => %#x. Entries used: %i, tid:%i\n", __func__,
addrStacks[tid].tos, (history->ras_entry.get() != nullptr)
? history->ras_entry->instAddr() : 0,
addrStacks[tid].usedEntries, tid);
// DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10));
return history->ras_entry.get();
}
void
ReturnAddrStack::squash(ThreadID tid, void * &ras_history)
{
if (ras_history == nullptr) {
// If ras_history is null no stack operation was performed for
// this branch. Nothing to be done.
return;
}
stats.squashes++;
RASHistory *history = static_cast<RASHistory*>(ras_history);
if (history->pushed) {
stats.pops++;
addrStacks[tid].pop();
DPRINTF(RAS, "RAS::%s Incorrect push. Pop RAS[%i]. "
"Entries used: %i, tid:%i\n", __func__,
addrStacks[tid].tos, addrStacks[tid].usedEntries, tid);
}
if (history->poped) {
stats.pushes++;
addrStacks[tid].restore(history->tos, history->ras_entry.get());
DPRINTF(RAS, "RAS::%s Incorrect pop. Restore to: RAS[%i]:%#x. "
"Entries used: %i, tid:%i\n", __func__,
history->tos, (history->ras_entry.get() != nullptr)
? history->ras_entry->instAddr() : 0,
addrStacks[tid].usedEntries, tid);
}
// DPRINTF(RAS, "[%s]\n", addrStacks[tid].toString(10));
delete history;
ras_history = nullptr;
}
void
ReturnAddrStack::commit(ThreadID tid, bool misp,
const BranchType brType, void * &ras_history)
{
// Skip branches that are not call or returns
if (!(brType == BranchType::Return ||
brType == BranchType::CallDirect ||
brType == BranchType::CallIndirect)) {
// If its not a call or return there should be no ras history.
assert(ras_history == nullptr);
return;
}
DPRINTF(RAS, "RAS::%s Commit Branch inst: %s, tid:%i\n",
__func__, toString(brType),tid);
if (ras_history == nullptr) {
/**
* The only case where we could have no history at this point is
* for a conditional call that is not taken.
*
* Conditional calls
*
* Conditional calls have different scenarios:
* 1. the call was predicted as non taken but was actually taken
* 2. the call was predicted taken but was actually not taken.
* 3. the call was taken but the target was incorrect.
* 4. the call was correct.
*
* In case of mispredictions they will be handled during squashing
* of the BPU. It will push and pop the RAS accordingly.
**/
return;
}
/* Handle all other commited returns and calls */
RASHistory *history = static_cast<RASHistory*>(ras_history);
if (history->poped) {
stats.used++;
if (misp) {
stats.incorrect++;
} else {
stats.correct++;
}
DPRINTF(RAS, "RAS::%s Commit Return PC %#x, correct:%i, tid:%i\n",
__func__, !misp, (history->ras_entry.get() != nullptr)
? history->ras_entry->instAddr() : 0, tid);
}
delete history;
ras_history = nullptr;
}
ReturnAddrStack::ReturnAddrStackStats::ReturnAddrStackStats(
statistics::Group *parent)
: statistics::Group(parent),
ADD_STAT(pushes, statistics::units::Count::get(),
"Number of times a PC was pushed onto the RAS"),
ADD_STAT(pops, statistics::units::Count::get(),
"Number of times a PC was poped from the RAS"),
ADD_STAT(squashes, statistics::units::Count::get(),
"Number of times the stack operation was squashed due to "
"wrong speculation."),
ADD_STAT(used, statistics::units::Count::get(),
"Number of times the RAS is the provider"),
ADD_STAT(correct, statistics::units::Count::get(),
"Number of times the RAS is the provider and the "
"prediction is correct"),
ADD_STAT(incorrect, statistics::units::Count::get(),
"Number of times the RAS is the provider and the "
"prediction is wrong")
{
}
} // namespace branch_prediction
} // namespace gem5

View File

@@ -1,4 +1,16 @@
/*
* Copyright (c) 2022-2023 The University of Edinburgh
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Copyright (c) 2004-2005 The Regents of The University of Michigan
* All rights reserved.
*
@@ -32,7 +44,12 @@
#include <vector>
#include "arch/generic/pcstate.hh"
#include "base/statistics.hh"
#include "base/types.hh"
#include "cpu/pred/branch_type.hh"
#include "cpu/static_inst.hh"
#include "params/ReturnAddrStack.hh"
#include "sim/sim_object.hh"
namespace gem5
{
@@ -41,70 +58,164 @@ namespace branch_prediction
{
/** Return address stack class, implements a simple RAS. */
class ReturnAddrStack
class ReturnAddrStack : public SimObject
{
public:
/** Creates a return address stack, but init() must be called prior to
* use.
*/
ReturnAddrStack() {}
/** Initializes RAS with a specified number of entries.
* @param numEntries Number of entries in the RAS.
/** Subclass that implements the actual address stack. ******
*/
void init(unsigned numEntries);
class AddrStack
{
public:
AddrStack(ReturnAddrStack &_parent)
: parent(_parent)
{}
/** Initializes RAS with a specified number of entries.
* @param numEntries Number of entries in the RAS.
*/
void init(unsigned numEntries);
void reset();
/** Returns the top address on the RAS. */
const PCStateBase *top();
/** Returns the index of the top of the RAS. */
unsigned topIdx() { return tos; }
/** Pushes an address onto the RAS. */
void push(const PCStateBase &return_addr);
/** Pops the top address from the RAS. */
void pop();
/** Changes index to the top of the RAS, and replaces the top address
* with a new target.
* @param top_of_stack the index saved at the time of the prediction.
* @param restored The new target address of the new top of the RAS.
*/
void restore(unsigned top_of_stack, const PCStateBase *restored);
bool empty() { return usedEntries == 0; }
bool full() { return usedEntries >= numEntries; }
/** Returns the top n entries of the stack as string. For debugging. */
std::string toString(int n);
/** Increments the top of stack index. */
inline void
incrTos()
{
if (++tos == numEntries)
tos = 0;
}
/** Decrements the top of stack index. */
inline void
decrTos()
{
tos = (tos == 0 ? numEntries - 1 : tos - 1);
}
/** The Stack itself. */
std::vector<std::unique_ptr<PCStateBase>> addrStack;
/** The number of entries in the RAS. */
unsigned numEntries;
/** The number of used entries in the RAS. */
unsigned usedEntries;
/** The top of stack index. */
unsigned tos;
protected:
ReturnAddrStack &parent;
};
public:
// typedef RASParams Params;
typedef ReturnAddrStackParams Params;
// ReturnAddrStack(BPredUnit &_parent, const RASParams);
ReturnAddrStack(const Params &p);
void reset();
/** Returns the top address on the RAS. */
const PCStateBase *top() { return addrStack[tos].get(); }
/** Returns the index of the top of the RAS. */
unsigned topIdx() { return tos; }
/** Pushes an address onto the RAS. */
void push(const PCStateBase &return_addr);
/** Pops the top address from the RAS. */
void pop();
/** Changes index to the top of the RAS, and replaces the top address with
* a new target.
* @param top_entry_idx The index of the RAS that will now be the top.
* @param restored The new target address of the new top of the RAS.
/**
* Pushes an address onto the RAS.
* @param PC The current PC (should be a call).
* @param ras_history Pointer that will be set to an object that
* has the return address state associated when the address was pushed.
*/
void restore(unsigned top_entry_idx, const PCStateBase *restored);
void push(ThreadID tid, const PCStateBase &pc, void * &ras_history);
bool empty() { return usedEntries == 0; }
/**
* Pops the top address from the RAS.
* @param ras_history Pointer that will be set to an object that
* has the return address state associated when an address was poped.
* @return The address that got poped from the stack.
* */
const PCStateBase* pop(ThreadID tid, void * &ras_history);
/**
* The branch (call/return) got squashed.
* Restores the state of the RAS and delete history
* @param res_history The pointer to the history object.
*/
void squash(ThreadID tid, void * &ras_history);
/**
* A branch got finally got finally commited.
* @param misp Whether the branch was mispredicted.
* @param brType The type of the branch.
* @param ras_history The pointer to the history object.
*/
void commit(ThreadID tid, bool misp,
const BranchType brType, void * &ras_history);
bool full() { return usedEntries == numEntries; }
private:
/** Increments the top of stack index. */
inline void
incrTos()
{
if (++tos == numEntries)
tos = 0;
}
/** Decrements the top of stack index. */
inline void
decrTos()
class RASHistory
{
tos = (tos == 0 ? numEntries - 1 : tos - 1);
}
public:
/* Was the RAS pushed or poped for this branch. */
bool pushed = false;
bool poped = false;
/* Was it a call */
bool wasReturn = false;
bool wasCall = false;
/** The entry that poped from the RAS (only valid if a return). */
std::unique_ptr<PCStateBase> ras_entry;
/** The RAS index (top of stack pointer) of the instruction */
unsigned tos = 0;
};
void makeRASHistory(void* &ras_history);
/** The RAS itself. */
std::vector<std::unique_ptr<PCStateBase>> addrStack;
std::vector<AddrStack> addrStacks;
/** The number of entries in the RAS. */
unsigned numEntries;
/** The number of threads */
unsigned numThreads;
/** The number of used entries in the RAS. */
unsigned usedEntries;
/** The top of stack index. */
unsigned tos;
struct ReturnAddrStackStats : public statistics::Group
{
ReturnAddrStackStats(statistics::Group *parent);
statistics::Scalar pushes;
statistics::Scalar pops;
statistics::Scalar squashes;
statistics::Scalar used;
statistics::Scalar correct;
statistics::Scalar incorrect;
} stats;
};
} // namespace branch_prediction