cpu: Fix incorrect speculative branch predictor behavior
When a branch mispredicted gem5 would squash all history after and including the mispredicted branch. However, the mispredicted branch is still speculative and its history is required to rollback state if another, older, branch mispredicts. This leads to things like RAS corruption.
This commit is contained in:
parent
ecd5300971
commit
daedc5a491
5 changed files with 41 additions and 19 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 ARM Limited
|
* Copyright (c) 2011, 2014 ARM Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
*
|
*
|
||||||
* The license below extends only to copyright in the software and shall
|
* The license below extends only to copyright in the software and shall
|
||||||
|
@ -92,6 +92,9 @@ class LocalBP : public BPredUnit
|
||||||
*/
|
*/
|
||||||
void update(Addr branch_addr, bool taken, void *bp_history, bool squashed);
|
void update(Addr branch_addr, bool taken, void *bp_history, bool squashed);
|
||||||
|
|
||||||
|
void retireSquashed(void *bp_history)
|
||||||
|
{ assert(bp_history == NULL); }
|
||||||
|
|
||||||
void squash(void *bp_history)
|
void squash(void *bp_history)
|
||||||
{ assert(bp_history == NULL); }
|
{ assert(bp_history == NULL); }
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,13 @@ class BPredUnit : public SimObject
|
||||||
*/
|
*/
|
||||||
virtual void update(Addr instPC, bool taken, void *bp_history,
|
virtual void update(Addr instPC, bool taken, void *bp_history,
|
||||||
bool squashed) = 0;
|
bool squashed) = 0;
|
||||||
|
/**
|
||||||
|
* Deletes the associated history with a branch, performs no predictor
|
||||||
|
* updates. Used for branches that mispredict and update tables but
|
||||||
|
* are still speculative and later retire.
|
||||||
|
* @param bp_history History to delete associated with this predictor
|
||||||
|
*/
|
||||||
|
virtual void retireSquashed(void *bp_history) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the BTB with the target of a branch.
|
* Updates the BTB with the target of a branch.
|
||||||
|
@ -200,7 +207,7 @@ class BPredUnit : public SimObject
|
||||||
ThreadID _tid)
|
ThreadID _tid)
|
||||||
: seqNum(seq_num), pc(instPC), bpHistory(bp_history), RASTarget(0),
|
: seqNum(seq_num), pc(instPC), bpHistory(bp_history), RASTarget(0),
|
||||||
RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), pushedRAS(0),
|
RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), pushedRAS(0),
|
||||||
wasCall(0), wasReturn(0)
|
wasCall(0), wasReturn(0), wasSquashed(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool operator==(const PredictorHistory &entry) const {
|
bool operator==(const PredictorHistory &entry) const {
|
||||||
|
@ -234,7 +241,7 @@ class BPredUnit : public SimObject
|
||||||
/** Whether or not the RAS was used. */
|
/** Whether or not the RAS was used. */
|
||||||
bool usedRAS;
|
bool usedRAS;
|
||||||
|
|
||||||
/* Wether or not the RAS was pushed */
|
/* Whether or not the RAS was pushed */
|
||||||
bool pushedRAS;
|
bool pushedRAS;
|
||||||
|
|
||||||
/** Whether or not the instruction was a call. */
|
/** Whether or not the instruction was a call. */
|
||||||
|
@ -242,6 +249,9 @@ class BPredUnit : public SimObject
|
||||||
|
|
||||||
/** Whether or not the instruction was a return. */
|
/** Whether or not the instruction was a return. */
|
||||||
bool wasReturn;
|
bool wasReturn;
|
||||||
|
|
||||||
|
/** Whether this instruction has already mispredicted/updated bp */
|
||||||
|
bool wasSquashed;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::deque<PredictorHistory> History;
|
typedef std::deque<PredictorHistory> History;
|
||||||
|
|
|
@ -372,8 +372,12 @@ BPredUnit::update(const InstSeqNum &done_sn, ThreadID tid)
|
||||||
while (!predHist[tid].empty() &&
|
while (!predHist[tid].empty() &&
|
||||||
predHist[tid].back().seqNum <= done_sn) {
|
predHist[tid].back().seqNum <= done_sn) {
|
||||||
// Update the branch predictor with the correct results.
|
// Update the branch predictor with the correct results.
|
||||||
update(predHist[tid].back().pc, predHist[tid].back().predTaken,
|
if (!predHist[tid].back().wasSquashed) {
|
||||||
predHist[tid].back().bpHistory, false);
|
update(predHist[tid].back().pc, predHist[tid].back().predTaken,
|
||||||
|
predHist[tid].back().bpHistory, false);
|
||||||
|
} else {
|
||||||
|
retireSquashed(predHist[tid].back().bpHistory);
|
||||||
|
}
|
||||||
|
|
||||||
predHist[tid].pop_back();
|
predHist[tid].pop_back();
|
||||||
}
|
}
|
||||||
|
@ -465,12 +469,15 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
|
||||||
|
|
||||||
update((*hist_it).pc, actually_taken,
|
update((*hist_it).pc, actually_taken,
|
||||||
pred_hist.front().bpHistory, true);
|
pred_hist.front().bpHistory, true);
|
||||||
|
hist_it->wasSquashed = true;
|
||||||
|
|
||||||
if (actually_taken) {
|
if (actually_taken) {
|
||||||
if (hist_it->wasReturn && !hist_it->usedRAS) {
|
if (hist_it->wasReturn && !hist_it->usedRAS) {
|
||||||
DPRINTF(Branch, "[tid: %i] Incorrectly predicted"
|
DPRINTF(Branch, "[tid: %i] Incorrectly predicted"
|
||||||
" return [sn:%i] PC: %s\n", tid, hist_it->seqNum,
|
" return [sn:%i] PC: %s\n", tid, hist_it->seqNum,
|
||||||
hist_it->pc);
|
hist_it->pc);
|
||||||
RAS[tid].pop();
|
RAS[tid].pop();
|
||||||
|
hist_it->usedRAS = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINTF(Branch,"[tid: %i] BTB Update called for [sn:%i]"
|
DPRINTF(Branch,"[tid: %i] BTB Update called for [sn:%i]"
|
||||||
|
@ -488,23 +495,16 @@ BPredUnit::squash(const InstSeqNum &squashed_sn,
|
||||||
" to: %i, target: %s.\n", tid,
|
" to: %i, target: %s.\n", tid,
|
||||||
hist_it->RASIndex, hist_it->RASTarget);
|
hist_it->RASIndex, hist_it->RASTarget);
|
||||||
RAS[tid].restore(hist_it->RASIndex, hist_it->RASTarget);
|
RAS[tid].restore(hist_it->RASIndex, hist_it->RASTarget);
|
||||||
|
hist_it->usedRAS = false;
|
||||||
} else if (hist_it->wasCall && hist_it->pushedRAS) {
|
} else if (hist_it->wasCall && hist_it->pushedRAS) {
|
||||||
//Was a Call but predicated false. Pop RAS here
|
//Was a Call but predicated false. Pop RAS here
|
||||||
DPRINTF(Branch, "[tid: %i] Incorrectly predicted"
|
DPRINTF(Branch, "[tid: %i] Incorrectly predicted"
|
||||||
" Call [sn:%i] PC: %s Popping RAS\n", tid,
|
" Call [sn:%i] PC: %s Popping RAS\n", tid,
|
||||||
hist_it->seqNum, hist_it->pc);
|
hist_it->seqNum, hist_it->pc);
|
||||||
RAS[tid].pop();
|
RAS[tid].pop();
|
||||||
|
hist_it->pushedRAS = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DPRINTF(Branch, "[tid:%i]: Removing history for [sn:%i]"
|
|
||||||
" PC %s Actually Taken: %i\n", tid, hist_it->seqNum,
|
|
||||||
hist_it->pc, actually_taken);
|
|
||||||
|
|
||||||
pred_hist.erase(hist_it);
|
|
||||||
|
|
||||||
DPRINTF(Branch, "[tid:%i]: predHist.size(): %i\n", tid,
|
|
||||||
predHist[tid].size());
|
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(Branch, "[tid:%i]: [sn:%i] pred_hist empty, can't "
|
DPRINTF(Branch, "[tid:%i]: [sn:%i] pred_hist empty, can't "
|
||||||
"update.\n", tid, squashed_sn);
|
"update.\n", tid, squashed_sn);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 ARM Limited
|
* Copyright (c) 2011, 2014 ARM Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
*
|
*
|
||||||
* The license below extends only to copyright in the software and shall
|
* The license below extends only to copyright in the software and shall
|
||||||
|
@ -346,10 +346,10 @@ TournamentBP::update(Addr branch_addr, bool taken, void *bp_history,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// We're done with this history, now delete it.
|
||||||
|
delete history;
|
||||||
}
|
}
|
||||||
// We're done with this history, now delete it.
|
|
||||||
delete history;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(local_history_idx < localHistoryTableSize);
|
assert(local_history_idx < localHistoryTableSize);
|
||||||
|
@ -357,6 +357,13 @@ TournamentBP::update(Addr branch_addr, bool taken, void *bp_history,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TournamentBP::retireSquashed(void *bp_history)
|
||||||
|
{
|
||||||
|
BPHistory *history = static_cast<BPHistory *>(bp_history);
|
||||||
|
delete history;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TournamentBP::squash(void *bp_history)
|
TournamentBP::squash(void *bp_history)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011 ARM Limited
|
* Copyright (c) 2011, 2014 ARM Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
*
|
*
|
||||||
* The license below extends only to copyright in the software and shall
|
* The license below extends only to copyright in the software and shall
|
||||||
|
@ -104,6 +104,8 @@ class TournamentBP : public BPredUnit
|
||||||
*/
|
*/
|
||||||
void update(Addr branch_addr, bool taken, void *bp_history, bool squashed);
|
void update(Addr branch_addr, bool taken, void *bp_history, bool squashed);
|
||||||
|
|
||||||
|
void retireSquashed(void *bp_history);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the global branch history on a squash.
|
* Restores the global branch history on a squash.
|
||||||
* @param bp_history Pointer to the BPHistory object that has the
|
* @param bp_history Pointer to the BPHistory object that has the
|
||||||
|
|
Loading…
Reference in a new issue