cpu: Refactor indirect predictor

Simplify indirect predictor interface. Several of the existing
functions where merged together into four clear once. Those
four are similar to the main direction predictor interface.
'lookup', 'update', 'squash' and 'commit'. This makes the
interface much more clear, allows better functionality isolation
and makes it simpler to develop new predictor models.

A new parameter is added to allow additional buffer space for
speculative path history.

Change-Id: I6d6b43965b2986ef959953a64c428e50bc68d38e
Signed-off-by: David Schall <david.schall@ed.ac.uk>
This commit is contained in:
David Schall
2023-10-08 17:30:54 +00:00
parent bbe05b0cba
commit f65df9b959
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