diff --git a/Cargo.lock b/Cargo.lock index 500bd34..c768882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,10 +315,12 @@ dependencies = [ "num-bigint", "num-traits", "os_pipe", + "petgraph", "serde", "serde_json", "tempfile", "trybuild", + "vec_map", "which", ] @@ -357,6 +359,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "funty" version = "2.0.0" @@ -472,11 +480,10 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -515,6 +522,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "prettyplease" version = "0.2.20" @@ -720,6 +737,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index b6b8616..a9cc312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,9 +26,10 @@ eyre = "0.6.12" hashbrown = "0.14.3" indexmap = { version = "2.2.6", features = ["serde"] } jobslot = "0.2.19" -num-bigint = "0.4.4" +num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" +petgraph = "0.6.5" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" @@ -39,4 +40,5 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] tempfile = "3.10.1" thiserror = "1.0.61" trybuild = "1.0" +vec_map = "0.8.2" which = "6.0.1" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 5724a80..2652792 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -25,9 +25,11 @@ jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true os_pipe.workspace = true +petgraph.workspace = true serde_json.workspace = true serde.workspace = true tempfile.workspace = true +vec_map.workspace = true which.workspace = true [dev-dependencies] diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index 0f85f62..849be00 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -14,7 +14,7 @@ use crate::{ }; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathBundleField { pub name: Interned, } @@ -25,7 +25,7 @@ impl fmt::Display for TargetPathBundleField { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathArrayElement { pub index: usize, } @@ -36,7 +36,7 @@ impl fmt::Display for TargetPathArrayElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct TargetPathDynArrayElement {} impl fmt::Display for TargetPathDynArrayElement { @@ -45,7 +45,7 @@ impl fmt::Display for TargetPathDynArrayElement { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum TargetPathElement { BundleField(TargetPathBundleField), ArrayElement(TargetPathArrayElement), @@ -197,7 +197,7 @@ macro_rules! impl_target_base { } impl_target_base! { - #[derive(Clone, PartialEq, Eq, Hash)] + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum TargetBase { #[is = is_module_io] #[to = module_io] @@ -313,7 +313,7 @@ impl TargetChild { } } -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum Target { Base(Interned), Child(TargetChild), diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 03b2c88..68f1cac 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -227,12 +227,29 @@ macro_rules! impl_int { impl BoolOrIntType for $name { type Width = Width; type Signed = ConstBool<$SIGNED>; + type Value = $value; fn width(self) -> usize { $name::width(self) } fn new(width: Width::SizeType) -> Self { $name { width } } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value { + $value::::from_bigint_wrapping(self, v) + } + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value { + #[derive(Copy, Clone, Eq, PartialEq, Hash)] + struct MemoizeBitsToValue; + impl Memoize for MemoizeBitsToValue { + type Input = BitSlice; + type InputOwned = BitVec; + type Output = Arc; + fn inner(self, input: &Self::Input) -> Self::Output { + Arc::new(input.to_bitvec()) + } + } + $value::new(MemoizeBitsToValue.get_cow(bits)) + } fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr { #[derive(Copy, Clone, Eq, PartialEq, Hash)] struct MemoizeBitsToExpr; @@ -334,6 +351,24 @@ macro_rules! impl_int { } } + impl PartialOrd for $value { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for $value { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.to_bigint().cmp(&other.to_bigint()) + } + } + + impl From<$value> for BigInt { + fn from(v: $value) -> BigInt { + v.to_bigint() + } + } + impl $value { pub fn width(&self) -> usize { if let Some(retval) = Width::KNOWN_VALUE { @@ -482,6 +517,19 @@ macro_rules! impl_prim_int { $(#[$meta:meta])* $prim_int:ident, $ty:ty ) => { + impl From<$prim_int> for <$ty as BoolOrIntType>::Value { + fn from(v: $prim_int) -> Self { + <$ty>::le_bytes_to_value_wrapping( + &v.to_le_bytes(), + <$ty as BoolOrIntType>::Width::VALUE, + ) + } + } + impl From> for <$ty as BoolOrIntType>::Value { + fn from(v: NonZero<$prim_int>) -> Self { + v.get().into() + } + } $(#[$meta])* impl ToExpr for $prim_int { type Type = $ty; @@ -498,10 +546,7 @@ macro_rules! impl_prim_int { type Type = $ty; fn to_expr(&self) -> Expr { - <$ty>::le_bytes_to_expr_wrapping( - &self.get().to_le_bytes(), - <$ty as BoolOrIntType>::Width::VALUE, - ) + self.get().to_expr() } } }; @@ -531,6 +576,15 @@ impl_prim_int!( pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { type Width: Size; type Signed: GenericConstBool; + type Value: Clone + + Ord + + std::hash::Hash + + fmt::Debug + + Send + + Sync + + 'static + + ToExpr + + Into; fn width(self) -> usize; fn new(width: ::SizeType) -> Self; fn new_static() -> Self @@ -545,6 +599,10 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { fn as_same_width_uint(self) -> UIntType { UIntType::new(Self::Width::from_usize(self.width())) } + fn value_from_int_wrapping(self, v: impl Into) -> Self::Value { + self.value_from_bigint_wrapping(v.into()) + } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value; fn bits_from_bigint_wrapping(self, v: BigInt) -> BitVec { let width = self.width(); let mut bytes = v.to_signed_bytes_le(); @@ -567,8 +625,9 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { BitSlice::::from_slice_mut(&mut bytes)[..bits.len()].clone_from_bitslice(bits); BigInt::from_signed_bytes_le(&bytes) } + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value; fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr; - fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr { + fn le_bytes_to_bits_wrapping(bytes: &[u8], bit_width: usize) -> BitVec { let bitslice = BitSlice::::from_slice(bytes); let bitslice = &bitslice[..bit_width.min(bitslice.len())]; let mut bits = BitVec::new(); @@ -577,7 +636,17 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { bit_width, Self::Signed::VALUE && bits.last().as_deref().copied().unwrap_or(false), ); - Self::bits_to_expr(Cow::Owned(bits)) + bits + } + fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr { + Self::bits_to_expr(Cow::Owned(Self::le_bytes_to_bits_wrapping( + bytes, bit_width, + ))) + } + fn le_bytes_to_value_wrapping(bytes: &[u8], bit_width: usize) -> Self::Value { + Self::bits_to_value(Cow::Owned(Self::le_bytes_to_bits_wrapping( + bytes, bit_width, + ))) } } @@ -629,6 +698,7 @@ impl sealed::BoolOrIntTypeSealed for Bool {} impl BoolOrIntType for Bool { type Width = ConstUsize<1>; type Signed = ConstBool; + type Value = bool; fn width(self) -> usize { 1 @@ -639,10 +709,19 @@ impl BoolOrIntType for Bool { Bool } + fn value_from_bigint_wrapping(self, v: BigInt) -> Self::Value { + v.bit(0) + } + fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr { assert_eq!(bits.len(), 1); bits[0].to_expr() } + + fn bits_to_value(bits: Cow<'_, BitSlice>) -> Self::Value { + assert_eq!(bits.len(), 1); + bits[0] + } } impl Bool { diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index eedb1bb..fba7ada 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -46,6 +46,7 @@ pub mod module; pub mod prelude; pub mod reg; pub mod reset; +pub mod sim; pub mod source_location; pub mod testing; pub mod ty; diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 7387832..16010cf 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -714,6 +714,18 @@ impl Instance { source_location, } } + pub fn from_canonical(v: Instance) -> Self { + let Instance { + scoped_name, + instantiated, + source_location, + } = v; + Self { + scoped_name, + instantiated: Module::from_canonical(*instantiated).intern_sized(), + source_location, + } + } pub fn containing_module_name(self) -> Interned { self.containing_module_name_id().0 } diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs new file mode 100644 index 0000000..fb5a1e0 --- /dev/null +++ b/crates/fayalite/src/sim.rs @@ -0,0 +1,4175 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +//! Fayalite Simulation + +use crate::{ + bundle::{BundleField, BundleType}, + expr::{ + ops, + target::{ + GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, + TargetPathElement, + }, + ExprEnum, Flow, ToLiteralBits, + }, + int::BoolOrIntType, + intern::{Intern, Interned, Memoize}, + module::{ + AnnotatedModuleIO, Block, Id, ModuleBody, NameId, NormalModuleBody, ScopedNameId, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, + StmtWire, + }, + prelude::*, + sim::{ + interpreter::{ + Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, + SlotDebugData, SmallUInt, State, StatePartArrayIndex, StatePartArrayIndexed, + StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, + StatePartKindSmallSlots, StatePartLayout, StatePartLen, StatePartsValue, + TypeArrayIndex, TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, + TypeParts, + }, + time::{SimDuration, SimInstant}, + }, + ty::StaticType, + util::{BitSliceWriteWithBase, DebugAsDisplay}, +}; +use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec}; +use hashbrown::{HashMap, HashSet}; +use num_bigint::BigInt; +use num_traits::{Signed, ToPrimitive, Zero}; +use petgraph::visit::{ + GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, +}; +use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; + +mod interpreter; +pub mod time; +pub mod vcd; + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CondBody { + IfTrue { + cond: CompiledValue, + }, + IfFalse { + cond: CompiledValue, + }, + MatchArm { + enum_expr: CompiledValue, + variant_index: usize, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct Cond { + body: CondBody, + source_location: SourceLocation, +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +enum InstantiatedModule { + Base(Interned>), + Child { + parent: Interned, + instance: Interned>, + }, +} + +impl InstantiatedModule { + fn leaf_module(self) -> Interned> { + match self { + InstantiatedModule::Base(base) => base, + InstantiatedModule::Child { instance, .. } => instance.instantiated(), + } + } + fn write_path(self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + InstantiatedModule::Base(base) => fmt::Debug::fmt(&base.name_id(), f), + InstantiatedModule::Child { parent, instance } => { + parent.write_path(f)?; + write!(f, ".{}", instance.name_id()) + } + } + } +} + +impl fmt::Debug for InstantiatedModule { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InstantiatedModule(")?; + self.write_path(f)?; + write!(f, ": {})", self.leaf_module().name_id()) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct TargetInInstantiatedModule { + instantiated_module: InstantiatedModule, + target: Target, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledBundleField { + offset: TypeIndex, + ty: CompiledTypeLayout, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CompiledTypeLayoutBody { + Scalar, + Array { + /// debug names are ignored, use parent's layout instead + element: Interned>, + }, + Bundle { + /// debug names are ignored, use parent's layout instead + fields: Interned<[CompiledBundleField]>, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledTypeLayout { + ty: T, + layout: TypeLayout, + body: CompiledTypeLayoutBody, +} + +impl CompiledTypeLayout { + fn with_prefixed_debug_names(self, prefix: &str) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_prefixed_debug_names(prefix), + body, + } + } + fn with_anonymized_debug_info(self) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_anonymized_debug_info(), + body, + } + } + fn get(ty: T) -> Self { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = CompiledTypeLayout; + + fn inner(self, input: &Self::Input) -> Self::Output { + match input { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::Enum(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => { + let mut layout = TypeLayout::empty(); + let debug_data = SlotDebugData { + name: Interned::default(), + ty: *input, + }; + layout.big_slots = StatePartLayout::scalar(debug_data); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Scalar, + } + } + CanonicalType::Array(array) => { + let mut layout = TypeLayout::empty(); + let element = CompiledTypeLayout::get(array.element()).intern_sized(); + for index in 0..array.len() { + layout.allocate( + &element + .layout + .with_prefixed_debug_names(&format!("[{index}]")), + ); + } + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Array { element }, + } + } + CanonicalType::Bundle(bundle) => { + let mut layout = TypeLayout::empty(); + let fields = bundle + .fields() + .iter() + .map( + |BundleField { + name, + flipped: _, + ty, + }| { + let ty = CompiledTypeLayout::get(*ty); + let offset = layout + .allocate( + &ty.layout + .with_prefixed_debug_names(&format!(".{name}")), + ) + .start(); + CompiledBundleField { offset, ty } + }, + ) + .collect(); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Bundle { fields }, + } + } + } + } + } + let CompiledTypeLayout { + ty: _, + layout, + body, + } = MyMemoize.get_owned(ty.canonical()); + Self { ty, layout, body } + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledValue { + layout: CompiledTypeLayout, + range: TypeIndexRange, + write: Option<(CompiledTypeLayout, TypeIndexRange)>, +} + +impl CompiledValue { + fn write(self) -> (CompiledTypeLayout, TypeIndexRange) { + self.write.unwrap_or((self.layout, self.range)) + } + fn map( + self, + mut f: impl FnMut( + CompiledTypeLayout, + TypeIndexRange, + ) -> (CompiledTypeLayout, TypeIndexRange), + ) -> CompiledValue { + let (layout, range) = f(self.layout, self.range); + CompiledValue { + layout, + range, + write: self.write.map(|(layout, range)| f(layout, range)), + } + } + fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledValue { + self.map(|CompiledTypeLayout { ty, layout, body }, range| { + ( + CompiledTypeLayout { + ty: f(ty), + layout, + body, + }, + range, + ) + }) + } +} + +impl CompiledValue { + fn add_discriminant_to_set(self, inputs: &mut SlotSet) { + inputs.extend([self.range]); + } +} + +impl CompiledValue { + fn field_by_index(self, field_index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayout { + ty: _, + layout: _, + body: CompiledTypeLayoutBody::Bundle { fields }, + } = layout + else { + unreachable!(); + }; + ( + fields[field_index].ty, + range.slice(TypeIndexRange::new( + fields[field_index].offset, + fields[field_index].ty.layout.len(), + )), + ) + }) + } + fn field_by_name(self, name: Interned) -> CompiledValue { + self.field_by_index(self.layout.ty.name_indexes()[&name]) + } +} + +impl CompiledValue { + fn element(self, index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(element.layout.len(), index)) + }) + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + CompiledExpr::from(self).element_dyn(index_slot) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExpr { + static_part: CompiledValue, + indexes: TypeArrayIndexes, +} + +impl From> for CompiledExpr { + fn from(static_part: CompiledValue) -> Self { + Self { + static_part, + indexes: TypeArrayIndexes::default(), + } + } +} + +impl CompiledExpr { + fn map_ty(self, f: impl FnMut(T) -> U) -> CompiledExpr { + let Self { + static_part, + indexes, + } = self; + CompiledExpr { + static_part: static_part.map_ty(f), + indexes, + } + } + fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + indexes, + } = self; + indexes.as_ref().for_each_offset(|offset| { + inputs.extend([static_part.range.offset(offset)]); + }); + } + fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part: _, + indexes, + } = self; + self.add_target_without_indexes_to_set(inputs); + inputs.extend(indexes.as_ref().iter()); + } +} + +impl CompiledExpr { + fn field_by_index(self, field_index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_index(field_index), + indexes: self.indexes, + } + } + fn field_by_name(self, name: Interned) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_name(name), + indexes: self.indexes, + } + } +} + +impl CompiledExpr { + fn element(self, index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.element(index), + indexes: self.indexes, + } + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else { + unreachable!(); + }; + let stride = element.layout.len(); + let indexes = self.indexes.join(TypeArrayIndex::from_parts( + index_slot, + self.static_part.layout.ty.len(), + stride, + )); + CompiledExpr { + static_part: self.static_part.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(stride, 0)) + }), + indexes, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentOrSlotIndex { + AssignmentIndex(usize), + SmallSlots(StatePartIndex), + BigSlots(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(Debug)] +enum Assignments { + Accumulating { + assignments: Vec, + }, + Finalized { + assignments: Box<[Assignment]>, + 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(), + } + } +} + +impl Assignments { + fn finalize(&mut self, slots_len: TypeLen) { + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + let assignments = mem::take(assignments).into_boxed_slice(); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_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]); + 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); + } + } + *self = Self::Finalized { + assignments, + 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 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 + } +} + +impl GraphBase for Assignments { + type EdgeId = AssignmentIO; + type NodeId = AssignmentOrSlotIndex; +} + +struct AssignmentsNodeIdentifiers { + assignment_indexes: std::ops::Range, + small_slots: std::ops::Range, + big_slots: std::ops::Range, +} + +impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + let Self { + assignment_indexes, + small_slots, + big_slots, + } = self; + assignment_indexes + .next() + .map(AssignmentOrSlotIndex::AssignmentIndex) + .or_else(|| { + small_slots.next().map(|value| { + AssignmentOrSlotIndex::SmallSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + .or_else(|| { + big_slots.next().map(|value| { + AssignmentOrSlotIndex::BigSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + } +} + +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 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::SmallSlots) + { + retval + } else if let retval @ Some(_) = big_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::BigSlots) + { + 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(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, + ), + }; + AssignmentsNeighborsDirected { + assignment_indexes: assignment_indexes.iter(), + small_slots: small_slots.iter(), + big_slots: big_slots.iter(), + } + } + AssignmentOrSlotIndex::SmallSlots(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + small_slots: Default::default(), + big_slots: Default::default(), + }, + AssignmentOrSlotIndex::BigSlots(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + small_slots: Default::default(), + big_slots: Default::default(), + }, + } + } +} + +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::SmallSlots(slot) => self.slots.insert(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.insert(slot), + } + } + + fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { + match *n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + self.assignments[assignment_index] + } + AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.contains(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]); + } + CondBody::MatchArm { + enum_expr, + variant_index: _, + } => enum_expr.add_discriminant_to_set(self), + }) + } +} + +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 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 { + 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::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} + } + } + } + Self { + inputs, + outputs, + conditions, + insns, + source_location, + } + } +} + +#[derive(Debug)] +pub struct Compiler { + insns: Insns, + base_module: Interned>, + modules: HashMap, + compiled_values: HashMap>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + decl_conditions: HashMap>, + compiled_values_to_dyn_array_indexes: + HashMap, StatePartIndex>, + assignments: Assignments, + traces: Vec>, +} + +impl Compiler { + pub fn new(base_module: Interned>) -> Self { + Self { + insns: Insns::new(), + base_module, + modules: HashMap::new(), + compiled_values: HashMap::new(), + compiled_exprs: HashMap::new(), + compiled_exprs_to_values: HashMap::new(), + decl_conditions: HashMap::new(), + compiled_values_to_dyn_array_indexes: HashMap::new(), + assignments: Assignments::default(), + traces: Vec::new(), + } + } + fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { + let id = TraceScalarId(self.traces.len()); + self.traces.push(SimTrace { + id, + kind, + state: (), + last_state: (), + }); + id + } + fn make_trace_scalar_helper( + &mut self, + target: TargetInInstantiatedModule, + small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, + big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, + ) -> TraceScalarId { + let compiled_value = self.compile_value(target); + 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!(), + }) + } + fn make_trace_scalar( + &mut self, + target: TargetInInstantiatedModule, + name: Interned, + ) -> TraceDecl { + let flow = target.target.flow(); + match target.target.canonical_ty() { + CanonicalType::UInt(ty) => TraceUInt { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallUInt { index, ty }, + |index| SimTraceKind::BigUInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::SInt(ty) => TraceSInt { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallSInt { index, ty }, + |index| SimTraceKind::BigSInt { index, ty }, + ), + name, + ty, + flow, + } + .into(), + CanonicalType::Bool(ty) => TraceBool { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallBool { index }, + |index| SimTraceKind::BigBool { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Array(ty) => unreachable!(), + CanonicalType::Enum(ty) => todo!(), + CanonicalType::Bundle(ty) => unreachable!(), + CanonicalType::AsyncReset(ty) => TraceAsyncReset { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallAsyncReset { index }, + |index| SimTraceKind::BigAsyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::SyncReset(ty) => TraceSyncReset { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallSyncReset { index }, + |index| SimTraceKind::BigSyncReset { index }, + ), + name, + flow, + } + .into(), + CanonicalType::Reset(ty) => todo!(), + CanonicalType::Clock(ty) => TraceClock { + id: self.make_trace_scalar_helper( + target, + |index| SimTraceKind::SmallClock { index }, + |index| SimTraceKind::BigClock { index }, + ), + name, + flow, + } + .into(), + } + } + fn make_trace_decl_child( + &mut self, + target: TargetInInstantiatedModule, + name: Interned, + ) -> TraceDecl { + match target.target.canonical_ty() { + CanonicalType::Array(ty) => { + let elements = Interned::from_iter((0..ty.len()).map(|index| { + self.make_trace_decl_child( + TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: target.target.join( + TargetPathElement::from(TargetPathArrayElement { index }) + .intern_sized(), + ), + }, + Intern::intern_owned(format!("[{index}]")), + ) + })); + TraceArray { + name, + elements, + ty, + flow: target.target.flow(), + } + .into() + } + CanonicalType::Enum(ty) => todo!(), + CanonicalType::Bundle(ty) => { + let fields = Interned::from_iter(ty.fields().iter().map(|field| { + self.make_trace_decl_child( + TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: target.target.join( + TargetPathElement::from(TargetPathBundleField { name: field.name }) + .intern_sized(), + ), + }, + field.name, + ) + })); + TraceBundle { + name, + fields, + ty, + flow: target.target.flow(), + } + .into() + } + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => self.make_trace_scalar(target, name), + } + } + fn make_trace_decl( + &mut self, + instantiated_module: InstantiatedModule, + target_base: TargetBase, + ) -> TraceDecl { + let target = TargetInInstantiatedModule { + instantiated_module, + target: target_base.into(), + }; + match target_base { + TargetBase::ModuleIO(module_io) => TraceModuleIO { + name: module_io.name(), + child: self + .make_trace_decl_child(target, module_io.name()) + .intern(), + ty: module_io.ty(), + flow: module_io.flow(), + } + .into(), + TargetBase::MemPort(mem_port) => { + let name = Intern::intern_owned(mem_port.port_name().to_string()); + let TraceDecl::Scope(TraceScope::Bundle(bundle)) = + self.make_trace_decl_child(target, name) + else { + unreachable!() + }; + TraceMemPort { + name, + bundle, + ty: mem_port.ty(), + } + .into() + } + TargetBase::Reg(reg) => TraceReg { + name: reg.name(), + child: self.make_trace_decl_child(target, reg.name()).intern(), + ty: reg.ty(), + } + .into(), + TargetBase::Wire(wire) => TraceWire { + name: wire.name(), + child: self.make_trace_decl_child(target, wire.name()).intern(), + ty: wire.ty(), + } + .into(), + TargetBase::Instance(instance) => { + let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = + self.make_trace_decl_child(target, instance.name()) + else { + unreachable!() + }; + let compiled_module = &self.modules[&InstantiatedModule::Child { + parent: target.instantiated_module.intern(), + instance: instance.intern(), + }]; + TraceInstance { + name: instance.name(), + instance_io, + module: compiled_module.trace_decls, + ty: instance.ty(), + } + .into() + } + } + } + fn compile_value( + &mut self, + target: TargetInInstantiatedModule, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_values.get(&target) { + return retval; + } + let retval = match target.target { + Target::Base(base) => { + let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty()); + let layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}", + target.instantiated_module, + base.target_name() + )); + let range = self.insns.allocate_variable(&layout.layout); + let write = match *base { + TargetBase::ModuleIO(_) + | TargetBase::MemPort(_) + | TargetBase::Wire(_) + | TargetBase::Instance(_) => None, + TargetBase::Reg(_) => { + let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}$next", + target.instantiated_module, + base.target_name() + )); + Some(( + write_layout, + self.insns.allocate_variable(&write_layout.layout), + )) + } + }; + CompiledValue { + range, + layout, + write, + } + } + Target::Child(target_child) => { + let parent = self.compile_value(TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: *target_child.parent(), + }); + match *target_child.path_element() { + TargetPathElement::BundleField(TargetPathBundleField { name }) => { + parent.map_ty(Bundle::from_canonical).field_by_name(name) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { + parent.map_ty(Array::from_canonical).element(index) + } + TargetPathElement::DynArrayElement(_) => unreachable!(), + } + } + }; + 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]>, + insns: impl IntoIterator, + source_location: SourceLocation, + ) { + let insns = Vec::from_iter(insns); + self.assignments + .push(Assignment::new(conditions, insns, source_location)); + } + fn simple_nary_big_expr( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + ) -> CompiledValue { + let inputs = inputs.map(|input| { + let input = self.compile_expr(instantiated_module, input); + let input = self + .compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); + let TypeIndexRange { + small_slots, + big_slots, + } = input.range; + assert_eq!(small_slots.len.value, 0); + assert_eq!(big_slots.len.value, 1); + big_slots.start + }); + let layout = CompiledTypeLayout::get(dest_ty); + let range = self.insns.allocate_variable(&layout.layout); + let TypeIndexRange { + small_slots, + big_slots, + } = range; + assert_eq!(small_slots.len.value, 0); + assert_eq!(big_slots.len.value, 1); + let retval = CompiledValue { + layout, + range, + write: None, + }; + self.add_assignment( + Interned::default(), + make_insns(big_slots.start, inputs), + instantiated_module.leaf_module().source_location(), + ); + retval + } + fn compiled_value_to_dyn_array_index( + &mut self, + compiled_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self + .compiled_values_to_dyn_array_indexes + .get(&compiled_value) + { + return retval; + } + let retval = match compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => compiled_value.range.small_slots.start, + TypeLen::A_BIG_SLOT => { + let debug_data = SlotDebugData { + name: Interned::default(), + ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(), + }; + let dest = self + .insns + .allocate_variable(&TypeLayout { + small_slots: StatePartLayout::scalar(debug_data), + big_slots: StatePartLayout::empty(), + }) + .small_slots + .start; + self.add_assignment( + Interned::default(), + vec![Insn::CastBigToArrayIndex { + dest, + src: compiled_value.range.big_slots.start, + }], + source_location, + ); + dest + } + _ => unreachable!(), + }; + self.compiled_values_to_dyn_array_indexes + .insert(compiled_value, retval); + retval + } + fn compile_expr( + &mut self, + instantiated_module: InstantiatedModule, + expr: Expr, + ) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&expr) { + return retval; + } + let mut cast_bit = |arg: Expr| { + let src_signed = match Expr::ty(arg) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + let dest_signed = match Expr::ty(expr) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { + match (src_signed, dest_signed) { + (false, false) | (true, true) => { + vec![Insn::Copy { dest, src }] + } + (false, true) => vec![Insn::CastToSInt { + dest, + src, + dest_width: 1, + }], + (true, false) => vec![Insn::CastToUInt { + dest, + src, + dest_width: 1, + }], + } + }) + .into() + }; + let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { + ExprEnum::UIntLiteral(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) + .into(), + ExprEnum::SIntLiteral(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) + .into(), + ExprEnum::BoolLiteral(expr) => self + .simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: BigInt::from(expr).intern_sized(), + }] + }) + .into(), + ExprEnum::BundleLiteral(expr) => todo!(), + ExprEnum::ArrayLiteral(expr) => todo!(), + ExprEnum::EnumLiteral(expr) => todo!(), + ExprEnum::Uninit(expr) => todo!(), + ExprEnum::NotU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::NotS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::NotS { dest, src }], + ) + .into(), + ExprEnum::NotB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: 1, + }] + }, + ) + .into(), + ExprEnum::Neg(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Neg { dest, src }], + ) + .into(), + ExprEnum::BitAndU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::SubU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| { + vec![Insn::SubU { + dest, + lhs, + rhs, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::SubS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::FixedShlU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShlS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrU(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrS(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::CmpLtB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeB(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeU(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeS(expr) => self + .simple_nary_big_expr( + instantiated_module, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CastUIntToUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastUIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastSIntToUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastSIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::FieldAccess(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Bundle::from_canonical) + .field_by_index(expr.field_index()), + ExprEnum::VariantAccess(expr) => todo!(), + ExprEnum::ArrayIndex(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element(expr.element_index()), + ExprEnum::DynArrayIndex(expr) => { + let element_index = + self.compile_expr(instantiated_module, Expr::canonical(expr.element_index())); + let element_index = self.compiled_expr_to_value( + element_index, + instantiated_module.leaf_module().source_location(), + ); + let index_slot = self.compiled_value_to_dyn_array_index( + element_index.map_ty(UInt::from_canonical), + instantiated_module.leaf_module().source_location(), + ); + self.compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element_dyn(index_slot) + } + ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitXorU(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::ReduceBitXorS(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::SliceUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::SliceSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::CastToBits(expr) => todo!(), + ExprEnum::CastBitsTo(expr) => todo!(), + ExprEnum::ModuleIO(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Instance(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Wire(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Reg(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::MemPort(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + }; + 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, + lhs_conditions: Interned<[Cond]>, + lhs: Expr, + rhs_instantiated_module: InstantiatedModule, + rhs_conditions: Interned<[Cond]>, + mut rhs: Expr, + source_location: SourceLocation, + ) { + if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { + match Expr::ty(lhs) { + CanonicalType::UInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::SInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::Bool(_) => unreachable!(), + CanonicalType::Array(lhs_ty) => { + let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.len(), rhs_ty.len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for index in 0..lhs_ty.len() { + self.compile_connect( + lhs_instantiated_module, + lhs_conditions, + lhs[index], + rhs_instantiated_module, + rhs_conditions, + rhs[index], + source_location, + ); + } + return; + } + CanonicalType::Enum(lhs_ty) => { + let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + todo!("handle connect with different enum types"); + } + CanonicalType::Bundle(lhs_ty) => { + let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for ( + field_index, + ( + BundleField { + name, + flipped, + ty: _, + }, + rhs_field, + ), + ) in lhs_ty.fields().into_iter().zip(rhs_ty.fields()).enumerate() + { + assert_eq!(name, rhs_field.name); + assert_eq!(flipped, rhs_field.flipped); + let lhs_expr = ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); + let rhs_expr = ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + if flipped { + // swap lhs/rhs + self.compile_connect( + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + source_location, + ); + } else { + self.compile_connect( + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + source_location, + ); + } + } + return; + } + CanonicalType::AsyncReset(_) => unreachable!(), + CanonicalType::SyncReset(_) => unreachable!(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => unreachable!(), + } + } + let Some(target) = lhs.target() else { + unreachable!("connect lhs must have target"); + }; + let lhs_decl_conditions = self.decl_conditions[&TargetInInstantiatedModule { + instantiated_module: lhs_instantiated_module, + target: target.base().into(), + }]; + let lhs = self.compile_expr(lhs_instantiated_module, lhs); + let rhs = self.compile_expr(rhs_instantiated_module, rhs); + let rhs = self.compiled_expr_to_value(rhs, source_location); + self.compile_simple_connect( + lhs_conditions[lhs_decl_conditions.len()..].intern(), + lhs, + rhs, + source_location, + ); + } + fn compile_declaration( + &mut self, + declaration: StmtDeclaration, + parent_module: Interned, + conditions: Interned<[Cond]>, + ) -> TraceDecl { + let target_base: TargetBase = match &declaration { + StmtDeclaration::Wire(v) => v.wire.into(), + StmtDeclaration::Reg(v) => v.reg.into(), + StmtDeclaration::Instance(v) => v.instance.into(), + }; + let target = TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: target_base.into(), + }; + self.decl_conditions.insert(target, conditions); + self.compile_value(target); + match declaration { + StmtDeclaration::Wire(StmtWire { annotations, wire }) => {} + StmtDeclaration::Reg(StmtReg { annotations, reg }) => { + todo!(); + } + StmtDeclaration::Instance(StmtInstance { + annotations, + instance, + }) => { + let inner_instantiated_module = InstantiatedModule::Child { + parent: parent_module, + instance: instance.intern_sized(), + } + .intern_sized(); + let instance_expr = instance.to_expr(); + self.compile_module(inner_instantiated_module); + for (field_index, module_io) in + instance.instantiated().module_io().into_iter().enumerate() + { + let instance_field = + ops::FieldAccess::new_by_index(instance_expr, field_index).to_expr(); + match Expr::flow(instance_field) { + Flow::Source => { + // we need to supply the value to the instance since the + // parent module expects to read from the instance + self.compile_connect( + *parent_module, + conditions, + instance_field, + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + instance.source_location(), + ); + } + Flow::Sink => { + // we need to take the value from the instance since the + // parent module expects to write to the instance + self.compile_connect( + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + *parent_module, + conditions, + instance_field, + instance.source_location(), + ); + } + Flow::Duplex => unreachable!(), + } + } + } + } + self.make_trace_decl(*parent_module, target_base) + } + fn compile_block( + &mut self, + parent_module: Interned, + block: Block, + conditions: Interned<[Cond]>, + trace_decls: &mut Vec, + ) { + let Block { memories, stmts } = block; + for memory in memories { + todo!("implement memory"); + } + for stmt in stmts { + match stmt { + Stmt::Connect(StmtConnect { + lhs, + rhs, + source_location, + }) => self.compile_connect( + *parent_module, + conditions, + lhs, + *parent_module, + conditions, + rhs, + source_location, + ), + Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"), + Stmt::If(StmtIf { + cond, + source_location, + blocks: [then_block, else_block], + }) => { + let cond = self.compile_expr(*parent_module, Expr::canonical(cond)); + let cond = self.compiled_expr_to_value(cond, source_location); + let cond = cond.map_ty(Bool::from_canonical); + self.compile_block( + parent_module, + then_block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfTrue { cond }, + source_location, + }])), + trace_decls, + ); + self.compile_block( + parent_module, + else_block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfFalse { cond }, + source_location, + }])), + trace_decls, + ); + } + Stmt::Match(StmtMatch { + expr, + source_location, + blocks, + }) => { + let enum_expr = self.compile_expr(*parent_module, Expr::canonical(expr)); + let enum_expr = self.compiled_expr_to_value(enum_expr, source_location); + let enum_expr = enum_expr.map_ty(Enum::from_canonical); + for (variant_index, block) in blocks.into_iter().enumerate() { + self.compile_block( + parent_module, + block, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::MatchArm { + enum_expr, + variant_index, + }, + source_location, + }])), + trace_decls, + ); + } + } + Stmt::Declaration(declaration) => { + trace_decls.push(self.compile_declaration( + declaration, + parent_module, + conditions, + )); + } + } + } + } + fn compile_module(&mut self, module: Interned) -> &CompiledModule { + let mut trace_decls = Vec::new(); + let module_io = module + .leaf_module() + .module_io() + .iter() + .map( + |&AnnotatedModuleIO { + annotations: _, + module_io, + }| { + let target = TargetInInstantiatedModule { + instantiated_module: *module, + target: Target::from(module_io), + }; + self.decl_conditions.insert(target, Interned::default()); + trace_decls.push(self.make_trace_decl(*module, module_io.into())); + self.compile_value(target) + }, + ) + .collect(); + match module.leaf_module().body() { + ModuleBody::Normal(NormalModuleBody { body }) => { + self.compile_block(module, body, Interned::default(), &mut trace_decls); + } + ModuleBody::Extern(_extern_module_body) => { + todo!("simulating extern module: {:?}", module); + } + } + let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { + unreachable!("compiled same instantiated module twice"); + }; + entry.insert(CompiledModule { + module_io, + trace_decls: TraceModule { + name: module.leaf_module().name(), + children: Intern::intern_owned(trace_decls), + }, + }) + } + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().len().ty); + 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::SmallSlots(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, + ), + AssignmentOrSlotIndex::BigSlots(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_index: usize, + } + 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_index, + } = cond_stack.pop().expect("just checked len"); + self.insns.define_label_at_next_insn(end_label_index); + } + for cond in &conditions[cond_stack.len()..] { + let end_label_index = 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_index, + value: cond_value.range.small_slots.start, + }, + Insn::BranchIfSmallNonZero { + target: end_label_index, + value: cond_value.range.small_slots.start, + }, + ), + TypeLen::A_BIG_SLOT => ( + Insn::BranchIfZero { + target: end_label_index, + value: cond_value.range.big_slots.start, + }, + Insn::BranchIfNonZero { + target: end_label_index, + 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 { + enum_expr, + variant_index, + } => todo!(), + } + cond_stack.push(CondStackEntry { + cond, + end_label_index, + }); + } + for insn in insns { + self.insns.push(*insn, *source_location); + } + } + } + pub fn compile(mut self) -> Compiled { + let base_module = + *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + self.process_assignments(); + self.insns + .push(Insn::Return, self.base_module.source_location()); + Compiled { + insns: Insns::from(self.insns).intern_sized(), + base_module, + io: Instance::new_unchecked( + ScopedNameId( + NameId("".intern(), Id::new()), + self.base_module.name_id(), + ), + self.base_module, + self.base_module.source_location(), + ), + traces: Intern::intern_owned(self.traces), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct CompiledModule { + module_io: Interned<[CompiledValue]>, + trace_decls: TraceModule, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Compiled { + insns: Interned>, + base_module: CompiledModule, + io: Instance, + traces: Interned<[SimTrace<()>]>, +} + +impl Compiled { + pub fn new(module: Interned>) -> Self { + Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) + } + pub fn canonical(self) -> Compiled { + let Self { + insns, + base_module, + io, + traces, + } = self; + Compiled { + insns, + base_module, + io: Instance::from_canonical(io.canonical()), + traces, + } + } + pub fn from_canonical(canonical: Compiled) -> Self { + let Compiled { + insns, + base_module, + io, + traces, + } = canonical; + Self { + insns, + base_module, + io: Instance::from_canonical(io.canonical()), + traces, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TraceScalarId(usize); + +impl fmt::Debug for TraceScalarId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraceScalarId({})", self.0) + } +} + +impl TraceScalarId { + pub fn as_usize(self) -> usize { + self.0 + } +} + +macro_rules! impl_trace_decl { + ( + $( + #[kind = $category_kind:ident] + $(#[$category_meta:meta])* + $category_variant:ident($category_enum:ident { + fn $category_property_fn:ident(self) -> $category_property_fn_ret_ty:ty; + $( + $(#[$meta:meta])* + $variant:ident($struct:ident { + fn $property_fn:ident($property_fn_self:ident) -> _ $property_fn_block:block + $($(#[$field_meta:meta])* + $field_name:ident: $field_ty:ty,)* + }), + )* + }), + )* + ) => { + $( + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + #[non_exhaustive] + $(#[$category_meta])* + pub enum $category_kind { + $($(#[$meta])* + $variant,)* + } + + impl From<$category_kind> for TraceKind { + fn from(v: $category_kind) -> Self { + TraceKind::$category_variant(v) + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + #[non_exhaustive] + $(#[$category_meta])* + pub enum $category_enum { + $($(#[$meta])* + $variant($struct),)* + } + + impl fmt::Debug for $category_enum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$variant(v) => v.fmt(f),)* + } + } + } + + impl $category_enum { + pub fn kind(self) -> $category_kind { + match self { + $(Self::$variant(_) => $category_kind::$variant,)* + } + } + pub fn name(self) -> Interned { + match self { + $(Self::$variant(v) => v.name,)* + } + } + pub fn $category_property_fn(self) -> $category_property_fn_ret_ty { + match self { + $(Self::$variant(v) => v.$property_fn(),)* + } + } + } + + impl From<$category_enum> for TraceDecl { + fn from(v: $category_enum) -> Self { + TraceDecl::$category_variant(v) + } + } + + $( + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + #[non_exhaustive] + $(#[$meta])* + pub struct $struct { + $($(#[$field_meta])* + pub $field_name: $field_ty,)* + } + + impl $struct { + pub fn $property_fn($property_fn_self) -> $category_property_fn_ret_ty $property_fn_block + } + + impl From<$struct> for $category_enum { + fn from(v: $struct) -> Self { + $category_enum::$variant(v) + } + } + + impl From<$struct> for TraceDecl { + fn from(v: $struct) -> Self { + TraceDecl::$category_variant($category_enum::$variant(v)) + } + } + )* + )* + + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum TraceKind { + $($(#[$category_meta])* + $category_variant($category_kind),)* + } + + impl fmt::Debug for TraceKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$category_variant(v) => v.fmt(f),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub enum TraceDecl { + $($(#[$category_meta])* + $category_variant($category_enum),)* + } + + impl fmt::Debug for TraceDecl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + $(Self::$category_variant(v) => v.fmt(f),)* + } + } + } + }; +} + +impl_trace_decl! { + #[kind = TraceScopeKind] + Scope(TraceScope { + fn children(self) -> Interned<[TraceDecl]>; + Module(TraceModule { + fn children(self) -> _ { + self.children + } + name: Interned, + children: Interned<[TraceDecl]>, + }), + Instance(TraceInstance { + fn children(self) -> _ { + [self.instance_io.into(), self.module.into()][..].intern() + } + name: Interned, + instance_io: TraceBundle, + module: TraceModule, + ty: Bundle, + }), + MemPort(TraceMemPort { + fn children(self) -> _ { + [self.bundle.into()][..].intern() + } + name: Interned, + bundle: TraceBundle, + ty: Bundle, + }), + Wire(TraceWire { + fn children(self) -> _ { + [*self.child][..].intern() + } + name: Interned, + child: Interned, + ty: CanonicalType, + }), + Reg(TraceReg { + fn children(self) -> _ { + [*self.child][..].intern() + } + name: Interned, + child: Interned, + ty: CanonicalType, + }), + ModuleIO(TraceModuleIO { + fn children(self) -> _ { + [*self.child][..].intern() + } + name: Interned, + child: Interned, + ty: CanonicalType, + flow: Flow, + }), + Bundle(TraceBundle { + fn children(self) -> _ { + self.fields + } + name: Interned, + fields: Interned<[TraceDecl]>, + ty: Bundle, + flow: Flow, + }), + Array(TraceArray { + fn children(self) -> _ { + self.elements + } + name: Interned, + elements: Interned<[TraceDecl]>, + ty: Array, + flow: Flow, + }), + EnumWithFields(TraceEnumWithFields { + fn children(self) -> _ { + Interned::from_iter([self.discriminant.into()].into_iter().chain(self.non_empty_fields)) + } + name: Interned, + discriminant: TraceEnumDiscriminant, + non_empty_fields: Interned<[TraceDecl]>, + ty: Enum, + flow: Flow, + }), + }), + #[kind = TraceScalarKind] + Scalar(TraceScalar { + fn id(self) -> TraceScalarId; + UInt(TraceUInt { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + ty: UInt, + flow: Flow, + }), + SInt(TraceSInt { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + ty: SInt, + flow: Flow, + }), + Bool(TraceBool { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + flow: Flow, + }), + FieldlessEnum(TraceFieldlessEnum { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + ty: Enum, + flow: Flow, + }), + EnumDiscriminant(TraceEnumDiscriminant { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + ty: Enum, + flow: Flow, + }), + Clock(TraceClock { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + flow: Flow, + }), + SyncReset(TraceSyncReset { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + flow: Flow, + }), + AsyncReset(TraceAsyncReset { + fn id(self) -> _ { + self.id + } + id: TraceScalarId, + name: Interned, + flow: Flow, + }), + }), +} + +pub trait TraceWriterDecls: fmt::Debug + 'static + Sized { + type Error: std::error::Error + Send + Sync + 'static; + type TraceWriter: TraceWriter; + fn write_decls(self, module: TraceModule) -> Result; +} + +trait TraceWriterDeclsDynTrait: fmt::Debug { + fn write_decls_dyn(self: Box, module: TraceModule) -> std::io::Result; +} + +fn err_into_io(e: E) -> std::io::Error { + match ::downcast::(Box::new(e)) { + Ok(retval) => *retval, + Err(e) => std::io::Error::other(e), + } +} + +impl TraceWriterDeclsDynTrait for T { + fn write_decls_dyn(self: Box, module: TraceModule) -> std::io::Result { + Ok(DynTraceWriter(Box::new( + TraceWriterDecls::write_decls(*self, module).map_err(err_into_io)?, + ))) + } +} + +pub trait TraceWriter: fmt::Debug + 'static { + type Error: std::error::Error + Send + Sync + 'static; + fn finish_init(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> { + let _ = instant; + Ok(()) + } + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + fn close(self) -> Result<(), Self::Error> + where + Self: Sized, + { + Ok(()) + } + fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>; + fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error>; + fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + if value { + self.set_signal_uint(id, bits![1]) + } else { + self.set_signal_uint(id, bits![0]) + } + } + fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + self.set_signal_bool(id, value) + } + fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + self.set_signal_bool(id, value) + } + fn set_signal_async_reset( + &mut self, + id: TraceScalarId, + value: bool, + ) -> Result<(), Self::Error> { + self.set_signal_bool(id, value) + } +} + +pub struct DynTraceWriterDecls(Box); + +impl DynTraceWriterDecls { + pub fn new(writer: W) -> Self { + Self(Box::new(writer)) + } +} + +impl fmt::Debug for DynTraceWriterDecls { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl TraceWriterDecls for DynTraceWriterDecls { + type Error = std::io::Error; + type TraceWriter = DynTraceWriter; + fn write_decls(self, module: TraceModule) -> Result { + self.0.write_decls_dyn(module) + } +} + +trait TraceWriterDynTrait: fmt::Debug + 'static { + fn finish_init_dyn(&mut self) -> std::io::Result<()>; + fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()>; + fn flush_dyn(&mut self) -> std::io::Result<()>; + fn close_dyn(self: Box) -> std::io::Result<()>; + fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>; + fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()>; + fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; + fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; + fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()>; + fn set_signal_async_reset_dyn(&mut self, id: TraceScalarId, value: bool) + -> std::io::Result<()>; +} + +impl TraceWriterDynTrait for T { + fn finish_init_dyn(&mut self) -> std::io::Result<()> { + Ok(TraceWriter::finish_init(self).map_err(err_into_io)?) + } + fn change_time_to_dyn(&mut self, instant: SimInstant) -> std::io::Result<()> { + Ok(TraceWriter::change_time_to(self, instant).map_err(err_into_io)?) + } + fn flush_dyn(&mut self) -> std::io::Result<()> { + Ok(TraceWriter::flush(self).map_err(err_into_io)?) + } + fn close_dyn(self: Box) -> std::io::Result<()> { + Ok(TraceWriter::close(*self).map_err(err_into_io)?) + } + fn set_signal_uint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_uint(self, id, value).map_err(err_into_io)?) + } + fn set_signal_sint_dyn(&mut self, id: TraceScalarId, value: &BitSlice) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_sint(self, id, value).map_err(err_into_io)?) + } + fn set_signal_bool_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_bool(self, id, value).map_err(err_into_io)?) + } + fn set_signal_clock_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_clock(self, id, value).map_err(err_into_io)?) + } + fn set_signal_sync_reset_dyn(&mut self, id: TraceScalarId, value: bool) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_sync_reset(self, id, value).map_err(err_into_io)?) + } + fn set_signal_async_reset_dyn( + &mut self, + id: TraceScalarId, + value: bool, + ) -> std::io::Result<()> { + Ok(TraceWriter::set_signal_async_reset(self, id, value).map_err(err_into_io)?) + } +} + +pub struct DynTraceWriter(Box); + +impl fmt::Debug for DynTraceWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl TraceWriter for DynTraceWriter { + type Error = std::io::Error; + fn finish_init(&mut self) -> Result<(), Self::Error> { + self.0.finish_init_dyn() + } + fn flush(&mut self) -> Result<(), Self::Error> { + self.0.flush_dyn() + } + fn close(self) -> Result<(), Self::Error> { + self.0.close_dyn() + } + fn change_time_to(&mut self, instant: SimInstant) -> Result<(), Self::Error> { + self.0.change_time_to_dyn(instant) + } + fn set_signal_uint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { + self.0.set_signal_uint_dyn(id, value) + } + fn set_signal_sint(&mut self, id: TraceScalarId, value: &BitSlice) -> Result<(), Self::Error> { + self.0.set_signal_sint_dyn(id, value) + } + fn set_signal_bool(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + self.0.set_signal_bool_dyn(id, value) + } + fn set_signal_clock(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + self.0.set_signal_clock_dyn(id, value) + } + fn set_signal_sync_reset(&mut self, id: TraceScalarId, value: bool) -> Result<(), Self::Error> { + self.0.set_signal_sync_reset_dyn(id, value) + } + fn set_signal_async_reset( + &mut self, + id: TraceScalarId, + value: bool, + ) -> Result<(), Self::Error> { + self.0.set_signal_async_reset_dyn(id, value) + } +} + +#[derive(Debug)] +enum TraceWriterState { + Decls(T), + Init(T::TraceWriter), + Running(T::TraceWriter), + Errored(Option), +} + +#[derive(Clone, PartialEq, Eq, Hash)] +struct SimTrace { + id: TraceScalarId, + kind: SimTraceKind, + state: S, + last_state: S, +} + +impl fmt::Debug for SimTrace<()> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + id, + kind, + state, + last_state, + } = self; + f.debug_struct("SimTrace") + .field("id", id) + .field("kind", kind) + .field("state", state) + .field("last_state", last_state) + .finish() + } +} + +impl fmt::Debug for SimTrace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + id, + kind, + state, + last_state, + } = self; + f.debug_struct("SimTrace") + .field("id", id) + .field("kind", kind) + .field("state", &BitSliceWriteWithBase(state)) + .field("last_state", &BitSliceWriteWithBase(last_state)) + .finish() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +enum SimTraceKind { + BigUInt { + index: StatePartIndex, + ty: UInt, + }, + BigSInt { + index: StatePartIndex, + ty: SInt, + }, + BigBool { + index: StatePartIndex, + }, + BigAsyncReset { + index: StatePartIndex, + }, + BigSyncReset { + index: StatePartIndex, + }, + BigClock { + index: StatePartIndex, + }, + SmallUInt { + index: StatePartIndex, + ty: UInt, + }, + SmallSInt { + index: StatePartIndex, + ty: SInt, + }, + SmallBool { + index: StatePartIndex, + }, + SmallAsyncReset { + index: StatePartIndex, + }, + SmallSyncReset { + index: StatePartIndex, + }, + SmallClock { + index: StatePartIndex, + }, +} + +impl SimTraceKind { + fn make_state(self) -> BitVec { + match self { + SimTraceKind::BigUInt { index: _, ty } | SimTraceKind::SmallUInt { index: _, ty } => { + BitVec::repeat(false, ty.width) + } + SimTraceKind::BigSInt { index: _, ty } | SimTraceKind::SmallSInt { index: _, ty } => { + BitVec::repeat(false, ty.width) + } + SimTraceKind::BigBool { index: _ } + | SimTraceKind::BigAsyncReset { index: _ } + | SimTraceKind::BigSyncReset { index: _ } + | SimTraceKind::BigClock { index: _ } + | SimTraceKind::SmallBool { index: _ } + | SimTraceKind::SmallAsyncReset { index: _ } + | SimTraceKind::SmallSyncReset { index: _ } + | SimTraceKind::SmallClock { index: _ } => BitVec::repeat(false, 1), + } + } +} + +struct SimulationImpl { + state: interpreter::State, + io: Expr, + uninitialized_inputs: HashSet, + io_targets: HashMap>, + made_initial_step: bool, + needs_settle: bool, + trace_decls: TraceModule, + traces: Box<[SimTrace]>, + trace_writers: Vec>, + instant: SimInstant, +} + +impl SimulationImpl { + fn parse_io(&mut self, target: Target, value: CompiledValue) { + self.io_targets.insert(target, value); + match value.layout.body { + CompiledTypeLayoutBody::Scalar => match target.flow() { + Flow::Source => {} + Flow::Sink => { + self.uninitialized_inputs.insert(target); + } + Flow::Duplex => unreachable!(), + }, + CompiledTypeLayoutBody::Array { .. } => { + let value = value.map_ty(Array::from_canonical); + for index in 0..value.layout.ty.len() { + self.parse_io( + target.join( + TargetPathElement::from(TargetPathArrayElement { index }) + .intern_sized(), + ), + value.element(index), + ); + } + } + CompiledTypeLayoutBody::Bundle { .. } => { + let value = value.map_ty(Bundle::from_canonical); + for BundleField { name, .. } in value.layout.ty.fields() { + self.parse_io( + target.join( + TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), + ), + value.field_by_name(name), + ); + } + } + } + } + fn new(compiled: Compiled) -> Self { + let mut retval = Self { + state: State::new(compiled.insns), + io: compiled.io.to_expr(), + uninitialized_inputs: HashSet::new(), + io_targets: HashMap::new(), + made_initial_step: false, + needs_settle: true, + trace_decls: compiled.base_module.trace_decls, + traces: Box::from_iter(compiled.traces.iter().map( + |&SimTrace { + id, + kind, + state: _, + last_state: _, + }| SimTrace { + id, + kind, + state: kind.make_state(), + last_state: kind.make_state(), + }, + )), + trace_writers: vec![], + instant: SimInstant::START, + }; + let io_target = Target::from(compiled.io); + for (BundleField { name, .. }, value) in compiled + .io + .ty() + .fields() + .into_iter() + .zip(compiled.base_module.module_io) + { + retval.parse_io( + io_target + .join(TargetPathElement::from(TargetPathBundleField { name }).intern_sized()), + value, + ); + } + retval + } + fn write_traces( + &mut self, + mut trace_writer: DynTraceWriter, + ) -> std::io::Result { + for &SimTrace { + id, + kind, + ref state, + ref last_state, + } in &self.traces + { + if ONLY_IF_CHANGED && state == last_state { + continue; + } + match kind { + SimTraceKind::BigUInt { .. } | SimTraceKind::SmallUInt { .. } => { + trace_writer.set_signal_uint(id, state)?; + } + SimTraceKind::BigSInt { .. } | SimTraceKind::SmallSInt { .. } => { + trace_writer.set_signal_sint(id, state)?; + } + SimTraceKind::BigBool { .. } | SimTraceKind::SmallBool { .. } => { + trace_writer.set_signal_bool(id, state[0])?; + } + SimTraceKind::BigAsyncReset { .. } | SimTraceKind::SmallAsyncReset { .. } => { + trace_writer.set_signal_async_reset(id, state[0])?; + } + SimTraceKind::BigSyncReset { .. } | SimTraceKind::SmallSyncReset { .. } => { + trace_writer.set_signal_sync_reset(id, state[0])?; + } + SimTraceKind::BigClock { .. } | SimTraceKind::SmallClock { .. } => { + trace_writer.set_signal_clock(id, state[0])?; + } + } + } + Ok(trace_writer) + } + fn init_trace_writer( + &mut self, + trace_writer: DynTraceWriter, + ) -> std::io::Result { + let mut trace_writer = self.write_traces::(trace_writer)?; + trace_writer.finish_init()?; + Ok(trace_writer) + } + fn update_trace_writer( + &mut self, + trace_writer: DynTraceWriter, + ) -> std::io::Result { + self.write_traces::(trace_writer) + } + #[inline(never)] + fn read_traces(&mut self) { + for &mut SimTrace { + id: _, + kind, + ref mut state, + ref mut last_state, + } in &mut self.traces + { + if !IS_INITIAL_STEP { + mem::swap(state, last_state); + } + match kind { + SimTraceKind::BigUInt { index, ty: _ } | SimTraceKind::BigSInt { index, ty: _ } => { + let bigint = &self.state.big_slots[index]; + let mut bytes = bigint.to_signed_bytes_le(); + bytes.resize( + state.len().div_ceil(8), + if bigint.is_negative() { 0xFF } else { 0 }, + ); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..state.len()]; + state.clone_from_bitslice(bitslice); + } + SimTraceKind::BigBool { index } + | SimTraceKind::BigAsyncReset { index } + | SimTraceKind::BigSyncReset { index } + | SimTraceKind::BigClock { index } => { + state.set(0, !self.state.big_slots[index].is_zero()); + } + SimTraceKind::SmallUInt { index, ty: _ } + | SimTraceKind::SmallSInt { index, ty: _ } => { + let bytes = self.state.small_slots[index].to_le_bytes(); + let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = &bitslice[..state.len()]; + state.clone_from_bitslice(bitslice); + } + SimTraceKind::SmallBool { index } + | SimTraceKind::SmallAsyncReset { index } + | SimTraceKind::SmallSyncReset { index } + | SimTraceKind::SmallClock { index } => { + state.set(0, self.state.small_slots[index] != 0); + } + } + if IS_INITIAL_STEP { + last_state.copy_from_bitslice(state); + } + } + } + #[track_caller] + fn advance_time(&mut self, duration: SimDuration) { + self.settle_step(); + self.instant += duration; + self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| { + match &mut trace_writer_state { + TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(), + TraceWriterState::Running(trace_writer) => { + trace_writer.change_time_to(this.instant)?; + } + TraceWriterState::Errored(_) => {} + } + Ok(trace_writer_state) + }); + } + #[track_caller] + fn settle_step(&mut self) { + assert!( + self.uninitialized_inputs.is_empty(), + "didn't initialize all inputs", + ); + if !self.needs_settle { + return; + } + self.state.setup_call(0); + self.state.run(); + if self.made_initial_step { + self.read_traces::(); + } else { + self.read_traces::(); + } + self.made_initial_step = true; + self.needs_settle = false; + self.for_each_trace_writer_storing_error(|this, trace_writer_state| { + Ok(match trace_writer_state { + TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running( + this.init_trace_writer(trace_writer_decls.write_decls(this.trace_decls)?)?, + ), + TraceWriterState::Init(trace_writer) => { + TraceWriterState::Running(this.init_trace_writer(trace_writer)?) + } + TraceWriterState::Running(trace_writer) => { + TraceWriterState::Running(this.update_trace_writer(trace_writer)?) + } + TraceWriterState::Errored(e) => TraceWriterState::Errored(e), + }) + }); + } + #[track_caller] + fn get_io(&self, target: Target) -> CompiledValue { + if let Some(&retval) = self.io_targets.get(&target) { + return retval; + } + if Some(&target) == self.io.target().as_deref() + || Some(target.base()) != self.io.target().map(|v| v.base()) + { + panic!("simulator read/write expression must be an array element/field of `Simulator::io()`"); + }; + panic!("simulator read/write expression must not have dynamic array indexes"); + } + #[track_caller] + fn read_bool_or_int(&mut self, io: Expr) -> I::Value { + let Some(target) = io.target() else { + panic!("can't read from expression that's not a field/element of `Simulation::io()`"); + }; + let compiled_value = self.get_io(*target); + if self.made_initial_step { + self.settle_step(); + } else { + match target.flow() { + Flow::Source => { + panic!("can't read from an output before the simulation has made any steps"); + } + Flow::Sink => { + if self.uninitialized_inputs.contains(&*target) { + panic!("can't read from an uninitialized input"); + } + } + Flow::Duplex => unreachable!(), + } + } + match compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => Expr::ty(io).value_from_int_wrapping( + self.state.small_slots[compiled_value.range.small_slots.start], + ), + TypeLen::A_BIG_SLOT => Expr::ty(io).value_from_int_wrapping( + self.state.big_slots[compiled_value.range.big_slots.start].clone(), + ), + _ => unreachable!(), + } + } + #[track_caller] + fn write_bool_or_int(&mut self, io: Expr, value: I::Value) { + let Some(target) = io.target() else { + panic!("can't write to an expression that's not a field/element of `Simulation::io()`"); + }; + let compiled_value = self.get_io(*target); + match target.flow() { + Flow::Source => { + panic!("can't write to an output"); + } + Flow::Sink => {} + Flow::Duplex => unreachable!(), + } + let value: BigInt = value.into(); + if !self.made_initial_step { + self.uninitialized_inputs.remove(&*target); + } + self.needs_settle = true; + match compiled_value.range.len() { + TypeLen::A_SMALL_SLOT => { + self.state.small_slots[compiled_value.range.small_slots.start] = + value.to_u64().expect("value out of range"); + } + TypeLen::A_BIG_SLOT => { + self.state.big_slots[compiled_value.range.big_slots.start] = value + } + _ => unreachable!(), + } + } + fn close_all_trace_writers(&mut self) -> std::io::Result<()> { + let trace_writers = mem::take(&mut self.trace_writers); + let mut retval = Ok(()); + let close_trace_writer = + |trace_writer: TraceWriterState| match trace_writer { + TraceWriterState::Decls(v) => v.write_decls(self.trace_decls)?.close(), + TraceWriterState::Init(v) => v.close(), + TraceWriterState::Running(v) => v.close(), + TraceWriterState::Errored(Some(e)) => Err(e), + TraceWriterState::Errored(None) => Ok(()), + }; + for trace_writer in trace_writers { + retval = retval.and(close_trace_writer(trace_writer)); + } + retval + } + fn for_each_trace_writer_storing_error( + &mut self, + mut f: impl FnMut( + &mut Self, + TraceWriterState, + ) -> std::io::Result>, + ) { + let mut trace_writers = mem::take(&mut self.trace_writers); + for trace_writer in &mut trace_writers { + *trace_writer = match f( + self, + mem::replace(trace_writer, TraceWriterState::Errored(None)), + ) { + Ok(v) => v, + Err(e) => TraceWriterState::Errored(Some(e)), + }; + } + self.trace_writers = trace_writers; + } + fn for_each_trace_writer_getting_error( + &mut self, + mut f: impl FnMut( + &mut Self, + TraceWriterState, + ) -> std::io::Result>, + ) -> std::io::Result<()> { + let mut retval = Ok(()); + let mut trace_writers = mem::take(&mut self.trace_writers); + for trace_writer in &mut trace_writers { + *trace_writer = match f( + self, + mem::replace(trace_writer, TraceWriterState::Errored(None)), + ) { + Ok(v) => v, + Err(e) => { + if retval.is_ok() { + retval = Err(e); + TraceWriterState::Errored(None) + } else { + TraceWriterState::Errored(Some(e)) + } + } + }; + } + self.trace_writers = trace_writers; + retval + } + fn close(mut self) -> std::io::Result<()> { + if self.made_initial_step { + self.settle_step(); + } + self.close_all_trace_writers() + } + fn flush_traces(&mut self) -> std::io::Result<()> { + if self.made_initial_step { + self.settle_step(); + } + self.for_each_trace_writer_getting_error( + |this, trace_writer: TraceWriterState| match trace_writer { + TraceWriterState::Decls(v) => { + let mut v = v.write_decls(this.trace_decls)?; + v.flush()?; + Ok(TraceWriterState::Init(v)) + } + TraceWriterState::Init(mut v) => { + v.flush()?; + Ok(TraceWriterState::Init(v)) + } + TraceWriterState::Running(mut v) => { + v.flush()?; + Ok(TraceWriterState::Running(v)) + } + TraceWriterState::Errored(Some(e)) => Err(e), + TraceWriterState::Errored(None) => Ok(TraceWriterState::Errored(None)), + }, + ) + } +} + +impl Drop for SimulationImpl { + fn drop(&mut self) { + self.close_all_trace_writers() + .expect("error closing trace writers"); + } +} + +pub struct Simulation { + sim_impl: SimulationImpl, + io: Expr, +} + +struct SortedSetDebug<'a, T>(&'a HashSet); + +impl fmt::Debug for SortedSetDebug<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = Vec::from_iter(self.0.iter().map(|v| { + if f.alternate() { + format!("{v:#?}") + } else { + format!("{v:?}") + } + })); + entries.sort(); + f.debug_set() + .entries(entries.iter().map(DebugAsDisplay)) + .finish() + } +} + +struct SortedMapDebug<'a, K, V>(&'a HashMap); + +impl fmt::Debug for SortedMapDebug<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut entries = Vec::from_iter(self.0.iter().map(|(k, v)| { + if f.alternate() { + (format!("{k:#?}"), format!("{v:#?}")) + } else { + (format!("{k:?}"), format!("{v:?}")) + } + })); + entries.sort(); + f.debug_map() + .entries( + entries + .iter() + .map(|(k, v)| (DebugAsDisplay(k), DebugAsDisplay(v))), + ) + .finish() + } +} + +impl fmt::Debug for Simulation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + sim_impl: + SimulationImpl { + state, + io: _, + uninitialized_inputs, + io_targets, + made_initial_step, + needs_settle, + trace_decls, + traces, + trace_writers, + instant, + }, + io, + } = self; + f.debug_struct("Simulation") + .field("state", state) + .field("io", io) + .field( + "uninitialized_inputs", + &SortedSetDebug(uninitialized_inputs), + ) + .field("io_targets", &SortedMapDebug(io_targets)) + .field("made_initial_step", made_initial_step) + .field("needs_settle", needs_settle) + .field("trace_decls", trace_decls) + .field("traces", traces) + .field("trace_writers", trace_writers) + .field("instant", instant) + .finish() + } +} + +impl Simulation { + pub fn new(module: Interned>) -> Self { + Self::from_compiled(Compiled::new(module)) + } + pub fn add_trace_writer(&mut self, writer: W) { + self.sim_impl + .trace_writers + .push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer))); + } + pub fn flush_traces(&mut self) -> std::io::Result<()> { + self.sim_impl.flush_traces() + } + pub fn close(self) -> std::io::Result<()> { + self.sim_impl.close() + } + pub fn canonical(self) -> Simulation { + let Self { sim_impl, io } = self; + Simulation { + sim_impl, + io: Expr::as_bundle(io), + } + } + pub fn from_canonical(canonical: Simulation) -> Self { + let Simulation { sim_impl, io } = canonical; + Self { + sim_impl, + io: Expr::from_bundle(io), + } + } + pub fn io(&self) -> Expr { + self.io.to_expr() + } + pub fn from_compiled(compiled: Compiled) -> Self { + let sim_impl = SimulationImpl::new(compiled.canonical()); + Self { + io: Expr::from_bundle(sim_impl.io), + sim_impl, + } + } + #[track_caller] + pub fn settle_step(&mut self) { + self.sim_impl.settle_step(); + } + #[track_caller] + pub fn advance_time(&mut self, duration: SimDuration) { + self.sim_impl.advance_time(duration); + } + #[track_caller] + pub fn read_bool_or_int(&mut self, io: Expr) -> I::Value { + self.sim_impl.read_bool_or_int(io) + } + #[track_caller] + pub fn write_bool_or_int( + &mut self, + io: Expr, + value: impl ToExpr, + ) { + let value = value.to_expr(); + assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch"); + let value = value + .to_literal_bits() + .expect("the value that is being written to an input must be a literal"); + self.sim_impl + .write_bool_or_int(io, I::bits_to_value(Cow::Borrowed(&value))); + } +} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs new file mode 100644 index 0000000..5ef2fbc --- /dev/null +++ b/crates/fayalite/src/sim/interpreter.rs @@ -0,0 +1,2577 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + intern::{Intern, Interned, Memoize}, + source_location::SourceLocation, + ty::CanonicalType, + util::get_many_mut, +}; +use hashbrown::HashMap; +use num_bigint::BigInt; +use num_traits::{One, Signed, ToPrimitive, Zero}; +use std::{ + borrow::BorrowMut, + convert::Infallible, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + ops::{Deref, DerefMut, Index, IndexMut}, +}; +use vec_map::VecMap; + +pub(crate) type SmallUInt = u64; +pub(crate) type SmallSInt = i64; +pub(crate) const MIN_BITS_FOR_NEEDING_BIG: usize = SmallUInt::BITS as usize + 1; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) enum InsnFieldKind { + Input, + Output, + Immediate, + BranchTarget, +} + +pub(crate) trait InsnFieldTypeTransform: Eq + Hash + fmt::Debug + Send + Sync { + type Type: Eq + Hash + fmt::Debug + Send + Sync; + fn empty_type() -> Self::Type<[(); 0]>; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnFieldTypeTransformUnit; + +impl InsnFieldTypeTransform for InsnFieldTypeTransformUnit { + type Type = (); + fn empty_type() -> Self::Type<[(); 0]> { + () + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformRef<'a>(PhantomData<&'a ()>); + +impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRef<'a> { + type Type = &'a FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + &[] + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformRefMut<'a>(PhantomData<&'a mut ()>); + +impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRefMut<'a> { + type Type = &'a mut FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + &mut [] + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformValue; + +impl InsnFieldTypeTransform for InsnFieldTypeTransformValue { + type Type = FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + [] + } +} + +pub trait InsnFieldTrait: Send + Sync + 'static + Copy + Eq + Hash + fmt::Debug { + const UNIT: InsnFieldType; + fn variant( + v: Transform::Type, + ) -> InsnFieldType; +} + +macro_rules! insn_field_enum { + ( + $enum_vis:vis enum $InsnFieldType:ident<$Transform:ident: $InsnFieldTypeTransform:ident> { + $($Variant:ident($Transform2:ident::$Type:ident<$variant_ty:ty>),)* + } + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + $enum_vis enum $InsnFieldType<$Transform: $InsnFieldTypeTransform> { + $($Variant($Transform2::$Type<$variant_ty>),)* + } + + $(impl InsnFieldTrait for $variant_ty { + const UNIT: $InsnFieldType = $InsnFieldType::$Variant(()); + fn variant<$Transform2: $InsnFieldTypeTransform>( + v: $Transform2::$Type, + ) -> $InsnFieldType<$Transform2> { + $InsnFieldType::$Variant(v) + } + })* + }; +} + +insn_field_enum! { + pub(crate) enum InsnFieldType { + 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]>), + } +} + +impl InsnFieldType { + pub(crate) fn empty() -> Self { + Self::Empty(Transform::empty_type()) + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnField { + pub(crate) ty: InsnFieldType, + pub(crate) kind: InsnFieldKind, +} + +impl Clone for InsnField +where + InsnFieldType: Clone, +{ + fn clone(&self) -> Self { + Self { + ty: self.ty.clone(), + kind: self.kind.clone(), + } + } +} + +impl Copy for InsnField where + InsnFieldType: Copy +{ +} + +fn make_array_into_iter( + input: [T; I], + mut default: impl FnMut() -> T, +) -> std::array::IntoIter { + const { + assert!(I <= N); + }; + let mut input = input.into_iter(); + let array = std::array::from_fn(|_| input.next().unwrap_or_else(&mut default)); + let mut retval = array.into_iter(); + // remove unneeded trailing elements + if I < N { + retval.nth_back(N - I - 1); + } + retval +} + +impl fmt::Debug for Insn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt::(f, None, None) + } +} + +impl Insn { + fn debug_fmt( + &self, + f: &mut fmt::Formatter<'_>, + labels: Option<&Labels>, + state_layout: Option<&StateLayout>, + ) -> fmt::Result { + let (insn_name, fields) = self.fields_with_names(); + write!(f, "{insn_name}")?; + if fields.len() == 0 { + return Ok(()); + } + writeln!(f, " {{")?; + for (field_name, field) in fields { + write!(f, " {field_name}: ")?; + match field.kind { + InsnFieldKind::BranchTarget => match field.ty { + InsnFieldType::USize(&label_index) => { + if let Some(labels) = labels { + write!(f, "L{label_index}")?; + if let Some(label) = labels.labels.get(label_index) { + if let Some(address) = label.address { + write!(f, " (at {address})")?; + } else { + write!(f, " (not yet defined)")?; + } + } else { + write!(f, " (invalid)")?; + } + writeln!(f, ",")?; + continue; + } + } + InsnFieldType::SmallSlot(_) + | InsnFieldType::BigSlot(_) + | InsnFieldType::SmallSlotArrayIndexed(_) + | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::Empty(_) => {} + }, + InsnFieldKind::Input | InsnFieldKind::Output | InsnFieldKind::Immediate => {} + } + match field.ty { + InsnFieldType::SmallSlot(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::BigSlot(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::SmallSlotArrayIndexed(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::BigSlotArrayIndexed(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::SmallUInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::SmallSInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::InternedBigInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::U8(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::USize(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::Empty(v) => fmt::Debug::fmt(v, f)?, + } + writeln!(f, ",")?; + } + write!(f, "}}") + } +} + +macro_rules! impl_insns { + ( + #[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] + $vis:vis fn $State:ident::$run:ident(&mut $self:ident) -> $run_ret_ty:ty { + #[state] + let mut $state:ident = $state_init:expr; + setup! { + $($setup:tt)* + } + main_loop!(); + cleanup! { + $($cleanup:tt)* + } + } + $( + $(#[$insn_meta:meta])* + $insn_name:ident $({ + $( + #[kind = $field_kind:ident] + $(#[$field_meta:meta])* + $field_name:ident: $field_ty:ty, + )* + })? => $block:block + )* + ) => { + #[derive(Copy, Clone, Eq, PartialEq, Hash)] + $vis enum $Insn { + $( + $(#[$insn_meta])* + $insn_name $({ + $( + $(#[$field_meta])* + $field_name: $field_ty, + )* + })?, + )* + } + + impl $Insn { + $vis const MAX_FIELDS: usize = { + let mut retval = 0; + $($( + let fields = [$(stringify!($field_name),)*].len(); + if retval < fields { + retval = fields; + } + )?)* + retval + }; + } + + impl $Insn { + $vis const fn fields_unit(&self) -> &'static [InsnField] { + match self { + $( + $Insn::$insn_name {..} => &[ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::UNIT, + kind: InsnFieldKind::$field_kind, + },)*)? + ], + )* + } + } + $vis fn fields<'a>(&'a self) -> std::array::IntoIter>, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + $vis fn fields_with_names<'a>(&'a self) -> (&'static str, std::array::IntoIter<(&'static str, InsnField>), { $Insn::MAX_FIELDS }>) { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => ( + stringify!($insn_name), + make_array_into_iter([ + $($((stringify!($field_name), InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + }),)*)? + ], + || ("", InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }), + ), + ), + )* + } + } + $vis fn fields_mut<'a>(&'a mut self) -> std::array::IntoIter>, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + $vis fn into_fields(self) -> std::array::IntoIter, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + } + + impl $State { + $vis fn $run(&mut $self) -> $run_ret_ty { + let mut $state = $state_init; + $($setup)* + let mut insn = $state.insns[$state.pc]; + let retval = 'main_loop: loop { + macro_rules! $next_macro { + () => { + $state.pc += 1; + insn = $state.insns[$state.pc]; + continue 'main_loop; + }; + } + macro_rules! $branch_macro { + ($next_pc:expr) => { + $state.pc = $next_pc; + insn = $state.insns[$state.pc]; + continue 'main_loop; + }; + } + let _: Infallible = match insn { + $( + $Insn::$insn_name $({ + $( + $field_name, + )* + })? => { + $block + } + )* + }; + }; + $($cleanup)* + retval + } + } + }; +} + +pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { + type Vec: fmt::Debug + + Clone + + Eq + + Hash + + Send + + Sync + + 'static + + Default + + Deref + + FromIterator; + type Labels: Default; + fn labels(labels: &Self::Labels) -> Option<&Labels>; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuildingDone; + +impl InsnsBuildingKind for InsnsBuildingDone { + type Vec = Interned<[T]>; + type Labels = (); + fn labels(_labels: &Self::Labels) -> Option<&Labels> { + None + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuilding; + +impl InsnsBuildingKind for InsnsBuilding { + type Vec = Vec; + type Labels = Labels; + fn labels(labels: &Self::Labels) -> Option<&Labels> { + Some(labels) + } +} + +struct Label { + address: Option, +} + +#[derive(Default)] +pub(crate) struct Labels { + labels: Vec