cpu: implement a bi-mode branch predictor
This commit is contained in:
parent
db267da822
commit
f34a8f0d61
7 changed files with 368 additions and 5 deletions
|
@ -1332,8 +1332,8 @@ DefaultFetch<Impl>::fetch(bool &status_change)
|
||||||
|
|
||||||
nextPC = thisPC;
|
nextPC = thisPC;
|
||||||
|
|
||||||
// If we're branching after this instruction, quite fetching
|
// If we're branching after this instruction, quit fetching
|
||||||
// from the same block then.
|
// from the same block.
|
||||||
predictedBranch |= thisPC.branching();
|
predictedBranch |= thisPC.branching();
|
||||||
predictedBranch |=
|
predictedBranch |=
|
||||||
lookupAndUpdateNextPC(instruction, nextPC);
|
lookupAndUpdateNextPC(instruction, nextPC);
|
||||||
|
|
|
@ -36,7 +36,7 @@ class BranchPredictor(SimObject):
|
||||||
|
|
||||||
numThreads = Param.Unsigned(1, "Number of threads")
|
numThreads = Param.Unsigned(1, "Number of threads")
|
||||||
predType = Param.String("tournament",
|
predType = Param.String("tournament",
|
||||||
"Branch predictor type ('local', 'tournament')")
|
"Branch predictor type ('local', 'tournament', 'bi-mode')")
|
||||||
localPredictorSize = Param.Unsigned(2048, "Size of local predictor")
|
localPredictorSize = Param.Unsigned(2048, "Size of local predictor")
|
||||||
localCtrBits = Param.Unsigned(2, "Bits per counter")
|
localCtrBits = Param.Unsigned(2, "Bits per counter")
|
||||||
localHistoryTableSize = Param.Unsigned(2048, "Size of local history table")
|
localHistoryTableSize = Param.Unsigned(2048, "Size of local history table")
|
||||||
|
|
|
@ -38,5 +38,6 @@ if 'InOrderCPU' in env['CPU_MODELS'] or 'O3CPU' in env['CPU_MODELS']:
|
||||||
Source('btb.cc')
|
Source('btb.cc')
|
||||||
Source('ras.cc')
|
Source('ras.cc')
|
||||||
Source('tournament.cc')
|
Source('tournament.cc')
|
||||||
|
Source ('bi_mode.cc')
|
||||||
DebugFlag('FreeList')
|
DebugFlag('FreeList')
|
||||||
DebugFlag('Branch')
|
DebugFlag('Branch')
|
||||||
|
|
245
src/cpu/pred/bi_mode.cc
Normal file
245
src/cpu/pred/bi_mode.cc
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014 The Regents of The University of Michigan
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met: redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer;
|
||||||
|
* redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution;
|
||||||
|
* neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Authors: Anthony Gutierrez
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @file
|
||||||
|
* Implementation of a bi-mode branch predictor
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/bitfield.hh"
|
||||||
|
#include "base/intmath.hh"
|
||||||
|
#include "cpu/pred/bi_mode.hh"
|
||||||
|
|
||||||
|
BiModeBP::BiModeBP(const Params *params)
|
||||||
|
: BPredUnit(params), instShiftAmt(params->instShiftAmt),
|
||||||
|
globalHistoryReg(0),
|
||||||
|
globalHistoryBits(ceilLog2(params->globalPredictorSize)),
|
||||||
|
choicePredictorSize(params->choicePredictorSize),
|
||||||
|
choiceCtrBits(params->choiceCtrBits),
|
||||||
|
globalPredictorSize(params->globalPredictorSize),
|
||||||
|
globalCtrBits(params->globalCtrBits)
|
||||||
|
{
|
||||||
|
if (!isPowerOf2(choicePredictorSize))
|
||||||
|
fatal("Invalid choice predictor size.\n");
|
||||||
|
if (!isPowerOf2(globalPredictorSize))
|
||||||
|
fatal("Invalid global history predictor size.\n");
|
||||||
|
|
||||||
|
choiceCounters.resize(choicePredictorSize);
|
||||||
|
takenCounters.resize(globalPredictorSize);
|
||||||
|
notTakenCounters.resize(globalPredictorSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < choicePredictorSize; ++i) {
|
||||||
|
choiceCounters[i].setBits(choiceCtrBits);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < globalPredictorSize; ++i) {
|
||||||
|
takenCounters[i].setBits(globalCtrBits);
|
||||||
|
notTakenCounters[i].setBits(globalCtrBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
historyRegisterMask = mask(globalHistoryBits);
|
||||||
|
choiceHistoryMask = choicePredictorSize - 1;
|
||||||
|
globalHistoryMask = globalPredictorSize - 1;
|
||||||
|
|
||||||
|
choiceThreshold = (ULL(1) << (choiceCtrBits - 1)) - 1;
|
||||||
|
takenThreshold = (ULL(1) << (choiceCtrBits - 1)) - 1;
|
||||||
|
notTakenThreshold = (ULL(1) << (choiceCtrBits - 1)) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For an unconditional branch we set its history such that
|
||||||
|
* everything is set to taken. I.e., its choice predictor
|
||||||
|
* chooses the taken array and the taken array predicts taken.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BiModeBP::uncondBranch(void * &bpHistory)
|
||||||
|
{
|
||||||
|
BPHistory *history = new BPHistory;
|
||||||
|
history->globalHistoryReg = globalHistoryReg;
|
||||||
|
history->takenUsed = true;
|
||||||
|
history->takenPred = true;
|
||||||
|
history->notTakenPred = true;
|
||||||
|
history->finalPred = true;
|
||||||
|
bpHistory = static_cast<void*>(history);
|
||||||
|
updateGlobalHistReg(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BiModeBP::squash(void *bpHistory)
|
||||||
|
{
|
||||||
|
BPHistory *history = static_cast<BPHistory*>(bpHistory);
|
||||||
|
globalHistoryReg = history->globalHistoryReg;
|
||||||
|
|
||||||
|
delete history;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here we lookup the actual branch prediction. We use the PC to
|
||||||
|
* identify the bias of a particular branch, which is based on the
|
||||||
|
* prediction in the choice array. A hash of the global history
|
||||||
|
* register and a branch's PC is used to index into both the taken
|
||||||
|
* and not-taken predictors, which both present a prediction. The
|
||||||
|
* choice array's prediction is used to select between the two
|
||||||
|
* direction predictors for the final branch prediction.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
BiModeBP::lookup(Addr branchAddr, void * &bpHistory)
|
||||||
|
{
|
||||||
|
unsigned choiceHistoryIdx = ((branchAddr >> instShiftAmt)
|
||||||
|
& choiceHistoryMask);
|
||||||
|
unsigned globalHistoryIdx = (((branchAddr >> instShiftAmt)
|
||||||
|
^ globalHistoryReg)
|
||||||
|
& globalHistoryMask);
|
||||||
|
|
||||||
|
assert(choiceHistoryIdx < choicePredictorSize);
|
||||||
|
assert(globalHistoryIdx < globalPredictorSize);
|
||||||
|
|
||||||
|
bool choicePrediction = choiceCounters[choiceHistoryIdx].read()
|
||||||
|
> choiceThreshold;
|
||||||
|
bool takenGHBPrediction = takenCounters[globalHistoryIdx].read()
|
||||||
|
> takenThreshold;
|
||||||
|
bool notTakenGHBPrediction = notTakenCounters[globalHistoryIdx].read()
|
||||||
|
> notTakenThreshold;
|
||||||
|
bool finalPrediction;
|
||||||
|
|
||||||
|
BPHistory *history = new BPHistory;
|
||||||
|
history->globalHistoryReg = globalHistoryReg;
|
||||||
|
history->takenUsed = choicePrediction;
|
||||||
|
history->takenPred = takenGHBPrediction;
|
||||||
|
history->notTakenPred = notTakenGHBPrediction;
|
||||||
|
|
||||||
|
if (choicePrediction) {
|
||||||
|
finalPrediction = takenGHBPrediction;
|
||||||
|
} else {
|
||||||
|
finalPrediction = notTakenGHBPrediction;
|
||||||
|
}
|
||||||
|
|
||||||
|
history->finalPred = finalPrediction;
|
||||||
|
bpHistory = static_cast<void*>(history);
|
||||||
|
updateGlobalHistReg(finalPrediction);
|
||||||
|
|
||||||
|
return finalPrediction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BiModeBP::btbUpdate(Addr branchAddr, void * &bpHistory)
|
||||||
|
{
|
||||||
|
globalHistoryReg &= (historyRegisterMask & ~ULL(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only the selected direction predictor will be updated with the final
|
||||||
|
* outcome; the status of the unselected one will not be altered. The choice
|
||||||
|
* predictor is always updated with the branch outcome, except when the
|
||||||
|
* choice is opposite to the branch outcome but the selected counter of
|
||||||
|
* the direction predictors makes a correct final prediction.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BiModeBP::update(Addr branchAddr, bool taken, void *bpHistory, bool squashed)
|
||||||
|
{
|
||||||
|
if (bpHistory) {
|
||||||
|
BPHistory *history = static_cast<BPHistory*>(bpHistory);
|
||||||
|
|
||||||
|
unsigned choiceHistoryIdx = ((branchAddr >> instShiftAmt)
|
||||||
|
& choiceHistoryMask);
|
||||||
|
unsigned globalHistoryIdx = (((branchAddr >> instShiftAmt)
|
||||||
|
^ globalHistoryReg)
|
||||||
|
& globalHistoryMask);
|
||||||
|
|
||||||
|
assert(choiceHistoryIdx < choicePredictorSize);
|
||||||
|
assert(globalHistoryIdx < globalPredictorSize);
|
||||||
|
|
||||||
|
if (history->takenUsed) {
|
||||||
|
// if the taken array's prediction was used, update it
|
||||||
|
if (taken) {
|
||||||
|
takenCounters[globalHistoryIdx].increment();
|
||||||
|
} else {
|
||||||
|
takenCounters[globalHistoryIdx].decrement();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the not-taken array's prediction was used, update it
|
||||||
|
if (taken) {
|
||||||
|
notTakenCounters[globalHistoryIdx].increment();
|
||||||
|
} else {
|
||||||
|
notTakenCounters[globalHistoryIdx].decrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (history->finalPred == taken) {
|
||||||
|
/* If the final prediction matches the actual branch's
|
||||||
|
* outcome and the choice predictor matches the final
|
||||||
|
* outcome, we update the choice predictor, otherwise it
|
||||||
|
* is not updated. While the designers of the bi-mode
|
||||||
|
* predictor don't explicity say why this is done, one
|
||||||
|
* can infer that it is to preserve the choice predictor's
|
||||||
|
* bias with respect to the branch being predicted; afterall,
|
||||||
|
* the whole point of the bi-mode predictor is to identify the
|
||||||
|
* atypical case when a branch deviates from its bias.
|
||||||
|
*/
|
||||||
|
if (history->finalPred == history->takenUsed) {
|
||||||
|
if (taken) {
|
||||||
|
choiceCounters[choiceHistoryIdx].increment();
|
||||||
|
} else {
|
||||||
|
choiceCounters[choiceHistoryIdx].decrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// always update the choice predictor on an incorrect prediction
|
||||||
|
if (taken) {
|
||||||
|
choiceCounters[choiceHistoryIdx].increment();
|
||||||
|
} else {
|
||||||
|
choiceCounters[choiceHistoryIdx].decrement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squashed) {
|
||||||
|
if (taken) {
|
||||||
|
globalHistoryReg = (history->globalHistoryReg << 1) | 1;
|
||||||
|
} else {
|
||||||
|
globalHistoryReg = (history->globalHistoryReg << 1);
|
||||||
|
}
|
||||||
|
globalHistoryReg &= historyRegisterMask;
|
||||||
|
} else {
|
||||||
|
delete history;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BiModeBP::retireSquashed(void *bp_history)
|
||||||
|
{
|
||||||
|
BPHistory *history = static_cast<BPHistory*>(bp_history);
|
||||||
|
delete history;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BiModeBP::updateGlobalHistReg(bool taken)
|
||||||
|
{
|
||||||
|
globalHistoryReg = taken ? (globalHistoryReg << 1) | 1 :
|
||||||
|
(globalHistoryReg << 1);
|
||||||
|
globalHistoryReg &= historyRegisterMask;
|
||||||
|
}
|
114
src/cpu/pred/bi_mode.hh
Normal file
114
src/cpu/pred/bi_mode.hh
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014 The Regents of The University of Michigan
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met: redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer;
|
||||||
|
* redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution;
|
||||||
|
* neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Authors: Anthony Gutierrez
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @file
|
||||||
|
* Implementation of a bi-mode branch predictor
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CPU_PRED_BI_MODE_PRED_HH__
|
||||||
|
#define __CPU_PRED_BI_MODE_PRED_HH__
|
||||||
|
|
||||||
|
#include "cpu/pred/bpred_unit.hh"
|
||||||
|
#include "cpu/pred/sat_counter.hh"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a bi-mode branch predictor. The bi-mode predictor is a two-level
|
||||||
|
* branch predictor that has three seprate history arrays: a taken array, a
|
||||||
|
* not-taken array, and a choice array. The taken/not-taken arrays are indexed
|
||||||
|
* by a hash of the PC and the global history. The choice array is indexed by
|
||||||
|
* the PC only. Because the taken/not-taken arrays use the same index, they must
|
||||||
|
* be the same size.
|
||||||
|
*
|
||||||
|
* The bi-mode branch predictor aims to eliminate the destructive aliasing that
|
||||||
|
* occurs when two branches of opposite biases share the same global history
|
||||||
|
* pattern. By separating the predictors into taken/not-taken arrays, and using
|
||||||
|
* the branch's PC to choose between the two, destructive aliasing is reduced.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class BiModeBP : public BPredUnit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BiModeBP(const Params *params);
|
||||||
|
void uncondBranch(void * &bp_history);
|
||||||
|
void squash(void *bp_history);
|
||||||
|
bool lookup(Addr branch_addr, void * &bp_history);
|
||||||
|
void btbUpdate(Addr branch_addr, void * &bp_history);
|
||||||
|
void update(Addr branch_addr, bool taken, void *bp_history, bool squashed);
|
||||||
|
void retireSquashed(void *bp_history);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateGlobalHistReg(bool taken);
|
||||||
|
|
||||||
|
struct BPHistory {
|
||||||
|
unsigned globalHistoryReg;
|
||||||
|
// was the taken array's prediction used?
|
||||||
|
// true: takenPred used
|
||||||
|
// false: notPred used
|
||||||
|
bool takenUsed;
|
||||||
|
// prediction of the taken array
|
||||||
|
// true: predict taken
|
||||||
|
// false: predict not-taken
|
||||||
|
bool takenPred;
|
||||||
|
// prediction of the not-taken array
|
||||||
|
// true: predict taken
|
||||||
|
// false: predict not-taken
|
||||||
|
bool notTakenPred;
|
||||||
|
// the final taken/not-taken prediction
|
||||||
|
// true: predict taken
|
||||||
|
// false: predict not-taken
|
||||||
|
bool finalPred;
|
||||||
|
};
|
||||||
|
|
||||||
|
// choice predictors
|
||||||
|
std::vector<SatCounter> choiceCounters;
|
||||||
|
// taken direction predictors
|
||||||
|
std::vector<SatCounter> takenCounters;
|
||||||
|
// not-taken direction predictors
|
||||||
|
std::vector<SatCounter> notTakenCounters;
|
||||||
|
|
||||||
|
unsigned instShiftAmt;
|
||||||
|
|
||||||
|
unsigned globalHistoryReg;
|
||||||
|
unsigned globalHistoryBits;
|
||||||
|
unsigned historyRegisterMask;
|
||||||
|
|
||||||
|
unsigned choicePredictorSize;
|
||||||
|
unsigned choiceCtrBits;
|
||||||
|
unsigned choiceHistoryMask;
|
||||||
|
unsigned globalPredictorSize;
|
||||||
|
unsigned globalCtrBits;
|
||||||
|
unsigned globalHistoryMask;
|
||||||
|
|
||||||
|
unsigned choiceThreshold;
|
||||||
|
unsigned takenThreshold;
|
||||||
|
unsigned notTakenThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __CPU_PRED_BI_MODE_PRED_HH__
|
|
@ -32,6 +32,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cpu/pred/2bit_local.hh"
|
#include "cpu/pred/2bit_local.hh"
|
||||||
|
#include "cpu/pred/bi_mode.hh"
|
||||||
#include "cpu/pred/bpred_unit_impl.hh"
|
#include "cpu/pred/bpred_unit_impl.hh"
|
||||||
#include "cpu/pred/tournament.hh"
|
#include "cpu/pred/tournament.hh"
|
||||||
|
|
||||||
|
@ -43,6 +44,8 @@ BranchPredictorParams::create()
|
||||||
return new LocalBP(this);
|
return new LocalBP(this);
|
||||||
} else if (predType == "tournament") {
|
} else if (predType == "tournament") {
|
||||||
return new TournamentBP(this);
|
return new TournamentBP(this);
|
||||||
|
} else if (predType == "bi-mode") {
|
||||||
|
return new BiModeBP(this);
|
||||||
} else {
|
} else {
|
||||||
fatal("Invalid BP selected!");
|
fatal("Invalid BP selected!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ class BPredUnit : public SimObject
|
||||||
bool predict(StaticInstPtr &inst, const InstSeqNum &seqNum,
|
bool predict(StaticInstPtr &inst, const InstSeqNum &seqNum,
|
||||||
TheISA::PCState &pc, ThreadID tid);
|
TheISA::PCState &pc, ThreadID tid);
|
||||||
bool predictInOrder(StaticInstPtr &inst, const InstSeqNum &seqNum,
|
bool predictInOrder(StaticInstPtr &inst, const InstSeqNum &seqNum,
|
||||||
int asid, TheISA::PCState &instPC, TheISA::PCState &predPC,
|
int asid, TheISA::PCState &instPC,
|
||||||
ThreadID tid);
|
TheISA::PCState &predPC, ThreadID tid);
|
||||||
|
|
||||||
// @todo: Rename this function.
|
// @todo: Rename this function.
|
||||||
virtual void uncondBranch(void * &bp_history) = 0;
|
virtual void uncondBranch(void * &bp_history) = 0;
|
||||||
|
|
Loading…
Reference in a new issue