use crate::exec::execute::{branch_taken, check_target_alignment, is_jalr, jump_target};
use crate::exec::signals::ControlFlow;
use crate::isa::privileged::Trap;
use crate::isa::reg;
use crate::uarch::ctx::StageCtx;
use crate::uarch::pipeline::exception::ExceptionStage;
use crate::uarch::pipeline::latches::{ExMem1Entry, RenameIssueEntry};
use crate::uarch::pipeline::rob::{BpOutcome, Rob};
use crate::uarch::pipeline::squash::{BranchRepair, Redirect};
use crate::{trace_branch, trace_trap};
pub fn fault(
state: &StageCtx<'_>,
id: &RenameIssueEntry,
trap: Trap,
stage: ExceptionStage,
) -> ExMem1Entry {
trace_trap!(state.trace_trap_enabled(&trap);
event = "fault",
stage = ?stage,
pc = %crate::common::trace::Hex(id.inst.pc),
rob_tag = id.rob_tag.0,
trap = ?trap,
"EX: instruction faulted"
);
ExMem1Entry {
trap: Some(trap),
exception_stage: Some(stage),
..ExMem1Entry::from_issue(id, 0, 0)
}
}
pub fn propagate_trap(state: &StageCtx<'_>, id: &RenameIssueEntry, trap: Trap) -> ExMem1Entry {
fault(state, id, trap, id.exception_stage.unwrap_or(ExceptionStage::Execute))
}
pub fn resolve_control_flow(
state: &mut StageCtx<'_>,
rob: &mut Rob,
id: &RenameIssueEntry,
op_a: u64,
op_b: u64,
) -> Result<Option<Redirect>, Trap> {
match id.inst.ctrl.control_flow {
ControlFlow::Branch => resolve_branch(state, rob, id, op_a, op_b),
ControlFlow::Jump => resolve_jump(state, rob, id),
ControlFlow::Sequential => Ok(None),
}
}
fn resolve_branch(
state: &mut StageCtx<'_>,
rob: &mut Rob,
id: &RenameIssueEntry,
op_a: u64,
op_b: u64,
) -> Result<Option<Redirect>, Trap> {
let taken = branch_taken(id.inst.bits, op_a, op_b);
let actual_target = id.inst.pc.wrapping_add(id.inst.imm as u64);
let fallthrough = id.inst.next_pc();
let predicted_next_pc = if id.pred_taken { id.pred_target } else { fallthrough };
let actual_next_pc = if taken { actual_target } else { fallthrough };
if taken {
check_target_alignment(state.hart(), actual_target)?;
}
let mispredicted = predicted_next_pc != actual_next_pc;
rob.set_control_outcome(
id.rob_tag,
BpOutcome { taken, mispredicted },
taken.then_some(actual_target),
);
trace_branch!(state.config.general.trace_instructions;
event = "resolve",
pc = %crate::common::trace::Hex(id.inst.pc),
rob_tag = id.rob_tag.0,
pred_taken = id.pred_taken,
pred_target = %crate::common::trace::Hex(predicted_next_pc),
actual_taken = taken,
actual_target = %crate::common::trace::Hex(actual_next_pc),
mispredicted,
"EX: branch resolved"
);
let repair = BranchRepair { seq: id.seq, taken, target: actual_target };
Ok(count_prediction(state, mispredicted).then(|| Redirect::mispredict(actual_next_pc, repair)))
}
fn resolve_jump(
state: &mut StageCtx<'_>,
rob: &mut Rob,
id: &RenameIssueEntry,
) -> Result<Option<Redirect>, Trap> {
let inst = &id.inst;
let is_jalr = is_jalr(inst);
let actual_target = jump_target(inst);
check_target_alignment(state.hart(), actual_target)?;
let predicted_target = if id.pred_taken { id.pred_target } else { id.inst.next_pc() };
let mispredicted = actual_target != predicted_target;
rob.set_control_outcome(
id.rob_tag,
BpOutcome { taken: true, mispredicted },
Some(actual_target),
);
let rd_link = id.inst.rd == reg::REG_RA || id.inst.rd == reg::REG_T0;
let rs1_link = is_jalr && (id.inst.rs1 == reg::REG_RA || id.inst.rs1 == reg::REG_T0);
trace_branch!(state.config.general.trace_instructions;
event = "resolve",
pc = %crate::common::trace::Hex(id.inst.pc),
rob_tag = id.rob_tag.0,
bp_type = if rs1_link && !rd_link { "JALR/RAS" } else if rd_link { "JAL/call" } else { "JAL/JALR" },
pred_taken = id.pred_taken,
pred_target = %crate::common::trace::Hex(predicted_target),
actual_taken = true,
actual_target = %crate::common::trace::Hex(actual_target),
mispredicted,
"EX: jump resolved"
);
let repair = BranchRepair { seq: id.seq, taken: true, target: actual_target };
Ok(count_prediction(state, mispredicted).then(|| Redirect::mispredict(actual_target, repair)))
}
fn count_prediction(state: &mut StageCtx<'_>, mispredicted: bool) -> bool {
let paths = &state.core().stat_paths.bp;
let path = if mispredicted { paths.spec_mispredicts } else { paths.spec_hits };
state.counter(path).inc();
mispredicted
}