Compare commits

...

2 commits

8 changed files with 24277 additions and 641 deletions

View file

@ -10,27 +10,29 @@ use crate::{
CpuConfigUnitCount, PhantomConstCpuConfig, TwiceCpuConfigFetchWidth,
},
instruction::{
COMMON_MOP_SRC_LEN, L2RegNum, L2RegisterFileMOp, MOp, MOpDestReg, MOpRegNum, MOpTrait,
PRegNum, ReadL2RegMOp, UnitNum, UnitOutRegNum, WriteL2RegMOp,
COMMON_MOP_SRC_LEN, L2RegNum, MOp, MOpDestReg, MOpRegNum, MOpTrait, PRegNum, ReadL2RegMOp,
UnitNum, UnitOutRegNum, WriteL2RegMOp,
},
next_pc::{CallStackOp, SimValueDefault},
register::PRegValue,
rename_execute_retire::to_unit_interfaces::ExecuteToUnitInterfaces,
rename_execute_retire::{
rename_table::{RenameTable, RenameTableDebugState, RenameTableEntry, RenameTableUpdate},
reorder_buffer::{ReorderBuffer, ReorderBufferDebugState, RobEntry},
to_unit_interfaces::ExecuteToUnitInterfaces,
},
unit::{UnitKind, UnitMOp},
util::{LFSR31, array_vec::ArrayVec},
};
use fayalite::{
int::{UIntInRangeInclusiveType, UIntInRangeType},
int::UIntInRangeInclusiveType,
prelude::*,
ty::{OpaqueSimValue, SimValueDebug, StaticType},
util::ready_valid::ReadyValid,
};
use std::{
collections::{BTreeSet, VecDeque},
fmt, mem,
num::NonZero,
};
use std::{collections::VecDeque, fmt, num::NonZero};
mod rename_table;
mod reorder_buffer;
pub mod to_unit_interfaces;
pub const MOP_ID_WIDTH: usize = 16;
@ -255,191 +257,9 @@ impl<C: PhantomConstCpuConfig> SimValueDefault for RenameExecuteRetireDebugState
}
}
#[hdl(no_static)]
struct RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
l1: HdlOption<PRegNum<C>>,
l2: HdlOption<L2RegNum>,
}
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
#[hdl]
fn const_zero(self) -> SimValue<Self> {
#[hdl(sim)]
Self {
l1: #[hdl(sim)]
(self.l1).HdlSome(self.l1.HdlSome.const_zero()),
l2: #[hdl(sim)]
HdlNone(),
}
}
}
/// make arrays dynamically-sized to avoid putting large types on the stack
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(no_static)]
struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
entries: ArrayType<TraceAsString<RenameTableEntry<C>>, MOpRegCount<C>>,
config: C,
}
#[derive(Debug)]
struct RenameTable<C: PhantomConstCpuConfig> {
entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]>,
config: C,
}
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
fn clone(&self) -> Self {
Self {
entries: self.entries.clone(),
config: self.config.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
let Self { entries, config } = self;
entries.clone_from(&source.entries);
*config = source.config;
}
}
#[derive(Debug, Clone)]
enum RenameTableUpdate<C: PhantomConstCpuConfig> {
Write {
unrenamed_reg_num: u32,
new: SimValue<TraceAsString<RenameTableEntry<C>>>,
},
UpdateForReadL2Reg {
dest: SimValue<PRegNum<C>>,
src: SimValue<L2RegNum>,
},
UpdateForWriteL2Reg {
dest: SimValue<L2RegNum>,
src: SimValue<PRegNum<C>>,
},
DropAllL2RegFileOutputs,
}
impl<C: PhantomConstCpuConfig> RenameTable<C> {
fn new(config: C) -> Self {
let entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]> =
vec![
RenameTableEntry[config].const_zero().into_trace_as_string();
1 << MOpRegNum::WIDTH
]
.try_into()
.expect("size is known to match");
Self { entries, config }
}
#[hdl]
fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
let Self { entries, config } = self;
let ty = RenameTableDebugState[*config];
#[hdl(sim)]
RenameTableDebugState::<_> {
entries: entries.to_sim_value_with_type(ty.entries),
config,
}
}
#[hdl]
fn update(&mut self, update: &RenameTableUpdate<C>, rename_table_name: &str) {
match update {
RenameTableUpdate::Write {
unrenamed_reg_num,
new,
} => {
if *unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
// writing to const zero reg does nothing
return;
}
println!("{rename_table_name}: Write: {unrenamed_reg_num:#x} <- {new:?}");
self.entries[*unrenamed_reg_num as usize] = new.clone();
}
RenameTableUpdate::UpdateForReadL2Reg { dest, src } => {
let new = #[hdl(sim)]
RenameTableEntry::<_> {
l1: #[hdl(sim)]
(HdlOption[dest.ty()]).HdlSome(dest),
l2: #[hdl(sim)]
HdlSome(src),
};
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(l2) = &entry.inner().l2 {
if L2RegNum::value_sim(l2) == L2RegNum::value_sim(src) {
println!(
"{rename_table_name}: UpdateForReadL2Reg: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l1 {
unreachable!("l1 should be HdlNone: {entry:?}");
}
*entry = new.to_trace_as_string();
}
}
}
}
RenameTableUpdate::UpdateForWriteL2Reg { dest, src } => {
let new = #[hdl(sim)]
RenameTableEntry::<_> {
l1: #[hdl(sim)]
(HdlOption[src.ty()]).HdlNone(),
l2: #[hdl(sim)]
HdlSome(dest),
};
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(l1) = &entry.inner().l1 {
if l1 == src {
println!(
"{rename_table_name}: UpdateForWriteL2Reg: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l2 {
unreachable!("l2 should be HdlNone: {entry:?}");
}
*entry = new.to_trace_as_string();
}
}
}
}
RenameTableUpdate::DropAllL2RegFileOutputs => {
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l1 {
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l2 {
let mut new = entry.inner().clone();
new.l1 = #[hdl(sim)]
(new.l1.ty()).HdlNone();
println!(
"{rename_table_name}: DropAllL2RegFileOutputs: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
*entry = new.to_trace_as_string();
}
}
}
}
}
}
#[hdl]
fn used_unit_out_reg_count(&self, unit_index: usize) -> usize {
let mut seen = BTreeSet::new();
for entry in self.entries.iter() {
#[hdl(sim)]
if let HdlSome(v) = &entry.inner().l1 {
if UnitNum::index_sim(&v.unit_num) == Some(unit_index) {
seen.insert(UnitOutRegNum::value_sim(&v.unit_out_reg));
}
}
}
seen.len()
}
}
#[hdl(get(|_| L2RegNum.l2_reg_count()))]
type L2RegFileLen<C: PhantomConstGet<CpuConfig>> = DynSize;
#[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Default)]
enum MOpInUnitState {
@ -631,176 +451,6 @@ impl fmt::Debug for MOpInUnitState {
}
}
#[hdl]
type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
#[hdl(no_static)]
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
/// See [`RobEntry::is_register_fence`]
is_register_fence: Bool,
/// See [`RobEntry::is_register_fence`]
done_waiting_for_register_fences: Bool,
mop: MOpInstance<RenamedMOp<C>>,
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
mop_in_unit_state: SimOnlyMOpInUnitState,
is_speculative: Bool,
all_prior_mops_finished_and_or_caused_cancel: Bool,
output: HdlOption<NextPcPredictorOp<C>>,
caused_cancel: HdlOption<UnitCausedCancel<C>>,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
mop,
is_register_fence: _,
done_waiting_for_register_fences: _,
unit_index,
mop_in_unit_state: _,
is_speculative: _,
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
} = self;
#[hdl(sim)]
Self {
mop: zeroed(mop),
is_register_fence: false,
done_waiting_for_register_fences: false,
unit_index: zeroed(unit_index),
mop_in_unit_state: SimOnlyValue::default(),
is_speculative: false,
all_prior_mops_finished_and_or_caused_cancel: false,
output: #[hdl(sim)]
output.HdlNone(),
caused_cancel: #[hdl(sim)]
caused_cancel.HdlNone(),
}
}
}
#[derive(Debug)]
struct RobEntry<C: PhantomConstCpuConfig> {
/// Block this and all later µOps until all prior µOps are finished and didn't cause a cancel.
/// Not to be confused with memory fences.
is_register_fence: bool,
/// See [`Self::is_register_fence`]
done_waiting_for_register_fences: bool,
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
unit_index: usize,
mop_in_unit_state: MOpInUnitState,
is_speculative: bool,
all_prior_mops_finished_and_or_caused_cancel: bool,
output: Option<SimValue<NextPcPredictorOp<C>>>,
caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
}
impl<C: PhantomConstCpuConfig> RobEntry<C> {
fn new(
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
unit_index: usize,
is_register_fence: bool,
) -> Self {
Self {
mop,
is_register_fence,
done_waiting_for_register_fences: false,
unit_index,
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
is_speculative: true,
all_prior_mops_finished_and_or_caused_cancel: false,
output: None,
caused_cancel: None,
}
}
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
let dest_reg = MOpTrait::dest_reg_sim_ref(self.mop.mop.inner());
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
assert_eq!(unit_index, self.unit_index);
Some(dest_reg)
}
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
Some(&self.dest_reg()?.unit_out_reg)
}
fn unit_out_reg_index(&self) -> Option<usize> {
Some(UnitOutRegNum::value_sim(self.unit_out_reg()?))
}
#[hdl]
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
let Self {
is_register_fence,
done_waiting_for_register_fences,
mop,
unit_index,
mop_in_unit_state,
is_speculative,
all_prior_mops_finished_and_or_caused_cancel,
output,
caused_cancel,
} = self;
let ret_ty = RobEntryDebugState[config];
#[hdl(sim)]
RobEntryDebugState::<C> {
is_register_fence,
done_waiting_for_register_fences,
mop,
unit_index: unit_index.into_sim_value_with_type(ret_ty.unit_index),
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
is_speculative,
all_prior_mops_finished_and_or_caused_cancel,
output: output.into_sim_value_with_type(ret_ty.output),
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
}
}
}
#[hdl]
struct RobEntriesDebugState {
unrenamed: MOpInstance<MOp>,
/// number of renamed &micro;Ops that this unrenamed &micro;Op corresponds to
renamed_entries_len: UInt<8>,
}
impl SimValueDefault for RobEntriesDebugState {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
unrenamed,
renamed_entries_len,
} = self;
#[hdl(sim)]
Self {
unrenamed: zeroed(unrenamed),
renamed_entries_len: renamed_entries_len.sim_value_default(),
}
}
}
#[derive(Debug)]
struct RobEntries<C: PhantomConstCpuConfig> {
unrenamed: SimValue<MOpInstance<MOp>>,
rename_table_updates: Vec<RenameTableUpdate<C>>,
renamed_entries: VecDeque<RobEntry<C>>,
}
impl<C: PhantomConstCpuConfig> RobEntries<C> {
#[hdl]
fn debug_state(&self) -> SimValue<RobEntriesDebugState> {
let Self {
unrenamed,
rename_table_updates: _,
renamed_entries,
} = self;
#[hdl(sim)]
RobEntriesDebugState {
unrenamed,
renamed_entries_len: u8::try_from(renamed_entries.len())
.expect("renamed_entries.len() should fit in u8"),
}
}
}
#[hdl(no_static)]
struct NeedSendCancelDebugState<C: PhantomConstGet<CpuConfig>> {
send_to_next_pc: HdlOption<UInt<64>>,
@ -849,240 +499,6 @@ impl NextPcCancelingState {
}
}
#[hdl(no_static)]
pub struct ReorderBufferDebugState<C: PhantomConstGet<CpuConfig>> {
next_renamed_mop_id: MOpId,
entries: ArrayVec<RobEntriesDebugState, CpuConfigRobSize<C>>,
incomplete_back_entry: HdlOption<RobEntriesDebugState>,
renamed: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
config: C,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for ReorderBufferDebugState<C> {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
next_renamed_mop_id,
entries,
incomplete_back_entry,
renamed,
config,
} = self;
#[hdl(sim)]
Self {
next_renamed_mop_id: next_renamed_mop_id.sim_value_default(),
entries: entries.sim_value_default(),
incomplete_back_entry: incomplete_back_entry.sim_value_default(),
renamed: renamed.sim_value_default(),
config,
}
}
}
#[derive(Debug)]
struct ReorderBuffer<C: PhantomConstCpuConfig> {
next_renamed_mop_id: SimValue<MOpId>,
entries: VecDeque<RobEntries<C>>,
incomplete_back_entry: Option<RobEntries<C>>,
config: C,
}
impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
fn new(config: C) -> Self {
Self {
next_renamed_mop_id: MOpId.zero().into_sim_value(),
entries: VecDeque::new(),
incomplete_back_entry: None,
config,
}
}
#[hdl]
fn debug_state(&self) -> SimValue<ReorderBufferDebugState<C>> {
let Self {
next_renamed_mop_id,
entries,
incomplete_back_entry,
config,
} = self;
let ty = ReorderBufferDebugState[*config];
#[hdl(sim)]
ReorderBufferDebugState::<_> {
next_renamed_mop_id,
entries: ty
.entries
.from_iter_sim(
zeroed(StaticType::TYPE),
entries.iter().map(RobEntries::debug_state),
)
.expect("known to fit"),
incomplete_back_entry: incomplete_back_entry.as_ref().map(|v| v.debug_state()),
renamed: ty
.renamed
.from_iter_sim(
ty.renamed.element().sim_value_default(),
self.renamed().map(|v| v.debug_state(*config)),
)
.ok()
.expect("known to fit"),
config,
}
}
fn unrenamed_len(&self) -> usize {
self.entries.len()
}
fn unrenamed(&self) -> impl DoubleEndedIterator<Item = &SimValue<MOpInstance<MOp>>> + Clone {
self.entries.iter().map(|v| &v.unrenamed)
}
fn unrenamed_mut(
&mut self,
) -> impl DoubleEndedIterator<Item = &mut SimValue<MOpInstance<MOp>>> {
self.entries.iter_mut().map(|v| &mut v.unrenamed)
}
fn retire_groups_unrenamed_ranges(
&self,
) -> impl Clone + Iterator<Item = std::ops::Range<usize>> {
let mut next_group_start = 0;
self.entries
.iter()
.enumerate()
.filter_map(move |(index, entry)| {
if *entry.unrenamed.is_last_mop_in_insn {
let group_start = next_group_start;
next_group_start = index + 1;
Some(group_start..next_group_start)
} else {
None
}
})
}
fn retire_groups(
&self,
) -> impl Clone + Iterator<Item: DoubleEndedIterator<Item = &RobEntries<C>> + Clone> {
self.retire_groups_unrenamed_ranges()
.map(|range| self.entries.range(range))
}
fn renamed_len(&self) -> usize {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
config: _,
} = self;
entries
.iter()
.chain(incomplete_back_entry)
.map(|entries| entries.renamed_entries.len())
.sum()
}
fn renamed(&self) -> impl DoubleEndedIterator<Item = &RobEntry<C>> + Clone {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
config: _,
} = self;
entries
.iter()
.chain(incomplete_back_entry)
.flat_map(|entries| &entries.renamed_entries)
}
fn renamed_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut RobEntry<C>> {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
config: _,
} = self;
entries
.iter_mut()
.chain(incomplete_back_entry)
.flat_map(|entries| &mut entries.renamed_entries)
}
fn try_renamed_by_id(&self, id: &SimValue<MOpId>) -> Option<&RobEntry<C>> {
self.renamed().find(|v| v.mop.id == *id)
}
fn try_renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut RobEntry<C>> {
self.renamed_mut().find(|v| v.mop.id == *id)
}
#[track_caller]
fn renamed_by_id(&self, id: &SimValue<MOpId>) -> &RobEntry<C> {
match self.try_renamed_by_id(id) {
Some(v) => v,
None => panic!("MOpId not found: {id:?}"),
}
}
fn renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut RobEntry<C> {
match self.try_renamed_by_id_mut(id) {
Some(v) => v,
None => panic!("MOpId not found: {id:?}"),
}
}
fn renamed_push_back_with_new_id(
&mut self,
unrenamed: &SimValue<MOpInstance<MOp>>,
mut renamed: RobEntry<C>,
) -> &SimValue<MOpId> {
let replacement_id = self
.next_renamed_mop_id
.as_int()
.wrapping_add(1)
.into_sim_value();
renamed.mop.id = mem::replace(&mut self.next_renamed_mop_id, replacement_id);
println!("renamed_push_back_with_new_id: {:?}", renamed.mop);
let renamed_entries = &mut self
.incomplete_back_entry
.get_or_insert_with(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
})
.renamed_entries;
renamed_entries.push_back(renamed);
&renamed_entries.back().expect("just pushed").mop.id
}
fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
let entry = self
.incomplete_back_entry
.take()
.unwrap_or_else(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
});
self.entries.push_back(entry);
}
fn clear(&mut self) {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
config: _,
} = self;
entries.clear();
*incomplete_back_entry = None;
}
fn unrenamed_back_append_rename_table_update(
&mut self,
unrenamed: &SimValue<MOpInstance<MOp>>,
update: RenameTableUpdate<C>,
) {
self.incomplete_back_entry
.get_or_insert_with(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
})
.rename_table_updates
.push(update);
}
fn all_mops_are_finished_and_or_caused_cancel(&self) -> bool {
self.renamed().next().is_none_or(|entry| {
entry.all_prior_mops_finished_and_or_caused_cancel
&& entry.mop_in_unit_state.is_finished_and_or_caused_cancel()
})
}
}
type SimOnlyString = SimOnly<String>;
#[expect(non_upper_case_globals)]
const SimOnlyString: SimOnlyString = SimOnlyString::TYPE;
@ -1321,9 +737,9 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
}
for entry in self
.rename_table
.entries
.entries()
.iter()
.chain(self.retire_rename_table.entries.iter())
.chain(self.retire_rename_table.entries().iter())
{
#[hdl(sim)]
if let HdlSome(l1) = &entry.inner().l1 {
@ -1336,31 +752,14 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
}
#[hdl]
fn find_free_l2_reg(&self) -> Option<usize> {
// TODO: replace searching through instructions and rename tables with tracking when regs are free
let mut allocated_regs = vec![false; L2RegNum.l2_reg_count()];
for renamed in self.rob.renamed() {
#[hdl(sim)]
if let RenamedMOp::<_>::TransformedMove(l2_register_file_op) = renamed.mop.mop.inner() {
let l2_reg = #[hdl(sim)]
match l2_register_file_op {
L2RegisterFileMOp::<_, _>::ReadL2Reg(v) => &v.common.imm,
L2RegisterFileMOp::<_, _>::WriteL2Reg(v) => &v.common.imm,
};
allocated_regs[L2RegNum::value_sim(l2_reg)] = true;
}
}
for entry in self
.rename_table
.entries
let rob_l2_reg_ref_counts = self.rob.l2_reg_ref_counts();
let rename_table_l2_ref_counts = self.rename_table.l2_ref_counts();
let retire_rename_table_l2_ref_counts = self.retire_rename_table.l2_ref_counts();
rob_l2_reg_ref_counts
.iter()
.chain(self.retire_rename_table.entries.iter())
{
#[hdl(sim)]
if let HdlSome(l2) = &entry.inner().l2 {
allocated_regs[L2RegNum::value_sim(l2)] = true;
}
}
allocated_regs.iter().position(|v| !v)
.zip(rename_table_l2_ref_counts)
.zip(retire_rename_table_l2_ref_counts)
.position(|((a, b), c)| *a == 0 && *b == 0 && *c == 0)
}
fn add_renamed_with_new_id(
&mut self,
@ -1422,11 +821,11 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
});
let [src_reg] = src_regs;
let renamed_reg =
self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize].clone();
self.rename_table.entries()[MOpRegNum::reg_num_sim(&src_reg) as usize].clone();
println!("moving from {src_reg:?} renamed: {renamed_reg:?}");
let unrenamed_dest_regs =
MOpDestReg::regs_sim(MOpTrait::dest_reg_sim_ref(move_reg_mop));
assert!(self.rob.incomplete_back_entry.is_none());
assert!(!self.rob.has_incomplete_back_entry());
for unrenamed_reg_num in unrenamed_dest_regs {
self.update_rename_table(
&insn,
@ -1537,7 +936,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
let mut any_collisions = false;
MOpTrait::for_each_src_reg_sim_ref(insn.mop.inner(), &mut |src_reg, _| {
let renamed =
&self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize];
&self.rename_table.entries()[MOpRegNum::reg_num_sim(&src_reg) as usize];
println!(
"try_rename: checking that mop src reg ({renamed:?}) doesn't conflict with picked reg"
);
@ -1569,7 +968,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
src: reg_to_free.clone(),
},
);
let l2_store_id = self.add_renamed_with_new_id(
self.add_renamed_with_new_id(
&insn,
RobEntry::new(
#[hdl(sim)]
@ -1635,7 +1034,8 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
&renamed_dest_reg,
PRegNum[self.config],
&mut |src_reg, index| {
let renamed = &self.rename_table.entries[MOpRegNum::reg_num_sim(&src_reg) as usize];
let renamed =
&self.rename_table.entries()[MOpRegNum::reg_num_sim(&src_reg) as usize];
println!("renaming src[{index}] from {src_reg:?} to {renamed:?}");
#[hdl(sim)]
match &renamed.inner().l1 {
@ -2043,7 +1443,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
fn peek_retiring_insns(&self) -> Vec<SimValue<NextPcPredictorOp<C>>> {
let mut retval = Vec::new();
for retire_group in self.rob.retire_groups() {
for renamed_entry in retire_group.clone().flat_map(|v| &v.renamed_entries) {
for renamed_entry in retire_group.clone().flatten() {
if let RobEntry {
is_register_fence: _,
done_waiting_for_register_fences: _,
@ -2066,12 +1466,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
return retval;
}
}
for RobEntries {
unrenamed: _,
rename_table_updates: _,
renamed_entries,
} in retire_group
{
for renamed_entries in retire_group {
let caused_cancel = renamed_entries.iter().any(|v| v.caused_cancel.is_some());
let caused_cancel_after_retire =
renamed_entries
@ -2168,12 +1563,7 @@ impl<C: PhantomConstCpuConfig> RenameExecuteRetireState<C> {
cond_br_taken: _,
config: _,
} = retire;
let Some(RobEntries {
unrenamed: _,
rename_table_updates,
renamed_entries,
}) = self.rob.entries.pop_front()
else {
let Some((rename_table_updates, renamed_entries)) = self.rob.entries_pop_front() else {
unreachable!();
};
rename_table_updates

View file

@ -0,0 +1,258 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, PhantomConstCpuConfig},
instruction::{L2RegNum, MOpRegNum, PRegNum, UnitNum, UnitOutRegNum},
rename_execute_retire::L2RegFileLen,
};
use fayalite::{int::UIntInRangeInclusiveType, prelude::*};
use std::collections::BTreeSet;
#[hdl(no_static)]
pub(crate) struct RenameTableEntry<C: PhantomConstGet<CpuConfig>> {
pub(crate) l1: HdlOption<PRegNum<C>>,
pub(crate) l2: HdlOption<L2RegNum>,
}
impl<C: PhantomConstCpuConfig> RenameTableEntry<C> {
#[hdl]
pub(crate) fn const_zero(self) -> SimValue<Self> {
#[hdl(sim)]
Self {
l1: #[hdl(sim)]
(self.l1).HdlSome(self.l1.HdlSome.const_zero()),
l2: #[hdl(sim)]
HdlNone(),
}
}
}
/// make arrays dynamically-sized to avoid putting large types on the stack
#[hdl(get(|c| 1 << MOpRegNum::WIDTH))]
type MOpRegCount<C: PhantomConstGet<CpuConfig>> = DynSize;
#[hdl(no_static)]
pub(crate) struct RenameTableDebugState<C: PhantomConstGet<CpuConfig>> {
entries: ArrayType<TraceAsString<RenameTableEntry<C>>, MOpRegCount<C>>,
l2_ref_counts:
ArrayType<UIntInRangeInclusiveType<ConstUsize<0>, MOpRegCount<C>>, L2RegFileLen<C>>,
config: C,
}
#[derive(Debug)]
pub(crate) struct RenameTable<C: PhantomConstCpuConfig> {
entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]>,
l2_ref_counts: Box<[usize]>,
config: C,
}
impl<C: PhantomConstCpuConfig> Clone for RenameTable<C> {
fn clone(&self) -> Self {
Self {
entries: self.entries.clone(),
config: self.config.clone(),
l2_ref_counts: self.l2_ref_counts.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
let Self {
entries,
l2_ref_counts,
config,
} = self;
entries.clone_from(&source.entries);
l2_ref_counts.clone_from(&source.l2_ref_counts);
*config = source.config;
}
}
#[derive(Debug, Clone)]
pub(crate) enum RenameTableUpdate<C: PhantomConstCpuConfig> {
Write {
unrenamed_reg_num: u32,
new: SimValue<TraceAsString<RenameTableEntry<C>>>,
},
UpdateForReadL2Reg {
dest: SimValue<PRegNum<C>>,
src: SimValue<L2RegNum>,
},
UpdateForWriteL2Reg {
dest: SimValue<L2RegNum>,
src: SimValue<PRegNum<C>>,
},
DropAllL2RegFileOutputs,
}
impl<C: PhantomConstCpuConfig> RenameTable<C> {
pub(crate) fn new(config: C) -> Self {
let entries: Box<[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH]> =
vec![
RenameTableEntry[config].const_zero().into_trace_as_string();
1 << MOpRegNum::WIDTH
]
.try_into()
.expect("size is known to match");
Self {
entries,
l2_ref_counts: vec![0; L2RegFileLen[config]].into_boxed_slice(),
config,
}
}
pub(crate) fn entries(
&self,
) -> &[SimValue<TraceAsString<RenameTableEntry<C>>>; 1 << MOpRegNum::WIDTH] {
&self.entries
}
#[hdl]
pub(crate) fn to_debug_state(&self) -> SimValue<RenameTableDebugState<C>> {
let Self {
entries,
l2_ref_counts,
config,
} = self;
let ty = RenameTableDebugState[*config];
#[hdl(sim)]
RenameTableDebugState::<_> {
entries: entries.to_sim_value_with_type(ty.entries),
l2_ref_counts: l2_ref_counts.to_sim_value_with_type(ty.l2_ref_counts),
config,
}
}
#[hdl]
pub(crate) fn l2_ref_counts(&self) -> &[usize] {
let mut expected = vec![0usize; L2RegNum.l2_reg_count()];
for entry in self.entries.iter() {
#[hdl(sim)]
let RenameTableEntry::<_> { l1: _, l2 } = entry.inner();
#[hdl(sim)]
if let HdlSome(l2) = l2 {
expected[L2RegNum::value_sim(l2)] += 1
}
}
assert_eq!(*expected, *self.l2_ref_counts);
&self.l2_ref_counts
}
#[hdl]
pub(crate) fn update<'a>(&mut self, update: &RenameTableUpdate<C>, rename_table_name: &str) {
let mut update_entry =
|entry: &mut SimValue<TraceAsString<RenameTableEntry<C>>>,
new: SimValue<TraceAsString<RenameTableEntry<C>>>| {
#[hdl(sim)]
let RenameTableEntry::<_> { l1: _, l2 } = entry.inner();
#[hdl(sim)]
if let HdlSome(l2) = l2 {
let ref_count = &mut self.l2_ref_counts[L2RegNum::value_sim(l2)];
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
unreachable!("{rename_table_name}: l2 ref count went negative: {l2:?}")
});
}
#[hdl(sim)]
let RenameTableEntry::<_> { l1: _, l2 } = new.inner();
#[hdl(sim)]
if let HdlSome(l2) = l2 {
let ref_count = &mut self.l2_ref_counts[L2RegNum::value_sim(l2)];
*ref_count += 1;
assert!(
*ref_count <= L2RegNum.l2_reg_count(),
"{rename_table_name}: l2 ref count overflowed: {l2:?}",
)
}
*entry = new;
};
match update {
RenameTableUpdate::Write {
unrenamed_reg_num,
new,
} => {
if *unrenamed_reg_num == MOpRegNum::CONST_ZERO_REG_NUM {
// writing to const zero reg does nothing
return;
}
println!("{rename_table_name}: Write: {unrenamed_reg_num:#x} <- {new:?}");
update_entry(&mut self.entries[*unrenamed_reg_num as usize], new.clone());
}
RenameTableUpdate::UpdateForReadL2Reg { dest, src } => {
let new = #[hdl(sim)]
RenameTableEntry::<_> {
l1: #[hdl(sim)]
(HdlOption[dest.ty()]).HdlSome(dest),
l2: #[hdl(sim)]
HdlSome(src),
};
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(l2) = &entry.inner().l2 {
if L2RegNum::value_sim(l2) == L2RegNum::value_sim(src) {
println!(
"{rename_table_name}: UpdateForReadL2Reg: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l1 {
unreachable!("l1 should be HdlNone: {entry:?}");
}
update_entry(entry, new.to_trace_as_string());
}
}
}
}
RenameTableUpdate::UpdateForWriteL2Reg { dest, src } => {
let new = #[hdl(sim)]
RenameTableEntry::<_> {
l1: #[hdl(sim)]
(HdlOption[src.ty()]).HdlNone(),
l2: #[hdl(sim)]
HdlSome(dest),
};
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(l1) = &entry.inner().l1 {
if l1 == src {
println!(
"{rename_table_name}: UpdateForWriteL2Reg: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l2 {
unreachable!("l2 should be HdlNone: {entry:?}");
}
update_entry(entry, new.to_trace_as_string());
}
}
}
}
RenameTableUpdate::DropAllL2RegFileOutputs => {
for (unrenamed_reg_num, entry) in self.entries.iter_mut().enumerate() {
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l1 {
#[hdl(sim)]
if let HdlSome(_) = &entry.inner().l2 {
let mut new = entry.inner().clone();
new.l1 = #[hdl(sim)]
(new.l1.ty()).HdlNone();
println!(
"{rename_table_name}: DropAllL2RegFileOutputs: {unrenamed_reg_num:#x} \
updating from {entry:?} to {new:?}",
);
update_entry(entry, new.to_trace_as_string());
}
}
}
}
}
}
#[hdl]
pub(crate) fn used_unit_out_reg_count(&self, unit_index: usize) -> usize {
let mut seen = BTreeSet::new();
for entry in self.entries.iter() {
#[hdl(sim)]
if let HdlSome(v) = &entry.inner().l1 {
if UnitNum::index_sim(&v.unit_num) == Some(unit_index) {
seen.insert(UnitOutRegNum::value_sim(&v.unit_out_reg));
}
}
}
seen.len()
}
}

View file

@ -0,0 +1,501 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
config::{CpuConfig, CpuConfigRobSize, CpuConfigUnitCount, PhantomConstCpuConfig},
instruction::{L2RegNum, L2RegisterFileMOp, MOp, MOpTrait, PRegNum, UnitNum, UnitOutRegNum},
next_pc::SimValueDefault,
rename_execute_retire::{
L2RegFileLen, MOpId, MOpInUnitState, MOpInstance, NextPcPredictorOp, RenamedMOp,
UnitCausedCancel, rename_table::RenameTableUpdate, zeroed,
},
util::array_vec::ArrayVec,
};
use fayalite::{
int::{UIntInRangeInclusiveType, UIntInRangeType},
prelude::*,
ty::StaticType,
};
use std::{collections::VecDeque, mem};
#[hdl]
type SimOnlyMOpInUnitState = SimOnly<MOpInUnitState>;
#[hdl(no_static)]
struct RobEntryDebugState<C: PhantomConstGet<CpuConfig>> {
/// See [`RobEntry::is_register_fence`]
is_register_fence: Bool,
/// See [`RobEntry::is_register_fence`]
done_waiting_for_register_fences: Bool,
mop: MOpInstance<RenamedMOp<C>>,
unit_index: UIntInRangeType<ConstUsize<0>, CpuConfigUnitCount<C>>,
mop_in_unit_state: SimOnlyMOpInUnitState,
is_speculative: Bool,
all_prior_mops_finished_and_or_caused_cancel: Bool,
output: HdlOption<NextPcPredictorOp<C>>,
caused_cancel: HdlOption<UnitCausedCancel<C>>,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for RobEntryDebugState<C> {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
mop,
is_register_fence: _,
done_waiting_for_register_fences: _,
unit_index,
mop_in_unit_state: _,
is_speculative: _,
all_prior_mops_finished_and_or_caused_cancel: _,
output,
caused_cancel,
} = self;
#[hdl(sim)]
Self {
mop: zeroed(mop),
is_register_fence: false,
done_waiting_for_register_fences: false,
unit_index: zeroed(unit_index),
mop_in_unit_state: SimOnlyValue::default(),
is_speculative: false,
all_prior_mops_finished_and_or_caused_cancel: false,
output: #[hdl(sim)]
output.HdlNone(),
caused_cancel: #[hdl(sim)]
caused_cancel.HdlNone(),
}
}
}
#[derive(Debug)]
pub(crate) struct RobEntry<C: PhantomConstCpuConfig> {
/// Block this and all later µOps until all prior µOps are finished and didn't cause a cancel.
/// Not to be confused with memory fences.
pub(crate) is_register_fence: bool,
/// See [`Self::is_register_fence`]
pub(crate) done_waiting_for_register_fences: bool,
pub(crate) mop: SimValue<MOpInstance<RenamedMOp<C>>>,
pub(crate) unit_index: usize,
pub(crate) mop_in_unit_state: MOpInUnitState,
pub(crate) is_speculative: bool,
pub(crate) all_prior_mops_finished_and_or_caused_cancel: bool,
pub(crate) output: Option<SimValue<NextPcPredictorOp<C>>>,
pub(crate) caused_cancel: Option<SimValue<UnitCausedCancel<C>>>,
}
impl<C: PhantomConstCpuConfig> RobEntry<C> {
pub(crate) fn new(
mop: SimValue<MOpInstance<RenamedMOp<C>>>,
unit_index: usize,
is_register_fence: bool,
) -> Self {
Self {
mop,
is_register_fence,
done_waiting_for_register_fences: false,
unit_index,
mop_in_unit_state: MOpInUnitState::NotYetEnqueued,
is_speculative: true,
all_prior_mops_finished_and_or_caused_cancel: false,
output: None,
caused_cancel: None,
}
}
fn dest_reg(&self) -> Option<&SimValue<PRegNum<C>>> {
let dest_reg = MOpTrait::dest_reg_sim_ref(self.mop.mop.inner());
let unit_index = UnitNum::index_sim(&dest_reg.unit_num)?;
assert_eq!(unit_index, self.unit_index);
Some(dest_reg)
}
fn unit_out_reg(&self) -> Option<&SimValue<UnitOutRegNum<C>>> {
Some(&self.dest_reg()?.unit_out_reg)
}
pub(crate) fn unit_out_reg_index(&self) -> Option<usize> {
Some(UnitOutRegNum::value_sim(self.unit_out_reg()?))
}
#[hdl]
fn debug_state(&self, config: C) -> SimValue<RobEntryDebugState<C>> {
let Self {
is_register_fence,
done_waiting_for_register_fences,
mop,
unit_index,
mop_in_unit_state,
is_speculative,
all_prior_mops_finished_and_or_caused_cancel,
output,
caused_cancel,
} = self;
let ret_ty = RobEntryDebugState[config];
#[hdl(sim)]
RobEntryDebugState::<C> {
is_register_fence,
done_waiting_for_register_fences,
mop,
unit_index: unit_index.into_sim_value_with_type(ret_ty.unit_index),
mop_in_unit_state: SimOnlyValue::new(*mop_in_unit_state),
is_speculative,
all_prior_mops_finished_and_or_caused_cancel,
output: output.into_sim_value_with_type(ret_ty.output),
caused_cancel: caused_cancel.into_sim_value_with_type(ret_ty.caused_cancel),
}
}
#[hdl]
fn for_each_reg<S>(
&self,
mut shared_state: S,
mut l1_reg: impl FnMut(&mut S, &SimValue<PRegNum<C>>),
mut l2_reg: impl FnMut(&mut S, &SimValue<L2RegNum>),
) {
l1_reg(
&mut shared_state,
MOpTrait::dest_reg_sim_ref(self.mop.mop.inner()),
);
MOpTrait::for_each_src_reg_sim_ref(self.mop.mop.inner(), &mut |l1, _| {
l1_reg(&mut shared_state, l1);
});
#[hdl(sim)]
if let RenamedMOp::<_>::TransformedMove(mop) = self.mop.mop.inner() {
#[hdl(sim)]
match mop {
L2RegisterFileMOp::<_, _>::ReadL2Reg(v) => {
l2_reg(&mut shared_state, &v.common.imm);
}
L2RegisterFileMOp::<_, _>::WriteL2Reg(v) => {
l2_reg(&mut shared_state, &v.common.imm);
}
}
}
}
}
#[hdl]
struct RobEntriesDebugState {
unrenamed: MOpInstance<MOp>,
/// number of renamed &micro;Ops that this unrenamed &micro;Op corresponds to
renamed_entries_len: UInt<8>,
}
impl SimValueDefault for RobEntriesDebugState {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
unrenamed,
renamed_entries_len,
} = self;
#[hdl(sim)]
Self {
unrenamed: zeroed(unrenamed),
renamed_entries_len: renamed_entries_len.sim_value_default(),
}
}
}
#[derive(Debug)]
struct RobEntries<C: PhantomConstCpuConfig> {
unrenamed: SimValue<MOpInstance<MOp>>,
rename_table_updates: Vec<RenameTableUpdate<C>>,
renamed_entries: VecDeque<RobEntry<C>>,
}
impl<C: PhantomConstCpuConfig> RobEntries<C> {
#[hdl]
fn debug_state(&self) -> SimValue<RobEntriesDebugState> {
let Self {
unrenamed,
rename_table_updates: _,
renamed_entries,
} = self;
#[hdl(sim)]
RobEntriesDebugState {
unrenamed,
renamed_entries_len: u8::try_from(renamed_entries.len())
.expect("renamed_entries.len() should fit in u8"),
}
}
}
#[hdl(no_static)]
pub(crate) struct ReorderBufferDebugState<C: PhantomConstGet<CpuConfig>> {
next_renamed_mop_id: MOpId,
entries: ArrayVec<RobEntriesDebugState, CpuConfigRobSize<C>>,
incomplete_back_entry: HdlOption<RobEntriesDebugState>,
renamed: ArrayVec<RobEntryDebugState<C>, CpuConfigRobSize<C>>,
l2_reg_ref_counts:
ArrayType<UIntInRangeInclusiveType<ConstUsize<0>, CpuConfigRobSize<C>>, L2RegFileLen<C>>,
config: C,
}
impl<C: PhantomConstCpuConfig> SimValueDefault for ReorderBufferDebugState<C> {
#[hdl]
fn sim_value_default(self) -> SimValue<Self> {
let Self {
next_renamed_mop_id,
entries,
incomplete_back_entry,
renamed,
l2_reg_ref_counts,
config,
} = self;
#[hdl(sim)]
Self {
next_renamed_mop_id: next_renamed_mop_id.sim_value_default(),
entries: entries.sim_value_default(),
incomplete_back_entry: incomplete_back_entry.sim_value_default(),
renamed: renamed.sim_value_default(),
l2_reg_ref_counts: zeroed(l2_reg_ref_counts),
config,
}
}
}
#[derive(Debug)]
pub(crate) struct ReorderBuffer<C: PhantomConstCpuConfig> {
next_renamed_mop_id: SimValue<MOpId>,
entries: VecDeque<RobEntries<C>>,
incomplete_back_entry: Option<RobEntries<C>>,
l2_reg_ref_counts: Box<[usize]>,
config: C,
}
impl<C: PhantomConstCpuConfig> ReorderBuffer<C> {
pub(crate) fn new(config: C) -> Self {
Self {
next_renamed_mop_id: MOpId.zero().into_sim_value(),
entries: VecDeque::new(),
incomplete_back_entry: None,
l2_reg_ref_counts: vec![0; L2RegNum.l2_reg_count()].into_boxed_slice(),
config,
}
}
#[hdl]
pub(crate) fn l2_reg_ref_counts(&self) -> &[usize] {
let mut expected = vec![0usize; L2RegNum.l2_reg_count()];
for entry in self.renamed() {
#[hdl(sim)]
if let RenamedMOp::<_>::TransformedMove(mop) = entry.mop.mop.inner() {
let l2_reg = #[hdl(sim)]
match mop {
L2RegisterFileMOp::<_, _>::ReadL2Reg(v) => &v.common.imm,
L2RegisterFileMOp::<_, _>::WriteL2Reg(v) => &v.common.imm,
};
expected[L2RegNum::value_sim(l2_reg)] += 1;
}
}
assert_eq!(*expected, *self.l2_reg_ref_counts);
&self.l2_reg_ref_counts
}
#[hdl]
pub(crate) fn debug_state(&self) -> SimValue<ReorderBufferDebugState<C>> {
let Self {
next_renamed_mop_id,
entries,
incomplete_back_entry,
l2_reg_ref_counts,
config,
} = self;
let ty = ReorderBufferDebugState[*config];
#[hdl(sim)]
ReorderBufferDebugState::<_> {
next_renamed_mop_id,
entries: ty
.entries
.from_iter_sim(
zeroed(StaticType::TYPE),
entries.iter().map(RobEntries::debug_state),
)
.expect("known to fit"),
incomplete_back_entry: incomplete_back_entry.as_ref().map(|v| v.debug_state()),
renamed: ty
.renamed
.from_iter_sim(
ty.renamed.element().sim_value_default(),
self.renamed().map(|v| v.debug_state(*config)),
)
.ok()
.expect("known to fit"),
l2_reg_ref_counts: l2_reg_ref_counts.to_sim_value_with_type(ty.l2_reg_ref_counts),
config,
}
}
pub(crate) fn unrenamed_len(&self) -> usize {
self.entries.len()
}
pub(crate) fn unrenamed(
&self,
) -> impl DoubleEndedIterator<Item = &SimValue<MOpInstance<MOp>>> + Clone {
self.entries.iter().map(|v| &v.unrenamed)
}
fn retire_groups_unrenamed_ranges(
&self,
) -> impl Clone + Iterator<Item = std::ops::Range<usize>> {
let mut next_group_start = 0;
self.entries
.iter()
.enumerate()
.filter_map(move |(index, entry)| {
if *entry.unrenamed.is_last_mop_in_insn {
let group_start = next_group_start;
next_group_start = index + 1;
Some(group_start..next_group_start)
} else {
None
}
})
}
pub(crate) fn retire_groups(
&self,
) -> impl Clone + Iterator<Item: DoubleEndedIterator<Item = &VecDeque<RobEntry<C>>> + Clone>
{
self.retire_groups_unrenamed_ranges().map(|range| {
self.entries
.range(range)
.map(|entries| &entries.renamed_entries)
})
}
pub(crate) fn renamed_len(&self) -> usize {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
l2_reg_ref_counts: _,
config: _,
} = self;
entries
.iter()
.chain(incomplete_back_entry)
.map(|entries| entries.renamed_entries.len())
.sum()
}
pub(crate) fn renamed(&self) -> impl DoubleEndedIterator<Item = &RobEntry<C>> + Clone {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
l2_reg_ref_counts: _,
config: _,
} = self;
entries
.iter()
.chain(incomplete_back_entry)
.flat_map(|entries| &entries.renamed_entries)
}
pub(crate) fn renamed_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut RobEntry<C>> {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
l2_reg_ref_counts: _,
config: _,
} = self;
entries
.iter_mut()
.chain(incomplete_back_entry)
.flat_map(|entries| &mut entries.renamed_entries)
}
fn try_renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> Option<&mut RobEntry<C>> {
self.renamed_mut().find(|v| v.mop.id == *id)
}
pub(crate) fn renamed_by_id_mut(&mut self, id: &SimValue<MOpId>) -> &mut RobEntry<C> {
match self.try_renamed_by_id_mut(id) {
Some(v) => v,
None => panic!("MOpId not found: {id:?}"),
}
}
pub(crate) fn renamed_push_back_with_new_id(
&mut self,
unrenamed: &SimValue<MOpInstance<MOp>>,
mut renamed: RobEntry<C>,
) -> &SimValue<MOpId> {
let replacement_id = self
.next_renamed_mop_id
.as_int()
.wrapping_add(1)
.into_sim_value();
renamed.mop.id = mem::replace(&mut self.next_renamed_mop_id, replacement_id);
println!("renamed_push_back_with_new_id: {:?}", renamed.mop);
renamed.for_each_reg(
(),
|(), _l1| {},
|(), l2| self.l2_reg_ref_counts[L2RegNum::value_sim(l2)] += 1,
);
let renamed_entries = &mut self
.incomplete_back_entry
.get_or_insert_with(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
})
.renamed_entries;
renamed_entries.push_back(renamed);
&renamed_entries.back().expect("just pushed").mop.id
}
pub(crate) fn has_incomplete_back_entry(&self) -> bool {
self.incomplete_back_entry.is_some()
}
pub(crate) fn finished_unrenamed_push_back(&mut self, unrenamed: &SimValue<MOpInstance<MOp>>) {
let entry = self
.incomplete_back_entry
.take()
.unwrap_or_else(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
});
self.entries.push_back(entry);
}
pub(crate) fn clear(&mut self) {
let Self {
next_renamed_mop_id: _,
entries,
incomplete_back_entry,
l2_reg_ref_counts,
config: _,
} = self;
entries.clear();
l2_reg_ref_counts.fill(0);
*incomplete_back_entry = None;
}
pub(crate) fn unrenamed_back_append_rename_table_update(
&mut self,
unrenamed: &SimValue<MOpInstance<MOp>>,
update: RenameTableUpdate<C>,
) {
self.incomplete_back_entry
.get_or_insert_with(|| RobEntries {
unrenamed: unrenamed.clone(),
rename_table_updates: Vec::new(),
renamed_entries: VecDeque::new(),
})
.rename_table_updates
.push(update);
}
pub(crate) fn all_mops_are_finished_and_or_caused_cancel(&self) -> bool {
self.renamed().next().is_none_or(|entry| {
entry.all_prior_mops_finished_and_or_caused_cancel
&& entry.mop_in_unit_state.is_finished_and_or_caused_cancel()
})
}
pub(crate) fn entries_pop_front(
&mut self,
) -> Option<(Vec<RenameTableUpdate<C>>, VecDeque<RobEntry<C>>)> {
let RobEntries {
unrenamed: _,
rename_table_updates,
renamed_entries,
} = self.entries.pop_front()?;
for entry in &renamed_entries {
entry.for_each_reg(
(),
|(), _l1| {},
|(), l2| {
let ref_count = &mut self.l2_reg_ref_counts[L2RegNum::value_sim(l2)];
*ref_count = ref_count.checked_sub(1).unwrap_or_else(|| {
unreachable!("ReorderBuffer: l2 ref count went negative: {l2:?}")
});
},
);
}
Some((rename_table_updates, renamed_entries))
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff