use crate::exec::signals::ControlFlow;
use crate::isa::rvv::ElemIdx;
use crate::uarch::ctx::CoreCtx;
use crate::uarch::pipeline::backend::shared::vec_mem::{expand_span, retire_access};
use crate::uarch::pipeline::backend::shared::{memory1, memory2, writeback};
use crate::uarch::pipeline::latches::Mem2WbEntry;
use super::O3Engine;
use super::complete::wake_vector_dests;
use super::squash::OrderViolation;
impl O3Engine {
pub(super) fn memory2(&mut self, state: &mut CoreCtx<'_>) -> Option<OrderViolation> {
let mut memory2_results = Vec::with_capacity(self.mem1_mem2.len());
let violation = memory2::memory2_stage(
&state.stage(),
&mut self.mem1_mem2,
&mut memory2_results,
&mut self.store_buffer,
Some(&mut self.load_queue),
Some(&mut self.vec_store_buffer),
);
for entry in &memory2_results {
if entry.ctrl.mem_write
&& (entry.ctrl.atomic_op.is_some() || entry.vec_mem.is_some())
&& let Some(store_tag) = self.mdp.store_resolved(entry.rob_tag)
{
self.issue_queue.wakeup_mem_dep(&[store_tag]);
}
}
self.mem2_wb.extend(memory2_results);
violation
}
pub(super) fn retire_vector_memory_results(&mut self) {
let mut scalar_wb = Vec::with_capacity(self.mem2_wb.len());
for wb in std::mem::take(&mut self.mem2_wb) {
let Some(ref vme) = wb.vec_mem else {
scalar_wb.push(wb);
continue;
};
let retired = retire_access(&wb, vme, &mut self.vec_mem_inflight, &mut self.rob);
let vlen_bits = self.vec_prf.vlen().bits();
for write in retired.writes {
let elems_per_reg = (vlen_bits / (write.eew.bytes() * 8)).max(1);
let local = ElemIdx::new(write.elem_idx.as_usize() % elems_per_reg);
self.vec_prf.write_element(write.vd_phys, local, write.eew, write.value);
}
if !vme.is_store {
self.load_queue.deallocate_micro_op(wb.rob_tag, vme.micro_op);
}
if retired.completed
&& let Some(parent) =
self.vec_mem_inflight.iter_mut().find(|m| m.rob_tag == wb.rob_tag)
&& !parent.wakeup_fired
{
wake_vector_dests(
&mut self.vec_prf,
&mut self.issue_queue,
&parent.vd_phys[..parent.vd_count as usize],
);
parent.wakeup_fired = true;
}
}
self.mem2_wb = scalar_wb;
}
pub(super) fn writeback_memory_results(&mut self, state: &mut CoreCtx<'_>) -> usize {
let later = self.mem2_wb.split_off(self.mem2_wb.len().min(self.writeback_width));
let now = std::mem::replace(&mut self.mem2_wb, later);
let used = now.len();
self.write_back(state, now);
self.writeback_width - used
}
pub(super) fn writeback_forwarded_loads(
&mut self,
state: &mut CoreCtx<'_>,
slots: usize,
) -> usize {
let mut now = std::mem::take(&mut self.common.forwarded_results);
let mut later = now.split_off(slots.min(now.len()));
let used = now.len();
self.write_back(state, now);
if !later.is_empty() {
later.append(&mut self.mem2_wb);
self.mem2_wb = later;
}
used
}
fn write_back(&mut self, state: &mut CoreCtx<'_>, mut entries: Vec<Mem2WbEntry>) {
let wakeups: Vec<_> = entries
.iter()
.filter(|wb| wb.trap.is_none())
.map(|wb| {
let val = if wb.ctrl.mem_read {
wb.load_data
} else if wb.ctrl.control_flow == ControlFlow::Jump {
wb.pc.wrapping_add(wb.inst_size.as_u64())
} else {
wb.alu
};
(wb.rd_phys, val)
})
.collect();
writeback::writeback_stage(&state.stage(), &mut entries, &mut self.rob);
for (rd_phys, val) in wakeups {
self.prf.write(rd_phys, val);
self.issue_queue.wakeup_phys(rd_phys, val);
}
}
pub(super) fn memory1(&mut self, state: &mut CoreCtx<'_>, now: u64) -> Option<OrderViolation> {
self.send_generated_addresses_to_memory1(now);
let mut input = std::mem::take(&mut self.execute_mem1);
let resolved = memory1::memory1_stage(&mut state.stage(), self, &mut input);
self.execute_mem1.extend(input);
for span in resolved.expanded_spans {
let (rob_tag, is_load) = (span.rob_tag, span.ctrl.mem_read);
if let Some(micro_op) = expand_span(&span, &mut self.vec_mem_inflight)
&& is_load
{
self.load_queue.deallocate_micro_op(rob_tag, micro_op);
}
}
for store_tag in resolved.resolved_stores {
if let Some(tag) = self.mdp.store_resolved(store_tag) {
self.issue_queue.wakeup_mem_dep(&[tag]);
}
}
resolved.violation
}
pub(super) fn note_memory_backpressure(&self, state: &mut CoreCtx<'_>) -> bool {
let backpressured = !self.execute_mem1.is_empty();
if backpressured {
state.uncore.stats.counter(state.core.stat_paths.pipeline.stalls_backpressure).inc();
}
backpressured
}
fn send_generated_addresses_to_memory1(&mut self, now: u64) {
let (ready, waiting) = std::mem::take(&mut self.pending_addresses)
.into_iter()
.partition(|p| p.complete_cycle <= now);
self.pending_addresses = waiting;
for done in ready {
self.execute_mem1.push(done.entry);
}
}
pub(super) fn issue_vec_mem_waves(&mut self) {
for inflight in &mut self.vec_mem_inflight {
while let Some(front) = inflight.pending_micro_ops.front() {
if !front.is_store
&& !self.load_queue.allocate(
front.entry.rob_tag,
front.bytes,
Some(front.micro_op),
)
{
break;
}
let Some(mop) = inflight.pending_micro_ops.pop_front() else { break };
self.vec_mem_pending.push_back(mop);
}
}
}
pub(super) fn send_vector_memory_micro_ops(&mut self) {
let mut loads_issued = 0usize;
let mut stores_issued = 0usize;
while let Some(front) = self.vec_mem_pending.front() {
if front.is_store {
if stores_issued >= self.store_ports {
break;
}
stores_issued += 1;
} else {
if loads_issued >= self.load_ports {
break;
}
loads_issued += 1;
}
let Some(mop) = self.vec_mem_pending.pop_front() else { break };
self.execute_mem1.push(mop.entry);
}
}
}