From 479d59b28722c79873e7c5fa1caf1230001b7ee1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 21:46:34 -0800 Subject: [PATCH] WIP implementing simulator --- crates/fayalite/src/expr/target.rs | 2 +- crates/fayalite/src/sim.rs | 389 ++++++++-- crates/fayalite/src/sim/interpreter.rs | 961 ++++++++++++++++++++----- 3 files changed, 1104 insertions(+), 248 deletions(-) diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index 0f85f62..e95ff84 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -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/sim.rs b/crates/fayalite/src/sim.rs index 3aa69a6..3b55379 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1,28 +1,32 @@ //! Fayalite Simulation use crate::{ - bundle::{Bundle, BundleType}, + bundle::{Bundle, BundleField, BundleType}, enum_::Enum, - expr::Expr, - int::Bool, - intern::{Intern, Interned}, - module::{ - Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf, - StmtMatch, + expr::{ + target::{ + Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, + }, + Expr, + }, + int::Bool, + intern::{Intern, Interned, Memoize}, + module::{ + AnnotatedModuleIO, Block, Instance, Module, ModuleBody, NormalModuleBody, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, + }, + sim::interpreter::{ + Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, StatePartLayout, TypeIndex, + TypeIndexRange, TypeLayout, }, - sim::interpreter::{Insn, Insns, InsnsBuilding}, source_location::SourceLocation, + ty::CanonicalType, }; use hashbrown::HashMap; -use std::{cell::RefCell, rc::Rc}; +use std::fmt; mod interpreter; -#[derive(Debug)] -struct CompilingModule { - compiled_module: Option, -} - #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] enum CondStack { Always, @@ -44,32 +48,279 @@ enum CondStack { }, } +#[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: CanonicalType, + 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 get(ty: CanonicalType) -> 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: "".intern() }; + if input.bit_width() > interpreter::SmallUInt::BITS as usize { + layout.big_slots = StatePartLayout::scalar(debug_data); + } else { + layout.small_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 }, + } + } + } + } + } + MyMemoize.get_owned(ty) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledValue { + layout: CompiledTypeLayout, + range: TypeIndexRange, + write: Option<(CompiledTypeLayout, TypeIndexRange)>, +} + +impl CompiledValue { + fn map( + self, + mut f: impl FnMut(CompiledTypeLayout, TypeIndexRange) -> (CompiledTypeLayout, TypeIndexRange), + ) -> Self { + let (layout, range) = f(self.layout, self.range); + Self { + layout, + range, + write: self.write.map(|(layout, range)| f(layout, range)), + } + } +} + #[derive(Debug)] pub struct Compiler { insns: Insns, - modules: HashMap>, Rc>>, - module_queue: Vec>>, + base_module: Interned>, + modules: HashMap, + compiled_values: HashMap, } impl Compiler { - pub fn new() -> Self { + pub fn new(base_module: Interned>) -> Self { Self { insns: Insns::new(), + base_module, modules: HashMap::new(), - module_queue: Vec::new(), + compiled_values: HashMap::new(), } } - pub fn add_module(&mut self, module: Interned>) { - self.modules.entry(module).or_insert_with(|| { - self.module_queue.push(module); - Rc::new(RefCell::new(CompilingModule { - compiled_module: None, - })) - }); + fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue { + if let Some(&retval) = self.compiled_values.get(&target) { + return 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(|layout, range| { + let CompiledTypeLayout { + ty: CanonicalType::Bundle(bundle), + layout: _, + body: CompiledTypeLayoutBody::Bundle { fields }, + } = layout + else { + unreachable!(); + }; + let field_index = bundle.name_indexes()[&name]; + ( + fields[field_index].ty, + range.slice(TypeIndexRange::new( + fields[field_index].offset, + fields[field_index].ty.layout.len(), + )), + ) + }) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => parent + .map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(element.layout.len(), index)) + }), + TargetPathElement::DynArrayElement(_) => unreachable!(), + } + } + } } fn compile_block( &mut self, - module: Interned>, + parent_module: Interned, block: Block, cond_stack: Interned, ) { @@ -98,7 +349,7 @@ impl Compiler { blocks: [then_block, else_block], }) => { self.compile_block( - module, + parent_module, then_block, CondStack::IfTrue { parent: cond_stack, @@ -108,7 +359,7 @@ impl Compiler { .intern_sized(), ); self.compile_block( - module, + parent_module, else_block, CondStack::IfFalse { parent: cond_stack, @@ -125,7 +376,7 @@ impl Compiler { }) => { for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( - module, + parent_module, block, CondStack::MatchArm { parent: cond_stack, @@ -137,67 +388,79 @@ impl Compiler { ); } } - Stmt::Declaration(stmt_declaration) => todo!(), + Stmt::Declaration(declaration) => match declaration { + StmtDeclaration::Wire(wire) => todo!(), + StmtDeclaration::Reg(reg) => todo!(), + StmtDeclaration::Instance(StmtInstance { + annotations, + instance, + }) => { + self.compile_module( + InstantiatedModule::Child { + parent: parent_module, + instance: instance.intern_sized(), + } + .intern_sized(), + ); + todo!() + } + }, } } } - fn compile_module(&mut self, module: Interned>) -> CompiledModule { - let step_entry_pc = self.insns.next_insn_pc(); - match module.body() { + fn compile_module(&mut self, module: Interned) -> &CompiledModule { + let module_io = module + .leaf_module() + .module_io() + .iter() + .map( + |&AnnotatedModuleIO { + annotations: _, + module_io, + }| { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *module, + target: Target::from(module_io), + }) + }, + ) + .collect(); + match module.leaf_module().body() { ModuleBody::Normal(NormalModuleBody { body }) => { self.compile_block(module, body, CondStack::Always.intern_sized()) } ModuleBody::Extern(_extern_module_body) => { - todo!("simulating extern module: {}", module.name_id()); + todo!("simulating extern module: {:?}", module); } } - self.insns.push(Insn::Return, module.source_location()); - CompiledModule { step_entry_pc } + let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { + unreachable!("compiled same instantiated module twice"); + }; + entry.insert(CompiledModule { module_io }) } pub fn run(mut self) -> Compiled { - while let Some(module) = self.module_queue.pop() { - let compiling_module = self.modules[&module].clone(); - let mut compiling_module = compiling_module.borrow_mut(); - let CompilingModule { compiled_module } = &mut *compiling_module; - assert!(compiled_module.is_none()); - *compiled_module = Some(self.compile_module(module)); - } + self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); Compiled { insns: Insns::from(self.insns).intern_sized(), - modules: self - .modules - .into_iter() - .map(|(module, compiling_module)| { - ( - module, - Rc::into_inner(compiling_module) - .expect("only reference is Compiler::modules") - .into_inner() - .compiled_module - .expect("module is compiled by Compiler::run"), - ) - }) - .collect(), + modules: self.modules, } } } #[derive(Debug)] struct CompiledModule { - step_entry_pc: usize, + module_io: Interned<[CompiledValue]>, } #[derive(Debug)] pub struct Compiled { - insns: Interned, - modules: HashMap>, CompiledModule>, + insns: Interned>, + modules: HashMap, } impl Compiled { pub fn new(module: Module) -> Self { - let mut compiler = Compiler::new(); - compiler.add_module(module.canonical().intern()); - compiler.run() + Compiler::new(module.canonical().intern()).run() } } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index eed64e4..06d0025 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -3,21 +3,26 @@ use crate::{ source_location::SourceLocation, }; use num_bigint::BigInt; -use std::{convert::Infallible, fmt, hash::Hash}; +use std::{ + convert::Infallible, + fmt, + hash::Hash, + marker::PhantomData, + ops::{Deref, DerefMut, Index, IndexMut}, +}; pub(crate) type SmallUInt = u64; pub(crate) type SmallSInt = i64; macro_rules! impl_insns { ( - #[next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] - $vis:vis fn $State:ident::$run:ident(&mut $self:ident, $insns:ident: &[$Insn:ident]) -> $run_ret_ty:ty { - #[pc] - let mut $pc:ident: usize = $pc_init:expr; + #[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)* } - $(let mut $local:ident = $local_init:expr;)* main_loop!(); cleanup! { $($cleanup:tt)* @@ -47,22 +52,22 @@ macro_rules! impl_insns { } impl $State { - $vis fn $run(&mut $self, $insns: &[$Insn]) -> $run_ret_ty { - let mut $pc: usize = $pc_init; + $vis fn $run(&mut $self) -> $run_ret_ty { + let mut $state = $state_init; $($setup)* - let mut insn = $insns[$pc]; + let mut insn = $state.insns[$state.pc]; let retval = 'main_loop: loop { macro_rules! $next_macro { () => { - $pc += 1; - insn = $insns[$pc]; + $state.pc += 1; + insn = $state.insns[$state.pc]; continue 'main_loop; }; } macro_rules! $branch_macro { ($next_pc:expr) => { - $pc = $next_pc; - insn = $insns[$pc]; + $state.pc = $next_pc; + insn = $state.insns[$state.pc]; continue 'main_loop; }; } @@ -86,34 +91,37 @@ macro_rules! impl_insns { } pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { - type BoxedSlice: fmt::Debug + type Vec: fmt::Debug + Clone + Eq - + std::ops::Deref; + + Hash + + Send + + Sync + + 'static + + Default + + Deref + + FromIterator; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct InsnsBuildingDone; impl InsnsBuildingKind for InsnsBuildingDone { - type BoxedSlice = Interned<[T]>; + type Vec = Interned<[T]>; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct InsnsBuilding; impl InsnsBuildingKind for InsnsBuilding { - type BoxedSlice = Vec; + type Vec = Vec; } #[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct Insns { - insns: BK::BoxedSlice, - small_stack_size: usize, - big_stack_size: usize, - small_state_debug_names: BK::BoxedSlice>, - big_state_debug_names: BK::BoxedSlice>, - insn_source_locations: BK::BoxedSlice, +pub(crate) struct Insns { + insns: BK::Vec, + insn_source_locations: BK::Vec, + state_layout: StateLayout, } struct InsnsDebug<'a> { @@ -141,17 +149,11 @@ impl fmt::Debug for Insns { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { insns, - small_stack_size, - big_stack_size, - small_state_debug_names, - big_state_debug_names, insn_source_locations, + state_layout, } = self; f.debug_struct("Insns") - .field("small_stack_size", small_stack_size) - .field("big_stack_size", big_stack_size) - .field("small_state_debug_names", small_state_debug_names) - .field("big_state_debug_names", big_state_debug_names) + .field("state_layout", state_layout) .field( "insns", &InsnsDebug { @@ -163,15 +165,662 @@ impl fmt::Debug for Insns { } } +pub(crate) trait StatePartKind: + Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default +{ + const NAME: &'static str; + type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type State: fmt::Debug + 'static + Clone; + type BorrowedState<'a>: 'a; + fn new_state(len: StatePartLen) -> Self::State; + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; +} + +macro_rules! make_state_part_kinds { + ( + $( + #[state, field = $state_field:ident] + impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt + )+ + $( + #[type, field = $type_field:ident] + impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt + )+ + ) => { + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $StateKind; + + impl $StateStatePartKind for $StateKind $state_impl_body + )+ + + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $TypeKind; + + impl $TypeStatePartKind for $TypeKind $type_impl_body + )+ + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLayout { + pub(crate) ty: TypeLayout, + $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)+ + } + + impl Copy for StateLayout {} + + impl StateLayout { + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_field: self.$state_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_field.is_empty())+ + } + pub(crate) fn empty() -> Self { + Self { + ty: TypeLayout::empty(), + $($state_field: StatePartLayout::empty(),)+ + } + } + } + + impl StateLayout { + pub(crate) fn next_index(&self) -> StateIndex { + StateIndex { + ty: self.ty.next_index(), + $($state_field: self.$state_field.next_index(),)+ + } + } + pub(crate) fn allocate( + &mut self, + layout: &StateLayout, + ) -> StateIndexRange { + StateIndexRange { + ty: self.ty.allocate(&layout.ty), + $($state_field: self.$state_field.allocate(&layout.$state_field),)+ + } + } + } + + impl From> for StateLayout { + fn from(v: StateLayout) -> Self { + Self { + ty: v.ty.into(), + $($state_field: v.$state_field.into(),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndexRange { + pub(crate) ty: TypeIndexRange, + $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)+ + } + + impl StateIndexRange { + pub(crate) fn start(self) -> StateIndex { + StateIndex { + ty: self.ty.start(), + $($state_field: self.$state_field.start(),)+ + } + } + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_field: self.$state_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_field.is_empty())+ + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLen { + pub(crate) ty: TypeLen, + $(pub(crate) $state_field: StatePartLen<$StateKind>,)+ + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndex { + pub(crate) ty: TypeIndex, + $(pub(crate) $state_field: StatePartIndex<$StateKind>,)+ + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLayout { + $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)+ + } + + impl Copy for TypeLayout {} + + impl TypeLayout { + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_field.is_empty())&&+ + } + pub(crate) fn empty() -> Self { + Self { + $($type_field: StatePartLayout::empty(),)+ + } + } + } + + impl TypeLayout { + pub(crate) fn next_index(&self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.next_index(),)+ + } + } + pub(crate) fn allocate( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + TypeIndexRange { + $($type_field: self.$type_field.allocate(&layout.$type_field),)+ + } + } + } + + impl From> for TypeLayout { + fn from(v: TypeLayout) -> Self { + Self { + $($type_field: v.$type_field.into(),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndexRange { + $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)+ + } + + impl TypeIndexRange { + pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { + Self { + $($type_field: StatePartIndexRange { + start: start.$type_field, + len: len.$type_field, + },)+ + } + } + pub(crate) fn start(self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.start(),)+ + } + } + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_field.is_empty()) &&+ + } + pub(crate) fn slice(self, index: TypeIndexRange) -> Self { + Self { + $($type_field: self.$type_field.slice(index.$type_field),)+ + } + } + pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { + Self { + $($type_field: self.$type_field.index_array(element_size.$type_field, index),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLen { + $(pub(crate) $type_field: StatePartLen<$TypeKind>,)+ + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndex { + $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)+ + } + + #[derive(Debug)] + pub(crate) struct State { + pub(crate) insns: Interned>, + pub(crate) pc: usize, + $(pub(crate) $state_field: StatePart<$StateKind>,)+ + $(pub(crate) $type_field: StatePart<$TypeKind>,)+ + } + + impl State { + pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { + BorrowedState { + orig_insns: self.insns, + insns: &self.insns.insns, + pc: self.pc, + orig_pc: &mut self.pc, + $($state_field: self.$state_field.borrow(),)+ + $($type_field: self.$type_field.borrow(),)+ + } + } + } + + #[derive(Debug)] + pub(crate) struct BorrowedState<'a> { + pub(crate) orig_insns: Interned>, + pub(crate) insns: &'a [Insn], + pub(crate) orig_pc: &'a mut usize, + pub(crate) pc: usize, + $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)+ + $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)+ + } + + impl Drop for BorrowedState<'_> { + fn drop(&mut self) { + *self.orig_pc = self.pc; + } + } + }; +} + +make_state_part_kinds! { + #[state, field = small_stack] + impl StatePartKind for StatePartKindSmallStack { + const NAME: &'static str = "SmallStack"; + type DebugData = (); + type State = Stack; + type BorrowedState<'a> = BorrowedStack<'a, SmallUInt>; + fn new_state(len: StatePartLen) -> Self::State { + Stack::new(len.value.try_into().expect("state is too big")) + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state.borrow() + } + } + #[state, field = big_stack] + impl StatePartKind for StatePartKindBigStack { + const NAME: &'static str = "BigStack"; + type DebugData = (); + type State = Stack; + type BorrowedState<'a> = BorrowedStack<'a, BigInt>; + fn new_state(len: StatePartLen) -> Self::State { + Stack::new(len.value.try_into().expect("state is too big")) + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state.borrow() + } + } + #[type, field = small_slots] + impl StatePartKind for StatePartKindSmallSlots { + const NAME: &'static str = "SmallSlots"; + type DebugData = SlotDebugData; + type State = Box<[SmallUInt]>; + type BorrowedState<'a> = &'a mut [SmallUInt]; + fn new_state(len: StatePartLen) -> Self::State { + vec![0; len.value.try_into().expect("state is too big")].into_boxed_slice() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + } + #[type, field = big_slots] + impl StatePartKind for StatePartKindBigSlots { + const NAME: &'static str = "BigSlots"; + type DebugData = SlotDebugData; + type State = Box<[BigInt]>; + type BorrowedState<'a> = &'a mut [BigInt]; + fn new_state(len: StatePartLen) -> Self::State { + std::iter::repeat_with(BigInt::default).take(len.value.try_into().expect("state is too big")).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Stack { + storage: Box<[T]>, + cur_len: usize, +} + +impl Stack { + pub(crate) fn new(len: usize) -> Self + where + T: Default, + { + Self { + storage: std::iter::repeat_with(T::default).take(len).collect(), + cur_len: 0, + } + } + pub(crate) fn borrow(&mut self) -> BorrowedStack<'_, T> { + BorrowedStack { + storage: &mut self.storage, + cur_len: self.cur_len, + stack_cur_len: &mut self.cur_len, + } + } + pub(crate) fn clear(&mut self) { + self.cur_len = 0; + } +} + +#[derive(Debug)] +pub(crate) struct BorrowedStack<'a, T> { + storage: &'a mut [T], + cur_len: usize, + stack_cur_len: &'a mut usize, +} + +impl<'a, T> BorrowedStack<'a, T> { + pub(crate) fn push(&mut self, value: T) { + self.storage[self.cur_len] = value; + self.cur_len += 1; + } + pub(crate) fn pop(&mut self) -> T + where + T: Default, + { + self.cur_len -= 1; + std::mem::take(&mut self.storage[self.cur_len]) + } +} + +impl Drop for BorrowedStack<'_, T> { + fn drop(&mut self) { + *self.stack_cur_len = self.cur_len; + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) struct SlotDebugData { + pub(crate) name: Interned, +} + +impl SlotDebugData { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + let mut name = String::with_capacity(self.name.len() + prefix.len()); + name.push_str(prefix); + name.push_str(&self.name); + Self { + name: Intern::intern_owned(name), + } + } +} + +impl, BK: InsnsBuildingKind> StatePartLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_prefixed_debug_names(prefix)) + .collect(), + _phantom: PhantomData, + } + } +} + +impl TypeLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + small_slots: self.small_slots.with_prefixed_debug_names(prefix), + big_slots: self.big_slots.with_prefixed_debug_names(prefix), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartIndex { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl fmt::Debug for StatePartIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartIndex<{}>({})", K::NAME, self.value) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartLen { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartLen { + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } +} + +impl fmt::Debug for StatePartLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartLen<{}>({})", K::NAME, self.value) + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartLayout { + pub(crate) debug_data: BK::Vec, + pub(crate) _phantom: PhantomData, +} + +impl Copy for StatePartLayout {} + +impl StatePartLayout { + pub(crate) fn len(&self) -> StatePartLen { + StatePartLen::new( + self.debug_data + .len() + .try_into() + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn is_empty(&self) -> bool { + self.debug_data.is_empty() + } + pub(crate) fn empty() -> Self { + Self { + debug_data: Default::default(), + _phantom: PhantomData, + } + } +} + +impl From> + for StatePartLayout +{ + fn from(value: StatePartLayout) -> Self { + Self { + debug_data: Intern::intern_owned(value.debug_data), + _phantom: PhantomData, + } + } +} + +impl StatePartLayout { + pub(crate) fn scalar(debug_data: K::DebugData) -> Self { + Self { + debug_data: vec![debug_data], + _phantom: PhantomData, + } + } + pub(crate) fn next_index(&self) -> StatePartIndex { + StatePartIndex { + value: self.len().value, + _phantom: PhantomData, + } + } + pub(crate) fn allocate( + &mut self, + layout: &StatePartLayout, + ) -> StatePartIndexRange { + let start = self.next_index(); + let len = layout.len(); + let Self { + debug_data, + _phantom: _, + } = self; + debug_data.extend_from_slice(&layout.debug_data); + StatePartIndexRange { start, len } + } +} + +impl fmt::Debug for StatePartLayout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + debug_data, + _phantom: _, + } = self; + write!(f, "StatePartAllocationLayout<{}>", K::NAME)?; + f.debug_struct("") + .field("len", &debug_data.len()) + .field("debug_data", debug_data) + .finish_non_exhaustive() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartIndexRange { + pub(crate) start: StatePartIndex, + pub(crate) len: StatePartLen, +} + +impl StatePartIndexRange { + pub(crate) fn start(self) -> StatePartIndex { + self.start + } + pub(crate) fn end(self) -> StatePartIndex { + StatePartIndex { + value: self + .start + .value + .checked_add(self.len.value) + .expect("state part allocation layout is too big"), + _phantom: PhantomData, + } + } + pub(crate) fn len(self) -> StatePartLen { + self.len + } + pub(crate) fn is_empty(self) -> bool { + self.len.value == 0 + } + pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { + assert!(index.end().value <= self.len.value, "index out of range"); + Self { + start: StatePartIndex { + value: self.start.value + index.start.value, + _phantom: PhantomData, + }, + len: index.len, + } + } + pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { + if element_size.value == 0 { + assert_eq!( + self.len.value, 0, + "array with zero-sized element must also be zero-sized", + ); + return self; + } + assert!( + self.len.value % element_size.value == 0, + "array's size must be a multiple of its element size", + ); + self.slice(StatePartIndexRange { + start: StatePartIndex { + value: index + .try_into() + .ok() + .and_then(|index| element_size.value.checked_mul(index)) + .expect("index out of range"), + _phantom: PhantomData, + }, + len: element_size, + }) + } +} + +impl fmt::Debug for StatePartIndexRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StatePartIndexRange<{}> {{ start: {}, len: {} }}", + K::NAME, + self.start.value, + self.len.value + ) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePart { + pub(crate) value: K::State, +} + +impl StatePart { + pub(crate) fn new(len: StatePartLen) -> Self { + Self { + value: K::new_state(len), + } + } + pub(crate) fn borrow<'a>(&'a mut self) -> BorrowedStatePart<'a, K> { + BorrowedStatePart { + value: K::borrow_state(&mut self.value), + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> { + pub(crate) value: K::BorrowedState<'a>, +} + +impl<'a, K: StatePartKind: Deref>>, T> + Index> for BorrowedStatePart<'a, K> +{ + type Output = T; + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.value[index.value as usize] + } +} + +impl<'a, K: StatePartKind: DerefMut>>, T> + IndexMut> for BorrowedStatePart<'a, K> +{ + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.value[index.value as usize] + } +} + +impl<'a, K: StatePartKind = BorrowedStack<'a, T>>, T: 'a> + BorrowedStatePart<'a, K> +{ + pub(crate) fn push(&mut self, value: T) { + self.value.push(value) + } + pub(crate) fn pop(&mut self) -> T + where + T: Default, + { + self.value.pop() + } +} + impl Insns { pub(crate) fn new() -> Self { Self { insns: Vec::new(), - small_stack_size: 0, - big_stack_size: 0, - small_state_debug_names: Vec::new(), - big_state_debug_names: Vec::new(), insn_source_locations: Vec::new(), + state_layout: StateLayout::empty(), } } pub(crate) fn last_insn_pc(&self) -> usize { @@ -187,39 +836,27 @@ impl Insns { self.insns.push(insn); self.insn_source_locations.push(source_location); } -} - -impl From> for Insns { - fn from(input: Insns) -> Self { - let Insns { - insns, - small_stack_size, - big_stack_size, - small_state_debug_names, - big_state_debug_names, - insn_source_locations, - } = input; - Insns { - insns: Intern::intern_owned(insns), - small_stack_size, - big_stack_size, - small_state_debug_names: Intern::intern_owned(small_state_debug_names), - big_state_debug_names: Intern::intern_owned(big_state_debug_names), - insn_source_locations: Intern::intern_owned(insn_source_locations), - } + pub(crate) fn allocate_variable( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + self.state_layout.ty.allocate(layout) } } -#[derive(Debug)] -pub(crate) struct State { - pub(crate) insns: Interned, - pub(crate) pc: usize, - pub(crate) small_stack: Box<[SmallUInt]>, - pub(crate) small_stack_top: usize, - pub(crate) small_states: Box<[SmallUInt]>, - pub(crate) big_stack: Box<[BigInt]>, - pub(crate) big_stack_top: usize, - pub(crate) big_states: Box<[BigInt]>, +impl From> for Insns { + fn from(input: Insns) -> Self { + let Insns { + insns, + insn_source_locations, + state_layout, + } = input; + Insns { + insns: Intern::intern_owned(insns), + insn_source_locations: Intern::intern_owned(insn_source_locations), + state_layout: state_layout.into(), + } + } } impl State { @@ -227,235 +864,191 @@ impl State { let Self { insns: _, pc, - small_stack: _, - small_stack_top, - small_states: _, - big_stack: _, - big_stack_top, - big_states: _, + small_stack, + big_stack, + small_slots: _, + big_slots: _, } = self; *pc = entry_pc; - *small_stack_top = 0; - *big_stack_top = 0; + small_stack.value.clear(); + big_stack.value.clear(); } } impl_insns! { - #[next_macro = next, branch_macro = branch] - pub(crate) fn State::run(&mut self, insns: &[Insn]) -> () { - #[pc] - let mut pc: usize = self.pc; - setup! { - let small_states = &mut *self.small_states; - let small_stack = &mut *self.small_stack; - let mut small_stack_top = self.small_stack_top; - macro_rules! small_stack_push { - ($v:expr) => { - { - small_stack[small_stack_top] = $v; - small_stack_top += 1; - } - }; - } - macro_rules! small_stack_pop { - () => { - { - small_stack_top -= 1; - small_stack[small_stack_top] - } - }; - } - let big_states = &mut *self.big_states; - let big_stack = &mut *self.big_stack; - let mut big_stack_top = self.big_stack_top; - macro_rules! big_stack_push { - ($v:expr) => { - { - big_stack[big_stack_top] = $v; - big_stack_top += 1; - } - }; - } - macro_rules! big_stack_pop { - () => { - { - big_stack_top -= 1; - big_stack[big_stack_top] - } - }; - } - } + #[insn = Insn, next_macro = next, branch_macro = branch] + pub(crate) fn State::run(&mut self) -> () { + #[state] + let mut state = self.borrow(); + setup! {} main_loop!(); - cleanup! { - self.pc = pc; - self.small_stack_top = small_stack_top; - } + cleanup! {} } ReadSmall { - index: u32, + index: StatePartIndex, } => { - small_stack_push!(small_states[index as usize]); + state.small_stack.push(state.small_slots[index]); next!(); } SExtSmall { /// number of bits in a [`SmallSInt`] that aren't used unused_bit_count: u8, } => { - let mut value = small_stack_pop!() as SmallSInt; + let mut value = state.small_stack.pop() as SmallSInt; value <<= unused_bit_count; value >>= unused_bit_count; - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } ZExtSmall { /// number of bits in a [`SmallUInt`] that aren't used unused_bit_count: u8, } => { - let mut value = small_stack_pop!(); + let mut value = state.small_stack.pop(); value <<= unused_bit_count; value >>= unused_bit_count; - small_stack_push!(value); + state.small_stack.push(value); next!(); } AndSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs & rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } OrSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs | rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } XorSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs ^ rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } NotSmall => { - let value = small_stack_pop!(); - small_stack_push!(!value); + let value = state.small_stack.pop(); + state.small_stack.push(!value); next!(); } CmpEqSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs == rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpNeSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs != rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLTSmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs < rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLESmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs <= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGTSmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs > rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGESmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs >= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLTSmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs < rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLESmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs <= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGTSmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs > rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGESmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs >= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } AddSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_add(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } SubSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_sub(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } NegSmall => { - let value = small_stack_pop!(); + let value = state.small_stack.pop(); let value = value.wrapping_neg(); - small_stack_push!(value); + state.small_stack.push(value); next!(); } MulSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_mul(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } ConstU32Small { value: u32, } => { - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } ConstS32Small { value: i32, } => { - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } WriteSmall { - index: u32, + index: StatePartIndex, } => { - small_states[index as usize] = small_stack_pop!(); + state.small_slots[index] = state.small_stack.pop(); next!(); } Return => {