diff --git a/src/cpu/pred/BranchPredictor.py b/src/cpu/pred/BranchPredictor.py index 3bf5e52ae0..ed356b166d 100644 --- a/src/cpu/pred/BranchPredictor.py +++ b/src/cpu/pred/BranchPredictor.py @@ -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", ) diff --git a/src/cpu/pred/bpred_unit.cc b/src/cpu/pred/bpred_unit.cc index 85cb7c9e2f..813ac86f24 100644 --- a/src/cpu/pred/bpred_unit.cc +++ b/src/cpu/pred/bpred_unit.cc @@ -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] " diff --git a/src/cpu/pred/indirect.hh b/src/cpu/pred/indirect.hh index 5f855b14fd..54c55d0dcb 100644 --- a/src/cpu/pred/indirect.hh +++ b/src/cpu/pred/indirect.hh @@ -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 diff --git a/src/cpu/pred/simple_indirect.cc b/src/cpu/pred/simple_indirect.cc index f09cdeef55..815e8bf31b 100644 --- a/src/cpu/pred/simple_indirect.cc +++ b/src/cpu/pred/simple_indirect.cc @@ -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(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(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(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(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(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(indirect_history); - delete previousGhr; + IndirectHistory *history = static_cast(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(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(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< +#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 ¶ms); - 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 > 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 pathHist; - unsigned headHistEntry = 0; + // Global direction history register unsigned ghr = 0; }; std::vector 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