use crate::isa::rvv::VRegIdx;
use crate::uarch::ctx::CoreCtx;
use crate::uarch::pipeline::backend::shared::commit::{
self, CommitEvent, CommitRegisters, CommitResources,
};
use crate::uarch::pipeline::backend::shared::flush_stats::count_flush;
use crate::uarch::pipeline::engine::ExecutionEngine;
use crate::uarch::pipeline::rob::RobTag;
use crate::uarch::pipeline::squash::{PendingSquash, Redirect, SquashCause};
use super::O3Engine;
pub(super) type OrderViolation = (RobTag, u64);
pub(super) const fn older_violation(
memory2: Option<OrderViolation>,
memory1: Option<OrderViolation>,
) -> Option<OrderViolation> {
match (memory2, memory1) {
(Some(m2), Some(m1)) if m1.0.is_older_than(m2.0) => Some(m1),
(Some(m2), _) => Some(m2),
(None, m1) => m1,
}
}
impl O3Engine {
pub(super) fn count_squash_stall(&mut self, state: &mut CoreCtx<'_>) {
if self.squash_stall_remaining > 0 {
self.squash_stall_remaining -= 1;
state.uncore.stats.counter(state.core.stat_paths.pipeline.stalls_squash).inc();
}
}
pub(super) fn squash_cycles(&self, squashed: usize) -> u64 {
squashed.div_ceil(self.squash_width.max(1)).max(1) as u64 + 1
}
pub(super) fn retire(&mut self, state: &mut CoreCtx<'_>, redirect: &mut Option<u64>) -> bool {
let commit_event = commit::commit_stage(
state,
CommitResources {
common: &mut self.common,
rob: &mut self.rob,
store_buffer: &mut self.store_buffer,
vec_store_buffer: &mut self.vec_store_buffer,
width: self.commit_width,
registers: CommitRegisters::Renamed {
rename_map: &mut self.committed_rename_map,
free_list: &mut self.free_list,
load_queue: &mut self.load_queue,
checkpoints: &mut self.checkpoints,
vec_prf: &mut self.vec_prf,
vec_free_list: &mut self.vec_free_list,
},
},
);
if let Some(event) = &commit_event {
count_flush(state, event.into(), self.rob.len());
}
match commit_event {
Some(CommitEvent::Trap(trap, pc)) => {
let squashed = self.rob.len();
self.flush(state);
self.squash_stall_remaining = self.squash_cycles(squashed);
state.trap(&trap, pc);
*redirect = Some(state.hart.pc);
true
}
Some(CommitEvent::ReExecute(pc, _) | CommitEvent::SquashAfter(pc)) => {
let squashed = self.rob.len();
self.flush(state);
self.squash_stall_remaining = self.squash_cycles(squashed);
state.hart.pc = pc;
*redirect = Some(pc);
true
}
None => false,
}
}
pub(super) fn squash_on_violation(
&mut self,
state: &mut CoreCtx<'_>,
now: u64,
order_violation: Option<OrderViolation>,
) {
let squash = match (order_violation, self.common.coherence_violation.take()) {
(Some((tag, _)), Some(coherence_tag)) if coherence_tag.is_older_than(tag) => {
Some((coherence_tag, None))
}
(Some((tag, store_pc)), _) => Some((tag, Some(store_pc))),
(None, Some(coherence_tag)) => Some((coherence_tag, None)),
(None, None) => None,
};
let Some((violating_tag, store_pc)) = squash else { return };
let violation_pc = self.rob.find_entry(violating_tag).map_or(state.hart.pc, |e| e.pc);
let cause = if let Some(store_pc) = store_pc {
self.mdp.violation(violation_pc, store_pc);
SquashCause::MemoryOrder
} else {
state.uncore.stats.counter(state.core.stat_paths.lsq.coherence_violations).inc();
SquashCause::Coherence
};
self.common.request_squash(PendingSquash {
keep_tag: self.rob.prev_tag_of(violating_tag),
redirect: Redirect::to(violation_pc, cause),
apply_at: now + self.redirect_latency,
});
}
fn rebuild_rename_map(&mut self) {
self.rename_map = self.committed_rename_map.clone();
for entry in self.rob.iter_in_order() {
if entry.ctrl.reg_write && !entry.rd.is_zero() {
self.rename_map.set(entry.rd, false, entry.phys_dst);
} else if entry.ctrl.fp_reg_write {
self.rename_map.set(entry.rd, true, entry.phys_dst);
}
if entry.vec_dst_count > 0 {
let vd_base = entry.ctrl.vd.as_u8();
for i in 0..entry.vec_dst_count as usize {
let vreg = VRegIdx::new(vd_base + i as u8);
self.rename_map.set_vec(vreg, entry.vec_phys_dst[i]);
}
}
}
}
pub(super) fn apply_squash(
&mut self,
state: &mut CoreCtx<'_>,
squash: PendingSquash,
redirect: &mut Option<u64>,
) {
self.serialization.squash(|tag| squash.squashes(tag));
let keep_tag = squash.keep_tag.filter(|tag| self.rob.find_entry(*tag).is_some());
let keep_seq = keep_tag.and_then(|tag| self.rob.find_entry(tag)).map(|entry| entry.seq);
let squashed = if let Some(keep_tag) = keep_tag {
for entry in self.rob.iter_after(keep_tag) {
self.free_list.reclaim(entry.phys_dst);
for i in 0..entry.vec_dst_count as usize {
self.vec_free_list.reclaim(entry.vec_phys_dst[i]);
}
}
self.rob.iter_after(keep_tag).count()
} else {
for entry in self.rob.iter_all() {
self.free_list.reclaim(entry.phys_dst);
for i in 0..entry.vec_dst_count as usize {
self.vec_free_list.reclaim(entry.vec_phys_dst[i]);
}
}
self.rob.len()
};
count_flush(state, squash.redirect.cause.into(), squashed);
if let Some(keep_tag) = keep_tag {
self.issue_queue.flush_after(keep_tag);
self.rob.flush_after(keep_tag);
self.store_buffer.flush_after(keep_tag);
self.load_queue.flush_after(keep_tag);
self.mdp.flush_after(keep_tag, &self.rob);
self.vec_store_buffer.flush_after(keep_tag);
self.common.squash_after(keep_tag);
} else {
self.issue_queue.flush();
self.rob.flush_all();
self.store_buffer.flush_speculative();
self.load_queue.flush();
self.mdp.flush();
self.vec_store_buffer.flush_speculative();
self.common.squash_all();
}
let survives = |tag: RobTag| keep_tag.is_some_and(|keep_tag| tag.is_older_or_eq(keep_tag));
self.mem1_mem2.retain(|e| survives(e.rob_tag));
self.mem2_wb.retain(|e| survives(e.rob_tag));
self.pending_results.retain(|p| survives(p.entry.rob_tag));
self.pending_addresses.retain(|p| survives(p.entry.rob_tag));
self.vec_pending.retain(|v| survives(v.rob_tag));
self.vec_mem_pending.retain(|m| survives(m.entry.rob_tag));
self.vec_mem_inflight.retain(|m| survives(m.rob_tag));
self.execute_mem1.retain(|e| survives(e.rob_tag));
let checkpoint = keep_tag
.filter(|_| self.checkpoints.capacity() > 0)
.and_then(|tag| self.checkpoints.find_by_tag(tag).map(|ckpt| ckpt.rename_map.clone()));
if let Some(rename_map) = checkpoint {
self.rename_map = rename_map;
} else {
self.rebuild_rename_map();
}
self.squash_stall_remaining = self.squash_cycles(squashed);
if let Some(keep_tag) = keep_tag {
self.checkpoints.flush_after(keep_tag);
} else {
self.checkpoints.flush_all();
}
*redirect = Some(squash.redirect.target);
let now = state.cycle;
self.common.squash_predictions(&mut state.core.branch_predictor, &squash, keep_seq, now);
}
}