From b5b1ee866c7664aa9818cac3f2a35bfbba1139a2 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 5 Sep 2025 03:35:40 -0700 Subject: [PATCH] converted to using get_state_part_kinds! --- crates/fayalite/src/sim.rs | 49 +- crates/fayalite/src/sim/compiler.rs | 3084 +++++++++--------- crates/fayalite/src/sim/interpreter.rs | 1386 +------- crates/fayalite/src/sim/interpreter/parts.rs | 864 ++++- crates/fayalite/src/util.rs | 1 + crates/fayalite/src/util/misc.rs | 15 + 6 files changed, 2551 insertions(+), 2848 deletions(-) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index d91427f..ffee3e7 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -21,9 +21,11 @@ use crate::{ CompiledValue, }, interpreter::{ - BreakAction, BreakpointsSet, RunResult, SmallUInt, State, StatePartIndex, - StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, TypeIndexRange, - TypeLen, + BreakAction, BreakpointsSet, RunResult, SmallUInt, State, + parts::{ + StatePartIndex, StatePartKindBigSlots, StatePartKindMemories, + StatePartKindSmallSlots, TypeIndexRange, TypeLenSingle, + }, }, time::{SimDuration, SimInstant}, value::SimValue, @@ -1325,14 +1327,15 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBitFn { type Output = bool; fn call(self, state: &mut interpreter::State) -> Self::Output { - match self.compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + match self.compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { state.small_slots[self.compiled_value.range.small_slots.start] != 0 } - TypeLen::A_BIG_SLOT => !state.big_slots[self.compiled_value.range.big_slots.start] + Some(TypeLenSingle::BigSlot) => !state.big_slots + [self.compiled_value.range.big_slots.start] .clone() .is_zero(), - _ => unreachable!(), + None => unreachable!(), } } } @@ -1347,13 +1350,13 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo fn call(self, state: &mut interpreter::State) -> Self::Output { let Self { compiled_value, io } = self; - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => Expr::ty(io) + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => Expr::ty(io) .value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]), - TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( + Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping( state.big_slots[compiled_value.range.big_slots.start].clone(), ), - _ => unreachable!(), + None => unreachable!(), } } } @@ -1980,14 +1983,14 @@ impl SimulationImpl { .get_module_mut(which_module) .write_helper(io, which_module); self.state_ready_to_run = true; - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { self.state.small_slots[compiled_value.range.small_slots.start] = value as _; } - TypeLen::A_BIG_SLOT => { + Some(TypeLenSingle::BigSlot) => { self.state.big_slots[compiled_value.range.big_slots.start] = value.into() } - _ => unreachable!(), + None => unreachable!(), } } #[track_caller] @@ -2013,18 +2016,18 @@ impl SimulationImpl { .write_helper(Expr::canonical(io), which_module); self.state_ready_to_run = true; let value: BigInt = value.into(); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => { + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => { let mut small_value = value.iter_u64_digits().next().unwrap_or(0); if value.is_negative() { small_value = small_value.wrapping_neg(); } self.state.small_slots[compiled_value.range.small_slots.start] = small_value; } - TypeLen::A_BIG_SLOT => { + Some(TypeLenSingle::BigSlot) => { self.state.big_slots[compiled_value.range.big_slots.start] = value } - _ => unreachable!(), + None => unreachable!(), } } #[track_caller] @@ -2053,20 +2056,20 @@ impl SimulationImpl { }; let bit_indexes = start_bit_index..start_bit_index + compiled_value.layout.ty.bit_width(); - match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => read_write_small_scalar( + match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => read_write_small_scalar( signed, bit_indexes, bits, &mut state.small_slots[compiled_value.range.small_slots.start], ), - TypeLen::A_BIG_SLOT => read_write_big_scalar( + Some(TypeLenSingle::BigSlot) => read_write_big_scalar( signed, bit_indexes, bits, &mut state.big_slots[compiled_value.range.big_slots.start], ), - _ => unreachable!(), + None => unreachable!(), } } CompiledTypeLayoutBody::Array { element } => { diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs index dd06267..e58ca04 100644 --- a/crates/fayalite/src/sim/compiler.rs +++ b/crates/fayalite/src/sim/compiler.rs @@ -32,15 +32,18 @@ use crate::{ TraceScope, TraceSyncReset, TraceUInt, TraceWire, interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, InsnOrLabel, Insns, InsnsBuilding, - InsnsBuildingDone, InsnsBuildingKind, Label, MemoryData, SlotDebugData, SmallUInt, - StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, StatePartIndexRange, - StatePartKind, StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, - StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, - TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, + InsnsBuildingDone, InsnsBuildingKind, Label, SmallUInt, StatePartArrayIndex, + StatePartArrayIndexed, + parts::{ + MemoryData, SlotDebugData, StatePartIndex, StatePartIndexRange, StatePartKind, + StatePartKindBigSlots, StatePartKindMemories, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, + TypeLenSingle, get_state_part_kinds, + }, }, }, ty::StaticType, - util::HashMap, + util::{HashMap, chain}, }; use bitvec::vec::BitVec; use num_bigint::BigInt; @@ -51,7 +54,7 @@ use petgraph::{ IntoNodeIdentifiers, IntoNodeReferences, NodeRef, VisitMap, Visitable, }, }; -use std::{collections::BTreeSet, fmt, hash::Hash, marker::PhantomData, mem, ops::IndexMut}; +use std::{collections::BTreeSet, fmt, hash::Hash, mem}; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] enum CondBody { @@ -299,6 +302,177 @@ impl CompiledValue { } } +macro_rules! make_type_array_indexes { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexes { + $(pub(crate) $type_plural_field: Interned<[StatePartArrayIndex<$type_kind>]>,)* + } + + impl TypeArrayIndexes { + pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { + TypeArrayIndexesRef { + $($type_plural_field: &self.$type_plural_field,)* + } + } + #[must_use] + pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_plural_field: Interned::from_iter(self.$type_plural_field.iter().copied().chain([next.$type_plural_field])),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndex { + $(pub(crate) $type_plural_field: StatePartArrayIndex<$type_kind>,)* + } + + impl TypeArrayIndex { + pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { + Self { + $($type_plural_field: StatePartArrayIndex { + index, + len, + stride: stride.$type_plural_field, + },)* + } + } + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len; + $(assert_eq!(self.$type_plural_field.len, len, "array length mismatch");)* + len + } + pub(crate) fn index(self) -> StatePartIndex { + let index = self.small_slots.index; + $(assert_eq!(self.$type_plural_field.index, index, "array index mismatch");)* + index + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn stride(self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.stride,)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexesRef<'a> { + $(pub(crate) $type_plural_field: &'a [StatePartArrayIndex<$type_kind>],)* + } + + impl<'a> TypeArrayIndexesRef<'a> { + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len(); + $(assert_eq!(self.$type_plural_field.len(), len, "indexes count mismatch");)* + len + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn iter(self) -> impl Iterator + 'a { + (0..self.len()).map(move |i| TypeArrayIndex { + $($type_plural_field: self.$type_plural_field[i],)* + }) + } + pub(crate) fn for_each_offset( + self, + mut f: impl FnMut(TypeIndex), + ) { + self.for_each_offset2(TypeIndex { + $($type_plural_field: StatePartIndex::new(0),)* + }, &mut f); + } + pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { + $(let $type_plural_field = self.$type_plural_field.split_first()?;)* + let next = TypeArrayIndex { + $($type_plural_field: *$type_plural_field.0,)* + }; + let rest = TypeArrayIndexesRef { + $($type_plural_field: $type_plural_field.1,)* + }; + Some((next, rest)) + } + pub(crate) fn for_each_offset2( + self, + base_offset: TypeIndex, + f: &mut (impl FnMut(TypeIndex) + ?Sized), + ) { + if let Some((next, rest)) = self.split_first() { + let stride = next.stride(); + for index in 0..next.len().try_into().expect("array too big") { + let mut offset = TypeIndex { + $($type_plural_field: StatePartIndex::new( + stride + .$type_plural_field + .value + .checked_mul(index) + .expect("array too big"), + ),)* + }; + $(offset.$type_plural_field.value = + base_offset + .$type_plural_field + .value + .checked_add(offset.$type_plural_field.value) + .expect("array too big");)* + rest.for_each_offset2(offset, f); + } + } else { + $(assert!(self.$type_plural_field.is_empty(), "indexes count mismatch");)* + f(base_offset); + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndexed { + $(pub(crate) $type_plural_field: StatePartArrayIndexed<$type_kind>,)* + } + + impl TypeArrayIndexed { + pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { + Self { + $($type_plural_field: StatePartArrayIndexed { + base: base.$type_plural_field, + indexes: indexes.$type_plural_field, + },)* + } + } + pub(crate) fn base(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.base,)* + } + } + pub(crate) fn indexes(self) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_plural_field: self.$type_plural_field.indexes,)* + } + } + } + + impl From for TypeArrayIndexed { + fn from(value: TypeIndex) -> Self { + TypeArrayIndexed { + $($type_plural_field: value.$type_plural_field.into(),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_type_array_indexes! { + type_plural_fields; + type_kinds; + } +} + #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExpr { static_part: CompiledValue, @@ -391,1145 +565,965 @@ impl CompiledExpr { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentOrSlotIndex { - AssignmentIndex(usize), - SmallSlot(StatePartIndex), - BigSlot(StatePartIndex), -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentIO { - BigInput { - assignment_index: usize, - slot: StatePartIndex, - }, - SmallInput { - assignment_index: usize, - slot: StatePartIndex, - }, - BigOutput { - assignment_index: usize, - slot: StatePartIndex, - }, - SmallOutput { - assignment_index: usize, - slot: StatePartIndex, - }, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -enum AssignmentsEdge { - IO(AssignmentIO), - AssignmentImmediatePredecessor { - predecessor_assignment_index: usize, - assignment_index: usize, - }, -} - -#[derive(Debug)] -enum Assignments { - Accumulating { - assignments: Vec, - }, - Finalized { - assignments: Box<[Assignment]>, - slots_layout: TypeLayout, - slot_readers: SlotToAssignmentIndexFullMap, - slot_writers: SlotToAssignmentIndexFullMap, - assignment_immediate_predecessors: Box<[Box<[usize]>]>, - assignment_immediate_successors: Box<[Box<[usize]>]>, - }, -} - -impl Default for Assignments { - fn default() -> Self { - Self::Accumulating { - assignments: Vec::new(), +macro_rules! make_assignment_graph { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + array_indexed_variants = [$($array_indexed_variant:ident,)*]; + input_variants = [$($input_variant:ident,)*]; + output_variants = [$($output_variant:ident,)*]; + ) => { + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentOrSlotIndex { + AssignmentIndex(usize), + $($type_singular_variant(StatePartIndex<$type_kind>),)* } - } -} -impl Assignments { - fn finalize(&mut self, slots_layout: TypeLayout) { - let Self::Accumulating { assignments } = self else { - unreachable!("already finalized"); - }; - let assignments = mem::take(assignments).into_boxed_slice(); - let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); - let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); - let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; - let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; - for (assignment_index, assignment) in assignments.iter().enumerate() { - slot_readers - .keys_for_assignment(assignment_index) - .extend([&assignment.inputs]); - slot_readers - .keys_for_assignment(assignment_index) - .extend(&assignment.conditions); - let SlotSet(TypeParts { - small_slots, - big_slots, - }) = &assignment.outputs; - for &slot in small_slots { - if let Some(&pred) = slot_writers[slot].last() { - assignment_immediate_predecessors[assignment_index].insert(pred); - assignment_immediate_successors[pred].insert(assignment_index); - } - slot_writers[slot].push(assignment_index); - } - for &slot in big_slots { - if let Some(&pred) = slot_writers[slot].last() { - assignment_immediate_predecessors[assignment_index].insert(pred); - assignment_immediate_successors[pred].insert(assignment_index); - } - slot_writers[slot].push(assignment_index); - } + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentIO { + $($input_variant { + assignment_index: usize, + slot: StatePartIndex<$type_kind>, + },)* + $($output_variant { + assignment_index: usize, + slot: StatePartIndex<$type_kind>, + },)* } - *self = Self::Finalized { - assignments, - slots_layout, - slot_readers, - slot_writers, - assignment_immediate_predecessors: assignment_immediate_predecessors - .into_iter() - .map(Box::from_iter) - .collect(), - assignment_immediate_successors: assignment_immediate_successors - .into_iter() - .map(Box::from_iter) - .collect(), - }; - } - fn push(&mut self, v: Assignment) { - let Self::Accumulating { assignments } = self else { - unreachable!("already finalized"); - }; - assignments.push(v); - } - fn assignments(&self) -> &[Assignment] { - let Self::Finalized { assignments, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - assignments - } - fn slots_layout(&self) -> TypeLayout { - let Self::Finalized { slots_layout, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - *slots_layout - } - fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { - let Self::Finalized { slot_readers, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - slot_readers - } - fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { - let Self::Finalized { slot_writers, .. } = self else { - unreachable!("Assignments::finalize should have been called"); - }; - slot_writers - } - fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] { - let Self::Finalized { - assignment_immediate_predecessors, - .. - } = self - else { - unreachable!("Assignments::finalize should have been called"); - }; - assignment_immediate_predecessors - } - fn assignment_immediate_successors(&self) -> &[Box<[usize]>] { - let Self::Finalized { - assignment_immediate_successors, - .. - } = self - else { - unreachable!("Assignments::finalize should have been called"); - }; - assignment_immediate_successors - } - fn elements(&self) -> AssignmentsElements<'_> { - let SlotToAssignmentIndexFullMap(TypeParts { - small_slots, - big_slots, - }) = self.slot_readers(); - AssignmentsElements { - node_indexes: HashMap::with_capacity_and_hasher( - self.assignments().len() + small_slots.len() + big_slots.len(), - Default::default(), - ), - nodes: self.node_references(), - edges: self.edge_references(), - } - } -} -impl GraphBase for Assignments { - type EdgeId = AssignmentsEdge; - type NodeId = AssignmentOrSlotIndex; -} - -#[derive(Debug, Clone, Copy)] -enum AssignmentsNodeRef<'a> { - Assignment { - index: usize, - #[allow(dead_code, reason = "used in Debug impl")] - assignment: &'a Assignment, - }, - SmallSlot( - StatePartIndex, - #[allow(dead_code, reason = "used in Debug impl")] SlotDebugData, - ), - BigSlot( - StatePartIndex, - #[allow(dead_code, reason = "used in Debug impl")] SlotDebugData, - ), -} - -impl<'a> NodeRef for AssignmentsNodeRef<'a> { - type NodeId = AssignmentOrSlotIndex; - type Weight = AssignmentsNodeRef<'a>; - - fn id(&self) -> Self::NodeId { - match *self { - AssignmentsNodeRef::Assignment { - index, - assignment: _, - } => AssignmentOrSlotIndex::AssignmentIndex(index), - AssignmentsNodeRef::SmallSlot(slot, _) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsNodeRef::BigSlot(slot, _) => AssignmentOrSlotIndex::BigSlot(slot), - } - } - - fn weight(&self) -> &Self::Weight { - self - } -} - -impl<'a> petgraph::visit::Data for &'a Assignments { - type NodeWeight = AssignmentsNodeRef<'a>; - type EdgeWeight = AssignmentsEdge; -} - -struct AssignmentsElements<'a> { - node_indexes: HashMap, - nodes: AssignmentsNodes<'a>, - edges: AssignmentsEdges<'a>, -} - -impl<'a> Iterator for AssignmentsElements<'a> { - type Item = petgraph::data::Element< - <&'a Assignments as petgraph::visit::Data>::NodeWeight, - <&'a Assignments as petgraph::visit::Data>::EdgeWeight, - >; - - fn next(&mut self) -> Option { - let Self { - node_indexes, - nodes, - edges, - } = self; - if let Some(node) = nodes.next() { - node_indexes.insert(node.id(), node_indexes.len()); - return Some(petgraph::data::Element::Node { weight: node }); - } - let edge = edges.next()?; - Some(petgraph::data::Element::Edge { - source: node_indexes[&edge.source()], - target: node_indexes[&edge.target()], - weight: *edge.weight(), - }) - } -} - -#[derive(Clone)] -struct AssignmentsNodeIdentifiers { - assignment_indexes: std::ops::Range, - small_slots: std::ops::Range, - big_slots: std::ops::Range, -} - -impl AssignmentsNodeIdentifiers { - fn internal_iter<'a>(&'a mut self) -> impl Iterator + 'a { - let Self { - assignment_indexes, - small_slots, - big_slots, - } = self; - assignment_indexes - .map(AssignmentOrSlotIndex::AssignmentIndex) - .chain(small_slots.map(|value| { - AssignmentOrSlotIndex::SmallSlot(StatePartIndex { - value, - _phantom: PhantomData, - }) - })) - .chain(big_slots.map(|value| { - AssignmentOrSlotIndex::BigSlot(StatePartIndex { - value, - _phantom: PhantomData, - }) - })) - } -} - -impl Iterator for AssignmentsNodeIdentifiers { - type Item = AssignmentOrSlotIndex; - fn next(&mut self) -> Option { - self.internal_iter().next() - } - - fn nth(&mut self, n: usize) -> Option { - self.internal_iter().nth(n) - } -} - -impl<'a> IntoNodeIdentifiers for &'a Assignments { - type NodeIdentifiers = AssignmentsNodeIdentifiers; - - fn node_identifiers(self) -> Self::NodeIdentifiers { - let TypeLen { - small_slots, - big_slots, - } = self.slot_readers().len(); - AssignmentsNodeIdentifiers { - assignment_indexes: 0..self.assignments().len(), - small_slots: 0..small_slots.value, - big_slots: 0..big_slots.value, - } - } -} - -struct AssignmentsNodes<'a> { - assignments: &'a Assignments, - nodes: AssignmentsNodeIdentifiers, -} - -impl<'a> Iterator for AssignmentsNodes<'a> { - type Item = AssignmentsNodeRef<'a>; - - fn next(&mut self) -> Option { - self.nodes.next().map(|node| match node { - AssignmentOrSlotIndex::AssignmentIndex(index) => AssignmentsNodeRef::Assignment { - index, - assignment: &self.assignments.assignments()[index], + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] + enum AssignmentsEdge { + IO(AssignmentIO), + AssignmentImmediatePredecessor { + predecessor_assignment_index: usize, + assignment_index: usize, }, - AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNodeRef::SmallSlot( - slot, - *self.assignments.slots_layout().small_slots.debug_data(slot), - ), - AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNodeRef::BigSlot( - slot, - *self.assignments.slots_layout().big_slots.debug_data(slot), - ), - }) - } -} - -impl<'a> IntoNodeReferences for &'a Assignments { - type NodeRef = AssignmentsNodeRef<'a>; - type NodeReferences = AssignmentsNodes<'a>; - - fn node_references(self) -> Self::NodeReferences { - AssignmentsNodes { - assignments: self, - nodes: self.node_identifiers(), } - } -} -struct AssignmentsNeighborsDirected<'a> { - assignment_indexes: std::slice::Iter<'a, usize>, - small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, -} - -impl Iterator for AssignmentsNeighborsDirected<'_> { - type Item = AssignmentOrSlotIndex; - fn next(&mut self) -> Option { - let Self { - assignment_indexes, - small_slots, - big_slots, - } = self; - if let retval @ Some(_) = assignment_indexes - .next() - .copied() - .map(AssignmentOrSlotIndex::AssignmentIndex) - { - retval - } else if let retval @ Some(_) = small_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::SmallSlot) - { - retval - } else if let retval @ Some(_) = big_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::BigSlot) - { - retval - } else { - None + #[derive(Debug)] + enum Assignments { + Accumulating { + assignments: Vec, + }, + Finalized { + assignments: Box<[Assignment]>, + slots_layout: TypeLayout, + slot_readers: SlotToAssignmentIndexFullMap, + slot_writers: SlotToAssignmentIndexFullMap, + assignment_immediate_predecessors: Box<[Box<[usize]>]>, + assignment_immediate_successors: Box<[Box<[usize]>]>, + }, } - } -} -impl<'a> IntoNeighbors for &'a Assignments { - type Neighbors = AssignmentsNeighborsDirected<'a>; + impl Default for Assignments { + fn default() -> Self { + Self::Accumulating { + assignments: Vec::new(), + } + } + } - fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { - self.neighbors_directed(n, petgraph::Direction::Outgoing) - } -} - -impl<'a> IntoNeighborsDirected for &'a Assignments { - type NeighborsDirected = AssignmentsNeighborsDirected<'a>; - - fn neighbors_directed( - self, - n: Self::NodeId, - d: petgraph::Direction, - ) -> Self::NeighborsDirected { - use petgraph::Direction::*; - let slot_map = match d { - Outgoing => self.slot_readers(), - Incoming => self.slot_writers(), - }; - match n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - let assignment = &self.assignments()[assignment_index]; - let ( - assignment_indexes, - SlotSet(TypeParts { - small_slots, - big_slots, - }), - ) = match d { - Outgoing => ( - &self.assignment_immediate_successors()[assignment_index], - &assignment.outputs, - ), - Incoming => ( - &self.assignment_immediate_predecessors()[assignment_index], - &assignment.inputs, - ), + impl Assignments { + fn finalize(&mut self, slots_layout: TypeLayout) { + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); }; - AssignmentsNeighborsDirected { - assignment_indexes: assignment_indexes.iter(), - small_slots: small_slots.iter(), - big_slots: big_slots.iter(), + let assignments = mem::take(assignments).into_boxed_slice(); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); + let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; + let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; + for (assignment_index, assignment) in assignments.iter().enumerate() { + slot_readers + .keys_for_assignment(assignment_index) + .extend([&assignment.inputs]); + slot_readers + .keys_for_assignment(assignment_index) + .extend(&assignment.conditions); + $(for &slot in &assignment.outputs.$type_plural_field { + if let Some(&pred) = slot_writers[slot].last() { + assignment_immediate_predecessors[assignment_index].insert(pred); + assignment_immediate_successors[pred].insert(assignment_index); + } + slot_writers[slot].push(assignment_index); + })* } + *self = Self::Finalized { + assignments, + slots_layout, + slot_readers, + slot_writers, + assignment_immediate_predecessors: assignment_immediate_predecessors + .into_iter() + .map(Box::from_iter) + .collect(), + assignment_immediate_successors: assignment_immediate_successors + .into_iter() + .map(Box::from_iter) + .collect(), + }; } - AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNeighborsDirected { - assignment_indexes: slot_map[slot].iter(), - small_slots: Default::default(), - big_slots: Default::default(), - }, - AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNeighborsDirected { - assignment_indexes: slot_map[slot].iter(), - small_slots: Default::default(), - big_slots: Default::default(), - }, - } - } -} - -impl EdgeRef for AssignmentsEdge { - type NodeId = AssignmentOrSlotIndex; - type EdgeId = AssignmentsEdge; - type Weight = AssignmentsEdge; - - fn source(&self) -> Self::NodeId { - match *self { - AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::BigSlot(slot), - AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index, - assignment_index: _, - } => AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), - } - } - - fn target(&self) -> Self::NodeId { - match *self { - AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index, - slot: _, - }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::BigSlot(slot), - AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index: _, - slot, - }) => AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index: _, - assignment_index, - } => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - } - } - - fn weight(&self) -> &Self::Weight { - self - } - - fn id(&self) -> Self::EdgeId { - *self - } -} - -struct AssignmentsEdges<'a> { - assignments: &'a Assignments, - nodes: AssignmentsNodeIdentifiers, - outgoing_neighbors: Option<(AssignmentOrSlotIndex, AssignmentsNeighborsDirected<'a>)>, -} - -impl Iterator for AssignmentsEdges<'_> { - type Item = AssignmentsEdge; - - fn next(&mut self) -> Option { - loop { - if let Some((node, outgoing_neighbors)) = &mut self.outgoing_neighbors { - if let Some(outgoing_neighbor) = outgoing_neighbors.next() { - return Some(match (*node, outgoing_neighbor) { - ( - AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), - AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), - ) => unreachable!(), - ( - AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::AssignmentImmediatePredecessor { - predecessor_assignment_index, - assignment_index, - }, - ( - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentOrSlotIndex::SmallSlot(slot), - ) => AssignmentsEdge::IO(AssignmentIO::SmallOutput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - AssignmentOrSlotIndex::BigSlot(slot), - ) => AssignmentsEdge::IO(AssignmentIO::BigOutput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::SmallSlot(slot), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::IO(AssignmentIO::SmallInput { - assignment_index, - slot, - }), - ( - AssignmentOrSlotIndex::BigSlot(slot), - AssignmentOrSlotIndex::AssignmentIndex(assignment_index), - ) => AssignmentsEdge::IO(AssignmentIO::BigInput { - assignment_index, - slot, - }), - }); - } + fn push(&mut self, v: Assignment) { + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + assignments.push(v); } - let node = self.nodes.next()?; - self.outgoing_neighbors = Some(( - node, - self.assignments - .neighbors_directed(node, petgraph::Direction::Outgoing), - )); - } - } -} - -impl<'a> IntoEdgeReferences for &'a Assignments { - type EdgeRef = AssignmentsEdge; - type EdgeReferences = AssignmentsEdges<'a>; - - fn edge_references(self) -> Self::EdgeReferences { - AssignmentsEdges { - assignments: self, - nodes: self.node_identifiers(), - outgoing_neighbors: None, - } - } -} - -struct AssignmentsVisitMap { - assignments: Vec, - slots: DenseSlotSet, -} - -impl VisitMap for AssignmentsVisitMap { - fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { - match n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - !mem::replace(&mut self.assignments[assignment_index], true) + fn assignments(&self) -> &[Assignment] { + let Self::Finalized { assignments, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + assignments } - AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.insert(slot), - AssignmentOrSlotIndex::BigSlot(slot) => self.slots.insert(slot), - } - } - - fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { - match *n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - self.assignments[assignment_index] + fn slots_layout(&self) -> TypeLayout { + let Self::Finalized { slots_layout, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + *slots_layout } - AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.contains(slot), - AssignmentOrSlotIndex::BigSlot(slot) => self.slots.contains(slot), - } - } - - fn unvisit(&mut self, n: AssignmentOrSlotIndex) -> bool { - match n { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - mem::replace(&mut self.assignments[assignment_index], false) + fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { + let Self::Finalized { slot_readers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_readers } - AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.remove(slot), - AssignmentOrSlotIndex::BigSlot(slot) => self.slots.remove(slot), - } - } -} - -impl Visitable for Assignments { - type Map = AssignmentsVisitMap; - - fn visit_map(self: &Self) -> Self::Map { - AssignmentsVisitMap { - assignments: vec![false; self.assignments().len()], - slots: DenseSlotSet::new(self.slot_readers().len()), - } - } - - fn reset_map(self: &Self, map: &mut Self::Map) { - let AssignmentsVisitMap { assignments, slots } = map; - assignments.clear(); - assignments.resize(self.assignments().len(), false); - if slots.len() != self.slot_readers().len() { - *slots = DenseSlotSet::new(self.slot_readers().len()); - } else { - slots.clear(); - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -struct DenseSlotSet(TypeParts); - -impl DenseSlotSet { - fn new(len: TypeLen) -> Self { - let TypeLen { - small_slots, - big_slots, - } = len; - Self(TypeParts { - small_slots: vec![false; small_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - big_slots: vec![false; big_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - }) - } - fn len(&self) -> TypeLen { - TypeLen { - small_slots: StatePartLen { - value: self.0.small_slots.len() as _, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: self.0.big_slots.len() as _, - _phantom: PhantomData, - }, - } - } - fn clear(&mut self) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.fill(false); - big_slots.fill(false); - } -} - -impl StatePartsValue for DenseSlotSet { - type Value = Box<[bool]>; -} - -trait DenseSlotSetMethods: Extend> { - fn contains(&self, k: StatePartIndex) -> bool; - fn remove(&mut self, k: StatePartIndex) -> bool { - self.take(k).is_some() - } - fn take(&mut self, k: StatePartIndex) -> Option>; - fn replace(&mut self, k: StatePartIndex) -> Option>; - fn insert(&mut self, k: StatePartIndex) -> bool { - self.replace(k).is_none() - } -} - -impl Extend> for DenseSlotSet -where - Self: DenseSlotSetMethods, -{ - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|v| { - self.insert(v); - }); - } -} - -impl DenseSlotSetMethods for DenseSlotSet { - fn contains(&self, k: StatePartIndex) -> bool { - self.0.small_slots[k.as_usize()] - } - - fn take( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(self.0.small_slots.get_mut(k.as_usize())?, false).then_some(k) - } - - fn replace( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(&mut self.0.small_slots[k.as_usize()], true).then_some(k) - } -} - -impl DenseSlotSetMethods for DenseSlotSet { - fn contains(&self, k: StatePartIndex) -> bool { - self.0.big_slots[k.as_usize()] - } - - fn take( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(self.0.big_slots.get_mut(k.as_usize())?, false).then_some(k) - } - - fn replace( - &mut self, - k: StatePartIndex, - ) -> Option> { - mem::replace(&mut self.0.big_slots[k.as_usize()], true).then_some(k) - } -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -struct SlotVec(TypeParts); - -impl SlotVec { - fn is_empty(&self) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.is_empty() && big_slots.is_empty() - } -} - -impl StatePartsValue for SlotVec { - type Value = Vec>; -} - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -struct SlotSet(TypeParts); - -impl SlotSet { - fn is_empty(&self) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.is_empty() && big_slots.is_empty() - } - fn for_each( - &self, - small_slots_fn: impl FnMut(StatePartIndex), - big_slots_fn: impl FnMut(StatePartIndex), - ) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().copied().for_each(small_slots_fn); - big_slots.iter().copied().for_each(big_slots_fn); - } - fn all( - &self, - small_slots_fn: impl FnMut(StatePartIndex) -> bool, - big_slots_fn: impl FnMut(StatePartIndex) -> bool, - ) -> bool { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().copied().all(small_slots_fn) - && big_slots.iter().copied().all(big_slots_fn) - } -} - -impl StatePartsValue for SlotSet { - type Value = BTreeSet>; -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.0.small_slots.extend(iter); - } -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.0.big_slots.extend(iter); - } -} - -impl Extend> for SlotSet -where - Self: Extend>, -{ - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().flat_map(|v| v.iter())); - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |TypeIndexRange { - small_slots, - big_slots, - }| { - self.extend(small_slots.iter()); - self.extend(big_slots.iter()); - }, - ) - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |TypeArrayIndex { - small_slots, - big_slots, - }| { - self.extend([small_slots]); - self.extend([big_slots]); - }, - ) - } -} - -impl Extend> for SlotSet { - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.index)); - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|cond_body| match cond_body { - CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { - self.extend([cond.range]); + fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { + let Self::Finalized { slot_writers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_writers } - CondBody::MatchArm { - discriminant, - variant_index: _, - } => self.extend([discriminant]), - }) - } -} - -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.body)) - } -} - -#[derive(Debug)] -struct Assignment { - inputs: SlotSet, - outputs: SlotSet, - conditions: Interned<[Cond]>, - insns: Vec, - source_location: SourceLocation, -} - -#[derive(Debug)] -struct SlotToAssignmentIndexFullMap(TypeParts); - -impl StatePartsValue for SlotToAssignmentIndexFullMap { - type Value = Box<[Vec]>; -} - -impl SlotToAssignmentIndexFullMap { - fn new(len: TypeLen) -> Self { - let TypeLen { - small_slots, - big_slots, - } = len; - Self(TypeParts { - small_slots: vec![Vec::new(); small_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - big_slots: vec![Vec::new(); big_slots.value.try_into().expect("length too big")] - .into_boxed_slice(), - }) - } - fn len(&self) -> TypeLen { - TypeLen { - small_slots: StatePartLen { - value: self.0.small_slots.len() as _, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: self.0.big_slots.len() as _, - _phantom: PhantomData, - }, - } - } - fn keys_for_assignment( - &mut self, - assignment_index: usize, - ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - SlotToAssignmentIndexFullMapKeysForAssignment { - map: self, - assignment_index, - } - } - fn for_each( - &self, - mut small_slots_fn: impl FnMut(StatePartIndex, &[usize]), - mut big_slots_fn: impl FnMut(StatePartIndex, &[usize]), - ) { - let Self(TypeParts { - small_slots, - big_slots, - }) = self; - small_slots.iter().enumerate().for_each(|(k, v)| { - small_slots_fn( - StatePartIndex { - value: k as _, - _phantom: PhantomData, - }, - v, - ) - }); - big_slots.iter().enumerate().for_each(|(k, v)| { - big_slots_fn( - StatePartIndex { - value: k as _, - _phantom: PhantomData, - }, - v, - ) - }); - } -} - -impl std::ops::Index> for SlotToAssignmentIndexFullMap { - type Output = Vec; - - fn index(&self, index: StatePartIndex) -> &Self::Output { - &self.0.small_slots[index.as_usize()] - } -} - -impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { - fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { - &mut self.0.small_slots[index.as_usize()] - } -} - -impl std::ops::Index> for SlotToAssignmentIndexFullMap { - type Output = Vec; - - fn index(&self, index: StatePartIndex) -> &Self::Output { - &self.0.big_slots[index.as_usize()] - } -} - -impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { - fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { - &mut self.0.big_slots[index.as_usize()] - } -} - -struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { - map: &'a mut SlotToAssignmentIndexFullMap, - assignment_index: usize, -} - -impl<'a, K: StatePartKind> Extend<&'a StatePartIndex> - for SlotToAssignmentIndexFullMapKeysForAssignment<'_> -where - Self: Extend>, -{ - fn extend>>(&mut self, iter: T) { - self.extend(iter.into_iter().copied()); - } -} - -impl Extend> - for SlotToAssignmentIndexFullMapKeysForAssignment<'_> -where - SlotToAssignmentIndexFullMap: IndexMut, Output = Vec>, -{ - fn extend>>(&mut self, iter: T) { - iter.into_iter() - .for_each(|slot| self.map[slot].push(self.assignment_index)); - } -} - -impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each( - |SlotSet(TypeParts { - small_slots, - big_slots, - })| { - self.extend(small_slots); - self.extend(big_slots); - }, - ); - } -} - -impl<'a> Extend<&'a Cond> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { - fn extend>(&mut self, iter: T) { - iter.into_iter().for_each(|cond| match cond.body { - CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { - let CompiledValue { - range: - TypeIndexRange { - small_slots, - big_slots, - }, - layout: _, - write: _, - } = cond; - self.extend(small_slots.iter()); - self.extend(big_slots.iter()); + fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_predecessors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_predecessors } - CondBody::MatchArm { - discriminant, - variant_index: _, - } => self.extend([discriminant]), - }); - } -} - -impl Assignment { - fn new( - conditions: Interned<[Cond]>, - insns: Vec, - source_location: SourceLocation, - ) -> Self { - let mut inputs = SlotSet::default(); - let mut outputs = SlotSet::default(); - for insn in &insns { - let insn = match insn { - InsnOrLabel::Insn(insn) => insn, - InsnOrLabel::Label(_) => continue, - }; - for InsnField { ty, kind } in insn.fields() { - match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { - inputs.extend([slot]); - } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { - inputs.extend([slot]); - } - ( - InsnFieldKind::Input, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => { - outputs.extend([slot]); - } - (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => { - outputs.extend([slot]); - } - ( - InsnFieldKind::Output, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| { - outputs.extend([slot]); - }); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| { - outputs.extend([slot]); - }); - inputs.extend(array_indexed.indexes); - } - ( - _, - InsnFieldType::Memory(_) - | InsnFieldType::SmallUInt(_) - | InsnFieldType::SmallSInt(_) - | InsnFieldType::InternedBigInt(_) - | InsnFieldType::U8(_) - | InsnFieldType::USize(_) - | InsnFieldType::Empty(_), - ) - | ( - InsnFieldKind::Immediate - | InsnFieldKind::Memory - | InsnFieldKind::BranchTarget, - _, - ) => {} + fn assignment_immediate_successors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_successors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_successors + } + fn elements(&self) -> AssignmentsElements<'_> { + let SlotToAssignmentIndexFullMap { + $($type_plural_field,)* + } = self.slot_readers(); + AssignmentsElements { + node_indexes: HashMap::with_capacity_and_hasher( + self.assignments().len() $(+ $type_plural_field.len())*, + Default::default(), + ), + nodes: self.node_references(), + edges: self.edge_references(), } } } - Self { - inputs, - outputs, - conditions, - insns, - source_location, + + impl GraphBase for Assignments { + type EdgeId = AssignmentsEdge; + type NodeId = AssignmentOrSlotIndex; } + + #[derive(Debug, Clone, Copy)] + enum AssignmentsNodeRef<'a> { + Assignment { + index: usize, + #[allow(dead_code, reason = "used in Debug impl")] + assignment: &'a Assignment, + }, + $($type_singular_variant( + StatePartIndex<$type_kind>, + #[allow(dead_code, reason = "used in Debug impl")] SlotDebugData, + ),)* + } + + impl<'a> NodeRef for AssignmentsNodeRef<'a> { + type NodeId = AssignmentOrSlotIndex; + type Weight = AssignmentsNodeRef<'a>; + + fn id(&self) -> Self::NodeId { + match *self { + AssignmentsNodeRef::Assignment { + index, + assignment: _, + } => AssignmentOrSlotIndex::AssignmentIndex(index), + $(AssignmentsNodeRef::$type_singular_variant(slot, _) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + } + } + + fn weight(&self) -> &Self::Weight { + self + } + } + + impl<'a> petgraph::visit::Data for &'a Assignments { + type NodeWeight = AssignmentsNodeRef<'a>; + type EdgeWeight = AssignmentsEdge; + } + + struct AssignmentsElements<'a> { + node_indexes: HashMap, + nodes: AssignmentsNodes<'a>, + edges: AssignmentsEdges<'a>, + } + + impl<'a> Iterator for AssignmentsElements<'a> { + type Item = petgraph::data::Element< + <&'a Assignments as petgraph::visit::Data>::NodeWeight, + <&'a Assignments as petgraph::visit::Data>::EdgeWeight, + >; + + fn next(&mut self) -> Option { + let Self { + node_indexes, + nodes, + edges, + } = self; + if let Some(node) = nodes.next() { + node_indexes.insert(node.id(), node_indexes.len()); + return Some(petgraph::data::Element::Node { weight: node }); + } + let edge = edges.next()?; + Some(petgraph::data::Element::Edge { + source: node_indexes[&edge.source()], + target: node_indexes[&edge.target()], + weight: *edge.weight(), + }) + } + } + + #[derive(Clone)] + struct AssignmentsNodeIdentifiers { + assignment_indexes: std::ops::Range, + $($type_plural_field: std::ops::Range,)* + } + + impl AssignmentsNodeIdentifiers { + fn internal_iter<'a>(&'a mut self) -> impl Iterator + 'a { + let Self { + assignment_indexes, + $($type_plural_field,)* + } = self; + assignment_indexes + .map(AssignmentOrSlotIndex::AssignmentIndex) + $(.chain($type_plural_field.map(|value| { + AssignmentOrSlotIndex::$type_singular_variant(StatePartIndex::new(value)) + })))* + } + } + + impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + self.internal_iter().next() + } + + fn nth(&mut self, n: usize) -> Option { + self.internal_iter().nth(n) + } + } + + impl<'a> IntoNodeIdentifiers for &'a Assignments { + type NodeIdentifiers = AssignmentsNodeIdentifiers; + + fn node_identifiers(self) -> Self::NodeIdentifiers { + let TypeLen { + $($type_plural_field,)* + } = self.slot_readers().len(); + AssignmentsNodeIdentifiers { + assignment_indexes: 0..self.assignments().len(), + $($type_plural_field: 0..$type_plural_field.value,)* + } + } + } + + struct AssignmentsNodes<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, + } + + impl<'a> Iterator for AssignmentsNodes<'a> { + type Item = AssignmentsNodeRef<'a>; + + fn next(&mut self) -> Option { + self.nodes.next().map(|node| match node { + AssignmentOrSlotIndex::AssignmentIndex(index) => AssignmentsNodeRef::Assignment { + index, + assignment: &self.assignments.assignments()[index], + }, + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => AssignmentsNodeRef::$type_singular_variant( + slot, + *self.assignments.slots_layout().$type_plural_field.debug_data(slot), + ),)* + }) + } + } + + impl<'a> IntoNodeReferences for &'a Assignments { + type NodeRef = AssignmentsNodeRef<'a>; + type NodeReferences = AssignmentsNodes<'a>; + + fn node_references(self) -> Self::NodeReferences { + AssignmentsNodes { + assignments: self, + nodes: self.node_identifiers(), + } + } + } + + struct AssignmentsNeighborsDirected<'a> { + assignment_indexes: std::slice::Iter<'a, usize>, + $($type_plural_field: std::collections::btree_set::Iter<'a, StatePartIndex<$type_kind>>,)* + } + + impl Iterator for AssignmentsNeighborsDirected<'_> { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + let Self { + assignment_indexes, + $($type_plural_field,)* + } = self; + if let retval @ Some(_) = assignment_indexes + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex) + { + retval + } $(else if let retval @ Some(_) = $type_plural_field + .next() + .copied() + .map(AssignmentOrSlotIndex::$type_singular_variant) + { + retval + })* else { + None + } + } + } + + impl<'a> IntoNeighbors for &'a Assignments { + type Neighbors = AssignmentsNeighborsDirected<'a>; + + fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { + self.neighbors_directed(n, petgraph::Direction::Outgoing) + } + } + + impl<'a> IntoNeighborsDirected for &'a Assignments { + type NeighborsDirected = AssignmentsNeighborsDirected<'a>; + + fn neighbors_directed( + self, + n: Self::NodeId, + d: petgraph::Direction, + ) -> Self::NeighborsDirected { + use petgraph::Direction::*; + let slot_map = match d { + Outgoing => self.slot_readers(), + Incoming => self.slot_writers(), + }; + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + let assignment = &self.assignments()[assignment_index]; + let ( + assignment_indexes, + SlotSet { + $($type_plural_field,)* + }, + ) = match d { + Outgoing => ( + &self.assignment_immediate_successors()[assignment_index], + &assignment.outputs, + ), + Incoming => ( + &self.assignment_immediate_predecessors()[assignment_index], + &assignment.inputs, + ), + }; + AssignmentsNeighborsDirected { + assignment_indexes: assignment_indexes.iter(), + $($type_plural_field: $type_plural_field.iter(),)* + } + } + AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + $($type_plural_field: Default::default(),)* + }, + AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + $($type_plural_field: Default::default(),)* + }, + } + } + } + + impl EdgeRef for AssignmentsEdge { + type NodeId = AssignmentOrSlotIndex; + type EdgeId = AssignmentsEdge; + type Weight = AssignmentsEdge; + + fn source(&self) -> Self::NodeId { + match *self { + $(AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + $(AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index),)* + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index: _, + } => AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + } + } + + fn target(&self) -> Self::NodeId { + match *self { + $(AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index),)* + $(AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::$type_singular_variant(slot),)* + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index: _, + assignment_index, + } => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + } + } + + fn weight(&self) -> &Self::Weight { + self + } + + fn id(&self) -> Self::EdgeId { + *self + } + } + + struct AssignmentsEdges<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, + outgoing_neighbors: Option<(AssignmentOrSlotIndex, AssignmentsNeighborsDirected<'a>)>, + } + + impl Iterator for AssignmentsEdges<'_> { + type Item = AssignmentsEdge; + + fn next(&mut self) -> Option { + loop { + if let Some((node, outgoing_neighbors)) = &mut self.outgoing_neighbors { + if let Some(outgoing_neighbor) = outgoing_neighbors.next() { + return Some(match (*node, outgoing_neighbor) { + ( + $(AssignmentOrSlotIndex::$type_singular_variant(_))|*, + $(AssignmentOrSlotIndex::$type_singular_variant(_))|*, + ) => unreachable!(), + ( + AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index, + }, + $(( + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentOrSlotIndex::$type_singular_variant(slot), + ) => AssignmentsEdge::IO(AssignmentIO::$output_variant { + assignment_index, + slot, + }),)* + $(( + AssignmentOrSlotIndex::$type_singular_variant(slot), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::IO(AssignmentIO::$input_variant { + assignment_index, + slot, + }),)* + }); + } + } + let node = self.nodes.next()?; + self.outgoing_neighbors = Some(( + node, + self.assignments + .neighbors_directed(node, petgraph::Direction::Outgoing), + )); + } + } + } + + impl<'a> IntoEdgeReferences for &'a Assignments { + type EdgeRef = AssignmentsEdge; + type EdgeReferences = AssignmentsEdges<'a>; + + fn edge_references(self) -> Self::EdgeReferences { + AssignmentsEdges { + assignments: self, + nodes: self.node_identifiers(), + outgoing_neighbors: None, + } + } + } + + struct AssignmentsVisitMap { + assignments: Vec, + slots: DenseSlotSet, + } + + impl VisitMap for AssignmentsVisitMap { + fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + !mem::replace(&mut self.assignments[assignment_index], true) + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.insert(slot),)* + } + } + + fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { + match *n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + self.assignments[assignment_index] + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.contains(slot),)* + } + } + + fn unvisit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + mem::replace(&mut self.assignments[assignment_index], false) + } + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => self.slots.remove(slot),)* + } + } + } + + impl Visitable for Assignments { + type Map = AssignmentsVisitMap; + + fn visit_map(self: &Self) -> Self::Map { + AssignmentsVisitMap { + assignments: vec![false; self.assignments().len()], + slots: DenseSlotSet::new(self.slot_readers().len()), + } + } + + fn reset_map(self: &Self, map: &mut Self::Map) { + let AssignmentsVisitMap { assignments, slots } = map; + assignments.clear(); + assignments.resize(self.assignments().len(), false); + if slots.len() != self.slot_readers().len() { + *slots = DenseSlotSet::new(self.slot_readers().len()); + } else { + slots.clear(); + } + } + } + + #[derive(Debug)] + struct Assignment { + inputs: SlotSet, + outputs: SlotSet, + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + } + + #[derive(Debug)] + struct SlotToAssignmentIndexFullMap { + $($type_plural_field: Box<[Vec]>,)* + } + + impl SlotToAssignmentIndexFullMap { + fn new(len: TypeLen) -> Self { + Self { + $($type_plural_field: vec![Vec::new(); len.$type_plural_field.value.try_into().expect("length too big")] + .into_boxed_slice(),)* + } + } + fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: StatePartLen::new(self.$type_plural_field.len() as _),)* + } + } + fn keys_for_assignment( + &mut self, + assignment_index: usize, + ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + SlotToAssignmentIndexFullMapKeysForAssignment { + map: self, + assignment_index, + } + } + fn for_each( + &self, + $(mut $type_plural_field: impl FnMut(StatePartIndex<$type_kind>, &[usize]),)* + ) { + $(self.$type_plural_field.iter().enumerate().for_each(|(k, v)| { + $type_plural_field(StatePartIndex::new(k as _), v) + });)* + } + } + + $(impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex<$type_kind>) -> &Self::Output { + &self.$type_plural_field[index.as_usize()] + } + } + + impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex<$type_kind>) -> &mut Self::Output { + &mut self.$type_plural_field[index.as_usize()] + } + })* + + struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { + map: &'a mut SlotToAssignmentIndexFullMap, + assignment_index: usize, + } + + $(impl<'a> Extend<&'a StatePartIndex<$type_kind>> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()); + } + })* + + $(impl Extend> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter() + .for_each(|slot| self.map[slot].push(self.assignment_index)); + } + })* + + impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |set| { + $(self.extend(&set.$type_plural_field);)* + }, + ); + } + } + + impl<'a> Extend<&'a Cond> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cond| match cond.body { + CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { + let CompiledValue { + range, + layout: _, + write: _, + } = cond; + $(self.extend(range.$type_plural_field.iter());)* + } + CondBody::MatchArm { + discriminant, + variant_index: _, + } => self.extend([discriminant]), + }); + } + } + + impl Assignment { + fn new( + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + ) -> Self { + let mut inputs = SlotSet::default(); + let mut outputs = SlotSet::default(); + for insn in &insns { + let insn = match insn { + InsnOrLabel::Insn(insn) => insn, + InsnOrLabel::Label(_) => continue, + }; + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + $((InsnFieldKind::Input, InsnFieldType::$type_singular_variant(&slot)) => { + inputs.extend([slot]); + })* + $(( + InsnFieldKind::Input, + InsnFieldType::$array_indexed_variant(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + })* + $((InsnFieldKind::Output, InsnFieldType::$type_singular_variant(&slot)) => { + outputs.extend([slot]); + })* + $(( + InsnFieldKind::Output, + InsnFieldType::$array_indexed_variant(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + })* + ( + _, + InsnFieldType::Memory(_) + | InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | ( + InsnFieldKind::Immediate + | InsnFieldKind::Memory + | InsnFieldKind::BranchTarget, + _, + ) => {} + } + } + } + Self { + inputs, + outputs, + conditions, + insns, + source_location, + } + } + } + }; +} + +get_state_part_kinds! { + make_assignment_graph! { + type_plural_fields; + type_singular_variants; + type_kinds; + array_indexed_variants; + #[custom] input_variants = [small_slot = SmallInput, big_slot = BigInput,]; + #[custom] output_variants = [small_slot = SmallOutput, big_slot = BigOutput,]; + } +} + +macro_rules! make_dense_slot_set { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + struct DenseSlotSet { + $($type_plural_field: Box<[bool]>,)* + } + + impl DenseSlotSet { + fn new(len: TypeLen) -> Self { + Self { + $($type_plural_field: vec![false; len.$type_plural_field.value.try_into().expect("length too big")] + .into_boxed_slice(),)* + } + } + fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: StatePartLen::new(self.$type_plural_field.len() as _),)* + } + } + fn clear(&mut self) { + $(self.$type_plural_field.fill(false);)* + } + } + + trait DenseSlotSetMethods: Extend> { + fn contains(&self, k: StatePartIndex) -> bool; + fn remove(&mut self, k: StatePartIndex) -> bool { + self.take(k).is_some() + } + fn take(&mut self, k: StatePartIndex) -> Option>; + fn replace(&mut self, k: StatePartIndex) -> Option>; + fn insert(&mut self, k: StatePartIndex) -> bool { + self.replace(k).is_none() + } + } + + impl Extend> for DenseSlotSet + where + Self: DenseSlotSetMethods, + { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|v| { + self.insert(v); + }); + } + } + + $(impl DenseSlotSetMethods<$type_kind> for DenseSlotSet { + fn contains(&self, k: StatePartIndex<$type_kind>) -> bool { + self.$type_plural_field[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex<$type_kind>, + ) -> Option> { + mem::replace(self.$type_plural_field.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex<$type_kind>, + ) -> Option> { + mem::replace(&mut self.$type_plural_field[k.as_usize()], true).then_some(k) + } + })* + }; +} + +get_state_part_kinds! { + make_dense_slot_set! { + type_plural_fields; + type_kinds; + } +} + +macro_rules! make_slot_vec { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] + struct SlotVec { + $($type_plural_field: Vec>,)* + } + + impl SlotVec { + fn is_empty(&self) -> bool { + true $(&& self.$type_plural_field.is_empty())* + } + } + }; +} + +get_state_part_kinds! { + make_slot_vec! { + type_plural_fields; + type_kinds; + } +} + +macro_rules! make_slot_set { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] + struct SlotSet { + $($type_plural_field: BTreeSet>,)* + } + + impl SlotSet { + fn is_empty(&self) -> bool { + true $(&& self.$type_plural_field.is_empty())* + } + fn for_each( + &self, + $($type_plural_field: impl FnMut(StatePartIndex<$type_kind>),)* + ) { + $(self.$type_plural_field.iter().copied().for_each($type_plural_field);)* + } + fn all( + &self, + $($type_plural_field: impl FnMut(StatePartIndex<$type_kind>) -> bool,)* + small_slots_fn: impl FnMut(StatePartIndex) -> bool, + big_slots_fn: impl FnMut(StatePartIndex) -> bool, + ) -> bool { + true $(&& self.$type_plural_field.iter().copied().all($type_plural_field))* + } + } + + $(impl Extend> for SlotSet { + fn extend>>( + &mut self, + iter: T, + ) { + self.$type_plural_field.extend(iter); + } + })* + + $(impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().flat_map(|v| v.iter())); + } + })* + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |range| { + $(self.extend(range.$type_plural_field.iter());)* + }, + ) + } + } + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |v| { + $(self.extend([v.$type_plural_field]);)* + }, + ) + } + } + + $(impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.index)); + } + })* + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cond_body| match cond_body { + CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { + self.extend([cond.range]); + } + CondBody::MatchArm { + discriminant, + variant_index: _, + } => self.extend([discriminant]), + }) + } + } + + impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.body)) + } + } + }; +} + +get_state_part_kinds! { + make_slot_set! { + type_plural_fields; + type_kinds; } } @@ -1663,6 +1657,395 @@ pub struct Compiler { dump_assignments_dot: Option>>, } +macro_rules! impl_compiler { + ( + type_plural_fields = [$($type_plural_field:ident,)*]; + type_singular_fields = [$($type_singular_field:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + copy_insns = [$($copy_insn:ident,)*]; + read_indexed_insns = [$($read_indexed_insn:ident,)*]; + write_indexed_insns = [$($write_indexed_insn:ident,)*]; + ) => { + impl Compiler { + fn make_trace_scalar_helper( + &mut self, + instantiated_module: InstantiatedModule, + target: MakeTraceDeclTarget, + source_location: SourceLocation, + $($type_singular_field: impl FnOnce(StatePartIndex<$type_kind>) -> SimTraceKind,)* + ) -> TraceLocation { + match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); + TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len().as_single() { + $(Some(TypeLenSingle::$type_singular_variant) => { + $type_singular_field(compiled_value.range.$type_plural_field.start) + })* + None => unreachable!(), + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.bit_width(), + }), + } + } + fn make_trace_scalar( + &mut self, + instantiated_module: InstantiatedModule, + target: MakeTraceDeclTarget, + name: Interned, + source_location: SourceLocation, + ) -> TraceDecl { + let flow = target.flow(); + match target.ty() { + CanonicalType::UInt(ty) => TraceUInt { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallUInt { index, ty }, + |index| SimTraceKind::BigUInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::SInt(ty) => TraceSInt { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallSInt { index, ty }, + |index| SimTraceKind::BigSInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::Bool(_) => TraceBool { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallBool { index }, + |index| SimTraceKind::BigBool { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(ty) => { + assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); + let location = match target { + MakeTraceDeclTarget::Expr(target) => { + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + })) + } + MakeTraceDeclTarget::Memory { + id, + depth, + stride, + start, + ty: _, + } => TraceLocation::Memory(TraceMemoryLocation { + id, + depth, + stride, + start, + len: ty.type_properties().bit_width, + }), + }; + TraceFieldlessEnum { + location, + name, + ty, + flow, + } + .into() + } + CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), + CanonicalType::AsyncReset(_) => TraceAsyncReset { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallAsyncReset { index }, + |index| SimTraceKind::BigAsyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::SyncReset(_) => TraceSyncReset { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallSyncReset { index }, + |index| SimTraceKind::BigSyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => TraceClock { + location: self.make_trace_scalar_helper( + instantiated_module, + target, + source_location, + |index| SimTraceKind::SmallClock { index }, + |index| SimTraceKind::BigClock { index }, + ), + name, + flow, + } + .into(), + } + } + fn compiled_expr_to_value( + &mut self, + expr: CompiledExpr, + source_location: SourceLocation, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + return retval; + } + assert!( + expr.static_part.layout.ty.is_passive(), + "invalid expression passed to compiled_expr_to_value -- type must be passive", + ); + let CompiledExpr { + static_part, + indexes, + } = expr; + let retval = if indexes.as_ref().is_empty() { + CompiledValue { + layout: static_part.layout, + range: static_part.range, + write: None, + } + } else { + let layout = static_part.layout.with_anonymized_debug_info(); + let retval = CompiledValue { + layout, + range: self.insns.allocate_variable(&layout.layout), + write: None, + }; + let TypeIndexRange { + $($type_plural_field,)* + } = retval.range; + self.add_assignment( + Interned::default(), + chain!( + $($type_plural_field + .iter() + .zip(static_part.range.$type_plural_field.iter()) + .map(|(dest, base)| Insn::$read_indexed_insn { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.$type_plural_field, + }, + }),)* + ), + source_location, + ); + retval + }; + self.compiled_exprs_to_values.insert(expr, retval); + retval + } + fn compile_simple_connect( + &mut self, + conditions: Interned<[Cond]>, + lhs: CompiledExpr, + rhs: CompiledValue, + source_location: SourceLocation, + ) { + let CompiledExpr { + static_part: lhs_static_part, + indexes, + } = lhs; + let (lhs_layout, lhs_range) = lhs_static_part.write(); + assert!( + lhs_layout.ty.is_passive(), + "invalid expression passed to compile_simple_connect -- type must be passive", + ); + self.add_assignment( + conditions, + chain!( + $(lhs_range.$type_plural_field + .iter() + .zip(rhs.range.$type_plural_field.iter()) + .map(|(base, src)| { + if indexes.$type_plural_field.is_empty() { + Insn::$copy_insn { dest: base, src } + } else { + Insn::$write_indexed_insn { + dest: StatePartArrayIndexed { + base, + indexes: indexes.$type_plural_field, + }, + src, + } + } + }),)* + ), + source_location, + ); + } + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().ty.clone().into()); + if let Some(DebugOpaque(dump_assignments_dot)) = &self.dump_assignments_dot { + let graph = + petgraph::graph::DiGraph::<_, _, usize>::from_elements(self.assignments.elements()); + dump_assignments_dot(&petgraph::dot::Dot::new(&graph)); + } + let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { + Ok(nodes) => nodes + .into_iter() + .filter_map(|n| match n { + AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), + _ => None, + }) + .collect(), + Err(e) => match e.node_id() { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( + "combinatorial logic cycle detected at: {}", + self.assignments.assignments()[assignment_index].source_location, + ), + $(AssignmentOrSlotIndex::$type_singular_variant(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.$type_plural_field.debug_data[slot.as_usize()].name, + ),)* + }, + }; + struct CondStackEntry<'a> { + cond: &'a Cond, + end_label: Label, + } + let mut cond_stack = Vec::>::new(); + for assignment_index in assignments_order { + let Assignment { + inputs: _, + outputs: _, + conditions, + insns, + source_location, + } = &self.assignments.assignments()[assignment_index]; + let mut same_len = 0; + for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() { + if entry.cond != cond { + break; + } + same_len = index + 1; + } + while cond_stack.len() > same_len { + let CondStackEntry { cond: _, end_label } = + cond_stack.pop().expect("just checked len"); + self.insns.define_label_at_next_insn(end_label); + } + for cond in &conditions[cond_stack.len()..] { + let end_label = self.insns.new_label(); + match cond.body { + CondBody::IfTrue { cond: cond_value } + | CondBody::IfFalse { cond: cond_value } => { + let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => ( + Insn::BranchIfSmallZero { + target: end_label.0, + value: cond_value.range.small_slots.start, + }, + Insn::BranchIfSmallNonZero { + target: end_label.0, + value: cond_value.range.small_slots.start, + }, + ), + Some(TypeLenSingle::BigSlot) => ( + Insn::BranchIfZero { + target: end_label.0, + value: cond_value.range.big_slots.start, + }, + Insn::BranchIfNonZero { + target: end_label.0, + value: cond_value.range.big_slots.start, + }, + ), + None => unreachable!(), + }; + self.insns.push( + if let CondBody::IfTrue { .. } = cond.body { + branch_if_zero + } else { + branch_if_non_zero + }, + cond.source_location, + ); + } + CondBody::MatchArm { + discriminant, + variant_index, + } => { + self.insns.push( + Insn::BranchIfSmallNeImmediate { + target: end_label.0, + lhs: discriminant, + rhs: variant_index as _, + }, + cond.source_location, + ); + } + } + cond_stack.push(CondStackEntry { cond, end_label }); + } + self.insns.extend(insns.iter().copied(), *source_location); + } + for CondStackEntry { cond: _, end_label } in cond_stack { + self.insns.define_label_at_next_insn(end_label); + } + } + } + }; +} + +get_state_part_kinds! { + impl_compiler! { + type_plural_fields; + type_singular_fields; + type_singular_variants; + type_kinds; + copy_insns; + read_indexed_insns; + write_indexed_insns; + } +} + impl Compiler { pub fn new(base_module: Interned>) -> Self { let original_base_module = base_module; @@ -1704,165 +2087,6 @@ impl Compiler { }); id } - fn make_trace_scalar_helper( - &mut self, - instantiated_module: InstantiatedModule, - target: MakeTraceDeclTarget, - source_location: SourceLocation, - small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, - big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, - ) -> TraceLocation { - match target { - MakeTraceDeclTarget::Expr(target) => { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); - TraceLocation::Scalar(self.new_sim_trace(match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), - TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), - _ => unreachable!(), - })) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty, - } => TraceLocation::Memory(TraceMemoryLocation { - id, - depth, - stride, - start, - len: ty.bit_width(), - }), - } - } - fn make_trace_scalar( - &mut self, - instantiated_module: InstantiatedModule, - target: MakeTraceDeclTarget, - name: Interned, - source_location: SourceLocation, - ) -> TraceDecl { - let flow = target.flow(); - match target.ty() { - CanonicalType::UInt(ty) => TraceUInt { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallUInt { index, ty }, - |index| SimTraceKind::BigUInt { index, ty }, - ), - name, - ty, - flow, - } - .into(), - CanonicalType::SInt(ty) => TraceSInt { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallSInt { index, ty }, - |index| SimTraceKind::BigSInt { index, ty }, - ), - name, - ty, - flow, - } - .into(), - CanonicalType::Bool(_) => TraceBool { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallBool { index }, - |index| SimTraceKind::BigBool { index }, - ), - name, - flow, - } - .into(), - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Enum(ty) => { - assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); - let location = match target { - MakeTraceDeclTarget::Expr(target) => { - let compiled_value = self.compile_expr(instantiated_module, target); - let compiled_value = - self.compiled_expr_to_value(compiled_value, source_location); - let discriminant = self.compile_enum_discriminant( - compiled_value.map_ty(Enum::from_canonical), - source_location, - ); - TraceLocation::Scalar(self.new_sim_trace(SimTraceKind::EnumDiscriminant { - index: discriminant, - ty, - })) - } - MakeTraceDeclTarget::Memory { - id, - depth, - stride, - start, - ty: _, - } => TraceLocation::Memory(TraceMemoryLocation { - id, - depth, - stride, - start, - len: ty.type_properties().bit_width, - }), - }; - TraceFieldlessEnum { - location, - name, - ty, - flow, - } - .into() - } - CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(), - CanonicalType::AsyncReset(_) => TraceAsyncReset { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallAsyncReset { index }, - |index| SimTraceKind::BigAsyncReset { index }, - ), - name, - flow, - } - .into(), - CanonicalType::SyncReset(_) => TraceSyncReset { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallSyncReset { index }, - |index| SimTraceKind::BigSyncReset { index }, - ), - name, - flow, - } - .into(), - CanonicalType::Reset(_) => unreachable!(), - CanonicalType::Clock(_) => TraceClock { - location: self.make_trace_scalar_helper( - instantiated_module, - target, - source_location, - |index| SimTraceKind::SmallClock { index }, - |index| SimTraceKind::BigClock { index }, - ), - name, - flow, - } - .into(), - } - } fn make_trace_decl_child( &mut self, instantiated_module: InstantiatedModule, @@ -2220,70 +2444,6 @@ impl Compiler { self.compiled_values.insert(target, retval); retval } - fn compiled_expr_to_value( - &mut self, - expr: CompiledExpr, - source_location: SourceLocation, - ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { - return retval; - } - assert!( - expr.static_part.layout.ty.is_passive(), - "invalid expression passed to compiled_expr_to_value -- type must be passive", - ); - let CompiledExpr { - static_part, - indexes, - } = expr; - let retval = if indexes.as_ref().is_empty() { - CompiledValue { - layout: static_part.layout, - range: static_part.range, - write: None, - } - } else { - let layout = static_part.layout.with_anonymized_debug_info(); - let retval = CompiledValue { - layout, - range: self.insns.allocate_variable(&layout.layout), - write: None, - }; - let TypeIndexRange { - small_slots, - big_slots, - } = retval.range; - self.add_assignment( - Interned::default(), - small_slots - .iter() - .zip(static_part.range.small_slots.iter()) - .map(|(dest, base)| Insn::ReadSmallIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - }) - .chain( - big_slots - .iter() - .zip(static_part.range.big_slots.iter()) - .map(|(dest, base)| Insn::ReadIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - }), - ), - source_location, - ); - retval - }; - self.compiled_exprs_to_values.insert(expr, retval); - retval - } fn add_assignment>( &mut self, conditions: Interned<[Cond]>, @@ -2302,7 +2462,7 @@ impl Compiler { let input = self.compile_expr(instantiated_module, input); let input = self.compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); - assert_eq!(input.range.len(), TypeLen::A_BIG_SLOT); + assert_eq!(input.range.len(), TypeLen::big_slot()); input.range.big_slots.start } fn compile_expr_helper( @@ -2333,7 +2493,7 @@ impl Compiler { make_insns: impl FnOnce(StatePartIndex) -> Vec, ) -> CompiledValue { self.compile_expr_helper(instantiated_module, dest_ty, |_, dest| { - assert_eq!(dest.len(), TypeLen::A_BIG_SLOT); + assert_eq!(dest.len(), TypeLen::big_slot()); make_insns(dest.big_slots.start) }) } @@ -2365,9 +2525,9 @@ impl Compiler { } let mut ty = compiled_value.layout.ty; ty.width = ty.width.min(SmallUInt::BITS as usize); - let retval = match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, - TypeLen::A_BIG_SLOT => { + let retval = match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => compiled_value.range.small_slots.start, + Some(TypeLenSingle::BigSlot) => { let debug_data = SlotDebugData { name: Interned::default(), ty: ty.canonical(), @@ -2390,7 +2550,7 @@ impl Compiler { ); dest } - _ => unreachable!(), + None => unreachable!(), }; self.compiled_values_to_dyn_array_indexes .insert(compiled_value, retval); @@ -2407,9 +2567,9 @@ impl Compiler { { return retval; } - let retval = match compiled_value.range.len() { - TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, - TypeLen::A_BIG_SLOT => { + let retval = match compiled_value.range.len().as_single() { + Some(TypeLenSingle::SmallSlot) => compiled_value.range.small_slots.start, + Some(TypeLenSingle::BigSlot) => { let debug_data = SlotDebugData { name: Interned::default(), ty: Bool.canonical(), @@ -2418,7 +2578,7 @@ impl Compiler { .insns .allocate_variable(&TypeLayout { small_slots: StatePartLayout::scalar(debug_data, ()), - big_slots: StatePartLayout::empty(), + ..TypeLayout::empty() }) .small_slots .start; @@ -2432,7 +2592,7 @@ impl Compiler { ); dest } - _ => unreachable!(), + None => unreachable!(), }; self.compiled_value_bool_dest_is_small_map .insert(compiled_value, retval); @@ -3431,65 +3591,6 @@ impl Compiler { self.compiled_exprs.insert(expr, retval); retval } - fn compile_simple_connect( - &mut self, - conditions: Interned<[Cond]>, - lhs: CompiledExpr, - rhs: CompiledValue, - source_location: SourceLocation, - ) { - let CompiledExpr { - static_part: lhs_static_part, - indexes, - } = lhs; - let (lhs_layout, lhs_range) = lhs_static_part.write(); - assert!( - lhs_layout.ty.is_passive(), - "invalid expression passed to compile_simple_connect -- type must be passive", - ); - let TypeIndexRange { - small_slots, - big_slots, - } = lhs_range; - self.add_assignment( - conditions, - small_slots - .iter() - .zip(rhs.range.small_slots.iter()) - .map(|(base, src)| { - if indexes.small_slots.is_empty() { - Insn::CopySmall { dest: base, src } - } else { - Insn::WriteSmallIndexed { - dest: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - src, - } - } - }) - .chain( - big_slots - .iter() - .zip(rhs.range.big_slots.iter()) - .map(|(base, src)| { - if indexes.big_slots.is_empty() { - Insn::Copy { dest: base, src } - } else { - Insn::WriteIndexed { - dest: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - src, - } - } - }), - ), - source_location, - ); - } fn compile_connect( &mut self, lhs_instantiated_module: InstantiatedModule, @@ -3674,7 +3775,7 @@ impl Compiler { .collect(), ); let retval = if retval_ty == enum_value.layout.ty - && enum_value.range.len() == TypeLen::A_SMALL_SLOT + && enum_value.range.len() == TypeLen::small_slot() { enum_value.range.small_slots.start } else { @@ -3693,13 +3794,13 @@ impl Compiler { .start; let discriminant_bit_width = enum_value.layout.ty.discriminant_bit_width(); let discriminant_mask = !(!0u64 << discriminant_bit_width); - let insn = match enum_value.range.len() { - TypeLen::A_BIG_SLOT => Insn::AndBigWithSmallImmediate { + let insn = match enum_value.range.len().as_single() { + Some(TypeLenSingle::BigSlot) => Insn::AndBigWithSmallImmediate { dest: retval, lhs: enum_value.range.big_slots.start, rhs: discriminant_mask, }, - TypeLen::A_SMALL_SLOT => { + Some(TypeLenSingle::SmallSlot) => { if discriminant_bit_width == enum_value.layout.ty.type_properties().bit_width { Insn::CopySmall { dest: retval, @@ -3713,7 +3814,7 @@ impl Compiler { } } } - _ => unreachable!(), + None => unreachable!(), }; self.add_assignment(Interned::default(), [insn], source_location); retval @@ -3919,7 +4020,7 @@ impl Compiler { }, (), ), - big_slots: StatePartLayout::empty(), + ..TypeLayout::empty() }, first, last, @@ -3964,8 +4065,8 @@ impl Compiler { }) = read { insns.push( - match data.len() { - TypeLen::A_BIG_SLOT => { + match data.len().as_single() { + Some(TypeLenSingle::BigSlot) => { let dest = data.big_slots.start; if signed { Insn::MemoryReadSInt { @@ -3987,11 +4088,11 @@ impl Compiler { } } } - TypeLen::A_SMALL_SLOT => { + Some(TypeLenSingle::SmallSlot) => { let _dest = data.small_slots.start; todo!("memory ports' data are always big for now"); } - _ => unreachable!(), + None => unreachable!(), } .into(), ); @@ -4007,22 +4108,22 @@ impl Compiler { { let end_label = self.insns.new_label(); insns.push( - match mask.len() { - TypeLen::A_BIG_SLOT => Insn::BranchIfZero { + match mask.len().as_single() { + Some(TypeLenSingle::BigSlot) => Insn::BranchIfZero { target: end_label.0, value: mask.big_slots.start, }, - TypeLen::A_SMALL_SLOT => Insn::BranchIfSmallZero { + Some(TypeLenSingle::SmallSlot) => Insn::BranchIfSmallZero { target: end_label.0, value: mask.small_slots.start, }, - _ => unreachable!(), + None => unreachable!(), } .into(), ); insns.push( - match data.len() { - TypeLen::A_BIG_SLOT => { + match data.len().as_single() { + Some(TypeLenSingle::BigSlot) => { let value = data.big_slots.start; if signed { Insn::MemoryWriteSInt { @@ -4044,11 +4145,11 @@ impl Compiler { } } } - TypeLen::A_SMALL_SLOT => { + Some(TypeLenSingle::SmallSlot) => { let _value = data.small_slots.start; todo!("memory ports' data are always big for now"); } - _ => unreachable!(), + None => unreachable!(), } .into(), ); @@ -4733,121 +4834,6 @@ impl Compiler { }, }) } - fn process_assignments(&mut self) { - self.assignments - .finalize(self.insns.state_layout().ty.clone().into()); - if let Some(DebugOpaque(dump_assignments_dot)) = &self.dump_assignments_dot { - let graph = - petgraph::graph::DiGraph::<_, _, usize>::from_elements(self.assignments.elements()); - dump_assignments_dot(&petgraph::dot::Dot::new(&graph)); - } - let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { - Ok(nodes) => nodes - .into_iter() - .filter_map(|n| match n { - AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), - _ => None, - }) - .collect(), - Err(e) => match e.node_id() { - AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( - "combinatorial logic cycle detected at: {}", - self.assignments.assignments()[assignment_index].source_location, - ), - AssignmentOrSlotIndex::SmallSlot(slot) => panic!( - "combinatorial logic cycle detected through: {}", - self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, - ), - AssignmentOrSlotIndex::BigSlot(slot) => panic!( - "combinatorial logic cycle detected through: {}", - self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, - ), - }, - }; - struct CondStackEntry<'a> { - cond: &'a Cond, - end_label: Label, - } - let mut cond_stack = Vec::>::new(); - for assignment_index in assignments_order { - let Assignment { - inputs: _, - outputs: _, - conditions, - insns, - source_location, - } = &self.assignments.assignments()[assignment_index]; - let mut same_len = 0; - for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() { - if entry.cond != cond { - break; - } - same_len = index + 1; - } - while cond_stack.len() > same_len { - let CondStackEntry { cond: _, end_label } = - cond_stack.pop().expect("just checked len"); - self.insns.define_label_at_next_insn(end_label); - } - for cond in &conditions[cond_stack.len()..] { - let end_label = self.insns.new_label(); - match cond.body { - CondBody::IfTrue { cond: cond_value } - | CondBody::IfFalse { cond: cond_value } => { - let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() { - TypeLen::A_SMALL_SLOT => ( - Insn::BranchIfSmallZero { - target: end_label.0, - value: cond_value.range.small_slots.start, - }, - Insn::BranchIfSmallNonZero { - target: end_label.0, - value: cond_value.range.small_slots.start, - }, - ), - TypeLen::A_BIG_SLOT => ( - Insn::BranchIfZero { - target: end_label.0, - value: cond_value.range.big_slots.start, - }, - Insn::BranchIfNonZero { - target: end_label.0, - value: cond_value.range.big_slots.start, - }, - ), - _ => unreachable!(), - }; - self.insns.push( - if let CondBody::IfTrue { .. } = cond.body { - branch_if_zero - } else { - branch_if_non_zero - }, - cond.source_location, - ); - } - CondBody::MatchArm { - discriminant, - variant_index, - } => { - self.insns.push( - Insn::BranchIfSmallNeImmediate { - target: end_label.0, - lhs: discriminant, - rhs: variant_index as _, - }, - cond.source_location, - ); - } - } - cond_stack.push(CondStackEntry { cond, end_label }); - } - self.insns.extend(insns.iter().copied(), *source_location); - } - for CondStackEntry { cond: _, end_label } in cond_stack { - self.insns.define_label_at_next_insn(end_label); - } - } fn process_clocks(&mut self) -> Interned<[StatePartIndex]> { mem::take(&mut self.clock_triggers) .into_iter() diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index adf0f14..7d412ca 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,22 +2,23 @@ // See Notices.txt for copyright information use crate::{ - array::Array, int::{BoolOrIntType, SInt, UInt}, intern::{Intern, Interned, Memoize}, + sim::interpreter::parts::{ + StateLayout, StatePartIndex, StatePartKind, StatePartKindBigSlots, StatePartKindMemories, + StatePartKindSmallSlots, StatePartLen, TypeIndexRange, TypeLayout, get_state_part_kinds, + }, source_location::SourceLocation, - ty::CanonicalType, util::{HashMap, HashSet}, }; -use bitvec::{boxed::BitBox, slice::BitSlice}; +use bitvec::slice::BitSlice; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ - any::TypeId, borrow::BorrowMut, convert::Infallible, fmt::{self, Write}, - hash::{Hash, Hasher}, + hash::Hash, marker::PhantomData, ops::{ControlFlow, Deref, DerefMut, Index, IndexMut}, }; @@ -112,19 +113,33 @@ macro_rules! insn_field_enum { }; } -insn_field_enum! { - pub(crate) enum InsnFieldType { - Memory(Transform::Type>), - SmallSlot(Transform::Type>), - BigSlot(Transform::Type>), - SmallSlotArrayIndexed(Transform::Type>), - BigSlotArrayIndexed(Transform::Type>), - SmallUInt(Transform::Type), - SmallSInt(Transform::Type), - InternedBigInt(Transform::Type>), - U8(Transform::Type), - USize(Transform::Type), - Empty(Transform::Type<[(); 0]>), +macro_rules! insn_field_enum2 { + ( + type_singular_variants = [$($type_singular_variant:ident,)*]; + array_indexed_variants = [$($array_indexed_variant:ident,)*]; + type_kinds = [$($type_kind:ident,)*]; + ) => { + insn_field_enum! { + pub(crate) enum InsnFieldType { + Memory(Transform::Type>), + $($type_singular_variant(Transform::Type>),)* + $($array_indexed_variant(Transform::Type>),)* + SmallUInt(Transform::Type), + SmallSInt(Transform::Type), + InternedBigInt(Transform::Type>), + U8(Transform::Type), + USize(Transform::Type), + Empty(Transform::Type<[(); 0]>), + } + } + }; +} + +get_state_part_kinds! { + insn_field_enum2! { + type_singular_variants; + array_indexed_variants; + type_kinds; } } @@ -776,911 +791,6 @@ impl fmt::Debug for Insns { } } -pub(crate) trait StatePartKind: - Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default -{ - const NAME: &'static str; - type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; - type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; - type State: fmt::Debug + 'static + Clone; - type BorrowedState<'a>: 'a; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State; - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData>; - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result; -} - -pub(crate) trait StatePartsValue { - type Value; -} - -macro_rules! impl_state_parts_traits { - ( - struct $Struct:ident<$V:ident: $StatePartsValue:ident> { - $(#[flatten] - $flattened_field:ident: $flattened_field_ty:ty, - $(#[field_in_flattened] - $field_in_flattened:ident: $field_in_flattened_ty:ty,)* - )? - $($field:ident: $field_ty:ty,)* - } - ) => { - impl<$V: $StatePartsValue> Copy for $Struct<$V> - where - $($flattened_field_ty: Copy,)? - $($field_ty: Copy,)* - { - } - - impl<$V: $StatePartsValue> Clone for $Struct<$V> - where - $($flattened_field_ty: Clone,)? - $($field_ty: Clone,)* - { - fn clone(&self) -> Self { - Self { - $($flattened_field: self.$flattened_field.clone(),)? - $($field: self.$field.clone(),)* - } - } - } - - impl<$V: $StatePartsValue> fmt::Debug for $Struct<$V> - where - $($($field_in_flattened_ty: fmt::Debug,)*)? - $($field_ty: fmt::Debug,)* - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct(stringify!($Struct)) - $($(.field(stringify!($field_in_flattened), &self.$flattened_field.$field_in_flattened))*)? - $(.field(stringify!($field), &self.$field))* - .finish() - } - } - - impl<$V: $StatePartsValue> PartialEq for $Struct<$V> - where - $($flattened_field_ty: PartialEq,)? - $($field_ty: PartialEq,)* - { - fn eq(&self, other: &Self) -> bool { - true $(&& self.$flattened_field == other.$flattened_field)? $(&& self.$field == other.$field)* - } - } - - impl<$V: $StatePartsValue> Eq for $Struct<$V> - where - $($flattened_field_ty: Eq,)? - $($field_ty: Eq,)* - { - } - - impl<$V: $StatePartsValue> Hash for $Struct<$V> - where - $($flattened_field_ty: Hash,)? - $($field_ty: Hash,)* - { - fn hash(&self, h: &mut H) { - $(self.$flattened_field.hash(h);)? - $(self.$field.hash(h);)* - } - } - - impl<$V: $StatePartsValue> Default for $Struct<$V> - where - $($flattened_field_ty: Default,)? - $($field_ty: Default,)* - { - fn default() -> Self { - Self { - $($flattened_field: Default::default(),)? - $($field: Default::default(),)* - } - } - } - }; -} - -macro_rules! make_state_part_kinds { - ( - $( - #[state, field = $state_field:ident] - impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt - )* - $( - #[type, field = $type_field:ident] - impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt - )* - ) => { - $( - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] - pub(crate) struct $StateKind; - - impl $StateStatePartKind for $StateKind $state_impl_body - )* - - $( - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] - pub(crate) struct $TypeKind; - - impl $TypeStatePartKind for $TypeKind $type_impl_body - )* - - pub(crate) struct StateParts { - pub(crate) ty: TypeParts, - $(pub(crate) $state_field: V::Value<$StateKind>,)* - } - - impl_state_parts_traits! { - struct StateParts { - #[flatten] - ty: TypeParts, - $(#[field_in_flattened] - $type_field: V::Value<$TypeKind>,)* - $($state_field: V::Value<$StateKind>,)* - } - } - - pub(crate) struct TypeParts { - $(pub(crate) $type_field: V::Value<$TypeKind>,)* - } - - impl_state_parts_traits! { - struct TypeParts { - $($type_field: V::Value<$TypeKind>,)* - } - } - - #[derive(Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateLayout { - pub(crate) ty: TypeLayout, - $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)* - } - - impl Copy for StateLayout {} - - impl StateLayout { - pub(crate) fn len(&self) -> StateLen { - StateLen { - ty: self.ty.len(), - $($state_field: self.$state_field.len(),)* - } - } - pub(crate) fn is_empty(&self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())* - } - pub(crate) fn empty() -> Self { - Self { - ty: TypeLayout::empty(), - $($state_field: StatePartLayout::empty(),)* - } - } - } - - impl StateLayout { - pub(crate) fn next_index(&self) -> StateIndex { - StateIndex { - ty: self.ty.next_index(), - $($state_field: self.$state_field.next_index(),)* - } - } - pub(crate) fn allocate( - &mut self, - layout: &StateLayout, - ) -> StateIndexRange { - StateIndexRange { - ty: self.ty.allocate(&layout.ty), - $($state_field: self.$state_field.allocate(&layout.$state_field),)* - } - } - } - - impl From> for StateLayout { - fn from(v: StateLayout) -> Self { - Self { - ty: v.ty.into(), - $($state_field: v.$state_field.into(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateIndexRange { - pub(crate) ty: TypeIndexRange, - $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)* - } - - impl StateIndexRange { - pub(crate) fn start(self) -> StateIndex { - StateIndex { - ty: self.ty.start(), - $($state_field: self.$state_field.start(),)* - } - } - pub(crate) fn len(self) -> StateLen { - StateLen { - ty: self.ty.len(), - $($state_field: self.$state_field.len(),)* - } - } - pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())* - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateLen { - pub(crate) ty: TypeLen, - $(pub(crate) $state_field: StatePartLen<$StateKind>,)* - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct StateIndex { - pub(crate) ty: TypeIndex, - $(pub(crate) $state_field: StatePartIndex<$StateKind>,)* - } - - #[derive(Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeLayout { - $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)* - } - - impl Copy for TypeLayout {} - - impl TypeLayout { - pub(crate) fn len(&self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.len(),)* - } - } - pub(crate) fn is_empty(&self) -> bool { - $(self.$type_field.is_empty())&&+ - } - pub(crate) fn empty() -> Self { - Self { - $($type_field: StatePartLayout::empty(),)* - } - } - } - - impl TypeLayout { - pub(crate) fn next_index(&self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.next_index(),)* - } - } - pub(crate) fn allocate( - &mut self, - layout: &TypeLayout, - ) -> TypeIndexRange { - TypeIndexRange { - $($type_field: self.$type_field.allocate(&layout.$type_field),)* - } - } - } - - impl From> for TypeLayout { - fn from(v: TypeLayout) -> Self { - Self { - $($type_field: v.$type_field.into(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeIndexRange { - $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)* - } - - impl TypeIndexRange { - pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { - Self { - $($type_field: StatePartIndexRange { - start: start.$type_field, - len: len.$type_field, - },)* - } - } - pub(crate) fn start(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.start(),)* - } - } - pub(crate) fn len(self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.len(),)* - } - } - pub(crate) fn is_empty(self) -> bool { - $(self.$type_field.is_empty()) &&+ - } - pub(crate) fn slice(self, index: TypeIndexRange) -> Self { - Self { - $($type_field: self.$type_field.slice(index.$type_field),)* - } - } - pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { - Self { - $($type_field: self.$type_field.index_array(element_size.$type_field, index),)* - } - } - pub(crate) fn offset(self, offset: TypeIndex) -> Self { - Self { - $($type_field: self.$type_field.offset(offset.$type_field),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeLen { - $(pub(crate) $type_field: StatePartLen<$TypeKind>,)* - } - - impl TypeLen { - pub(crate) const fn as_index(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.as_index(),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeIndex { - $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* - } - - impl TypeIndex { - pub(crate) const ZERO: Self = Self { - $($type_field: StatePartIndex::ZERO,)* - }; - pub(crate) fn offset(self, offset: TypeIndex) -> Self { - Self { - $($type_field: self.$type_field.offset(offset.$type_field),)* - } - } - } - - pub(crate) struct State { - pub(crate) insns: Interned>, - pub(crate) pc: usize, - pub(crate) memory_write_log: Vec<(StatePartIndex, usize)>, - $(pub(crate) $state_field: StatePart<$StateKind>,)* - $(pub(crate) $type_field: StatePart<$TypeKind>,)* - } - - impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - insns: _, - pc, - memory_write_log, - $($state_field,)* - $($type_field,)* - } = self; - f.debug_struct("State") - .field("insns", &InsnsOfState(self)) - .field("pc", pc) - .field("memory_write_log", memory_write_log) - $(.field(stringify!($state_field), $state_field))* - $(.field(stringify!($type_field), $type_field))* - .finish() - } - } - - impl State { - pub(crate) fn new(insns: Interned>) -> Self { - Self { - insns, - pc: 0, - memory_write_log: Vec::with_capacity(32), - $($state_field: StatePart::new(&insns.state_layout.$state_field.layout_data),)* - $($type_field: StatePart::new(&insns.state_layout.ty.$type_field.layout_data),)* - } - } - pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { - BorrowedState { - orig_insns: self.insns, - insns: &self.insns.insns, - pc: self.pc, - orig_pc: &mut self.pc, - memory_write_log: &mut self.memory_write_log, - $($state_field: self.$state_field.borrow(),)* - $($type_field: self.$type_field.borrow(),)* - } - } - } - - #[derive(Debug)] - pub(crate) struct BorrowedState<'a> { - pub(crate) orig_insns: Interned>, - pub(crate) insns: &'a [Insn], - pub(crate) orig_pc: &'a mut usize, - pub(crate) pc: usize, - pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex, usize)>, - $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)* - $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)* - } - - impl Drop for BorrowedState<'_> { - fn drop(&mut self) { - *self.orig_pc = self.pc; - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] - pub(crate) struct TypeArrayIndexes { - $(pub(crate) $type_field: Interned<[StatePartArrayIndex<$TypeKind>]>,)* - } - - impl TypeArrayIndexes { - pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { - TypeArrayIndexesRef { - $($type_field: &self.$type_field,)* - } - } - #[must_use] - pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { - TypeArrayIndexes { - $($type_field: Interned::from_iter(self.$type_field.iter().copied().chain([next.$type_field])),)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeArrayIndex { - $(pub(crate) $type_field: StatePartArrayIndex<$TypeKind>,)* - } - - impl TypeArrayIndex { - pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { - Self { - $($type_field: StatePartArrayIndex { - index, - len, - stride: stride.$type_field, - },)* - } - } - pub(crate) fn len(self) -> usize { - let len = self.small_slots.len; - $(assert_eq!(self.$type_field.len, len, "array length mismatch");)* - len - } - pub(crate) fn index(self) -> StatePartIndex { - let index = self.small_slots.index; - $(assert_eq!(self.$type_field.index, index, "array index mismatch");)* - index - } - pub(crate) fn is_empty(self) -> bool { - self.len() == 0 - } - pub(crate) fn stride(self) -> TypeLen { - TypeLen { - $($type_field: self.$type_field.stride,)* - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] - pub(crate) struct TypeArrayIndexesRef<'a> { - $(pub(crate) $type_field: &'a [StatePartArrayIndex<$TypeKind>],)* - } - - impl<'a> TypeArrayIndexesRef<'a> { - pub(crate) fn len(self) -> usize { - let len = self.small_slots.len(); - $(assert_eq!(self.$type_field.len(), len, "indexes count mismatch");)* - len - } - pub(crate) fn is_empty(self) -> bool { - self.len() == 0 - } - pub(crate) fn iter(self) -> impl Iterator + 'a { - (0..self.len()).map(move |i| TypeArrayIndex { - $($type_field: self.$type_field[i],)* - }) - } - pub(crate) fn for_each_offset( - self, - mut f: impl FnMut(TypeIndex), - ) { - self.for_each_offset2(TypeIndex { - $($type_field: StatePartIndex { - value: 0, - _phantom: PhantomData, - },)* - }, &mut f); - } - pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { - $(let $type_field = self.$type_field.split_first()?;)* - let next = TypeArrayIndex { - $($type_field: *$type_field.0,)* - }; - let rest = TypeArrayIndexesRef { - $($type_field: $type_field.1,)* - }; - Some((next, rest)) - } - pub(crate) fn for_each_offset2( - self, - base_offset: TypeIndex, - f: &mut (impl FnMut(TypeIndex) + ?Sized), - ) { - if let Some((next, rest)) = self.split_first() { - let stride = next.stride(); - for index in 0..next.len().try_into().expect("array too big") { - let mut offset = TypeIndex { - $($type_field: StatePartIndex { - value: stride - .$type_field - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - },)* - }; - $(offset.$type_field.value = - base_offset - .$type_field - .value - .checked_add(offset.$type_field.value) - .expect("array too big");)* - rest.for_each_offset2(offset, f); - } - } else { - $(assert!(self.$type_field.is_empty(), "indexes count mismatch");)* - f(base_offset); - } - } - } - - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] - pub(crate) struct TypeArrayIndexed { - $(pub(crate) $type_field: StatePartArrayIndexed<$TypeKind>,)* - } - - impl TypeArrayIndexed { - pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { - Self { - $($type_field: StatePartArrayIndexed { - base: base.$type_field, - indexes: indexes.$type_field, - },)* - } - } - pub(crate) fn base(self) -> TypeIndex { - TypeIndex { - $($type_field: self.$type_field.base,)* - } - } - pub(crate) fn indexes(self) -> TypeArrayIndexes { - TypeArrayIndexes { - $($type_field: self.$type_field.indexes,)* - } - } - } - - impl From for TypeArrayIndexed { - fn from(value: TypeIndex) -> Self { - TypeArrayIndexed { - $($type_field: value.$type_field.into(),)* - } - } - } - }; -} - -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub(crate) struct MemoryData> { - pub(crate) array_type: Array, - pub(crate) data: T, -} - -impl> fmt::Debug for MemoryData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { array_type, data } = self; - f.debug_struct("MemoryData") - .field("array_type", array_type) - .field( - "data", - &crate::memory::DebugMemoryData::from_bit_slice(*array_type, data), - ) - .finish() - } -} - -make_state_part_kinds! { - /*#[state, field = small_stack] - impl StatePartKind for StatePartKindSmallStack { - const NAME: &'static str = "SmallStack"; - type DebugData = (); - type LayoutData = (); - type State = Stack; - type BorrowedState<'a> = BorrowedStack<'a, SmallUInt>; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - Stack::new(layout_data.len()) - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state.borrow() - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.small_stack.debug_data.get(part_index.as_usize()) - } - } - #[state, field = big_stack] - impl StatePartKind for StatePartKindBigStack { - const NAME: &'static str = "BigStack"; - type DebugData = (); - type LayoutData = (); - type State = Stack; - type BorrowedState<'a> = BorrowedStack<'a, BigInt>; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - Stack::new(layout_data.len()) - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state.borrow() - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.big_stack.debug_data.get(part_index.as_usize()) - } - }*/ - #[state, field = memories] - impl StatePartKind for StatePartKindMemories { - const NAME: &'static str = "Memories"; - type DebugData = (); - type LayoutData = MemoryData>; - type State = Box<[MemoryData]>; - type BorrowedState<'a> = &'a mut [MemoryData]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - layout_data.iter().map(|MemoryData { array_type, data }| MemoryData { - array_type: *array_type, - data: BitBox::from_bitslice(data), - }).collect() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.memories.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - write!(f, "{:#?}", &state.memories[index]) - } - } - #[type, field = small_slots] - impl StatePartKind for StatePartKindSmallSlots { - const NAME: &'static str = "SmallSlots"; - type DebugData = SlotDebugData; - type LayoutData = (); - type State = Box<[SmallUInt]>; - type BorrowedState<'a> = &'a mut [SmallUInt]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - vec![0; layout_data.len()].into_boxed_slice() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.ty.small_slots.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - let value = state.small_slots[index]; - write!(f, "{value:#x} {}", value as SmallSInt)?; - Ok(()) - } - } - #[type, field = big_slots] - impl StatePartKind for StatePartKindBigSlots { - const NAME: &'static str = "BigSlots"; - type DebugData = SlotDebugData; - type LayoutData = (); - type State = Box<[BigInt]>; - type BorrowedState<'a> = &'a mut [BigInt]; - fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { - layout_data.iter().map(|_| BigInt::default()).collect() - } - fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { - state - } - fn part_debug_data( - state_layout: &StateLayout, - part_index: StatePartIndex, - ) -> Option<&Self::DebugData> { - state_layout.ty.big_slots.debug_data.get(part_index.as_usize()) - } - fn debug_fmt_state_value( - state: &State, - index: StatePartIndex, - f: &mut impl fmt::Write, - ) -> fmt::Result { - write!(f, "{:#x}", state.big_slots[index]) - } - } -} - -impl TypeLen { - pub(crate) fn only_small(self) -> Option> { - let Self { - small_slots, - big_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - } = self - else { - return None; - }; - Some(small_slots) - } - pub(crate) const A_SMALL_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - }; - pub(crate) const A_BIG_SLOT: Self = TypeLen { - small_slots: StatePartLen { - value: 0, - _phantom: PhantomData, - }, - big_slots: StatePartLen { - value: 1, - _phantom: PhantomData, - }, - }; -} - -#[derive(Debug, Clone)] -pub(crate) struct Stack { - storage: Box<[T]>, - cur_len: usize, -} - -impl Stack { - pub(crate) fn new(len: usize) -> Self - where - T: Default, - { - Self { - storage: std::iter::repeat_with(T::default).take(len).collect(), - cur_len: 0, - } - } - pub(crate) fn borrow(&mut self) -> BorrowedStack<'_, T> { - BorrowedStack { - storage: &mut self.storage, - cur_len: self.cur_len, - stack_cur_len: &mut self.cur_len, - } - } - pub(crate) fn clear(&mut self) { - self.cur_len = 0; - } -} - -#[derive(Debug)] -pub(crate) struct BorrowedStack<'a, T> { - storage: &'a mut [T], - cur_len: usize, - stack_cur_len: &'a mut usize, -} - -impl<'a, T> BorrowedStack<'a, T> { - pub(crate) fn push(&mut self, value: T) { - self.storage[self.cur_len] = value; - self.cur_len += 1; - } - pub(crate) fn pop(&mut self) -> T - where - T: Default, - { - self.cur_len -= 1; - std::mem::take(&mut self.storage[self.cur_len]) - } -} - -impl Drop for BorrowedStack<'_, T> { - fn drop(&mut self) { - *self.stack_cur_len = self.cur_len; - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub(crate) struct SlotDebugData { - pub(crate) name: Interned, - pub(crate) ty: CanonicalType, -} - -impl SlotDebugData { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - let mut name = String::with_capacity(self.name.len() + prefix.len()); - name.push_str(prefix); - name.push_str(&self.name); - Self { - name: Intern::intern_owned(name), - ty: self.ty, - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - name: Interned::default(), - ty: self.ty, - } - } -} - -impl, BK: InsnsBuildingKind> StatePartLayout { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - Self { - debug_data: self - .debug_data - .iter() - .map(|v| v.with_prefixed_debug_names(prefix)) - .collect(), - layout_data: self.layout_data.clone(), - _phantom: PhantomData, - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - debug_data: self - .debug_data - .iter() - .map(|v| v.with_anonymized_debug_info()) - .collect(), - layout_data: self.layout_data.clone(), - _phantom: PhantomData, - } - } -} - -impl TypeLayout { - pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { - Self { - small_slots: self.small_slots.with_prefixed_debug_names(prefix), - big_slots: self.big_slots.with_prefixed_debug_names(prefix), - } - } - pub(crate) fn with_anonymized_debug_info(&self) -> Self { - Self { - small_slots: self.small_slots.with_anonymized_debug_info(), - big_slots: self.big_slots.with_anonymized_debug_info(), - } - } -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StatePartArrayIndex { pub(crate) index: StatePartIndex, @@ -1752,13 +862,11 @@ impl StatePartArrayIndexed { if let [next, rest @ ..] = indexes { for i in 0..next.len.try_into().expect("array too big") { Self::for_each_target2( - StatePartIndex { - value: base - .value + StatePartIndex::new( + base.value .checked_add(next.stride.value.checked_mul(i).expect("array too big")) .expect("array too big"), - _phantom: PhantomData, - }, + ), rest, f, ); @@ -1772,285 +880,6 @@ impl StatePartArrayIndexed { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct StatePartIndex { - pub(crate) value: u32, - pub(crate) _phantom: PhantomData, -} - -impl StatePartIndex { - pub(crate) const ZERO: Self = Self { - value: 0, - _phantom: PhantomData, - }; - pub(crate) fn as_usize(self) -> usize { - self.value.try_into().expect("index too big") - } - pub(crate) fn offset(self, offset: StatePartIndex) -> Self { - Self { - value: self - .value - .checked_add(offset.value) - .expect("offset too big"), - _phantom: PhantomData, - } - } - pub(crate) fn debug_fmt( - &self, - f: &mut impl fmt::Write, - before_debug_info_text: &str, - comment_start: &str, - comment_line_start: &str, - comment_end: &str, - state_layout: Option<&StateLayout>, - state: Option<&State>, - ) -> fmt::Result { - write!(f, "StatePartIndex<{}>({})", K::NAME, self.value)?; - f.write_str(before_debug_info_text)?; - let debug_data = - state_layout.and_then(|state_layout| K::part_debug_data(state_layout, *self)); - if state.is_some() || debug_data.is_some() { - f.write_str(comment_start)?; - } - let mut f = PrefixLinesWrapper { - writer: f, - at_beginning_of_line: false, - blank_line_prefix: comment_line_start.trim_end(), - line_prefix: comment_line_start, - }; - if let Some(state) = state { - f.write_str("(")?; - K::debug_fmt_state_value(state, *self, &mut f)?; - f.write_str(")")?; - } - if state.is_some() && debug_data.is_some() { - f.write_str(" ")?; - } - if let Some(debug_data) = debug_data { - write!(f, "{debug_data:?}")?; - } - if state.is_some() || debug_data.is_some() { - f.writer.write_str(comment_end)?; - } - Ok(()) - } -} - -impl fmt::Debug for StatePartIndex { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.debug_fmt::(f, "", "", "", "", None, None) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct StatePartLen { - pub(crate) value: u32, - pub(crate) _phantom: PhantomData, -} - -impl StatePartLen { - pub(crate) const fn new(value: u32) -> Self { - Self { - value, - _phantom: PhantomData, - } - } - pub(crate) const fn as_index(self) -> StatePartIndex { - StatePartIndex { - value: self.value, - _phantom: PhantomData, - } - } -} - -impl fmt::Debug for StatePartLen { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "StatePartLen<{}>({})", K::NAME, self.value) - } -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct StatePartLayout { - pub(crate) debug_data: BK::Vec, - pub(crate) layout_data: BK::Vec, - pub(crate) _phantom: PhantomData, -} - -impl Copy for StatePartLayout {} - -impl StatePartLayout { - pub(crate) fn len(&self) -> StatePartLen { - StatePartLen::new( - self.debug_data - .len() - .try_into() - .expect("state part allocation layout is too big"), - ) - } - pub(crate) fn is_empty(&self) -> bool { - self.debug_data.is_empty() - } - pub(crate) fn empty() -> Self { - Self { - debug_data: Default::default(), - layout_data: Default::default(), - _phantom: PhantomData, - } - } - pub(crate) fn debug_data(&self, index: StatePartIndex) -> &K::DebugData { - &self.debug_data[index.as_usize()] - } -} - -impl From> - for StatePartLayout -{ - fn from(value: StatePartLayout) -> Self { - Self { - debug_data: Intern::intern_owned(value.debug_data), - layout_data: Intern::intern_owned(value.layout_data), - _phantom: PhantomData, - } - } -} - -impl StatePartLayout { - pub(crate) fn scalar(debug_data: K::DebugData, layout_data: K::LayoutData) -> Self { - Self { - debug_data: vec![debug_data], - layout_data: vec![layout_data], - _phantom: PhantomData, - } - } - pub(crate) fn next_index(&self) -> StatePartIndex { - StatePartIndex { - value: self.len().value, - _phantom: PhantomData, - } - } - pub(crate) fn allocate( - &mut self, - layout: &StatePartLayout, - ) -> StatePartIndexRange { - let start = self.next_index(); - let len = layout.len(); - let Self { - debug_data, - layout_data, - _phantom: _, - } = self; - debug_data.extend_from_slice(&layout.debug_data); - layout_data.extend_from_slice(&layout.layout_data); - StatePartIndexRange { start, len } - } -} - -impl fmt::Debug for StatePartLayout { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { - debug_data, - layout_data, - _phantom: _, - } = self; - write!(f, "StatePartLayout<{}>", K::NAME)?; - let mut debug_struct = f.debug_struct(""); - debug_struct - .field("len", &debug_data.len()) - .field("debug_data", debug_data); - if TypeId::of::() != TypeId::of::<()>() { - debug_struct.field("layout_data", layout_data); - } - debug_struct.finish_non_exhaustive() - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) struct StatePartIndexRange { - pub(crate) start: StatePartIndex, - pub(crate) len: StatePartLen, -} - -impl StatePartIndexRange { - pub(crate) fn start(self) -> StatePartIndex { - self.start - } - pub(crate) fn end(self) -> StatePartIndex { - StatePartIndex { - value: self - .start - .value - .checked_add(self.len.value) - .expect("state part allocation layout is too big"), - _phantom: PhantomData, - } - } - pub(crate) fn len(self) -> StatePartLen { - self.len - } - pub(crate) fn is_empty(self) -> bool { - self.len.value == 0 - } - pub(crate) fn iter(self) -> impl Iterator> { - (self.start.value..self.end().value).map(|value| StatePartIndex { - value, - _phantom: PhantomData, - }) - } - pub(crate) fn offset(self, offset: StatePartIndex) -> Self { - self.end().offset(offset); // check for overflow - Self { - start: self.start.offset(offset), - len: self.len, - } - } - pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { - assert!(index.end().value <= self.len.value, "index out of range"); - Self { - start: StatePartIndex { - value: self.start.value + index.start.value, - _phantom: PhantomData, - }, - len: index.len, - } - } - pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { - if element_size.value == 0 { - assert_eq!( - self.len.value, 0, - "array with zero-sized element must also be zero-sized", - ); - return self; - } - assert!( - self.len.value % element_size.value == 0, - "array's size must be a multiple of its element size", - ); - self.slice(StatePartIndexRange { - start: StatePartIndex { - value: index - .try_into() - .ok() - .and_then(|index| element_size.value.checked_mul(index)) - .expect("index out of range"), - _phantom: PhantomData, - }, - len: element_size, - }) - } -} - -impl fmt::Debug for StatePartIndexRange { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "StatePartIndexRange<{}> {{ start: {}, len: {} }}", - K::NAME, - self.start.value, - self.len.value - ) - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StatePart { pub(crate) value: K::State, @@ -2127,17 +956,84 @@ impl<'a, K: StatePartKind: DerefMut = BorrowedStack<'a, T>>, T: 'a> - BorrowedStatePart<'a, K> -{ - pub(crate) fn push(&mut self, value: T) { - self.value.push(value) - } - pub(crate) fn pop(&mut self) -> T - where - T: Default, - { - self.value.pop() +macro_rules! make_state { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + pub(crate) struct State { + pub(crate) insns: Interned>, + pub(crate) pc: usize, + pub(crate) memory_write_log: Vec<(StatePartIndex, usize)>, + $(pub(crate) $state_plural_field: StatePart<$state_kind>,)* + $(pub(crate) $type_plural_field: StatePart<$type_kind>,)* + } + + impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + insns: _, + pc, + memory_write_log, + $($state_plural_field,)* + $($type_plural_field,)* + } = self; + f.debug_struct("State") + .field("insns", &InsnsOfState(self)) + .field("pc", pc) + .field("memory_write_log", memory_write_log) + $(.field(stringify!($state_plural_field), $state_plural_field))* + $(.field(stringify!($type_plural_field), $type_plural_field))* + .finish() + } + } + + impl State { + pub(crate) fn new(insns: Interned>) -> Self { + Self { + insns, + pc: 0, + memory_write_log: Vec::with_capacity(32), + $($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)* + $($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)* + } + } + pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { + BorrowedState { + orig_insns: self.insns, + insns: &self.insns.insns, + pc: self.pc, + orig_pc: &mut self.pc, + memory_write_log: &mut self.memory_write_log, + $($state_plural_field: self.$state_plural_field.borrow(),)* + $($type_plural_field: self.$type_plural_field.borrow(),)* + } + } + } + + #[derive(Debug)] + pub(crate) struct BorrowedState<'a> { + pub(crate) orig_insns: Interned>, + pub(crate) insns: &'a [Insn], + pub(crate) orig_pc: &'a mut usize, + pub(crate) pc: usize, + pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex, usize)>, + $(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)* + $(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)* + } + + impl Drop for BorrowedState<'_> { + fn drop(&mut self) { + *self.orig_pc = self.pc; + } + } + }; +} + +get_state_part_kinds! { + make_state! { + state_plural_fields; + state_kinds; } } @@ -2174,10 +1070,7 @@ impl StatePartIndexMap { self.map.get_mut(key.as_usize()) } pub(crate) fn keys(&self) -> impl Iterator> + '_ { - self.map.keys().map(|k| StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }) + self.map.keys().map(|v| StatePartIndex::new(v as _)) } pub(crate) fn values(&self) -> vec_map::Values<'_, V> { self.map.values() @@ -2186,26 +1079,14 @@ impl StatePartIndexMap { self.map.values_mut() } pub(crate) fn iter(&self) -> impl Iterator, &V)> + '_ { - self.map.iter().map(|(k, v)| { - ( - StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }, - v, - ) - }) + self.map + .iter() + .map(|(k, v)| (StatePartIndex::new(k as u32), v)) } pub(crate) fn iter_mut(&mut self) -> impl Iterator, &mut V)> + '_ { - self.map.iter_mut().map(|(k, v)| { - ( - StatePartIndex { - value: k as u32, - _phantom: PhantomData, - }, - v, - ) - }) + self.map + .iter_mut() + .map(|(k, v)| (StatePartIndex::new(k as u32), v)) } pub(crate) fn len(&self) -> usize { self.map.len() @@ -2432,31 +1313,6 @@ impl BorrowedState<'_> { } } -impl TypeIndexRange { - #[must_use] - pub(crate) fn insns_for_copy_to(self, dest: TypeIndexRange) -> impl Iterator { - assert_eq!(self.len(), dest.len()); - let Self { - small_slots, - big_slots, - } = self; - small_slots - .iter() - .zip(dest.small_slots.iter()) - .map(|(src, dest)| Insn::CopySmall { dest, src }) - .chain( - big_slots - .iter() - .zip(dest.big_slots.iter()) - .map(|(src, dest)| Insn::Copy { dest, src }), - ) - } - #[must_use] - pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { - src.insns_for_copy_to(self) - } -} - fn bigint_pow2(width: usize) -> Interned { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct MyMemoize; diff --git a/crates/fayalite/src/sim/interpreter/parts.rs b/crates/fayalite/src/sim/interpreter/parts.rs index a19ed68..2decd53 100644 --- a/crates/fayalite/src/sim/interpreter/parts.rs +++ b/crates/fayalite/src/sim/interpreter/parts.rs @@ -1,7 +1,25 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::util::const_str_cmp; +use crate::{ + array::Array, + intern::{Intern, Interned}, + sim::interpreter::{ + Insn, InsnsBuilding, InsnsBuildingDone, InsnsBuildingKind, PrefixLinesWrapper, SmallSInt, + SmallUInt, State, + }, + ty::CanonicalType, + util::{chain, const_str_cmp}, +}; +use bitvec::{boxed::BitBox, slice::BitSlice}; +use num_bigint::BigInt; +use std::{ + any::TypeId, + fmt::{self, Write}, + hash::Hash, + marker::PhantomData, + ops::Deref, +}; #[rustfmt::skip] macro_rules! make_get_state_part_kinds { @@ -21,11 +39,15 @@ macro_rules! make_get_state_part_kinds { singular_variant = $type_singular_variants:ident; plural_variant = $type_plural_variants:ident; copy_insn = $copy_insn:ident; + read_indexed_insn = $read_indexed_insn:ident; + write_indexed_insn = $write_indexed_insn:ident; + array_indexed_variant = $array_indexed_variants:ident; })* ) => { make_get_state_part_kinds! { #![dollar = $d] parts! { + #![part_names = [$(#[state] $state_singular_fields,)* $(#[type] $type_singular_fields,)*]] [$(state_singular_fields = (#[state] $state_singular_fields),)* $(state_singular_fields = (#[type] $type_singular_fields),)*]; [$(type_singular_fields = ($type_singular_fields),)*]; [$(state_plural_fields = (#[state] $state_plural_fields),)* $(state_plural_fields = (#[type] $type_plural_fields),)*]; @@ -37,12 +59,16 @@ macro_rules! make_get_state_part_kinds { [$(state_plural_variants = (#[state] $state_plural_variants),)* $(state_plural_variants = (#[type] $type_plural_variants),)*]; [$(type_plural_variants = ($type_plural_variants),)*]; [$(copy_insns = ($copy_insn),)*]; + [$(read_indexed_insns = ($read_indexed_insn),)*]; + [$(write_indexed_insns = ($write_indexed_insn),)*]; + [$(array_indexed_variants = ($array_indexed_variants),)*]; } } }; ( #![dollar = $d:tt] parts! { + #![part_names = [$(#[state] $state_part_name:ident,)* $(#[type] $type_part_name:ident,)*]] $([ $first_name:ident = ($($first_value:tt)*), $($name:ident = ($($value:tt)*),)* @@ -105,14 +131,35 @@ macro_rules! make_get_state_part_kinds { ( @expand $d macro_ident:ident! $d group:tt - (custom = $d custom:tt $d ($d rest:tt)*) + (; $d ($d rest:tt)*) ($d ($d prev_args:tt)*) ) => { get_state_part_kinds! { @expand $d macro_ident! $d group ($d ($d rest)*) - ($d ($d prev_args)* custom = $d custom) + ($d ($d prev_args)*;) + } + }; + ( + @expand + $d macro_ident:ident! $d group:tt + (#[custom] $d name:ident = [ + $d ($($state_part_name = $d $state_part_name:tt,)*)? + $($type_part_name = $d $type_part_name:tt,)* + ] + $d ($d rest:tt)* + ) + ($d ($d prev_args:tt)*) + ) => { + get_state_part_kinds! { + @expand + $d macro_ident! $d group + ($d ($d rest)*) + ($d ($d prev_args)* $d name = [ + $d ($($d $state_part_name,)*)? + $($d $type_part_name,)* + ]) } }; $(( @@ -128,14 +175,6 @@ macro_rules! make_get_state_part_kinds { ($d ($d prev_args)* $first_name = [$($first_value)*, $($($value)*,)*]) } };)* - ( - @expand - $d macro_ident:ident! $d group:tt - ($d unexpected:tt $d ($d rest:tt)*) - ($d ($d prev_args:tt)*) - ) => { - compile_error! {concat!("Unexpected token: ", stringify!($d unexpected))} - }; } pub(crate) use get_state_part_kinds; @@ -158,6 +197,9 @@ make_get_state_part_kinds! { singular_variant = SmallSlot; plural_variant = SmallSlots; copy_insn = CopySmall; + read_indexed_insn = ReadSmallIndexed; + write_indexed_insn = WriteSmallIndexed; + array_indexed_variant = SmallSlotArrayIndexed; } type_part! { singular_field = big_slot; @@ -166,5 +208,805 @@ make_get_state_part_kinds! { singular_variant = BigSlot; plural_variant = BigSlots; copy_insn = Copy; + read_indexed_insn = ReadIndexed; + write_indexed_insn = WriteIndexed; + array_indexed_variant = BigSlotArrayIndexed; + } +} + +pub(crate) trait StatePartKind: + Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default +{ + const NAME: &'static str; + type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type LayoutData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type State: fmt::Debug + 'static + Clone; + type BorrowedState<'a>: 'a; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State; + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData>; + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result; +} + +macro_rules! make_state_part_kinds { + ( + state_kinds = [$(#[$kind_attr:ident] $kind:ident,)*]; + ) => { + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $kind; + )* + }; +} + +get_state_part_kinds! { + make_state_part_kinds! { + state_kinds; + } +} + +impl StatePartKind for StatePartKindMemories { + const NAME: &'static str = "Memories"; + type DebugData = (); + type LayoutData = MemoryData>; + type State = Box<[MemoryData]>; + type BorrowedState<'a> = &'a mut [MemoryData]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data + .iter() + .map(|MemoryData { array_type, data }| MemoryData { + array_type: *array_type, + data: BitBox::from_bitslice(data), + }) + .collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout.memories.debug_data.get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + write!(f, "{:#?}", &state.memories[index]) + } +} + +impl StatePartKind for StatePartKindSmallSlots { + const NAME: &'static str = "SmallSlots"; + type DebugData = SlotDebugData; + type LayoutData = (); + type State = Box<[SmallUInt]>; + type BorrowedState<'a> = &'a mut [SmallUInt]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + vec![0; layout_data.len()].into_boxed_slice() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout + .ty + .small_slots + .debug_data + .get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + let value = state.small_slots[index]; + write!(f, "{value:#x} {}", value as SmallSInt)?; + Ok(()) + } +} + +impl StatePartKind for StatePartKindBigSlots { + const NAME: &'static str = "BigSlots"; + type DebugData = SlotDebugData; + type LayoutData = (); + type State = Box<[BigInt]>; + type BorrowedState<'a> = &'a mut [BigInt]; + fn new_state(layout_data: &[Self::LayoutData]) -> Self::State { + layout_data.iter().map(|_| BigInt::default()).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + fn part_debug_data( + state_layout: &StateLayout, + part_index: StatePartIndex, + ) -> Option<&Self::DebugData> { + state_layout + .ty + .big_slots + .debug_data + .get(part_index.as_usize()) + } + fn debug_fmt_state_value( + state: &State, + index: StatePartIndex, + f: &mut impl fmt::Write, + ) -> fmt::Result { + write!(f, "{:#x}", state.big_slots[index]) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartIndex { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartIndex { + pub(crate) const ZERO: Self = Self::new(0); + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } + pub(crate) fn as_usize(self) -> usize { + self.value.try_into().expect("index too big") + } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + Self::new( + self.value + .checked_add(offset.value) + .expect("offset too big"), + ) + } + pub(crate) fn debug_fmt( + &self, + f: &mut impl fmt::Write, + before_debug_info_text: &str, + comment_start: &str, + comment_line_start: &str, + comment_end: &str, + state_layout: Option<&StateLayout>, + state: Option<&State>, + ) -> fmt::Result { + write!(f, "StatePartIndex<{}>({})", K::NAME, self.value)?; + f.write_str(before_debug_info_text)?; + let debug_data = + state_layout.and_then(|state_layout| K::part_debug_data(state_layout, *self)); + if state.is_some() || debug_data.is_some() { + f.write_str(comment_start)?; + } + let mut f = PrefixLinesWrapper { + writer: f, + at_beginning_of_line: false, + blank_line_prefix: comment_line_start.trim_end(), + line_prefix: comment_line_start, + }; + if let Some(state) = state { + f.write_str("(")?; + K::debug_fmt_state_value(state, *self, &mut f)?; + f.write_str(")")?; + } + if state.is_some() && debug_data.is_some() { + f.write_str(" ")?; + } + if let Some(debug_data) = debug_data { + write!(f, "{debug_data:?}")?; + } + if state.is_some() || debug_data.is_some() { + f.writer.write_str(comment_end)?; + } + Ok(()) + } +} + +impl fmt::Debug for StatePartIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt::(f, "", "", "", "", None, None) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartIndexRange { + pub(crate) start: StatePartIndex, + pub(crate) len: StatePartLen, +} + +impl StatePartIndexRange { + pub(crate) fn start(self) -> StatePartIndex { + self.start + } + pub(crate) fn end(self) -> StatePartIndex { + StatePartIndex::new( + self.start + .value + .checked_add(self.len.value) + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn len(self) -> StatePartLen { + self.len + } + pub(crate) fn is_empty(self) -> bool { + self.len.value == 0 + } + pub(crate) fn iter(self) -> impl Iterator> { + (self.start.value..self.end().value).map(StatePartIndex::new) + } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + self.end().offset(offset); // check for overflow + Self { + start: self.start.offset(offset), + len: self.len, + } + } + pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { + assert!(index.end().value <= self.len.value, "index out of range"); + Self { + start: StatePartIndex::new(self.start.value + index.start.value), + len: index.len, + } + } + pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { + if element_size.value == 0 { + assert_eq!( + self.len.value, 0, + "array with zero-sized element must also be zero-sized", + ); + return self; + } + assert!( + self.len.value % element_size.value == 0, + "array's size must be a multiple of its element size", + ); + self.slice(StatePartIndexRange { + start: StatePartIndex::new( + index + .try_into() + .ok() + .and_then(|index| element_size.value.checked_mul(index)) + .expect("index out of range"), + ), + len: element_size, + }) + } +} + +impl fmt::Debug for StatePartIndexRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StatePartIndexRange<{}> {{ start: {}, len: {} }}", + K::NAME, + self.start.value, + self.len.value + ) + } +} + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub(crate) struct MemoryData> { + pub(crate) array_type: Array, + pub(crate) data: T, +} + +impl> fmt::Debug for MemoryData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { array_type, data } = self; + f.debug_struct("MemoryData") + .field("array_type", array_type) + .field( + "data", + &crate::memory::DebugMemoryData::from_bit_slice(*array_type, data), + ) + .finish() + } +} + +macro_rules! make_state_layout { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLayout { + pub(crate) ty: TypeLayout, + $(pub(crate) $state_plural_field: StatePartLayout<$state_kind, BK>,)* + } + + impl Copy for StateLayout {} + + impl StateLayout { + pub(crate) fn len(&self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_plural_field: self.$state_plural_field.len(),)* + } + } + pub(crate) fn is_empty(&self) -> bool { + self.ty.is_empty() $(&& self.$state_plural_field.is_empty())* + } + pub(crate) fn empty() -> Self { + Self { + ty: TypeLayout::empty(), + $($state_plural_field: StatePartLayout::empty(),)* + } + } + } + + impl StateLayout { + pub(crate) fn next_index(&self) -> StateIndex { + StateIndex { + ty: self.ty.next_index(), + $($state_plural_field: self.$state_plural_field.next_index(),)* + } + } + pub(crate) fn allocate( + &mut self, + layout: &StateLayout, + ) -> StateIndexRange { + StateIndexRange { + ty: self.ty.allocate(&layout.ty), + $($state_plural_field: self.$state_plural_field.allocate(&layout.$state_plural_field),)* + } + } + } + + impl From> for StateLayout { + fn from(v: StateLayout) -> Self { + Self { + ty: v.ty.into(), + $($state_plural_field: v.$state_plural_field.into(),)* + } + } + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLayout { + $(pub(crate) $type_plural_field: StatePartLayout<$type_kind, BK>,)* + } + + impl Copy for TypeLayout {} + + impl TypeLayout { + pub(crate) fn len(&self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.len(),)* + } + } + pub(crate) fn is_empty(&self) -> bool { + $(self.$type_plural_field.is_empty())&&+ + } + pub(crate) fn empty() -> Self { + Self { + $($type_plural_field: StatePartLayout::empty(),)* + } + } + } + + impl TypeLayout { + pub(crate) fn next_index(&self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.next_index(),)* + } + } + pub(crate) fn allocate( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + TypeIndexRange { + $($type_plural_field: self.$type_plural_field.allocate(&layout.$type_plural_field),)* + } + } + } + + impl From> for TypeLayout { + fn from(v: TypeLayout) -> Self { + Self { + $($type_plural_field: v.$type_plural_field.into(),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_state_layout! { + state_plural_fields; + state_kinds; + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartLayout { + pub(crate) debug_data: BK::Vec, + pub(crate) layout_data: BK::Vec, + pub(crate) _phantom: PhantomData, +} + +impl Copy for StatePartLayout {} + +impl StatePartLayout { + pub(crate) fn len(&self) -> StatePartLen { + StatePartLen::new( + self.debug_data + .len() + .try_into() + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn is_empty(&self) -> bool { + self.debug_data.is_empty() + } + pub(crate) fn empty() -> Self { + Self { + debug_data: Default::default(), + layout_data: Default::default(), + _phantom: PhantomData, + } + } + pub(crate) fn debug_data(&self, index: StatePartIndex) -> &K::DebugData { + &self.debug_data[index.as_usize()] + } +} + +impl From> + for StatePartLayout +{ + fn from(value: StatePartLayout) -> Self { + Self { + debug_data: Intern::intern_owned(value.debug_data), + layout_data: Intern::intern_owned(value.layout_data), + _phantom: PhantomData, + } + } +} + +impl StatePartLayout { + pub(crate) fn scalar(debug_data: K::DebugData, layout_data: K::LayoutData) -> Self { + Self { + debug_data: vec![debug_data], + layout_data: vec![layout_data], + _phantom: PhantomData, + } + } + pub(crate) fn next_index(&self) -> StatePartIndex { + StatePartIndex::new(self.len().value) + } + pub(crate) fn allocate( + &mut self, + layout: &StatePartLayout, + ) -> StatePartIndexRange { + let start = self.next_index(); + let len = layout.len(); + let Self { + debug_data, + layout_data, + _phantom: _, + } = self; + debug_data.extend_from_slice(&layout.debug_data); + layout_data.extend_from_slice(&layout.layout_data); + StatePartIndexRange { start, len } + } +} + +impl fmt::Debug for StatePartLayout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + debug_data, + layout_data, + _phantom: _, + } = self; + write!(f, "StatePartLayout<{}>", K::NAME)?; + let mut debug_struct = f.debug_struct(""); + debug_struct + .field("len", &debug_data.len()) + .field("debug_data", debug_data); + if TypeId::of::() != TypeId::of::<()>() { + debug_struct.field("layout_data", layout_data); + } + debug_struct.finish_non_exhaustive() + } +} + +impl, BK: InsnsBuildingKind> StatePartLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_prefixed_debug_names(prefix)) + .collect(), + layout_data: self.layout_data.clone(), + _phantom: PhantomData, + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_anonymized_debug_info()) + .collect(), + layout_data: self.layout_data.clone(), + _phantom: PhantomData, + } + } +} + +impl TypeLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + small_slots: self.small_slots.with_prefixed_debug_names(prefix), + big_slots: self.big_slots.with_prefixed_debug_names(prefix), + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + small_slots: self.small_slots.with_anonymized_debug_info(), + big_slots: self.big_slots.with_anonymized_debug_info(), + } + } +} + +macro_rules! make_state_len { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + type_singular_variants = [$($type_singular_variant:ident,)*]; + type_singular_fields = [$($type_singular_field:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLen { + pub(crate) ty: TypeLen, + $(pub(crate) $state_plural_field: StatePartLen<$state_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLen { + $(pub(crate) $type_plural_field: StatePartLen<$type_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) enum TypeLenSingle { + $($type_singular_variant,)* + } + + impl TypeLen { + pub(crate) const fn as_index(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.as_index(),)* + } + } + pub(crate) const fn empty() -> Self { + Self { + $($type_plural_field: StatePartLen::new(0),)* + } + } + $(pub(crate) const fn $type_singular_field() -> Self { + let mut retval = Self::empty(); + retval.$type_plural_field.value = 1; + retval + })* + pub(crate) const fn as_single(self) -> Option { + $({ + const SINGLE: TypeLen = TypeLen::$type_singular_field(); + if let SINGLE = self { + return Some(TypeLenSingle::$type_singular_variant); + } + })* + None + } + pub(crate) const fn only_small(mut self) -> Option> { + const EMPTY: TypeLen = TypeLen::empty(); + let retval = self.small_slots; + self.small_slots.value = 0; + if let EMPTY = self { + Some(retval) + } else { + None + } + } + } + }; +} + +get_state_part_kinds! { + make_state_len! { + state_plural_fields; + state_kinds; + type_singular_variants; + type_singular_fields; + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartLen { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartLen { + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } + pub(crate) const fn as_index(self) -> StatePartIndex { + StatePartIndex::new(self.value) + } +} + +impl fmt::Debug for StatePartLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartLen<{}>({})", K::NAME, self.value) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) struct SlotDebugData { + pub(crate) name: Interned, + pub(crate) ty: CanonicalType, +} + +impl SlotDebugData { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + let mut name = String::with_capacity(self.name.len() + prefix.len()); + name.push_str(prefix); + name.push_str(&self.name); + Self { + name: Intern::intern_owned(name), + ty: self.ty, + } + } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + name: Interned::default(), + ty: self.ty, + } + } +} + +macro_rules! make_state_index { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndex { + pub(crate) ty: TypeIndex, + $(pub(crate) $state_plural_field: StatePartIndex<$state_kind>,)* + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndex { + $(pub(crate) $type_plural_field: StatePartIndex<$type_kind>,)* + } + + impl TypeIndex { + pub(crate) const ZERO: Self = Self { + $($type_plural_field: StatePartIndex::ZERO,)* + }; + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.offset(offset.$type_plural_field),)* + } + } + } + }; +} + +get_state_part_kinds! { + make_state_index! { + state_plural_fields; + state_kinds; + } +} + +macro_rules! make_state_index_range { + ( + state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*]; + state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*]; + copy_insns = [$($copy_insn:ident,)*]; + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndexRange { + pub(crate) ty: TypeIndexRange, + $(pub(crate) $state_plural_field: StatePartIndexRange<$state_kind>,)* + } + + impl StateIndexRange { + pub(crate) fn start(self) -> StateIndex { + StateIndex { + ty: self.ty.start(), + $($state_plural_field: self.$state_plural_field.start(),)* + } + } + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_plural_field: self.$state_plural_field.len(),)* + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_plural_field.is_empty())* + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndexRange { + $(pub(crate) $type_plural_field: StatePartIndexRange<$type_kind>,)* + } + + impl TypeIndexRange { + pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { + Self { + $($type_plural_field: StatePartIndexRange { + start: start.$type_plural_field, + len: len.$type_plural_field, + },)* + } + } + pub(crate) fn start(self) -> TypeIndex { + TypeIndex { + $($type_plural_field: self.$type_plural_field.start(),)* + } + } + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_plural_field: self.$type_plural_field.len(),)* + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_plural_field.is_empty()) &&+ + } + pub(crate) fn slice(self, index: TypeIndexRange) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.slice(index.$type_plural_field),)* + } + } + pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.index_array(element_size.$type_plural_field, index),)* + } + } + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_plural_field: self.$type_plural_field.offset(offset.$type_plural_field),)* + } + } + #[must_use] + pub(crate) fn insns_for_copy_to(self, dest: TypeIndexRange) -> impl Iterator { + assert_eq!(self.len(), dest.len()); + chain!($(self.$type_plural_field.iter().zip(dest.$type_plural_field.iter()).map(|(src, dest)| Insn::$copy_insn { dest, src })),*) + } + #[must_use] + pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { + src.insns_for_copy_to(self) + } + } + }; +} + +get_state_part_kinds! { + make_state_index_range! { + state_plural_fields; + state_kinds; + copy_insns; } } diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 4670a1f..ee43f94 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -33,6 +33,7 @@ pub use const_cmp::{ #[doc(inline)] pub use scoped_ref::ScopedRef; +pub(crate) use misc::chain; #[doc(inline)] pub use misc::{ BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, RcWriter, interned_bit, diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 99b7343..ee90071 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -209,3 +209,18 @@ impl std::io::Write for RcWriter { Ok(()) } } + +macro_rules! chain { + () => { + std::iter::empty() + }; + ($first:expr $(, $rest:expr)* $(,)?) => { + { + let retval = IntoIterator::into_iter($first); + $(let retval = Iterator::chain(retval, $rest);)* + retval + } + }; +} + +pub(crate) use chain;