cpu: Refactor indirect predictor (#429)

This commit is contained in:
Andreas Sandberg
2023-10-13 11:35:02 +01:00
committed by GitHub
5 changed files with 446 additions and 161 deletions

View File

@@ -100,6 +100,13 @@ class SimpleIndirectPredictor(IndirectPredictor):
indirectPathLength = Param.Unsigned(
3, "Previous indirect targets to use for path history"
)
speculativePathLength = Param.Unsigned(
256,
"Additional buffer space to store speculative path history. "
"If there are more speculative branches in flight the history cannot "
"be recovered. Set this to an appropriate value respective the CPU"
"pipeline depth or a high value e.g. 256 to make it 'unlimited'.",
)
indirectGHRBits = Param.Unsigned(13, "Indirect GHR number of bits")
instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by")
@@ -119,7 +126,8 @@ class BranchPredictor(SimObject):
indirectBranchPred = Param.IndirectPredictor(
SimpleIndirectPredictor(),
"Indirect branch predictor, set to NULL to disable indirect predictions",
"Indirect branch predictor, set to NULL to disable "
"indirect predictions",
)

View File

@@ -158,10 +158,6 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
tid, seqNum, pred_taken, pc);
}
const bool orig_pred_taken = pred_taken;
if (iPred) {
iPred->genIndirectInfo(tid, indirect_history);
}
DPRINTF(Branch,
"[tid:%i] [sn:%llu] Creating prediction history for PC %s\n",
@@ -250,11 +246,16 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
predict_record.wasIndirect = true;
++stats.indirectLookups;
//Consult indirect predictor on indirect control
if (iPred->lookup(pc.instAddr(), *target, tid)) {
const PCStateBase *itarget = iPred->lookup(tid,
seqNum, pc.instAddr(),
predict_record.indirectHistory);
if (itarget) {
// Indirect predictor hit
++stats.indirectHits;
set(target, *itarget);
DPRINTF(Branch,
"[tid:%i] [sn:%llu] Instruction %s predicted "
"[tid:%i, sn:%llu] Instruction %s predicted "
"indirect target is %s\n",
tid, seqNum, pc, *target);
} else {
@@ -262,9 +263,9 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
pred_taken = false;
predict_record.predTaken = pred_taken;
DPRINTF(Branch,
"[tid:%i] [sn:%llu] Instruction %s no indirect "
"target\n",
tid, seqNum, pc);
"[tid:%i, sn:%llu] PC:%#x no indirect target\n",
tid, seqNum, pc.instAddr());
if (!inst->isCall() && !inst->isReturn()) {
} else if (inst->isCall() && !inst->isUncondCtrl()) {
@@ -273,8 +274,6 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
}
inst->advancePC(*target);
}
iPred->recordIndirect(pc.instAddr(), target->instAddr(),
seqNum, tid);
}
}
} else {
@@ -289,11 +288,10 @@ BPredUnit::predict(const StaticInstPtr &inst, const InstSeqNum &seqNum,
if (iPred) {
// Update the indirect predictor with the direction prediction
// Note that this happens after indirect lookup, so it does not use
// the new information
// Note also that we use orig_pred_taken instead of pred_taken in
// as this is the actual outcome of the direction prediction
iPred->updateDirectionInfo(tid, orig_pred_taken);
iPred->update(tid, seqNum, predict_record.pc, false,
predict_record.predTaken, *target,
getBranchType(inst),
predict_record.indirectHistory);
}
predHist[tid].push_front(predict_record);
@@ -321,8 +319,10 @@ BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid)
predHist[tid].back().inst,
predHist[tid].back().target);
// Commite also Indirect predictor and RAS
if (iPred) {
iPred->commit(done_sn, tid, predHist[tid].back().indirectHistory);
iPred->commit(tid, predHist[tid].back().seqNum,
predHist[tid].back().indirectHistory);
}
predHist[tid].pop_back();
@@ -334,10 +334,6 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
{
History &pred_hist = predHist[tid];
if (iPred) {
iPred->squash(squashed_sn, tid);
}
while (!pred_hist.empty() &&
pred_hist.front().seqNum > squashed_sn) {
if (pred_hist.front().wasCall && pred_hist.front().pushedRAS) {
@@ -369,7 +365,8 @@ BPredUnit::squash(const InstSeqNum &squashed_sn, ThreadID tid)
// This call should delete the bpHistory.
squash(tid, pred_hist.front().bpHistory);
if (iPred) {
iPred->deleteIndirectInfo(tid, pred_hist.front().indirectHistory);
iPred->squash(tid, pred_hist.front().seqNum,
pred_hist.front().indirectHistory);
}
DPRINTF(Branch, "[tid:%i] [squash sn:%llu] "
@@ -453,9 +450,13 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
pred_hist.front().bpHistory, true, pred_hist.front().inst,
corr_target.instAddr());
// Correct Indirect predictor -------------------
if (iPred) {
iPred->changeDirectionPrediction(tid,
pred_hist.front().indirectHistory, actually_taken);
iPred->update(tid, squashed_sn, (*hist_it).pc,
true, actually_taken, corr_target,
getBranchType(pred_hist.front().inst),
(*hist_it).indirectHistory);
}
if (actually_taken) {
@@ -470,11 +471,6 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
}
if (hist_it->wasIndirect) {
++stats.indirectMispredicted;
if (iPred) {
iPred->recordTarget(
hist_it->seqNum, pred_hist.front().indirectHistory,
corr_target, tid);
}
} else {
DPRINTF(Branch,"[tid:%i] [squash sn:%llu] "
"BTB Update called for [sn:%llu] "

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2014 ARM Limited
* All rights reserved.
* Copyright (c) 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -26,11 +36,16 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* @file
* Indirect target predictor interface
*/
#ifndef __CPU_PRED_INDIRECT_BASE_HH__
#define __CPU_PRED_INDIRECT_BASE_HH__
#include "arch/generic/pcstate.hh"
#include "cpu/inst_seq.hh"
#include "cpu/pred/branch_type.hh"
#include "params/IndirectPredictor.hh"
#include "sim/sim_object.hh"
@@ -51,21 +66,57 @@ class IndirectPredictor : public SimObject
{
}
virtual bool lookup(Addr br_addr, PCStateBase& br_target,
ThreadID tid) = 0;
virtual void recordIndirect(Addr br_addr, Addr tgt_addr,
InstSeqNum seq_num, ThreadID tid) = 0;
virtual void commit(InstSeqNum seq_num, ThreadID tid,
void * indirect_history) = 0;
virtual void squash(InstSeqNum seq_num, ThreadID tid) = 0;
virtual void recordTarget(InstSeqNum seq_num, void * indirect_history,
const PCStateBase& target, ThreadID tid) = 0;
virtual void genIndirectInfo(ThreadID tid, void* & indirect_history) = 0;
virtual void updateDirectionInfo(ThreadID tid, bool actually_taken) = 0;
virtual void deleteIndirectInfo(ThreadID tid, void * indirect_history) = 0;
virtual void changeDirectionPrediction(ThreadID tid,
void * indirect_history,
bool actually_taken) = 0;
virtual void reset() {};
/**
* Predicts the indirect target of an indirect branch.
* @param tid Thread ID of the branch.
* @param sn The sequence number of the branch.
* @param pc The branch PC address.
* @param i_history The pointer to the history object.
* @return For a hit the predictor returns a pointer to the target PCState
* otherwise a nullptr is returned.
*/
virtual const PCStateBase* lookup(ThreadID tid, InstSeqNum sn,
Addr pc, void * &i_history) = 0;
/**
* Updates the indirect predictor with history information of a branch.
* Is called right after the prediction which updates the state
* speculatively. In case the branch was mispredicted the function
* is called again with the corrected information.
* The function is called for ALL branches as some predictors incooperate
* all branches in their history.
* @param tid Thread ID
* @param sn The sequence number of the branch.
* @param pc The branch PC address.
* @param squash Whether the update is called at a misprediction
* @param taken Whether a conditional branch was taken
* @param target The target address if this branch.
* @param br_type The branch instruction type.
* @param i_history The pointer to the history object.
*/
virtual void update(ThreadID tid, InstSeqNum sn, Addr pc, bool squash,
bool taken, const PCStateBase& target,
BranchType br_type, void * &i_history) = 0;
/**
* Squashes a branch. If the branch modified the history
* reverts the modification.
* @param tid Thread ID
* @param sn The sequence number of the branch.
* @param i_history The pointer to the history object.
*/
virtual void squash(ThreadID tid, InstSeqNum sn, void * &i_history) = 0;
/**
* A branch gets finally commited. Updates the internal state of
* the indirect predictor (counter and target information).
* @param tid Thread ID
* @param sn The sequence number of the branch.
* @param i_history The pointer to the history object.
*/
virtual void commit(ThreadID tid, InstSeqNum sn, void * &i_history) = 0;
};
} // namespace branch_prediction

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2014 ARM Limited
* All rights reserved.
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -46,9 +56,11 @@ SimpleIndirectPredictor::SimpleIndirectPredictor(
numWays(params.indirectWays),
tagBits(params.indirectTagSize),
pathLength(params.indirectPathLength),
speculativePathLength(params.speculativePathLength),
instShift(params.instShiftAmt),
ghrNumBits(params.indirectGHRBits),
ghrMask((1 << params.indirectGHRBits)-1)
ghrMask((1 << params.indirectGHRBits)-1),
stats(this)
{
if (!isPowerOf2(numSets)) {
panic("Indirect predictor requires power of 2 number of sets");
@@ -64,172 +76,292 @@ SimpleIndirectPredictor::SimpleIndirectPredictor(
fatal_if(ghrNumBits > (sizeof(ThreadInfo::ghr)*8), "ghr_size is too big");
}
void
SimpleIndirectPredictor::genIndirectInfo(ThreadID tid,
void* & indirect_history)
SimpleIndirectPredictor::reset()
{
// record the GHR as it was before this prediction
DPRINTF(Indirect, "Reset Indirect predictor\n");
for (auto& ti : threadInfo) {
ti.ghr = 0;
ti.pathHist.clear();
}
for (unsigned i = 0; i < numSets; i++) {
for (unsigned j = 0; j < numWays; j++) {
targetCache[i][j].tag = 0;
}
}
}
void
SimpleIndirectPredictor::genIndirectInfo(ThreadID tid, void* &i_history)
{
// Record the GHR as it was before this prediction
// It will be used to recover the history in case this prediction is
// wrong or belongs to bad path
indirect_history = new unsigned(threadInfo[tid].ghr);
IndirectHistory* history = new IndirectHistory;
history->ghr = threadInfo[tid].ghr;
i_history = static_cast<void*>(history);
}
void
SimpleIndirectPredictor::updateDirectionInfo(
ThreadID tid, bool actually_taken)
SimpleIndirectPredictor::updateDirectionInfo(ThreadID tid, bool taken,
Addr pc, Addr target)
{
// Direction history
threadInfo[tid].ghr <<= 1;
threadInfo[tid].ghr |= actually_taken;
threadInfo[tid].ghr |= taken;
threadInfo[tid].ghr &= ghrMask;
}
void
SimpleIndirectPredictor::changeDirectionPrediction(ThreadID tid,
void * indirect_history, bool actually_taken)
// Interface methods ------------------------------
const PCStateBase *
SimpleIndirectPredictor::lookup(ThreadID tid, InstSeqNum sn,
Addr pc, void * &i_history)
{
unsigned * previousGhr = static_cast<unsigned *>(indirect_history);
threadInfo[tid].ghr = ((*previousGhr) << 1) + actually_taken;
threadInfo[tid].ghr &= ghrMask;
assert(i_history==nullptr);
genIndirectInfo(tid, i_history);
IndirectHistory *history = static_cast<IndirectHistory*>(i_history);
history->pcAddr = pc;
history->was_indirect = true;
/** Do the prediction for indirect branches (no returns) */
PCStateBase* target = nullptr;
history->hit = lookup(tid, pc, target, history);
return target;
}
bool
SimpleIndirectPredictor::lookup(Addr br_addr, PCStateBase& target,
ThreadID tid)
SimpleIndirectPredictor::lookup(ThreadID tid, Addr br_addr,
PCStateBase * &target,
IndirectHistory * &history)
{
Addr set_index = getSetIndex(br_addr, threadInfo[tid].ghr, tid);
Addr tag = getTag(br_addr);
assert(set_index < numSets);
history->set_index = getSetIndex(br_addr, tid);
history->tag = getTag(br_addr);
assert(history->set_index < numSets);
stats.lookups++;
DPRINTF(Indirect, "Looking up %x (set:%d)\n", br_addr, set_index);
const auto &iset = targetCache[set_index];
DPRINTF(Indirect, "Looking up PC:%#x, (set:%d, tag:%d), "
"ghr:%#x, pathHist sz:%#x\n",
history->pcAddr, history->set_index, history->tag,
history->ghr, threadInfo[tid].pathHist.size());
const auto &iset = targetCache[history->set_index];
for (auto way = iset.begin(); way != iset.end(); ++way) {
// tag may be 0 and match the default in way->tag, so we also have to
// check that way->target has been initialized.
if (way->tag == tag && way->target) {
if (way->tag == history->tag && way->target) {
DPRINTF(Indirect, "Hit %x (target:%s)\n", br_addr, *way->target);
set(target, *way->target);
return true;
history->hit = true;
stats.hits++;
return history->hit;
}
}
DPRINTF(Indirect, "Miss %x\n", br_addr);
return false;
history->hit = false;
stats.misses++;
return history->hit;
}
void
SimpleIndirectPredictor::commit(ThreadID tid, InstSeqNum sn, void * &i_history)
{
if (i_history == nullptr) return;
// we do not need to recover the GHR, so delete the information
IndirectHistory *history = static_cast<IndirectHistory*>(i_history);
DPRINTF(Indirect, "Committing seq:%d, PC:%#x, ghr:%#x, pathHist sz:%lu\n",
sn, history->pcAddr, history->ghr,
threadInfo[tid].pathHist.size());
delete history;
i_history = nullptr;
/** Delete histories if the history grows to much */
while (threadInfo[tid].pathHist.size()
>= (pathLength + speculativePathLength)) {
threadInfo[tid].pathHist.pop_front();
}
}
void
SimpleIndirectPredictor::recordIndirect(Addr br_addr, Addr tgt_addr,
InstSeqNum seq_num, ThreadID tid)
SimpleIndirectPredictor::update(ThreadID tid, InstSeqNum sn, Addr pc,
bool squash, bool taken, const PCStateBase& target,
BranchType br_type, void * &i_history)
{
DPRINTF(Indirect, "Recording %x seq:%d\n", br_addr, seq_num);
HistoryEntry entry(br_addr, tgt_addr, seq_num);
threadInfo[tid].pathHist.push_back(entry);
// If there is no history we did not use the indirect predictor yet.
// Create one
if (i_history==nullptr) {
genIndirectInfo(tid, i_history);
}
IndirectHistory *history = static_cast<IndirectHistory*>(i_history);
assert(history!=nullptr);
DPRINTF(Indirect, "Update sn:%i PC:%#x, squash:%i, ghr:%#x,path sz:%i\n",
sn, pc, squash, history->ghr, threadInfo[tid].pathHist.size());
/** If update was called during squash we need to fix the indirect
* path history and the global path history.
* We restore the state before this branch incorrectly updated it
* and perform the update afterwards again.
*/
history->was_indirect = isIndirectNoReturn(br_type);
if (squash) {
/** restore global history */
threadInfo[tid].ghr = history->ghr;
/** For indirect branches recalculate index and tag */
if (history->was_indirect) {
if (!threadInfo[tid].pathHist.empty()) {
threadInfo[tid].pathHist.pop_back();
}
history->set_index = getSetIndex(history->pcAddr, tid);
history->tag = getTag(history->pcAddr);
DPRINTF(Indirect, "Record Target seq:%d, PC:%#x, TGT:%#x, "
"ghr:%#x, (set:%x, tag:%x)\n",
sn, history->pcAddr, target, history->ghr,
history->set_index, history->tag);
}
}
// Only indirect branches are recorded in the path history
if (history->was_indirect) {
DPRINTF(Indirect, "Recording %x seq:%d\n", history->pcAddr, sn);
threadInfo[tid].pathHist.emplace_back(
history->pcAddr, target.instAddr(), sn);
stats.indirectRecords++;
}
// All branches update the global history
updateDirectionInfo(tid,taken, history->pcAddr, target.instAddr());
// Finally if update is called during at squash we know the target
// we predicted was wrong therefore we update the target.
// We only record the target if the branch was indirect and taken
if (squash && history->was_indirect && taken)
recordTarget(tid, sn, target, history);
}
void
SimpleIndirectPredictor::commit(InstSeqNum seq_num, ThreadID tid,
void * indirect_history)
SimpleIndirectPredictor::squash(ThreadID tid, InstSeqNum sn, void * &i_history)
{
DPRINTF(Indirect, "Committing seq:%d\n", seq_num);
ThreadInfo &t_info = threadInfo[tid];
if (i_history == nullptr) return;
// we do not need to recover the GHR, so delete the information
unsigned * previousGhr = static_cast<unsigned *>(indirect_history);
delete previousGhr;
IndirectHistory *history = static_cast<IndirectHistory*>(i_history);
if (t_info.pathHist.empty()) return;
DPRINTF(Indirect, "Squashing seq:%d, PC:%#x, indirect:%i, "
"ghr:%#x, pathHist sz:%#x\n",
sn, history->pcAddr, history->was_indirect,
history->ghr,
threadInfo[tid].pathHist.size());
if (t_info.headHistEntry < t_info.pathHist.size() &&
t_info.pathHist[t_info.headHistEntry].seqNum <= seq_num) {
if (t_info.headHistEntry >= pathLength) {
t_info.pathHist.pop_front();
} else {
++t_info.headHistEntry;
// Revert the global history register.
threadInfo[tid].ghr = history->ghr;
// If we record this branch as indirect branch
// remove it from the history.
// Restore the old head in the history.
if (history->was_indirect) {
// Should not be empty
if (threadInfo[tid].pathHist.size() < pathLength) {
stats.speculativeOverflows++;
}
if (!threadInfo[tid].pathHist.empty()) {
threadInfo[tid].pathHist.pop_back();
}
}
delete history;
i_history = nullptr;
}
void
SimpleIndirectPredictor::squash(InstSeqNum seq_num, ThreadID tid)
{
DPRINTF(Indirect, "Squashing seq:%d\n", seq_num);
ThreadInfo &t_info = threadInfo[tid];
auto squash_itr = t_info.pathHist.begin();
while (squash_itr != t_info.pathHist.end()) {
if (squash_itr->seqNum > seq_num) {
break;
}
++squash_itr;
}
if (squash_itr != t_info.pathHist.end()) {
DPRINTF(Indirect, "Squashing series starting with sn:%d\n",
squash_itr->seqNum);
}
t_info.pathHist.erase(squash_itr, t_info.pathHist.end());
}
// Internal functions ------------------------------
void
SimpleIndirectPredictor::deleteIndirectInfo(ThreadID tid,
void * indirect_history)
SimpleIndirectPredictor::recordTarget(ThreadID tid, InstSeqNum sn,
const PCStateBase& target, IndirectHistory * &history)
{
unsigned * previousGhr = static_cast<unsigned *>(indirect_history);
threadInfo[tid].ghr = *previousGhr;
delete previousGhr;
}
void
SimpleIndirectPredictor::recordTarget(
InstSeqNum seq_num, void * indirect_history, const PCStateBase& target,
ThreadID tid)
{
ThreadInfo &t_info = threadInfo[tid];
unsigned * ghr = static_cast<unsigned *>(indirect_history);
// Should have just squashed so this branch should be the oldest
auto hist_entry = *(t_info.pathHist.rbegin());
// Temporarily pop it off the history so we can calculate the set
t_info.pathHist.pop_back();
Addr set_index = getSetIndex(hist_entry.pcAddr, *ghr, tid);
Addr tag = getTag(hist_entry.pcAddr);
hist_entry.targetAddr = target.instAddr();
t_info.pathHist.push_back(hist_entry);
// and it should be predicted as indirect.
assert(!threadInfo[tid].pathHist.empty());
assert(history->was_indirect);
assert(set_index < numSets);
if (threadInfo[tid].pathHist.rbegin()->pcAddr != history->pcAddr) {
DPRINTF(Indirect, "History seems to be corrupted. %#x != %#x\n",
history->pcAddr,
threadInfo[tid].pathHist.rbegin()->pcAddr);
}
auto &iset = targetCache[set_index];
DPRINTF(Indirect, "Record Target seq:%d, PC:%#x, TGT:%#x, "
"ghr:%#x, (set:%x, tag:%x)\n",
sn, history->pcAddr, target.instAddr(), history->ghr,
history->set_index, history->tag);
assert(history->set_index < numSets);
stats.targetRecords++;
// Update the target cache
auto &iset = targetCache[history->set_index];
for (auto way = iset.begin(); way != iset.end(); ++way) {
if (way->tag == tag) {
DPRINTF(Indirect, "Updating Target (seq: %d br:%x set:%d target:"
"%s)\n", seq_num, hist_entry.pcAddr, set_index, target);
if (way->tag == history->tag) {
DPRINTF(Indirect,
"Updating Target (seq: %d br:%x set:%d target:%s)\n",
sn, history->pcAddr, history->set_index, target);
set(way->target, target);
return;
}
}
DPRINTF(Indirect, "Allocating Target (seq: %d br:%x set:%d target:%s)\n",
seq_num, hist_entry.pcAddr, set_index, target);
sn, history->pcAddr, history->set_index, target);
// Did not find entry, random replacement
auto &way = iset[rand() % numWays];
way.tag = tag;
way.tag = history->tag;
set(way.target, target);
}
inline Addr
SimpleIndirectPredictor::getSetIndex(Addr br_addr, unsigned ghr, ThreadID tid)
{
ThreadInfo &t_info = threadInfo[tid];
inline Addr
SimpleIndirectPredictor::getSetIndex(Addr br_addr, ThreadID tid)
{
Addr hash = br_addr >> instShift;
if (hashGHR) {
hash ^= ghr;
hash ^= threadInfo[tid].ghr;
}
if (hashTargets) {
unsigned hash_shift = floorLog2(numSets) / pathLength;
for (int i = t_info.pathHist.size()-1, p = 0;
for (int i = threadInfo[tid].pathHist.size()-1, p = 0;
i >= 0 && p < pathLength; i--, p++) {
hash ^= (t_info.pathHist[i].targetAddr >>
hash ^= (threadInfo[tid].pathHist[i].targetAddr >>
(instShift + p*hash_shift));
}
}
@@ -242,5 +374,26 @@ SimpleIndirectPredictor::getTag(Addr br_addr)
return (br_addr >> instShift) & ((0x1<<tagBits)-1);
}
SimpleIndirectPredictor::IndirectStats::IndirectStats(
statistics::Group *parent)
: statistics::Group(parent),
ADD_STAT(lookups, statistics::units::Count::get(),
"Number of lookups"),
ADD_STAT(hits, statistics::units::Count::get(),
"Number of hits of a tag"),
ADD_STAT(misses, statistics::units::Count::get(),
"Number of misses"),
ADD_STAT(targetRecords, statistics::units::Count::get(),
"Number of targets that where recorded/installed in the cache"),
ADD_STAT(indirectRecords, statistics::units::Count::get(),
"Number of indirect branches/calls recorded in the"
" indirect hist"),
ADD_STAT(speculativeOverflows, statistics::units::Count::get(),
"Number of times more than the allowed capacity for speculative "
"branches/calls where in flight and destroy the path history")
{
}
} // namespace branch_prediction
} // namespace gem5

View File

@@ -1,6 +1,16 @@
/*
* Copyright (c) 2014 ARM Limited
* All rights reserved.
* 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.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -31,6 +41,7 @@
#include <deque>
#include "base/statistics.hh"
#include "cpu/inst_seq.hh"
#include "cpu/pred/indirect.hh"
#include "params/SimpleIndirectPredictor.hh"
@@ -46,19 +57,23 @@ class SimpleIndirectPredictor : public IndirectPredictor
public:
SimpleIndirectPredictor(const SimpleIndirectPredictorParams &params);
bool lookup(Addr br_addr, PCStateBase& br_target, ThreadID tid);
void recordIndirect(Addr br_addr, Addr tgt_addr, InstSeqNum seq_num,
ThreadID tid);
void commit(InstSeqNum seq_num, ThreadID tid, void * indirect_history);
void squash(InstSeqNum seq_num, ThreadID tid);
void recordTarget(InstSeqNum seq_num, void * indirect_history,
const PCStateBase& target, ThreadID tid);
void genIndirectInfo(ThreadID tid, void* & indirect_history);
void updateDirectionInfo(ThreadID tid, bool actually_taken);
void deleteIndirectInfo(ThreadID tid, void * indirect_history);
void changeDirectionPrediction(ThreadID tid, void * indirect_history,
bool actually_taken);
/** Indirect predictor interface */
void reset() override;
const PCStateBase * lookup(ThreadID tid, InstSeqNum sn,
Addr pc, void * &iHistory) override;
void update(ThreadID tid, InstSeqNum sn, Addr pc, bool squash,
bool taken, const PCStateBase& target,
BranchType br_type, void * &iHistory) override;
void squash(ThreadID tid, InstSeqNum sn, void * &iHistory) override;
void commit(ThreadID tid, InstSeqNum sn, void * &iHistory) override;
/** ------------------
* The actual predictor
* -------------------
* */
private:
const bool hashGHR;
const bool hashTargets;
@@ -66,6 +81,7 @@ class SimpleIndirectPredictor : public IndirectPredictor
const unsigned numWays;
const unsigned tagBits;
const unsigned pathLength;
const unsigned speculativePathLength;
const unsigned instShift;
const unsigned ghrNumBits;
const unsigned ghrMask;
@@ -78,27 +94,88 @@ class SimpleIndirectPredictor : public IndirectPredictor
std::vector<std::vector<IPredEntry> > targetCache;
Addr getSetIndex(Addr br_addr, unsigned ghr, ThreadID tid);
Addr getTag(Addr br_addr);
struct HistoryEntry
{
HistoryEntry(Addr br_addr, Addr tgt_addr, InstSeqNum seq_num)
: pcAddr(br_addr), targetAddr(tgt_addr), seqNum(seq_num) { }
HistoryEntry() : pcAddr(0), targetAddr(0), seqNum(0) { }
Addr pcAddr;
Addr targetAddr;
InstSeqNum seqNum;
};
/** Indirect branch history information
* Used for prediction, update and recovery
*/
struct IndirectHistory
{
/* data */
Addr pcAddr;
Addr targetAddr;
InstSeqNum seqNum;
Addr set_index;
Addr tag;
bool hit;
unsigned ghr;
uint64_t pathHist;
bool was_indirect;
IndirectHistory()
: pcAddr(MaxAddr),
targetAddr(MaxAddr),
was_indirect(false)
{}
};
/** Per thread path and global history registers*/
struct ThreadInfo
{
// Path history register
std::deque<HistoryEntry> pathHist;
unsigned headHistEntry = 0;
// Global direction history register
unsigned ghr = 0;
};
std::vector<ThreadInfo> threadInfo;
// ---- Internal functions ----- //
bool lookup(ThreadID tid, Addr br_addr,
PCStateBase * &target, IndirectHistory * &history);
void recordTarget(ThreadID tid, InstSeqNum sn,
const PCStateBase& target, IndirectHistory * &history);
// Helper functions to generate and modify the
// direction info
void genIndirectInfo(ThreadID tid, void* &iHistory);
void updateDirectionInfo(ThreadID tid, bool taken, Addr pc, Addr target);
// Helper to compute set and tag
inline Addr getSetIndex(Addr br_addr, ThreadID tid);
inline Addr getTag(Addr br_addr);
inline bool isIndirectNoReturn(BranchType type) {
return (type == BranchType::CallIndirect) ||
(type == BranchType::IndirectUncond);
}
protected:
struct IndirectStats : public statistics::Group
{
IndirectStats(statistics::Group *parent);
// STATS
statistics::Scalar lookups;
statistics::Scalar hits;
statistics::Scalar misses;
statistics::Scalar targetRecords;
statistics::Scalar indirectRecords;
statistics::Scalar speculativeOverflows;
} stats;
};
} // namespace branch_prediction