2fb632dbda
branch prediction, and makes memory dependence work properly. SConscript: Added return address stack, tournament predictor. cpu/base_cpu.cc: Added debug break and print statements. cpu/base_dyn_inst.cc: cpu/base_dyn_inst.hh: Comment out possibly unneeded variables. cpu/beta_cpu/2bit_local_pred.cc: 2bit predictor no longer speculatively updates itself. cpu/beta_cpu/alpha_dyn_inst.hh: Comment formatting. cpu/beta_cpu/alpha_full_cpu.hh: Formatting cpu/beta_cpu/alpha_full_cpu_builder.cc: Added new parameters for branch predictors, and IQ parameters. cpu/beta_cpu/alpha_full_cpu_impl.hh: Register stats. cpu/beta_cpu/alpha_params.hh: Added parameters for IQ, branch predictors, and store sets. cpu/beta_cpu/bpred_unit.cc: Removed one class. cpu/beta_cpu/bpred_unit.hh: Add in RAS, stats. Changed branch predictor unit functionality so that it holds a history of past branches so it can update, and also hold a proper history of the RAS so it can be restored on branch mispredicts. cpu/beta_cpu/bpred_unit_impl.hh: Added in stats, history of branches, RAS. Now bpred unit actually modifies the instruction's predicted next PC. cpu/beta_cpu/btb.cc: Add in sanity checks. cpu/beta_cpu/comm.hh: Add in communication where needed, remove it where it's not. cpu/beta_cpu/commit.hh: cpu/beta_cpu/rename.hh: cpu/beta_cpu/rename_impl.hh: Add in stats. cpu/beta_cpu/commit_impl.hh: Stats, update what is sent back on branch mispredict. cpu/beta_cpu/cpu_policy.hh: Change the bpred unit being used. cpu/beta_cpu/decode.hh: cpu/beta_cpu/decode_impl.hh: Stats. cpu/beta_cpu/fetch.hh: Stats, change squash so it can handle squashes from decode differently than squashes from commit. cpu/beta_cpu/fetch_impl.hh: Add in stats. Change how a cache line is fetched. Update to work with caches. Also have separate functions for different behavior if squash is coming from decode vs commit. cpu/beta_cpu/free_list.hh: Remove some old comments. cpu/beta_cpu/full_cpu.cc: cpu/beta_cpu/full_cpu.hh: Added function to remove instructions from back of instruction list until a certain sequence number. cpu/beta_cpu/iew.hh: Stats, separate squashing behavior due to branches vs memory. cpu/beta_cpu/iew_impl.hh: Stats, separate squashing behavior for branches vs memory. cpu/beta_cpu/inst_queue.cc: Debug stuff cpu/beta_cpu/inst_queue.hh: Stats, change how mem dep unit works, debug stuff cpu/beta_cpu/inst_queue_impl.hh: Stats, change how mem dep unit works, debug stuff. Also add in parameters that used to be hardcoded. cpu/beta_cpu/mem_dep_unit.hh: cpu/beta_cpu/mem_dep_unit_impl.hh: Add in stats, change how memory dependence unit works. It now holds the memory instructions that are waiting for their memory dependences to resolve. It provides which instructions are ready directly to the IQ. cpu/beta_cpu/regfile.hh: Fix up sanity checks. cpu/beta_cpu/rename_map.cc: Fix loop variable type. cpu/beta_cpu/rob_impl.hh: Remove intermediate DynInstPtr cpu/beta_cpu/store_set.cc: Add in debugging statements. cpu/beta_cpu/store_set.hh: Reorder function arguments to match the rest of the calls. --HG-- extra : convert_revision : aabf9b1fecd1d743265dfc3b174d6159937c6f44
392 lines
11 KiB
C++
392 lines
11 KiB
C++
|
|
#include <map>
|
|
|
|
#include "cpu/beta_cpu/mem_dep_unit.hh"
|
|
|
|
template <class MemDepPred, class Impl>
|
|
MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params ¶ms)
|
|
: depPred(params.SSITSize, params.LFSTSize)
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Creating MemDepUnit object.\n");
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::regStats()
|
|
{
|
|
insertedLoads
|
|
.name(name() + ".memDep.insertedLoads")
|
|
.desc("Number of loads inserted to the mem dependence unit.");
|
|
|
|
insertedStores
|
|
.name(name() + ".memDep.insertedStores")
|
|
.desc("Number of stores inserted to the mem dependence unit.");
|
|
|
|
conflictingLoads
|
|
.name(name() + ".memDep.conflictingLoads")
|
|
.desc("Number of conflicting loads.");
|
|
|
|
conflictingStores
|
|
.name(name() + ".memDep.conflictingStores")
|
|
.desc("Number of conflicting stores.");
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst)
|
|
{
|
|
InstSeqNum inst_seq_num = inst->seqNum;
|
|
|
|
Dependency unresolved_dependencies(inst_seq_num);
|
|
|
|
InstSeqNum producing_store = depPred.checkInst(inst->readPC());
|
|
|
|
if (producing_store == 0 ||
|
|
storeDependents.find(producing_store) == storeDependents.end()) {
|
|
|
|
DPRINTF(MemDepUnit, "MemDepUnit: No dependency for inst PC "
|
|
"%#x.\n", inst->readPC());
|
|
|
|
unresolved_dependencies.storeDep = storeDependents.end();
|
|
|
|
if (inst->readyToIssue()) {
|
|
readyInsts.insert(inst_seq_num);
|
|
} else {
|
|
unresolved_dependencies.memDepReady = true;
|
|
|
|
waitingInsts.insert(unresolved_dependencies);
|
|
}
|
|
} else {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Adding to dependency list; "
|
|
"inst PC %#x is dependent on seq num %i.\n",
|
|
inst->readPC(), producing_store);
|
|
|
|
if (inst->readyToIssue()) {
|
|
unresolved_dependencies.regsReady = true;
|
|
}
|
|
|
|
// Find the store that this instruction is dependent on.
|
|
sd_it_t store_loc = storeDependents.find(producing_store);
|
|
|
|
assert(store_loc != storeDependents.end());
|
|
|
|
// Record the location of the store that this instruction is
|
|
// dependent on.
|
|
unresolved_dependencies.storeDep = store_loc;
|
|
|
|
// If it's not already ready, then add it to the renamed
|
|
// list and the dependencies.
|
|
dep_it_t inst_loc =
|
|
(waitingInsts.insert(unresolved_dependencies)).first;
|
|
|
|
// Add this instruction to the list of dependents.
|
|
(*store_loc).second.push_back(inst_loc);
|
|
|
|
assert(!(*store_loc).second.empty());
|
|
|
|
if (inst->isLoad()) {
|
|
++conflictingLoads;
|
|
} else {
|
|
++conflictingStores;
|
|
}
|
|
}
|
|
|
|
if (inst->isStore()) {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n",
|
|
inst->readPC());
|
|
|
|
depPred.insertStore(inst->readPC(), inst_seq_num);
|
|
|
|
// Make sure this store isn't already in this list.
|
|
assert(storeDependents.find(inst_seq_num) == storeDependents.end());
|
|
|
|
// Put a dependency entry in at the store's sequence number.
|
|
// Uh, not sure how this works...I want to create an entry but
|
|
// I don't have anything to put into the value yet.
|
|
storeDependents[inst_seq_num];
|
|
|
|
assert(storeDependents.size() != 0);
|
|
|
|
++insertedStores;
|
|
|
|
} else if (inst->isLoad()) {
|
|
++insertedLoads;
|
|
} else {
|
|
panic("MemDepUnit: Unknown type! (most likely a barrier).");
|
|
}
|
|
|
|
memInsts[inst_seq_num] = inst;
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::insertNonSpec(DynInstPtr &inst)
|
|
{
|
|
InstSeqNum inst_seq_num = inst->seqNum;
|
|
|
|
Dependency non_spec_inst(inst_seq_num);
|
|
|
|
non_spec_inst.storeDep = storeDependents.end();
|
|
|
|
waitingInsts.insert(non_spec_inst);
|
|
|
|
// Might want to turn this part into an inline function or something.
|
|
// It's shared between both insert functions.
|
|
if (inst->isStore()) {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Inserting store PC %#x.\n",
|
|
inst->readPC());
|
|
|
|
depPred.insertStore(inst->readPC(), inst_seq_num);
|
|
|
|
// Make sure this store isn't already in this list.
|
|
assert(storeDependents.find(inst_seq_num) == storeDependents.end());
|
|
|
|
// Put a dependency entry in at the store's sequence number.
|
|
// Uh, not sure how this works...I want to create an entry but
|
|
// I don't have anything to put into the value yet.
|
|
storeDependents[inst_seq_num];
|
|
|
|
assert(storeDependents.size() != 0);
|
|
|
|
++insertedStores;
|
|
|
|
} else if (inst->isLoad()) {
|
|
++insertedLoads;
|
|
} else {
|
|
panic("MemDepUnit: Unknown type! (most likely a barrier).");
|
|
}
|
|
|
|
memInsts[inst_seq_num] = inst;
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
typename Impl::DynInstPtr &
|
|
MemDepUnit<MemDepPred, Impl>::top()
|
|
{
|
|
topInst = memInsts.find( (*readyInsts.begin()) );
|
|
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Top instruction is PC %#x.\n",
|
|
(*topInst).second->readPC());
|
|
|
|
return (*topInst).second;
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::pop()
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Removing instruction PC %#x.\n",
|
|
(*topInst).second->readPC());
|
|
|
|
wakeDependents((*topInst).second);
|
|
|
|
issue((*topInst).second);
|
|
|
|
memInsts.erase(topInst);
|
|
|
|
topInst = memInsts.end();
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst)
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Marking registers as ready for "
|
|
"instruction PC %#x.\n",
|
|
inst->readPC());
|
|
|
|
InstSeqNum inst_seq_num = inst->seqNum;
|
|
|
|
Dependency inst_to_find(inst_seq_num);
|
|
|
|
dep_it_t waiting_inst = waitingInsts.find(inst_to_find);
|
|
|
|
assert(waiting_inst != waitingInsts.end());
|
|
|
|
if ((*waiting_inst).memDepReady) {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Instruction has its memory "
|
|
"dependencies resolved, adding it to the ready list.\n");
|
|
|
|
moveToReady(waiting_inst);
|
|
} else {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Instruction still waiting on "
|
|
"memory dependency.\n");
|
|
|
|
(*waiting_inst).regsReady = true;
|
|
}
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst)
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Marking non speculative "
|
|
"instruction PC %#x as ready.\n",
|
|
inst->readPC());
|
|
|
|
InstSeqNum inst_seq_num = inst->seqNum;
|
|
|
|
Dependency inst_to_find(inst_seq_num);
|
|
|
|
dep_it_t waiting_inst = waitingInsts.find(inst_to_find);
|
|
|
|
assert(waiting_inst != waitingInsts.end());
|
|
|
|
moveToReady(waiting_inst);
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst)
|
|
{
|
|
assert(readyInsts.find(inst->seqNum) != readyInsts.end());
|
|
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Issuing instruction PC %#x.\n",
|
|
inst->readPC());
|
|
|
|
// Remove the instruction from the ready list.
|
|
readyInsts.erase(inst->seqNum);
|
|
|
|
depPred.issued(inst->readPC(), inst->seqNum, inst->isStore());
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::wakeDependents(DynInstPtr &inst)
|
|
{
|
|
// Only stores have dependents.
|
|
if (!inst->isStore()) {
|
|
return;
|
|
}
|
|
|
|
// Wake any dependencies.
|
|
sd_it_t sd_it = storeDependents.find(inst->seqNum);
|
|
|
|
// If there's no entry, then return. Really there should only be
|
|
// no entry if the instruction is a load.
|
|
if (sd_it == storeDependents.end()) {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Instruction PC %#x, sequence "
|
|
"number %i has no dependents.\n",
|
|
inst->readPC(), inst->seqNum);
|
|
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < (*sd_it).second.size(); ++i ) {
|
|
dep_it_t woken_inst = (*sd_it).second[i];
|
|
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Waking up a dependent inst, "
|
|
"sequence number %i.\n",
|
|
(*woken_inst).seqNum);
|
|
#if 0
|
|
// Should we have reached instructions that are actually squashed,
|
|
// there will be no more useful instructions in this dependency
|
|
// list. Break out early.
|
|
if (waitingInsts.find(woken_inst) == waitingInsts.end()) {
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Dependents on inst PC %#x "
|
|
"are squashed, starting at SN %i. Breaking early.\n",
|
|
inst->readPC(), woken_inst);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if ((*woken_inst).regsReady) {
|
|
moveToReady(woken_inst);
|
|
} else {
|
|
(*woken_inst).memDepReady = true;
|
|
}
|
|
}
|
|
|
|
storeDependents.erase(sd_it);
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num)
|
|
{
|
|
|
|
if (!waitingInsts.empty()) {
|
|
dep_it_t waiting_it = waitingInsts.end();
|
|
|
|
--waiting_it;
|
|
|
|
// Remove entries from the renamed list as long as we haven't reached
|
|
// the end and the entries continue to be younger than the squashed.
|
|
while (!waitingInsts.empty() &&
|
|
(*waiting_it).seqNum > squashed_num)
|
|
{
|
|
if (!(*waiting_it).memDepReady &&
|
|
(*waiting_it).storeDep != storeDependents.end()) {
|
|
sd_it_t sd_it = (*waiting_it).storeDep;
|
|
|
|
// Make sure the iterator that the store has pointing
|
|
// back is actually to this instruction.
|
|
assert((*sd_it).second.back() == waiting_it);
|
|
|
|
// Now remove this from the store's list of dependent
|
|
// instructions.
|
|
(*sd_it).second.pop_back();
|
|
}
|
|
|
|
waitingInsts.erase(waiting_it--);
|
|
}
|
|
}
|
|
|
|
if (!readyInsts.empty()) {
|
|
sn_it_t ready_it = readyInsts.end();
|
|
|
|
--ready_it;
|
|
|
|
// Same for the ready list.
|
|
while (!readyInsts.empty() &&
|
|
(*ready_it) > squashed_num)
|
|
{
|
|
readyInsts.erase(ready_it--);
|
|
}
|
|
}
|
|
|
|
if (!storeDependents.empty()) {
|
|
sd_it_t dep_it = storeDependents.end();
|
|
|
|
--dep_it;
|
|
|
|
// Same for the dependencies list.
|
|
while (!storeDependents.empty() &&
|
|
(*dep_it).first > squashed_num)
|
|
{
|
|
// This store's list of dependent instructions should be empty.
|
|
assert((*dep_it).second.empty());
|
|
|
|
storeDependents.erase(dep_it--);
|
|
}
|
|
}
|
|
|
|
// Tell the dependency predictor to squash as well.
|
|
depPred.squash(squashed_num);
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
void
|
|
MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst,
|
|
DynInstPtr &violating_load)
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Passing violating PCs to store sets,"
|
|
" load: %#x, store: %#x\n", violating_load->readPC(),
|
|
store_inst->readPC());
|
|
// Tell the memory dependence unit of the violation.
|
|
depPred.violation(violating_load->readPC(), store_inst->readPC());
|
|
}
|
|
|
|
template <class MemDepPred, class Impl>
|
|
inline void
|
|
MemDepUnit<MemDepPred, Impl>::moveToReady(dep_it_t &woken_inst)
|
|
{
|
|
DPRINTF(MemDepUnit, "MemDepUnit: Adding instruction sequence number %i "
|
|
"to the ready list.\n", (*woken_inst).seqNum);
|
|
|
|
// Add it to the ready list.
|
|
readyInsts.insert((*woken_inst).seqNum);
|
|
|
|
// Remove it from the waiting instructions.
|
|
waitingInsts.erase(woken_inst);
|
|
}
|