From 6f904148c492a4a5d00ba4030f65b4c7e58140b8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 01:19:07 -0800 Subject: [PATCH 001/109] WIP adding simulator --- crates/fayalite/src/lib.rs | 1 + crates/fayalite/src/sim.rs | 206 +++++++++++ crates/fayalite/src/sim/interpreter.rs | 464 +++++++++++++++++++++++++ 3 files changed, 671 insertions(+) create mode 100644 crates/fayalite/src/sim.rs create mode 100644 crates/fayalite/src/sim/interpreter.rs 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/sim.rs b/crates/fayalite/src/sim.rs new file mode 100644 index 0000000..3aa69a6 --- /dev/null +++ b/crates/fayalite/src/sim.rs @@ -0,0 +1,206 @@ +//! Fayalite Simulation + +use crate::{ + bundle::{Bundle, BundleType}, + enum_::Enum, + expr::Expr, + int::Bool, + intern::{Intern, Interned}, + module::{ + Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf, + StmtMatch, + }, + sim::interpreter::{Insn, Insns, InsnsBuilding}, + source_location::SourceLocation, +}; +use hashbrown::HashMap; +use std::{cell::RefCell, rc::Rc}; + +mod interpreter; + +#[derive(Debug)] +struct CompilingModule { + compiled_module: Option, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CondStack { + Always, + IfTrue { + parent: Interned, + cond: Expr, + source_location: SourceLocation, + }, + IfFalse { + parent: Interned, + cond: Expr, + source_location: SourceLocation, + }, + MatchArm { + parent: Interned, + enum_expr: Expr, + variant_index: usize, + source_location: SourceLocation, + }, +} + +#[derive(Debug)] +pub struct Compiler { + insns: Insns, + modules: HashMap>, Rc>>, + module_queue: Vec>>, +} + +impl Compiler { + pub fn new() -> Self { + Self { + insns: Insns::new(), + modules: HashMap::new(), + module_queue: Vec::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_block( + &mut self, + module: Interned>, + block: Block, + cond_stack: Interned, + ) { + 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, + }) => todo!(), + Stmt::Formal(StmtFormal { + kind, + clk, + pred, + en, + text, + source_location, + }) => todo!("implement simulating formal statements"), + Stmt::If(StmtIf { + cond, + source_location, + blocks: [then_block, else_block], + }) => { + self.compile_block( + module, + then_block, + CondStack::IfTrue { + parent: cond_stack, + cond, + source_location, + } + .intern_sized(), + ); + self.compile_block( + module, + else_block, + CondStack::IfFalse { + parent: cond_stack, + cond, + source_location, + } + .intern_sized(), + ); + } + Stmt::Match(StmtMatch { + expr, + source_location, + blocks, + }) => { + for (variant_index, block) in blocks.into_iter().enumerate() { + self.compile_block( + module, + block, + CondStack::MatchArm { + parent: cond_stack, + enum_expr: expr, + variant_index, + source_location, + } + .intern_sized(), + ); + } + } + Stmt::Declaration(stmt_declaration) => todo!(), + } + } + } + fn compile_module(&mut self, module: Interned>) -> CompiledModule { + let step_entry_pc = self.insns.next_insn_pc(); + match 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()); + } + } + self.insns.push(Insn::Return, module.source_location()); + CompiledModule { step_entry_pc } + } + 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)); + } + 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(), + } + } +} + +#[derive(Debug)] +struct CompiledModule { + step_entry_pc: usize, +} + +#[derive(Debug)] +pub struct Compiled { + insns: Interned, + modules: HashMap>, CompiledModule>, +} + +impl Compiled { + pub fn new(module: Module) -> Self { + let mut compiler = Compiler::new(); + compiler.add_module(module.canonical().intern()); + compiler.run() + } +} + +pub struct Simulation { + state: interpreter::State, +} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs new file mode 100644 index 0000000..eed64e4 --- /dev/null +++ b/crates/fayalite/src/sim/interpreter.rs @@ -0,0 +1,464 @@ +use crate::{ + intern::{Intern, Interned}, + source_location::SourceLocation, +}; +use num_bigint::BigInt; +use std::{convert::Infallible, fmt, hash::Hash}; + +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; + setup! { + $($setup:tt)* + } + $(let mut $local:ident = $local_init:expr;)* + main_loop!(); + cleanup! { + $($cleanup:tt)* + } + } + $( + $(#[$insn_meta:meta])* + $insn_name:ident $({ + $( + $(#[$field_meta:meta])* + $field_name:ident: $field_ty:ty, + )* + })? => $block:block + )+ + ) => { + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + $vis enum $Insn { + $( + $(#[$insn_meta])* + $insn_name $({ + $( + $(#[$field_meta])* + $field_name: $field_ty, + )* + })?, + )+ + } + + impl $State { + $vis fn $run(&mut $self, $insns: &[$Insn]) -> $run_ret_ty { + let mut $pc: usize = $pc_init; + $($setup)* + let mut insn = $insns[$pc]; + let retval = 'main_loop: loop { + macro_rules! $next_macro { + () => { + $pc += 1; + insn = $insns[$pc]; + continue 'main_loop; + }; + } + macro_rules! $branch_macro { + ($next_pc:expr) => { + $pc = $next_pc; + insn = $insns[$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 BoxedSlice: fmt::Debug + + Clone + + Eq + + std::ops::Deref; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuildingDone; + +impl InsnsBuildingKind for InsnsBuildingDone { + type BoxedSlice = Interned<[T]>; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuilding; + +impl InsnsBuildingKind for InsnsBuilding { + type BoxedSlice = 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, +} + +struct InsnsDebug<'a> { + insns: &'a [Insn], + insn_source_locations: &'a [SourceLocation], +} + +impl<'a> fmt::Debug for InsnsDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug_list = f.debug_list(); + let mut last_source_location = None; + for (insn, source_location) in self.insns.iter().zip(self.insn_source_locations) { + if Some(source_location) != last_source_location { + debug_list.entry(&format_args!("// at: {source_location}\n{insn:?}")); + } else { + debug_list.entry(insn); + } + last_source_location = Some(source_location); + } + debug_list.finish() + } +} + +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, + } = 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( + "insns", + &InsnsDebug { + insns, + insn_source_locations, + }, + ) + .finish_non_exhaustive() + } +} + +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(), + } + } + pub(crate) fn last_insn_pc(&self) -> usize { + self.insns + .len() + .checked_sub(1) + .expect("no last instruction") + } + pub(crate) fn next_insn_pc(&self) -> usize { + self.insns.len() + } + pub(crate) fn push(&mut self, insn: Insn, source_location: SourceLocation) { + 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), + } + } +} + +#[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 State { + pub(crate) fn setup_call(&mut self, entry_pc: usize) { + let Self { + insns: _, + pc, + small_stack: _, + small_stack_top, + small_states: _, + big_stack: _, + big_stack_top, + big_states: _, + } = self; + *pc = entry_pc; + *small_stack_top = 0; + *big_stack_top = 0; + } +} + +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] + } + }; + } + } + main_loop!(); + cleanup! { + self.pc = pc; + self.small_stack_top = small_stack_top; + } + } + ReadSmall { + index: u32, + } => { + small_stack_push!(small_states[index as usize]); + next!(); + } + SExtSmall { + /// number of bits in a [`SmallSInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = small_stack_pop!() as SmallSInt; + value <<= unused_bit_count; + value >>= unused_bit_count; + 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!(); + value <<= unused_bit_count; + value >>= unused_bit_count; + small_stack_push!(value); + next!(); + } + AndSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs & rhs; + small_stack_push!(value); + next!(); + } + OrSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs | rhs; + small_stack_push!(value); + next!(); + } + XorSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs ^ rhs; + small_stack_push!(value); + next!(); + } + NotSmall => { + let value = small_stack_pop!(); + small_stack_push!(!value); + next!(); + } + CmpEqSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs == rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpNeSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs != rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLTSmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs < rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLESmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs <= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGTSmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs > rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGESmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs >= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLTSmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs < rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLESmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs <= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGTSmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs > rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGESmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs >= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + AddSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_add(rhs); + small_stack_push!(value); + next!(); + } + SubSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_sub(rhs); + small_stack_push!(value); + next!(); + } + NegSmall => { + let value = small_stack_pop!(); + let value = value.wrapping_neg(); + small_stack_push!(value); + next!(); + } + MulSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_mul(rhs); + small_stack_push!(value); + next!(); + } + ConstU32Small { + value: u32, + } => { + small_stack_push!(value as SmallUInt); + next!(); + } + ConstS32Small { + value: i32, + } => { + small_stack_push!(value as SmallUInt); + next!(); + } + WriteSmall { + index: u32, + } => { + small_states[index as usize] = small_stack_pop!(); + next!(); + } + Return => { + break; + } +} From 479d59b28722c79873e7c5fa1caf1230001b7ee1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 21:46:34 -0800 Subject: [PATCH 002/109] 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 => { From b288d6f8f20d7bceee96375cb91ed8be336ee28b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 21:50:31 -0800 Subject: [PATCH 003/109] add missing copyright headers --- crates/fayalite/src/sim.rs | 3 +++ crates/fayalite/src/sim/interpreter.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 3b55379..6114044 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + //! Fayalite Simulation use crate::{ diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 06d0025..f327510 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + use crate::{ intern::{Intern, Interned}, source_location::SourceLocation, From 277d3e0d4d37a36eaba12fe684225dae38b0163d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 10 Nov 2024 22:12:57 -0800 Subject: [PATCH 004/109] working on simulator --- Cargo.lock | 7 + Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/sim.rs | 709 +++++++++++++++++++++++-- crates/fayalite/src/sim/interpreter.rs | 665 +++++++++++++++++------ crates/fayalite/src/util.rs | 3 +- crates/fayalite/src/util/misc.rs | 16 + 7 files changed, 1212 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 500bd34..2fca6fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,6 +319,7 @@ dependencies = [ "serde_json", "tempfile", "trybuild", + "vec_map", "which", ] @@ -720,6 +721,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..44f5c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,4 +39,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..8e90a74 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -28,6 +28,7 @@ os_pipe.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/sim.rs b/crates/fayalite/src/sim.rs index 6114044..9b40458 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -4,29 +4,28 @@ //! Fayalite Simulation use crate::{ - bundle::{Bundle, BundleField, BundleType}, - enum_::Enum, + bundle::{BundleField, BundleType}, expr::{ target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - Expr, + ExprEnum, }, - int::Bool, intern::{Intern, Interned, Memoize}, module::{ - AnnotatedModuleIO, Block, Instance, Module, ModuleBody, NormalModuleBody, Stmt, - StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, + AnnotatedModuleIO, Block, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, + StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, + prelude::*, sim::interpreter::{ - Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, StatePartLayout, TypeIndex, - TypeIndexRange, TypeLayout, + Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, SmallUInt, StatePartIndex, + StatePartIndexMap, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, + TypeLen, TypeParts, MIN_BITS_FOR_NEEDING_BIG, }, - source_location::SourceLocation, - ty::CanonicalType, }; use hashbrown::HashMap; -use std::fmt; +use std::{fmt, marker::PhantomData, mem}; mod interpreter; @@ -35,17 +34,17 @@ enum CondStack { Always, IfTrue { parent: Interned, - cond: Expr, + cond: CompiledExpr, source_location: SourceLocation, }, IfFalse { parent: Interned, - cond: Expr, + cond: CompiledExpr, source_location: SourceLocation, }, MatchArm { parent: Interned, - enum_expr: Expr, + enum_expr: CompiledExpr, variant_index: usize, source_location: SourceLocation, }, @@ -95,7 +94,7 @@ struct TargetInInstantiatedModule { #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] struct CompiledBundleField { offset: TypeIndex, - ty: CompiledTypeLayout, + ty: CompiledTypeLayout, } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -103,7 +102,7 @@ enum CompiledTypeLayoutBody { Scalar, Array { /// debug names are ignored, use parent's layout instead - element: Interned, + element: Interned>, }, Bundle { /// debug names are ignored, use parent's layout instead @@ -112,13 +111,13 @@ enum CompiledTypeLayoutBody { } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledTypeLayout { - ty: CanonicalType, +struct CompiledTypeLayout { + ty: T, layout: TypeLayout, body: CompiledTypeLayoutBody, } -impl CompiledTypeLayout { +impl CompiledTypeLayout { fn with_prefixed_debug_names(self, prefix: &str) -> Self { let Self { ty, layout, body } = self; Self { @@ -127,13 +126,13 @@ impl CompiledTypeLayout { body, } } - fn get(ty: CanonicalType) -> Self { + 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; + type Output = CompiledTypeLayout; fn inner(self, input: &Self::Input) -> Self::Output { match input { @@ -146,8 +145,10 @@ impl CompiledTypeLayout { | CanonicalType::Reset(_) | CanonicalType::Clock(_) => { let mut layout = TypeLayout::empty(); - let debug_data = SlotDebugData { name: "".intern() }; - if input.bit_width() > interpreter::SmallUInt::BITS as usize { + let debug_data = SlotDebugData { + name: Interned::default(), + }; + if input.bit_width() >= interpreter::MIN_BITS_FOR_NEEDING_BIG { layout.big_slots = StatePartLayout::scalar(debug_data); } else { layout.small_slots = StatePartLayout::scalar(debug_data); @@ -205,24 +206,32 @@ impl CompiledTypeLayout { } } } - MyMemoize.get_owned(ty) + 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, +struct CompiledValue { + layout: CompiledTypeLayout, range: TypeIndexRange, - write: Option<(CompiledTypeLayout, TypeIndexRange)>, + write: Option<(CompiledTypeLayout, TypeIndexRange)>, } -impl CompiledValue { - fn map( +impl CompiledValue { + fn map( self, - mut f: impl FnMut(CompiledTypeLayout, TypeIndexRange) -> (CompiledTypeLayout, TypeIndexRange), - ) -> Self { + mut f: impl FnMut( + CompiledTypeLayout, + TypeIndexRange, + ) -> (CompiledTypeLayout, TypeIndexRange), + ) -> CompiledValue { let (layout, range) = f(self.layout, self.range); - Self { + CompiledValue { layout, range, write: self.write.map(|(layout, range)| f(layout, range)), @@ -230,12 +239,98 @@ impl CompiledValue { } } +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExprDynIndex { + index_slot: StatePartIndex, + len: TypeLen, + stride: TypeLen, +} + +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExpr { + static_part: CompiledValue, + dyn_indexes: Interned<[CompiledExprDynIndex]>, +} + +impl From> for CompiledExpr { + fn from(static_part: CompiledValue) -> Self { + Self { + static_part, + dyn_indexes: Interned::default(), + } + } +} + +impl CompiledExpr { + fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledExpr { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExpr { + static_part: static_part.map(|CompiledTypeLayout { ty, layout, body }, range| { + ( + CompiledTypeLayout { + ty: f(ty), + layout, + body, + }, + range, + ) + }), + dyn_indexes, + } + } +} + +#[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() + } +} + +impl StatePartsValue for SlotSet { + type Value = Vec>; +} + +#[derive(Debug)] +struct Assignment { + inputs: SlotSet, + insns: Vec, +} + +#[derive(Debug, Default)] +struct StatePartAssignments { + written_slot_to_assignment_indexes_map: StatePartIndexMap>, +} + +#[derive(Debug, Default)] +struct SlotAssignments { + assignments: Vec, + parts: TypeParts, +} + +impl StatePartsValue for SlotAssignments { + type Value = StatePartAssignments; +} + #[derive(Debug)] pub struct Compiler { insns: Insns, base_module: Interned>, modules: HashMap, - compiled_values: HashMap, + compiled_values: HashMap>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + expanded_to_big: HashMap, CompiledValue>, + slots_assignments: SlotAssignments, } impl Compiler { @@ -245,13 +340,20 @@ impl Compiler { base_module, modules: HashMap::new(), compiled_values: HashMap::new(), + compiled_exprs: HashMap::new(), + compiled_exprs_to_values: HashMap::new(), + expanded_to_big: HashMap::new(), + slots_assignments: SlotAssignments::default(), } } - fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue { + fn compile_value( + &mut self, + target: TargetInInstantiatedModule, + ) -> CompiledValue { if let Some(&retval) = self.compiled_values.get(&target) { return retval; } - match target.target { + 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!( @@ -319,7 +421,494 @@ impl Compiler { TargetPathElement::DynArrayElement(_) => unreachable!(), } } + }; + self.compiled_values.insert(target, retval); + retval + } + fn compiled_expr_to_value( + &mut self, + expr: CompiledExpr, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + return retval; } + let CompiledExpr { + static_part: mut retval, + dyn_indexes, + } = expr; + for CompiledExprDynIndex { + index_slot, + len, + stride, + } in dyn_indexes + { + todo!(); + } + self.compiled_exprs_to_values.insert(expr, retval); + retval + } + fn simple_nary_expr( + &mut self, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns: impl FnOnce( + &mut Self, + CompiledValue, + &mut [CompiledValue; N], + ) -> Vec, + ) -> CompiledValue { + let mut inputs = inputs.map(|input| { + let input = self.compile_expr(input); + self.compiled_expr_to_value(input) + }); + let layout = CompiledTypeLayout::get(dest_ty); + let range = self.insns.allocate_variable(&layout.layout); + let retval = CompiledValue { + layout, + range, + write: None, + }; + let insns = make_insns(self, retval, &mut inputs); + let mut inputs_set = SlotSet::default(); + for input in inputs { + let TypeIndexRange { + small_slots, + big_slots, + } = input.range; + inputs_set.0.small_slots.extend(small_slots.iter()); + inputs_set.0.big_slots.extend(big_slots.iter()); + } + let assignment_index = self.slots_assignments.assignments.len(); + self.slots_assignments.assignments.push(Assignment { + inputs: inputs_set, + insns, + }); + let TypeIndexRange { + small_slots, + big_slots, + } = range; + for i in small_slots.iter() { + self.slots_assignments + .parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(i) + .or_insert_with(Vec::new) + .push(assignment_index); + } + for i in big_slots.iter() { + self.slots_assignments + .parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(i) + .or_insert_with(Vec::new) + .push(assignment_index); + } + retval + } + fn expand_to_big(&mut self, expr: Expr) -> CompiledValue { + if let Some(&retval) = self.expanded_to_big.get(&expr) { + return retval; + } + let input = self.compile_expr(expr); + let input = self.compiled_expr_to_value(input); + let retval = match input.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: _, + } => input, + len => { + assert_eq!( + Some(StatePartLen { + value: 1, + _phantom: PhantomData, + }), + len.only_small() + ); + let signed = match Expr::ty(expr) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Enum(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + self.simple_nary_expr( + if signed { + SInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() + } else { + UInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() + }, + [expr], + |_this, dest, [input]| { + let dest = dest.range.big_slots.start; + let src = input.range.small_slots.start; + let unused_bit_count = + interpreter::SmallUInt::BITS as u8 - Expr::ty(expr).bit_width() as u8; + if signed { + vec![Insn::SExtSmallToBig { + dest, + src, + unused_bit_count, + }] + } else { + vec![Insn::ZExtSmallToBig { + dest, + src, + unused_bit_count, + }] + } + }, + ) + } + }; + self.expanded_to_big.insert(expr, retval); + retval + } + fn simple_nary_small_or_big_expr( + &mut self, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns_small: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + make_insns_big: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + make_insns_big_to_small: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + ) -> CompiledValue { + self.simple_nary_expr(dest_ty, inputs, |this, dest, compiled_inputs| { + let all_inputs_only_small = compiled_inputs + .iter() + .all(|input| input.range.len().only_small().is_some()); + if all_inputs_only_small { + if dest.range.len().only_small().is_some() { + // all small + assert_eq!(dest.range.len().small_slots.value, 1); + return make_insns_small( + dest.range.small_slots.start, + compiled_inputs.map( + |CompiledValue { + layout, + range, + write: _, + }| { + assert_eq!(range.small_slots.len().value, 1); + range.small_slots.start + }, + ), + ); + } else { + // inputs small, dest big -- expand inputs to big + for (&input, compiled_input) in inputs.iter().zip(&mut *compiled_inputs) { + *compiled_input = this.expand_to_big(input); + } + } + } + let big_inputs = compiled_inputs.map( + |CompiledValue { + layout, + range: + TypeIndexRange { + small_slots, + big_slots, + }, + write: _, + }| { + assert_eq!(small_slots.len().value, 0); + assert_eq!(big_slots.len().value, 1); + big_slots.start + }, + ); + if dest.range.len().only_small().is_some() { + // inputs big, dest small + assert_eq!(dest.range.len().small_slots.value, 1); + return make_insns_big_to_small(dest.range.small_slots.start, big_inputs); + } + let TypeIndexRange { + small_slots, + big_slots, + } = dest.range; + assert_eq!(small_slots.len().value, 0); + assert_eq!(big_slots.len().value, 1); + make_insns_big(big_slots.start, big_inputs) + }) + } + fn compile_expr(&mut self, expr: Expr) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&expr) { + return retval; + } + let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { + ExprEnum::UIntLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr.to_bigint().try_into().expect("const too big"), + }] + }, + |dest, []| { + vec![Insn::ConstBig { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + |_, _| unreachable!(), + ) + .into(), + ExprEnum::SIntLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr.to_bigint().try_into().expect("const too big"), + }] + }, + |dest, []| { + vec![Insn::ConstBig { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BoolLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr as SmallUInt, + }] + }, + |_, _| unreachable!(), + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BundleLiteral(expr) => todo!(), + ExprEnum::ArrayLiteral(expr) => todo!(), + ExprEnum::EnumLiteral(expr) => todo!(), + ExprEnum::Uninit(expr) => todo!(), + ExprEnum::NotU(expr) => todo!(), + ExprEnum::NotS(expr) => todo!(), + ExprEnum::NotB(expr) => todo!(), + ExprEnum::Neg(expr) => self + .simple_nary_small_or_big_expr( + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::NegSmall { dest, src }], + |dest, [src]| vec![Insn::NegBig { dest, src }], + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BitAndU(expr) => todo!(), + ExprEnum::BitAndS(expr) => todo!(), + ExprEnum::BitAndB(expr) => todo!(), + ExprEnum::BitOrU(expr) => todo!(), + ExprEnum::BitOrS(expr) => todo!(), + ExprEnum::BitOrB(expr) => todo!(), + ExprEnum::BitXorU(expr) => todo!(), + ExprEnum::BitXorS(expr) => todo!(), + ExprEnum::BitXorB(expr) => todo!(), + ExprEnum::AddU(expr) => todo!(), + ExprEnum::AddS(expr) => todo!(), + ExprEnum::SubU(expr) => todo!(), + ExprEnum::SubS(expr) => todo!(), + ExprEnum::MulU(expr) => todo!(), + ExprEnum::MulS(expr) => todo!(), + ExprEnum::DivU(expr) => todo!(), + ExprEnum::DivS(expr) => todo!(), + ExprEnum::RemU(expr) => todo!(), + ExprEnum::RemS(expr) => todo!(), + ExprEnum::DynShlU(expr) => todo!(), + ExprEnum::DynShlS(expr) => todo!(), + ExprEnum::DynShrU(expr) => todo!(), + ExprEnum::DynShrS(expr) => todo!(), + ExprEnum::FixedShlU(expr) => todo!(), + ExprEnum::FixedShlS(expr) => todo!(), + ExprEnum::FixedShrU(expr) => todo!(), + ExprEnum::FixedShrS(expr) => todo!(), + ExprEnum::CmpLtB(expr) => todo!(), + ExprEnum::CmpLeB(expr) => todo!(), + ExprEnum::CmpGtB(expr) => todo!(), + ExprEnum::CmpGeB(expr) => todo!(), + ExprEnum::CmpEqB(expr) => todo!(), + ExprEnum::CmpNeB(expr) => todo!(), + ExprEnum::CmpLtU(expr) => todo!(), + ExprEnum::CmpLeU(expr) => todo!(), + ExprEnum::CmpGtU(expr) => todo!(), + ExprEnum::CmpGeU(expr) => todo!(), + ExprEnum::CmpEqU(expr) => todo!(), + ExprEnum::CmpNeU(expr) => todo!(), + ExprEnum::CmpLtS(expr) => todo!(), + ExprEnum::CmpLeS(expr) => todo!(), + ExprEnum::CmpGtS(expr) => todo!(), + ExprEnum::CmpGeS(expr) => todo!(), + ExprEnum::CmpEqS(expr) => todo!(), + ExprEnum::CmpNeS(expr) => todo!(), + ExprEnum::CastUIntToUInt(expr) => todo!(), + ExprEnum::CastUIntToSInt(expr) => todo!(), + ExprEnum::CastSIntToUInt(expr) => todo!(), + ExprEnum::CastSIntToSInt(expr) => todo!(), + ExprEnum::CastBoolToUInt(expr) => todo!(), + ExprEnum::CastBoolToSInt(expr) => todo!(), + ExprEnum::CastUIntToBool(expr) => todo!(), + ExprEnum::CastSIntToBool(expr) => todo!(), + ExprEnum::CastBoolToSyncReset(expr) => todo!(), + ExprEnum::CastUIntToSyncReset(expr) => todo!(), + ExprEnum::CastSIntToSyncReset(expr) => todo!(), + ExprEnum::CastBoolToAsyncReset(expr) => todo!(), + ExprEnum::CastUIntToAsyncReset(expr) => todo!(), + ExprEnum::CastSIntToAsyncReset(expr) => todo!(), + ExprEnum::CastSyncResetToBool(expr) => todo!(), + ExprEnum::CastSyncResetToUInt(expr) => todo!(), + ExprEnum::CastSyncResetToSInt(expr) => todo!(), + ExprEnum::CastSyncResetToReset(expr) => todo!(), + ExprEnum::CastAsyncResetToBool(expr) => todo!(), + ExprEnum::CastAsyncResetToUInt(expr) => todo!(), + ExprEnum::CastAsyncResetToSInt(expr) => todo!(), + ExprEnum::CastAsyncResetToReset(expr) => todo!(), + ExprEnum::CastResetToBool(expr) => todo!(), + ExprEnum::CastResetToUInt(expr) => todo!(), + ExprEnum::CastResetToSInt(expr) => todo!(), + ExprEnum::CastBoolToClock(expr) => todo!(), + ExprEnum::CastUIntToClock(expr) => todo!(), + ExprEnum::CastSIntToClock(expr) => todo!(), + ExprEnum::CastClockToBool(expr) => todo!(), + ExprEnum::CastClockToUInt(expr) => todo!(), + ExprEnum::CastClockToSInt(expr) => todo!(), + ExprEnum::FieldAccess(expr) => todo!(), + ExprEnum::VariantAccess(expr) => todo!(), + ExprEnum::ArrayIndex(expr) => todo!(), + ExprEnum::DynArrayIndex(expr) => todo!(), + ExprEnum::ReduceBitAndU(expr) => todo!(), + ExprEnum::ReduceBitAndS(expr) => todo!(), + ExprEnum::ReduceBitOrU(expr) => todo!(), + ExprEnum::ReduceBitOrS(expr) => todo!(), + ExprEnum::ReduceBitXorU(expr) => todo!(), + ExprEnum::ReduceBitXorS(expr) => todo!(), + ExprEnum::SliceUInt(expr) => todo!(), + ExprEnum::SliceSInt(expr) => todo!(), + ExprEnum::CastToBits(expr) => todo!(), + ExprEnum::CastBitsTo(expr) => todo!(), + ExprEnum::ModuleIO(expr) => todo!(), + ExprEnum::Instance(expr) => todo!(), + ExprEnum::Wire(expr) => todo!(), + ExprEnum::Reg(expr) => todo!(), + ExprEnum::MemPort(expr) => todo!(), + }; + self.compiled_exprs.insert(expr, retval); + retval + } + fn compile_connect( + &mut self, + parent_module: Interned, + cond_stack: Interned, + lhs: Expr, + 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( + parent_module, + cond_stack, + lhs[index], + 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 mut lhs_expr = + crate::expr::ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); + let mut rhs_expr = + crate::expr::ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + if flipped { + mem::swap(&mut lhs_expr, &mut rhs_expr); + } + self.compile_connect( + parent_module, + cond_stack, + lhs_expr, + rhs_expr, + source_location, + ); + } + return; + } + CanonicalType::AsyncReset(_) => unreachable!(), + CanonicalType::SyncReset(_) => unreachable!(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => unreachable!(), + } + } + let lhs = self.compile_expr(lhs); + let rhs = self.compile_expr(rhs); + let rhs = self.compiled_expr_to_value(rhs); + todo!(); } fn compile_block( &mut self, @@ -337,7 +926,7 @@ impl Compiler { lhs, rhs, source_location, - }) => todo!(), + }) => self.compile_connect(parent_module, cond_stack, lhs, rhs, source_location), Stmt::Formal(StmtFormal { kind, clk, @@ -351,6 +940,9 @@ impl Compiler { source_location, blocks: [then_block, else_block], }) => { + let cond = self + .compile_expr(Expr::canonical(cond)) + .map_ty(Bool::from_canonical); self.compile_block( parent_module, then_block, @@ -377,13 +969,16 @@ impl Compiler { source_location, blocks, }) => { + let enum_expr = self + .compile_expr(Expr::canonical(expr)) + .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, block, CondStack::MatchArm { parent: cond_stack, - enum_expr: expr, + enum_expr, variant_index, source_location, } @@ -392,19 +987,51 @@ impl Compiler { } } Stmt::Declaration(declaration) => match declaration { - StmtDeclaration::Wire(wire) => todo!(), - StmtDeclaration::Reg(reg) => todo!(), + StmtDeclaration::Wire(StmtWire { annotations, wire }) => { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: wire.into(), + }); + } + StmtDeclaration::Reg(StmtReg { annotations, reg }) => { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: reg.into(), + }); + todo!(); + } StmtDeclaration::Instance(StmtInstance { annotations, instance, }) => { - self.compile_module( + let CompiledValue { + layout: + CompiledTypeLayout { + ty: value_ty, + layout: ty_layout, + body: CompiledTypeLayoutBody::Bundle { fields }, + }, + range: value_range, + write: None, + } = self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: instance.into(), + }) + else { + unreachable!(); + }; + let CompiledModule { module_io } = *self.compile_module( InstantiatedModule::Child { parent: parent_module, instance: instance.intern_sized(), } .intern_sized(), ); + for (module_io, CompiledBundleField { offset, ty }) in + module_io.into_iter().zip(fields) + { + todo!(); + } todo!() } }, @@ -452,7 +1079,7 @@ impl Compiler { #[derive(Debug)] struct CompiledModule { - module_io: Interned<[CompiledValue]>, + module_io: Interned<[CompiledValue]>, } #[derive(Debug)] diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index f327510..82d3c11 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -4,18 +4,22 @@ use crate::{ intern::{Intern, Interned}, source_location::SourceLocation, + util::get_many_mut, }; use num_bigint::BigInt; use std::{ + borrow::BorrowMut, convert::Infallible, fmt, - hash::Hash, + 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; macro_rules! impl_insns { ( @@ -39,7 +43,7 @@ macro_rules! impl_insns { $field_name:ident: $field_ty:ty, )* })? => $block:block - )+ + )* ) => { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] $vis enum $Insn { @@ -51,7 +55,7 @@ macro_rules! impl_insns { $field_name: $field_ty, )* })?, - )+ + )* } impl $State { @@ -83,7 +87,7 @@ macro_rules! impl_insns { })? => { $block } - )+ + )* }; }; $($cleanup)* @@ -179,35 +183,151 @@ pub(crate) trait StatePartKind: fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; } +pub(crate) trait StatePartsValue { + type Value; +} + +macro_rules! impl_state_parts_traits { + ( + struct $Struct:ident<$V:ident: $StatePartsValue:ident> { + $(#[flatten] + $flattened_field:ident: $flattened_field_ty:ty, + $(#[field_in_flattened] + $field_in_flattened:ident: $field_in_flattened_ty:ty,)* + )? + $($field:ident: $field_ty:ty,)* + } + ) => { + impl<$V: $StatePartsValue> Copy for $Struct<$V> + where + $($flattened_field_ty: Copy,)? + $($field_ty: Copy,)* + { + } + + impl<$V: $StatePartsValue> Clone for $Struct<$V> + where + $($flattened_field_ty: Clone,)? + $($field_ty: Clone,)* + { + fn clone(&self) -> Self { + Self { + $($flattened_field: self.$flattened_field.clone(),)? + $($field: self.$field.clone(),)* + } + } + } + + impl<$V: $StatePartsValue> fmt::Debug for $Struct<$V> + where + $($($field_in_flattened_ty: fmt::Debug,)*)? + $($field_ty: fmt::Debug,)* + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(stringify!($Struct)) + $($(.field(stringify!($field_in_flattened), &self.$flattened_field.$field_in_flattened))*)? + $(.field(stringify!($field), &self.$field))* + .finish() + } + } + + impl<$V: $StatePartsValue> PartialEq for $Struct<$V> + where + $($flattened_field_ty: PartialEq,)? + $($field_ty: PartialEq,)* + { + fn eq(&self, other: &Self) -> bool { + true $(&& self.$flattened_field == other.$flattened_field)? $(&& self.$field == other.$field)* + } + } + + impl<$V: $StatePartsValue> Eq for $Struct<$V> + where + $($flattened_field_ty: Eq,)? + $($field_ty: Eq,)* + { + } + + impl<$V: $StatePartsValue> Hash for $Struct<$V> + where + $($flattened_field_ty: Hash,)? + $($field_ty: Hash,)* + { + fn hash(&self, h: &mut H) { + $(self.$flattened_field.hash(h);)? + $(self.$field.hash(h);)* + } + } + + impl<$V: $StatePartsValue> Default for $Struct<$V> + where + $($flattened_field_ty: Default,)? + $($field_ty: Default,)* + { + fn default() -> Self { + Self { + $($flattened_field: Default::default(),)? + $($field: Default::default(),)* + } + } + } + }; +} + macro_rules! make_state_part_kinds { ( $( #[state, field = $state_field:ident] impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt - )+ + )* $( #[type, field = $type_field:ident] impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt - )+ + )* ) => { $( #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] pub(crate) struct $StateKind; impl $StateStatePartKind for $StateKind $state_impl_body - )+ + )* $( #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] pub(crate) struct $TypeKind; impl $TypeStatePartKind for $TypeKind $type_impl_body - )+ + )* + + pub(crate) struct StateParts { + pub(crate) ty: TypeParts, + $(pub(crate) $state_field: V::Value<$StateKind>,)* + } + + impl_state_parts_traits! { + struct StateParts { + #[flatten] + ty: TypeParts, + $(#[field_in_flattened] + $type_field: V::Value<$TypeKind>,)* + $($state_field: V::Value<$StateKind>,)* + } + } + + pub(crate) struct TypeParts { + $(pub(crate) $type_field: V::Value<$TypeKind>,)* + } + + impl_state_parts_traits! { + struct TypeParts { + $($type_field: V::Value<$TypeKind>,)* + } + } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateLayout { pub(crate) ty: TypeLayout, - $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)+ + $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)* } impl Copy for StateLayout {} @@ -216,16 +336,16 @@ macro_rules! make_state_part_kinds { pub(crate) fn len(self) -> StateLen { StateLen { ty: self.ty.len(), - $($state_field: self.$state_field.len(),)+ + $($state_field: self.$state_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())+ + self.ty.is_empty() $(&& self.$state_field.is_empty())* } pub(crate) fn empty() -> Self { Self { ty: TypeLayout::empty(), - $($state_field: StatePartLayout::empty(),)+ + $($state_field: StatePartLayout::empty(),)* } } } @@ -234,7 +354,7 @@ macro_rules! make_state_part_kinds { pub(crate) fn next_index(&self) -> StateIndex { StateIndex { ty: self.ty.next_index(), - $($state_field: self.$state_field.next_index(),)+ + $($state_field: self.$state_field.next_index(),)* } } pub(crate) fn allocate( @@ -243,7 +363,7 @@ macro_rules! make_state_part_kinds { ) -> StateIndexRange { StateIndexRange { ty: self.ty.allocate(&layout.ty), - $($state_field: self.$state_field.allocate(&layout.$state_field),)+ + $($state_field: self.$state_field.allocate(&layout.$state_field),)* } } } @@ -252,7 +372,7 @@ macro_rules! make_state_part_kinds { fn from(v: StateLayout) -> Self { Self { ty: v.ty.into(), - $($state_field: v.$state_field.into(),)+ + $($state_field: v.$state_field.into(),)* } } } @@ -260,42 +380,42 @@ macro_rules! make_state_part_kinds { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateIndexRange { pub(crate) ty: TypeIndexRange, - $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)+ + $(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(),)+ + $($state_field: self.$state_field.start(),)* } } pub(crate) fn len(self) -> StateLen { StateLen { ty: self.ty.len(), - $($state_field: self.$state_field.len(),)+ + $($state_field: self.$state_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())+ + 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>,)+ + $(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>,)+ + $(pub(crate) $state_field: StatePartIndex<$StateKind>,)* } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeLayout { - $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)+ + $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)* } impl Copy for TypeLayout {} @@ -303,7 +423,7 @@ macro_rules! make_state_part_kinds { impl TypeLayout { pub(crate) fn len(self) -> TypeLen { TypeLen { - $($type_field: self.$type_field.len(),)+ + $($type_field: self.$type_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { @@ -311,7 +431,7 @@ macro_rules! make_state_part_kinds { } pub(crate) fn empty() -> Self { Self { - $($type_field: StatePartLayout::empty(),)+ + $($type_field: StatePartLayout::empty(),)* } } } @@ -319,7 +439,7 @@ macro_rules! make_state_part_kinds { impl TypeLayout { pub(crate) fn next_index(&self) -> TypeIndex { TypeIndex { - $($type_field: self.$type_field.next_index(),)+ + $($type_field: self.$type_field.next_index(),)* } } pub(crate) fn allocate( @@ -327,7 +447,7 @@ macro_rules! make_state_part_kinds { layout: &TypeLayout, ) -> TypeIndexRange { TypeIndexRange { - $($type_field: self.$type_field.allocate(&layout.$type_field),)+ + $($type_field: self.$type_field.allocate(&layout.$type_field),)* } } } @@ -335,14 +455,14 @@ macro_rules! make_state_part_kinds { impl From> for TypeLayout { fn from(v: TypeLayout) -> Self { Self { - $($type_field: v.$type_field.into(),)+ + $($type_field: v.$type_field.into(),)* } } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeIndexRange { - $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)+ + $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)* } impl TypeIndexRange { @@ -351,17 +471,17 @@ macro_rules! make_state_part_kinds { $($type_field: StatePartIndexRange { start: start.$type_field, len: len.$type_field, - },)+ + },)* } } pub(crate) fn start(self) -> TypeIndex { TypeIndex { - $($type_field: self.$type_field.start(),)+ + $($type_field: self.$type_field.start(),)* } } pub(crate) fn len(self) -> TypeLen { TypeLen { - $($type_field: self.$type_field.len(),)+ + $($type_field: self.$type_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { @@ -369,32 +489,32 @@ macro_rules! make_state_part_kinds { } pub(crate) fn slice(self, index: TypeIndexRange) -> Self { Self { - $($type_field: self.$type_field.slice(index.$type_field),)+ + $($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),)+ + $($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>,)+ + $(pub(crate) $type_field: StatePartLen<$TypeKind>,)* } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeIndex { - $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)+ + $(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>,)+ + $(pub(crate) $state_field: StatePart<$StateKind>,)* + $(pub(crate) $type_field: StatePart<$TypeKind>,)* } impl State { @@ -404,8 +524,8 @@ macro_rules! make_state_part_kinds { insns: &self.insns.insns, pc: self.pc, orig_pc: &mut self.pc, - $($state_field: self.$state_field.borrow(),)+ - $($type_field: self.$type_field.borrow(),)+ + $($state_field: self.$state_field.borrow(),)* + $($type_field: self.$type_field.borrow(),)* } } } @@ -416,8 +536,8 @@ macro_rules! make_state_part_kinds { 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>,)+ + $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)* + $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)* } impl Drop for BorrowedState<'_> { @@ -429,7 +549,7 @@ macro_rules! make_state_part_kinds { } make_state_part_kinds! { - #[state, field = small_stack] + /*#[state, field = small_stack] impl StatePartKind for StatePartKindSmallStack { const NAME: &'static str = "SmallStack"; type DebugData = (); @@ -454,7 +574,7 @@ make_state_part_kinds! { 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"; @@ -483,6 +603,23 @@ make_state_part_kinds! { } } +impl TypeLen { + pub(crate) fn only_small(self) -> Option> { + let Self { + small_slots, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } = self + else { + return None; + }; + Some(small_slots) + } +} + #[derive(Debug, Clone)] pub(crate) struct Stack { storage: Box<[T]>, @@ -544,6 +681,11 @@ pub(crate) struct SlotDebugData { } impl SlotDebugData { + pub(crate) fn empty() -> Self { + Self { + name: Interned::default(), + } + } 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); @@ -582,6 +724,12 @@ pub(crate) struct StatePartIndex { pub(crate) _phantom: PhantomData, } +impl StatePartIndex { + pub(crate) fn as_usize(self) -> usize { + self.value.try_into().expect("index too big") + } +} + impl fmt::Debug for StatePartIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "StatePartIndex<{}>({})", K::NAME, self.value) @@ -716,6 +864,12 @@ impl StatePartIndexRange { pub(crate) fn is_empty(self) -> bool { self.len.value == 0 } + pub(crate) fn iter(self) -> impl Iterator> { + (self.start.value..self.end().value).map(|value| StatePartIndex { + value, + _phantom: PhantomData, + }) + } pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { assert!(index.end().value <= self.len.value, "index out of range"); Self { @@ -787,6 +941,25 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> { pub(crate) value: K::BorrowedState<'a>, } +impl< + 'a, + K: StatePartKind< + BorrowedState<'a>: DerefMut + BorrowMut<[T]>>, + >, + T, + > BorrowedStatePart<'a, K> +{ + pub(crate) fn get_many_mut( + &mut self, + indexes: [StatePartIndex; N], + ) -> [&mut T; N] { + get_many_mut( + (*self.value).borrow_mut(), + indexes.map(|v| v.value as usize), + ) + } +} + impl<'a, K: StatePartKind: Deref>>, T> Index> for BorrowedStatePart<'a, K> { @@ -818,6 +991,139 @@ impl<'a, K: StatePartKind = BorrowedStack<'a, T>>, T: 'a> } } +#[derive(PartialEq, Eq, Hash, Clone)] +pub(crate) struct StatePartIndexMap { + pub(crate) map: VecMap, + _phantom: PhantomData, +} + +impl fmt::Debug for StatePartIndexMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl StatePartIndexMap { + pub(crate) fn new() -> Self { + Self { + map: VecMap::new(), + _phantom: PhantomData, + } + } + pub(crate) fn get(&self, key: StatePartIndex) -> Option<&V> { + self.map.get(key.as_usize()) + } + pub(crate) fn get_mut(&mut self, key: StatePartIndex) -> Option<&mut V> { + self.map.get_mut(key.as_usize()) + } + pub(crate) fn keys(&self) -> impl Iterator> + '_ { + self.map.keys().map(|k| StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }) + } + pub(crate) fn values(&self) -> vec_map::Values<'_, V> { + self.map.values() + } + pub(crate) fn values_mut(&mut self) -> vec_map::ValuesMut<'_, V> { + self.map.values_mut() + } + pub(crate) fn iter(&self) -> impl Iterator, &V)> + '_ { + self.map.iter().map(|(k, v)| { + ( + StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }, + v, + ) + }) + } + pub(crate) fn iter_mut(&mut self) -> impl Iterator, &mut V)> + '_ { + self.map.iter_mut().map(|(k, v)| { + ( + StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }, + v, + ) + }) + } + pub(crate) fn len(&self) -> usize { + self.map.len() + } + pub(crate) fn is_empty(&self) -> bool { + self.map.is_empty() + } + pub(crate) fn insert(&mut self, key: StatePartIndex, value: V) -> Option { + self.map.insert(key.as_usize(), value) + } + pub(crate) fn remove(&mut self, key: StatePartIndex) -> Option { + self.map.remove(key.as_usize()) + } + pub(crate) fn entry(&mut self, key: StatePartIndex) -> StatePartIndexMapEntry<'_, K, V> { + match self.map.entry(key.as_usize()) { + vec_map::Entry::Vacant(v) => { + StatePartIndexMapEntry::Vacant(StatePartIndexMapVacantEntry(v, PhantomData)) + } + vec_map::Entry::Occupied(v) => { + StatePartIndexMapEntry::Occupied(StatePartIndexMapOccupiedEntry(v, PhantomData)) + } + } + } +} + +impl Default for StatePartIndexMap { + fn default() -> Self { + Self::new() + } +} + +impl Index> for StatePartIndexMap { + type Output = V; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.map[index.as_usize()] + } +} + +impl IndexMut> for StatePartIndexMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.map[index.as_usize()] + } +} + +pub(crate) struct StatePartIndexMapVacantEntry<'a, K: StatePartKind, V>( + vec_map::VacantEntry<'a, V>, + PhantomData, +); + +pub(crate) struct StatePartIndexMapOccupiedEntry<'a, K: StatePartKind, V>( + vec_map::OccupiedEntry<'a, V>, + PhantomData, +); + +pub(crate) enum StatePartIndexMapEntry<'a, K: StatePartKind, V> { + Vacant(StatePartIndexMapVacantEntry<'a, K, V>), + Occupied(StatePartIndexMapOccupiedEntry<'a, K, V>), +} + +impl<'a, K: StatePartKind, V> StatePartIndexMapEntry<'a, K, V> { + pub(crate) fn or_insert(self, default: V) -> &'a mut V { + match self { + StatePartIndexMapEntry::Vacant(v) => v.0.insert(default), + StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(), + } + } + pub(crate) fn or_insert_with(self, f: impl FnOnce() -> V) -> &'a mut V { + match self { + StatePartIndexMapEntry::Vacant(v) => v.0.insert(f()), + StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(), + } + } +} + impl Insns { pub(crate) fn new() -> Self { Self { @@ -867,14 +1173,10 @@ impl State { let Self { insns: _, pc, - small_stack, - big_stack, small_slots: _, big_slots: _, } = self; *pc = entry_pc; - small_stack.value.clear(); - big_stack.value.clear(); } } @@ -887,171 +1189,238 @@ impl_insns! { main_loop!(); cleanup! {} } - ReadSmall { - index: StatePartIndex, + CopySmall { + dest: StatePartIndex, + src: StatePartIndex, } => { - state.small_stack.push(state.small_slots[index]); + state.small_slots[dest] = state.small_slots[src]; + next!(); + } + CopyBig { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); next!(); } SExtSmall { + dest: StatePartIndex, + src: StatePartIndex, /// number of bits in a [`SmallSInt`] that aren't used unused_bit_count: u8, } => { - let mut value = state.small_stack.pop() as SmallSInt; + let mut value = state.small_slots[src] as SmallSInt; value <<= unused_bit_count; value >>= unused_bit_count; - state.small_stack.push(value as SmallUInt); + state.small_slots[dest] = value as SmallUInt; next!(); } ZExtSmall { + dest: StatePartIndex, + src: StatePartIndex, /// number of bits in a [`SmallUInt`] that aren't used unused_bit_count: u8, } => { - let mut value = state.small_stack.pop(); + let mut value = state.small_slots[src]; value <<= unused_bit_count; value >>= unused_bit_count; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - AndSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + SExtSmallToBig { + dest: StatePartIndex, + src: StatePartIndex, + /// number of bits in a [`SmallSInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = state.small_slots[src] as SmallSInt; + value <<= unused_bit_count; + value >>= unused_bit_count; + state.big_slots[dest] = value.into(); + next!(); + } + ZExtSmallToBig { + dest: StatePartIndex, + src: StatePartIndex, + /// number of bits in a [`SmallUInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = state.small_slots[src]; + value <<= unused_bit_count; + value >>= unused_bit_count; + state.big_slots[dest] = value.into(); + next!(); + } + AndSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs & rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - OrSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + OrSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs | rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - XorSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + XorSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs ^ rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - NotSmall => { - let value = state.small_stack.pop(); - state.small_stack.push(!value); + NotSmall { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = state.small_slots[src]; + state.small_slots[dest] = !value; next!(); } - CmpEqSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpEqSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs == rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpNeSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpNeSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs != rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLTSmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpLTSmallUInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs < rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLESmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpLESmallUInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs <= rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpGTSmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); - let value = (lhs > rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpGESmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); - let value = (lhs >= rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpLTSmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; + CmpLTSmallSInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs] as SmallSInt; + let rhs = state.small_slots[rhs] as SmallSInt; let value = (lhs < rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLESmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; + CmpLESmallSInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs] as SmallSInt; + let rhs = state.small_slots[rhs] as SmallSInt; let value = (lhs <= rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpGTSmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; - let value = (lhs > rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpGESmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; - let value = (lhs >= rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - AddSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + AddSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_add(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - SubSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + SubSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_sub(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - NegSmall => { - let value = state.small_stack.pop(); + NegSmall { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = state.small_slots[src]; let value = value.wrapping_neg(); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - MulSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + NegBig { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = -&state.big_slots[src]; + state.big_slots[dest] = value; + next!(); + } + MulSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_mul(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - ConstU32Small { - value: u32, + ConstSmall { + dest: StatePartIndex, + value: SmallUInt, } => { - state.small_stack.push(value as SmallUInt); + state.small_slots[dest] = value; next!(); } - ConstS32Small { - value: i32, + ConstBig { + dest: StatePartIndex, + value: Interned, } => { - state.small_stack.push(value as SmallUInt); - next!(); - } - WriteSmall { - index: StatePartIndex, - } => { - state.small_slots[index] = state.small_stack.pop(); + state.big_slots[dest].clone_from(&value); next!(); } Return => { diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index f66654f..131c54c 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -24,7 +24,8 @@ pub use scoped_ref::ScopedRef; #[doc(inline)] pub use misc::{ - interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, + get_many_mut, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, + DebugAsRawString, MakeMutSlice, }; pub mod job_server; diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 884458f..85e376a 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -155,3 +155,19 @@ impl fmt::UpperHex for BitSliceWriteWithBase<'_> { self.fmt_with_base::<4, true>(f) } } + +#[inline] +#[track_caller] +pub fn get_many_mut(slice: &mut [T], indexes: [usize; N]) -> [&mut T; N] { + for i in 0..N { + for j in 0..i { + assert!(indexes[i] != indexes[j], "duplicate index"); + } + assert!(indexes[i] < slice.len(), "index out of bounds"); + } + // Safety: checked that no indexes are duplicates and no indexes are out of bounds + unsafe { + let base = slice.as_mut_ptr(); // convert to a raw pointer before loop to avoid aliasing with &mut [T] + std::array::from_fn(|i| &mut *base.add(indexes[i])) + } +} From f338f37d3ef6cf1ec5a9c2d4db03c50863a9dd93 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 12 Nov 2024 01:08:32 -0800 Subject: [PATCH 005/109] working on simulator --- Cargo.lock | 5 +- Cargo.toml | 2 +- crates/fayalite/src/sim.rs | 1230 +++++++++++++++++------- crates/fayalite/src/sim/interpreter.rs | 806 +++++++++++----- 4 files changed, 1454 insertions(+), 589 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fca6fc..c280158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,11 +473,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", ] diff --git a/Cargo.toml b/Cargo.toml index 44f5c5a..add4bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ 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" prettyplease = "0.2.20" diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 9b40458..dbcb88a 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -18,38 +18,39 @@ use crate::{ }, prelude::*, sim::interpreter::{ - Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, SmallUInt, StatePartIndex, - StatePartIndexMap, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, - StatePartLayout, StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, - TypeLen, TypeParts, MIN_BITS_FOR_NEEDING_BIG, + Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, + SlotDebugData, SmallUInt, StatePartIndex, StatePartIndexMap, StatePartIndexRange, + StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, StatePartLayout, + StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, + MIN_BITS_FOR_NEEDING_BIG, }, }; use hashbrown::HashMap; -use std::{fmt, marker::PhantomData, mem}; +use num_bigint::BigInt; +use std::{collections::BTreeSet, fmt, marker::PhantomData, mem}; mod interpreter; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -enum CondStack { - Always, +enum CondBody { IfTrue { - parent: Interned, cond: CompiledExpr, - source_location: SourceLocation, }, IfFalse { - parent: Interned, cond: CompiledExpr, - source_location: SourceLocation, }, MatchArm { - parent: Interned, enum_expr: CompiledExpr, variant_index: usize, - source_location: SourceLocation, }, } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct Cond { + body: CondBody, + source_location: SourceLocation, +} + #[derive(PartialEq, Eq, Hash, Clone, Copy)] enum InstantiatedModule { Base(Interned>), @@ -147,12 +148,9 @@ impl CompiledTypeLayout { let mut layout = TypeLayout::empty(); let debug_data = SlotDebugData { name: Interned::default(), + ty: *input, }; - if input.bit_width() >= interpreter::MIN_BITS_FOR_NEEDING_BIG { - layout.big_slots = StatePartLayout::scalar(debug_data); - } else { - layout.small_slots = StatePartLayout::scalar(debug_data); - }; + layout.big_slots = StatePartLayout::scalar(debug_data); CompiledTypeLayout { ty: *input, layout: layout.into(), @@ -239,13 +237,94 @@ impl CompiledValue { } } +impl CompiledValue { + fn add_discriminant_to_set(self, offset: TypeIndex, inputs: &mut SlotSet) { + inputs.extend([self.range.offset(offset)]); + } +} + #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExprDynIndex { index_slot: StatePartIndex, - len: TypeLen, + len: usize, stride: TypeLen, } +impl CompiledExprDynIndex { + fn offsets(self) -> impl Iterator { + (0..self.len.try_into().unwrap()).map(move |index| TypeIndex { + small_slots: StatePartIndex { + value: self + .stride + .small_slots + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: self + .stride + .big_slots + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + }, + }) + } + fn for_each_offset(dyn_indexes: &[CompiledExprDynIndex], mut f: impl FnMut(TypeIndex)) { + fn helper( + dyn_indexes: &[CompiledExprDynIndex], + offset: TypeIndex, + f: &mut impl FnMut(TypeIndex), + ) { + if let [next, rest @ ..] = dyn_indexes { + for next_offset in next.offsets() { + helper( + rest, + TypeIndex { + small_slots: StatePartIndex { + value: next_offset + .small_slots + .value + .checked_add(offset.small_slots.value) + .expect("array too big"), + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: next_offset + .big_slots + .value + .checked_add(offset.big_slots.value) + .expect("array too big"), + _phantom: PhantomData, + }, + }, + f, + ); + } + } else { + f(offset); + } + } + helper( + dyn_indexes, + TypeIndex { + small_slots: StatePartIndex { + value: 0, + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: 0, + _phantom: PhantomData, + }, + }, + &mut f, + ); + } +} + #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExpr { static_part: CompiledValue, @@ -281,6 +360,36 @@ impl CompiledExpr { dyn_indexes, } } + fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + inputs.extend([static_part.range.offset(offset)]); + }); + } + fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part: _, + dyn_indexes, + } = self; + self.add_target_without_indexes_to_set(inputs); + inputs.extend(dyn_indexes); + } +} + +impl CompiledExpr { + fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + static_part.add_discriminant_to_set(offset, inputs); + }); + inputs.extend(dyn_indexes); + } } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] @@ -297,12 +406,74 @@ impl SlotSet { } impl StatePartsValue for SlotSet { - type Value = Vec>; + 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) { + self.extend(iter.into_iter().map(|v| v.index_slot)); + } +} + +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 } => { + cond.add_target_and_indexes_to_set(self); + } + CondBody::MatchArm { + enum_expr, + variant_index, + } => enum_expr.add_discriminant_and_indexes_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, + conditions: Interned<[Cond]>, insns: Vec, } @@ -327,9 +498,9 @@ pub struct Compiler { base_module: Interned>, modules: HashMap, compiled_values: HashMap>, - compiled_exprs: HashMap, CompiledExpr>, - compiled_exprs_to_values: HashMap, CompiledValue>, - expanded_to_big: HashMap, CompiledValue>, + compiled_exprs: HashMap<(Interned<[Cond]>, Expr), CompiledExpr>, + compiled_exprs_to_values: + HashMap<(Interned<[Cond]>, CompiledExpr), CompiledValue>, slots_assignments: SlotAssignments, } @@ -342,7 +513,6 @@ impl Compiler { compiled_values: HashMap::new(), compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), - expanded_to_big: HashMap::new(), slots_assignments: SlotAssignments::default(), } } @@ -427,9 +597,10 @@ impl Compiler { } fn compiled_expr_to_value( &mut self, + conditions: Interned<[Cond]>, expr: CompiledExpr, ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + if let Some(&retval) = self.compiled_exprs_to_values.get(&(conditions, expr)) { return retval; } let CompiledExpr { @@ -444,359 +615,678 @@ impl Compiler { { todo!(); } - self.compiled_exprs_to_values.insert(expr, retval); + self.compiled_exprs_to_values + .insert((conditions, expr), retval); retval } - fn simple_nary_expr( + fn add_assignment( &mut self, + conditions: Interned<[Cond]>, + insns: impl IntoIterator, + ) { + let insns = Vec::from_iter(insns); + let assignment_index = self.slots_assignments.assignments.len(); + let mut inputs = SlotSet::default(); + for insn in &insns { + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&small_slot)) => { + inputs.0.small_slots.insert(small_slot); + } + (InsnFieldKind::Input, InsnFieldType::BigSlot(&big_slot)) => { + inputs.0.big_slots.insert(big_slot); + } + (InsnFieldKind::Output, InsnFieldType::SmallSlot(&small_slot)) => self + .slots_assignments + .parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(small_slot) + .or_insert_with(Vec::new) + .push(assignment_index), + (InsnFieldKind::Output, InsnFieldType::BigSlot(&big_slot)) => self + .slots_assignments + .parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(big_slot) + .or_insert_with(Vec::new) + .push(assignment_index), + ( + _, + InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} + } + } + } + self.slots_assignments.assignments.push(Assignment { + inputs, + conditions, + insns, + }); + } + fn simple_nary_big_expr( + &mut self, + conditions: Interned<[Cond]>, dest_ty: CanonicalType, inputs: [Expr; N], make_insns: impl FnOnce( - &mut Self, - CompiledValue, - &mut [CompiledValue; N], + StatePartIndex, + [StatePartIndex; N], ) -> Vec, ) -> CompiledValue { - let mut inputs = inputs.map(|input| { - let input = self.compile_expr(input); - self.compiled_expr_to_value(input) + let inputs = inputs.map(|input| { + let input = self.compile_expr(conditions, input); + let input = self.compiled_expr_to_value(conditions, input); + 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, }; - let insns = make_insns(self, retval, &mut inputs); - let mut inputs_set = SlotSet::default(); - for input in inputs { - let TypeIndexRange { - small_slots, - big_slots, - } = input.range; - inputs_set.0.small_slots.extend(small_slots.iter()); - inputs_set.0.big_slots.extend(big_slots.iter()); - } - let assignment_index = self.slots_assignments.assignments.len(); - self.slots_assignments.assignments.push(Assignment { - inputs: inputs_set, - insns, - }); - let TypeIndexRange { - small_slots, - big_slots, - } = range; - for i in small_slots.iter() { - self.slots_assignments - .parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(i) - .or_insert_with(Vec::new) - .push(assignment_index); - } - for i in big_slots.iter() { - self.slots_assignments - .parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(i) - .or_insert_with(Vec::new) - .push(assignment_index); - } + self.add_assignment(conditions, make_insns(big_slots.start, inputs)); retval } - fn expand_to_big(&mut self, expr: Expr) -> CompiledValue { - if let Some(&retval) = self.expanded_to_big.get(&expr) { - return retval; - } - let input = self.compile_expr(expr); - let input = self.compiled_expr_to_value(input); - let retval = match input.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: _, - } => input, - len => { - assert_eq!( - Some(StatePartLen { - value: 1, - _phantom: PhantomData, - }), - len.only_small() - ); - let signed = match Expr::ty(expr) { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => true, - CanonicalType::Bool(_) => false, - CanonicalType::Enum(_) => false, - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => false, - CanonicalType::Clock(_) => false, - }; - self.simple_nary_expr( - if signed { - SInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() - } else { - UInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() - }, - [expr], - |_this, dest, [input]| { - let dest = dest.range.big_slots.start; - let src = input.range.small_slots.start; - let unused_bit_count = - interpreter::SmallUInt::BITS as u8 - Expr::ty(expr).bit_width() as u8; - if signed { - vec![Insn::SExtSmallToBig { - dest, - src, - unused_bit_count, - }] - } else { - vec![Insn::ZExtSmallToBig { - dest, - src, - unused_bit_count, - }] - } - }, - ) - } - }; - self.expanded_to_big.insert(expr, retval); - retval - } - fn simple_nary_small_or_big_expr( + fn compile_expr( &mut self, - dest_ty: CanonicalType, - inputs: [Expr; N], - make_insns_small: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - make_insns_big: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - make_insns_big_to_small: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - ) -> CompiledValue { - self.simple_nary_expr(dest_ty, inputs, |this, dest, compiled_inputs| { - let all_inputs_only_small = compiled_inputs - .iter() - .all(|input| input.range.len().only_small().is_some()); - if all_inputs_only_small { - if dest.range.len().only_small().is_some() { - // all small - assert_eq!(dest.range.len().small_slots.value, 1); - return make_insns_small( - dest.range.small_slots.start, - compiled_inputs.map( - |CompiledValue { - layout, - range, - write: _, - }| { - assert_eq!(range.small_slots.len().value, 1); - range.small_slots.start - }, - ), - ); - } else { - // inputs small, dest big -- expand inputs to big - for (&input, compiled_input) in inputs.iter().zip(&mut *compiled_inputs) { - *compiled_input = this.expand_to_big(input); - } - } - } - let big_inputs = compiled_inputs.map( - |CompiledValue { - layout, - range: - TypeIndexRange { - small_slots, - big_slots, - }, - write: _, - }| { - assert_eq!(small_slots.len().value, 0); - assert_eq!(big_slots.len().value, 1); - big_slots.start - }, - ); - if dest.range.len().only_small().is_some() { - // inputs big, dest small - assert_eq!(dest.range.len().small_slots.value, 1); - return make_insns_big_to_small(dest.range.small_slots.start, big_inputs); - } - let TypeIndexRange { - small_slots, - big_slots, - } = dest.range; - assert_eq!(small_slots.len().value, 0); - assert_eq!(big_slots.len().value, 1); - make_insns_big(big_slots.start, big_inputs) - }) - } - fn compile_expr(&mut self, expr: Expr) -> CompiledExpr { - if let Some(&retval) = self.compiled_exprs.get(&expr) { + conditions: Interned<[Cond]>, + expr: Expr, + ) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&(conditions, 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(conditions, 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_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr.to_bigint().try_into().expect("const too big"), - }] - }, - |dest, []| { - vec![Insn::ConstBig { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }) .into(), ExprEnum::SIntLiteral(expr) => self - .simple_nary_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr.to_bigint().try_into().expect("const too big"), - }] - }, - |dest, []| { - vec![Insn::ConstBig { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }) .into(), ExprEnum::BoolLiteral(expr) => self - .simple_nary_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr as SmallUInt, - }] - }, - |_, _| unreachable!(), - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, 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) => todo!(), - ExprEnum::NotS(expr) => todo!(), - ExprEnum::NotB(expr) => todo!(), - ExprEnum::Neg(expr) => self - .simple_nary_small_or_big_expr( - expr.ty().canonical(), + ExprEnum::NotU(expr) => self + .simple_nary_big_expr( + conditions, + Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::NegSmall { dest, src }], - |dest, [src]| vec![Insn::NegBig { dest, src }], - |_, _| unreachable!(), + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::NotS(expr) => self + .simple_nary_big_expr( + conditions, + 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( + conditions, + 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( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Neg { dest, src }], + ) + .into(), + ExprEnum::BitAndU(expr) => self + .simple_nary_big_expr( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + 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( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, ) .into(), - ExprEnum::BitAndU(expr) => todo!(), - ExprEnum::BitAndS(expr) => todo!(), - ExprEnum::BitAndB(expr) => todo!(), - ExprEnum::BitOrU(expr) => todo!(), - ExprEnum::BitOrS(expr) => todo!(), - ExprEnum::BitOrB(expr) => todo!(), - ExprEnum::BitXorU(expr) => todo!(), - ExprEnum::BitXorS(expr) => todo!(), - ExprEnum::BitXorB(expr) => todo!(), - ExprEnum::AddU(expr) => todo!(), - ExprEnum::AddS(expr) => todo!(), - ExprEnum::SubU(expr) => todo!(), - ExprEnum::SubS(expr) => todo!(), - ExprEnum::MulU(expr) => todo!(), - ExprEnum::MulS(expr) => todo!(), - ExprEnum::DivU(expr) => todo!(), - ExprEnum::DivS(expr) => todo!(), - ExprEnum::RemU(expr) => todo!(), - ExprEnum::RemS(expr) => todo!(), - ExprEnum::DynShlU(expr) => todo!(), - ExprEnum::DynShlS(expr) => todo!(), - ExprEnum::DynShrU(expr) => todo!(), - ExprEnum::DynShrS(expr) => todo!(), - ExprEnum::FixedShlU(expr) => todo!(), - ExprEnum::FixedShlS(expr) => todo!(), - ExprEnum::FixedShrU(expr) => todo!(), - ExprEnum::FixedShrS(expr) => todo!(), - ExprEnum::CmpLtB(expr) => todo!(), - ExprEnum::CmpLeB(expr) => todo!(), - ExprEnum::CmpGtB(expr) => todo!(), - ExprEnum::CmpGeB(expr) => todo!(), - ExprEnum::CmpEqB(expr) => todo!(), - ExprEnum::CmpNeB(expr) => todo!(), - ExprEnum::CmpLtU(expr) => todo!(), - ExprEnum::CmpLeU(expr) => todo!(), - ExprEnum::CmpGtU(expr) => todo!(), - ExprEnum::CmpGeU(expr) => todo!(), - ExprEnum::CmpEqU(expr) => todo!(), - ExprEnum::CmpNeU(expr) => todo!(), - ExprEnum::CmpLtS(expr) => todo!(), - ExprEnum::CmpLeS(expr) => todo!(), - ExprEnum::CmpGtS(expr) => todo!(), - ExprEnum::CmpGeS(expr) => todo!(), - ExprEnum::CmpEqS(expr) => todo!(), - ExprEnum::CmpNeS(expr) => todo!(), - ExprEnum::CastUIntToUInt(expr) => todo!(), ExprEnum::CastUIntToSInt(expr) => todo!(), - ExprEnum::CastSIntToUInt(expr) => todo!(), + ExprEnum::CastSIntToUInt(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), ExprEnum::CastSIntToSInt(expr) => todo!(), - ExprEnum::CastBoolToUInt(expr) => todo!(), - ExprEnum::CastBoolToSInt(expr) => todo!(), - ExprEnum::CastUIntToBool(expr) => todo!(), - ExprEnum::CastSIntToBool(expr) => todo!(), - ExprEnum::CastBoolToSyncReset(expr) => todo!(), - ExprEnum::CastUIntToSyncReset(expr) => todo!(), - ExprEnum::CastSIntToSyncReset(expr) => todo!(), - ExprEnum::CastBoolToAsyncReset(expr) => todo!(), - ExprEnum::CastUIntToAsyncReset(expr) => todo!(), - ExprEnum::CastSIntToAsyncReset(expr) => todo!(), - ExprEnum::CastSyncResetToBool(expr) => todo!(), - ExprEnum::CastSyncResetToUInt(expr) => todo!(), - ExprEnum::CastSyncResetToSInt(expr) => todo!(), - ExprEnum::CastSyncResetToReset(expr) => todo!(), - ExprEnum::CastAsyncResetToBool(expr) => todo!(), - ExprEnum::CastAsyncResetToUInt(expr) => todo!(), - ExprEnum::CastAsyncResetToSInt(expr) => todo!(), - ExprEnum::CastAsyncResetToReset(expr) => todo!(), - ExprEnum::CastResetToBool(expr) => todo!(), - ExprEnum::CastResetToUInt(expr) => todo!(), - ExprEnum::CastResetToSInt(expr) => todo!(), - ExprEnum::CastBoolToClock(expr) => todo!(), - ExprEnum::CastUIntToClock(expr) => todo!(), - ExprEnum::CastSIntToClock(expr) => todo!(), - ExprEnum::CastClockToBool(expr) => todo!(), - ExprEnum::CastClockToUInt(expr) => todo!(), - ExprEnum::CastClockToSInt(expr) => todo!(), + 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) => todo!(), ExprEnum::VariantAccess(expr) => todo!(), ExprEnum::ArrayIndex(expr) => todo!(), @@ -817,13 +1307,13 @@ impl Compiler { ExprEnum::Reg(expr) => todo!(), ExprEnum::MemPort(expr) => todo!(), }; - self.compiled_exprs.insert(expr, retval); + self.compiled_exprs.insert((conditions, expr), retval); retval } fn compile_connect( &mut self, parent_module: Interned, - cond_stack: Interned, + conditions: Interned<[Cond]>, lhs: Expr, mut rhs: Expr, source_location: SourceLocation, @@ -847,7 +1337,7 @@ impl Compiler { for index in 0..lhs_ty.len() { self.compile_connect( parent_module, - cond_stack, + conditions, lhs[index], rhs[index], source_location, @@ -891,7 +1381,7 @@ impl Compiler { } self.compile_connect( parent_module, - cond_stack, + conditions, lhs_expr, rhs_expr, source_location, @@ -905,16 +1395,16 @@ impl Compiler { CanonicalType::Clock(_) => unreachable!(), } } - let lhs = self.compile_expr(lhs); - let rhs = self.compile_expr(rhs); - let rhs = self.compiled_expr_to_value(rhs); + let lhs = self.compile_expr(conditions, lhs); + let rhs = self.compile_expr(conditions, rhs); + let rhs = self.compiled_expr_to_value(conditions, rhs); todo!(); } fn compile_block( &mut self, parent_module: Interned, block: Block, - cond_stack: Interned, + conditions: Interned<[Cond]>, ) { let Block { memories, stmts } = block; for memory in memories { @@ -926,7 +1416,7 @@ impl Compiler { lhs, rhs, source_location, - }) => self.compile_connect(parent_module, cond_stack, lhs, rhs, source_location), + }) => self.compile_connect(parent_module, conditions, lhs, rhs, source_location), Stmt::Formal(StmtFormal { kind, clk, @@ -941,27 +1431,23 @@ impl Compiler { blocks: [then_block, else_block], }) => { let cond = self - .compile_expr(Expr::canonical(cond)) + .compile_expr(conditions, Expr::canonical(cond)) .map_ty(Bool::from_canonical); self.compile_block( parent_module, then_block, - CondStack::IfTrue { - parent: cond_stack, - cond, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfTrue { cond }, source_location, - } - .intern_sized(), + }])), ); self.compile_block( parent_module, else_block, - CondStack::IfFalse { - parent: cond_stack, - cond, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfFalse { cond }, source_location, - } - .intern_sized(), + }])), ); } Stmt::Match(StmtMatch { @@ -970,19 +1456,19 @@ impl Compiler { blocks, }) => { let enum_expr = self - .compile_expr(Expr::canonical(expr)) + .compile_expr(conditions, Expr::canonical(expr)) .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, block, - CondStack::MatchArm { - parent: cond_stack, - enum_expr, - variant_index, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::MatchArm { + enum_expr, + variant_index, + }, source_location, - } - .intern_sized(), + }])), ); } } @@ -1057,7 +1543,7 @@ impl Compiler { .collect(); match module.leaf_module().body() { ModuleBody::Normal(NormalModuleBody { body }) => { - self.compile_block(module, body, CondStack::Always.intern_sized()) + self.compile_block(module, body, Interned::default()); } ModuleBody::Extern(_extern_module_body) => { todo!("simulating extern module: {:?}", module); @@ -1068,8 +1554,9 @@ impl Compiler { }; entry.insert(CompiledModule { module_io }) } - pub fn run(mut self) -> Compiled { + pub fn compile(mut self) -> Compiled { self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + Compiled { insns: Insns::from(self.insns).intern_sized(), modules: self.modules, @@ -1090,10 +1577,11 @@ pub struct Compiled { impl Compiled { pub fn new(module: Module) -> Self { - Compiler::new(module.canonical().intern()).run() + Compiler::new(module.canonical().intern()).compile() } } +#[derive(Debug)] pub struct Simulation { state: interpreter::State, } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 82d3c11..8fc9038 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,11 +2,13 @@ // See Notices.txt for copyright information use crate::{ - intern::{Intern, Interned}, + intern::{Intern, Interned, Memoize}, source_location::SourceLocation, + ty::CanonicalType, util::get_many_mut, }; use num_bigint::BigInt; +use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ borrow::BorrowMut, convert::Infallible, @@ -21,6 +23,147 @@ 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>), + 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 +} + macro_rules! impl_insns { ( #[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] @@ -39,6 +182,7 @@ macro_rules! impl_insns { $(#[$insn_meta:meta])* $insn_name:ident $({ $( + #[kind = $field_kind:ident] $(#[$field_meta:meta])* $field_name:ident: $field_ty:ty, )* @@ -58,6 +202,91 @@ macro_rules! impl_insns { )* } + 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_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; @@ -497,6 +726,11 @@ macro_rules! make_state_part_kinds { $($type_field: self.$type_field.index_array(element_size.$type_field, index),)* } } + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_field: self.$type_field.offset(offset.$type_field),)* + } + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -509,6 +743,14 @@ macro_rules! make_state_part_kinds { $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* } + impl TypeIndex { + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_field: self.$type_field.offset(offset.$type_field),)* + } + } + } + #[derive(Debug)] pub(crate) struct State { pub(crate) insns: Interned>, @@ -678,20 +920,17 @@ impl Drop for BorrowedStack<'_, T> { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub(crate) struct SlotDebugData { pub(crate) name: Interned, + pub(crate) ty: CanonicalType, } impl SlotDebugData { - pub(crate) fn empty() -> Self { - Self { - name: Interned::default(), - } - } pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { let mut name = String::with_capacity(self.name.len() + prefix.len()); name.push_str(prefix); name.push_str(&self.name); Self { name: Intern::intern_owned(name), + ty: self.ty, } } } @@ -728,6 +967,15 @@ impl StatePartIndex { pub(crate) fn as_usize(self) -> usize { self.value.try_into().expect("index too big") } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + Self { + value: self + .value + .checked_add(offset.value) + .expect("offset too big"), + _phantom: PhantomData, + } + } } impl fmt::Debug for StatePartIndex { @@ -870,6 +1118,13 @@ impl StatePartIndexRange { _phantom: PhantomData, }) } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + self.end().offset(offset); // check for overflow + Self { + start: self.start.offset(offset), + len: self.len, + } + } pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { assert!(index.end().value <= self.len.value, "index out of range"); Self { @@ -1180,6 +1435,65 @@ impl State { } } +fn bigint_pow2(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + (BigInt::one() << *input).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn bigint_mask(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + ((BigInt::one() << *input) - BigInt::one()).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn bigint_not_mask(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + (-BigInt::one() << *input).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn cast_bigint_to_sint(src: &BigInt, dest_width: usize) -> BigInt { + if dest_width == 0 { + BigInt::ZERO + } else if src.bit((dest_width - 1) as u64) { + src | &*bigint_not_mask(dest_width) + } else { + src & &*bigint_mask(dest_width) + } +} + +fn cast_bigint_to_uint(src: &BigInt, dest_width: usize) -> BigInt { + src & &*bigint_mask(dest_width) +} + impl_insns! { #[insn = Insn, next_macro = next, branch_macro = branch] pub(crate) fn State::run(&mut self) -> () { @@ -1189,235 +1503,299 @@ impl_insns! { main_loop!(); cleanup! {} } - CopySmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - state.small_slots[dest] = state.small_slots[src]; - next!(); - } - CopyBig { + Copy { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] src: StatePartIndex, } => { - let [dest, src] = state.big_slots.get_many_mut([dest, src]); - dest.clone_from(src); + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } next!(); } - SExtSmall { - dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallSInt`] that aren't used - unused_bit_count: u8, - } => { - let mut value = state.small_slots[src] as SmallSInt; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.small_slots[dest] = value as SmallUInt; - next!(); - } - ZExtSmall { - dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallUInt`] that aren't used - unused_bit_count: u8, - } => { - let mut value = state.small_slots[src]; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.small_slots[dest] = value; - next!(); - } - SExtSmallToBig { + CastToSInt { + #[kind = Output] dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallSInt`] that aren't used - unused_bit_count: u8, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + dest_width: usize, } => { - let mut value = state.small_slots[src] as SmallSInt; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.big_slots[dest] = value.into(); + let value = cast_bigint_to_sint(&state.big_slots[src], dest_width); + state.big_slots[dest] = value; next!(); } - ZExtSmallToBig { + CastToUInt { + #[kind = Output] dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallUInt`] that aren't used - unused_bit_count: u8, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + dest_width: usize, } => { - let mut value = state.small_slots[src]; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.big_slots[dest] = value.into(); + let value = cast_bigint_to_uint(&state.big_slots[src], dest_width); + state.big_slots[dest] = value; next!(); } - AndSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs & rhs; - state.small_slots[dest] = value; - next!(); - } - OrSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs | rhs; - state.small_slots[dest] = value; - next!(); - } - XorSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs ^ rhs; - state.small_slots[dest] = value; - next!(); - } - NotSmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - let value = state.small_slots[src]; - state.small_slots[dest] = !value; - next!(); - } - CmpEqSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs == rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpNeSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs != rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLTSmallUInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs < rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLESmallUInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs <= rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLTSmallSInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs] as SmallSInt; - let rhs = state.small_slots[rhs] as SmallSInt; - let value = (lhs < rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLESmallSInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs] as SmallSInt; - let rhs = state.small_slots[rhs] as SmallSInt; - let value = (lhs <= rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - AddSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_add(rhs); - state.small_slots[dest] = value; - next!(); - } - SubSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_sub(rhs); - state.small_slots[dest] = value; - next!(); - } - NegSmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - let value = state.small_slots[src]; - let value = value.wrapping_neg(); - state.small_slots[dest] = value; - next!(); - } - NegBig { + And { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] & &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Or { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] | &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Xor { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] ^ &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + NotS { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + let value = !&state.big_slots[src]; + state.big_slots[dest] = value; + next!(); + } + NotU { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + width: usize, + } => { + let value = &state.big_slots[src] ^ &*bigint_mask(width); + state.big_slots[dest] = value; + next!(); + } + Neg { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] src: StatePartIndex, } => { let value = -&state.big_slots[src]; state.big_slots[dest] = value; next!(); } - MulSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_mul(rhs); - state.small_slots[dest] = value; - next!(); - } - ConstSmall { - dest: StatePartIndex, - value: SmallUInt, - } => { - state.small_slots[dest] = value; - next!(); - } - ConstBig { + Add { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] + &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + SubS { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] - &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + SubU { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + #[kind = Immediate] + dest_width: usize, + } => { + let mut value = &state.big_slots[lhs] - &state.big_slots[rhs]; + value &= &*bigint_mask(dest_width); + state.big_slots[dest] = value; + next!(); + } + Mul { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] * &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Div { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = state.big_slots[lhs].checked_div(&state.big_slots[rhs]).unwrap_or(BigInt::ZERO); + state.big_slots[dest] = value; + next!(); + } + Rem { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + // no checked_rem?! + let value = if state.big_slots[rhs].is_zero() { + BigInt::ZERO + } else { + &state.big_slots[lhs] % &state.big_slots[rhs] + }; + state.big_slots[dest] = value; + next!(); + } + DynShl { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] << state.big_slots[rhs].to_usize().expect("shl by invalid value"); + state.big_slots[dest] = value; + next!(); + } + DynShr { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + assert!(!state.big_slots[rhs].is_negative(), "shr by invalid value"); + let value = state.big_slots[rhs].to_usize().map_or(BigInt::ZERO, |rhs| &state.big_slots[lhs] >> rhs); + state.big_slots[dest] = value; + next!(); + } + Shl { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Immediate] + rhs: usize, + } => { + let value = &state.big_slots[lhs] << rhs; + state.big_slots[dest] = value; + next!(); + } + Shr { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Immediate] + rhs: usize, + } => { + let value = &state.big_slots[lhs] >> rhs; + state.big_slots[dest] = value; + next!(); + } + CmpEq { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs == rhs); + state.big_slots[dest] = value; + next!(); + } + CmpNe { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs != rhs); + state.big_slots[dest] = value; + next!(); + } + CmpLt { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs < rhs); + state.big_slots[dest] = value; + next!(); + } + CmpLe { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs <= rhs); + state.big_slots[dest] = value; + next!(); + } + Const { + #[kind = Output] + dest: StatePartIndex, + #[kind = Immediate] value: Interned, } => { state.big_slots[dest].clone_from(&value); From 3106a6fff6db72ab44bfccc62383c305ab79e492 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 12 Nov 2024 22:11:12 -0800 Subject: [PATCH 006/109] working on simulator... --- crates/fayalite/src/sim.rs | 1045 ++++++++++++++++-------- crates/fayalite/src/sim/interpreter.rs | 335 ++++++++ 2 files changed, 1044 insertions(+), 336 deletions(-) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index dbcb88a..8dc087b 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6,10 +6,12 @@ use crate::{ bundle::{BundleField, BundleType}, expr::{ + ops, target::{ - Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, + GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, + TargetPathElement, }, - ExprEnum, + ExprEnum, Flow, }, intern::{Intern, Interned, Memoize}, module::{ @@ -19,15 +21,16 @@ use crate::{ prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, SmallUInt, StatePartIndex, StatePartIndexMap, StatePartIndexRange, - StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, StatePartLayout, - StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, - MIN_BITS_FOR_NEEDING_BIG, + SlotDebugData, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, + StatePartIndexMap, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, + StatePartKindSmallSlots, StatePartLayout, StatePartsValue, TypeArrayIndex, + TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeParts, }, + ty::StaticType, }; use hashbrown::HashMap; use num_bigint::BigInt; -use std::{collections::BTreeSet, fmt, marker::PhantomData, mem}; +use std::{collections::BTreeSet, fmt}; mod interpreter; @@ -127,6 +130,14 @@ impl CompiledTypeLayout { 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; @@ -221,6 +232,9 @@ struct CompiledValue { } impl CompiledValue { + fn write(self) -> (CompiledTypeLayout, TypeIndexRange) { + self.write.unwrap_or((self.layout, self.range)) + } fn map( self, mut f: impl FnMut( @@ -235,6 +249,18 @@ impl CompiledValue { 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 { @@ -243,139 +269,90 @@ impl CompiledValue { } } -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] -struct CompiledExprDynIndex { - index_slot: StatePartIndex, - len: usize, - stride: TypeLen, -} - -impl CompiledExprDynIndex { - fn offsets(self) -> impl Iterator { - (0..self.len.try_into().unwrap()).map(move |index| TypeIndex { - small_slots: StatePartIndex { - value: self - .stride - .small_slots - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: self - .stride - .big_slots - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - }, +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 for_each_offset(dyn_indexes: &[CompiledExprDynIndex], mut f: impl FnMut(TypeIndex)) { - fn helper( - dyn_indexes: &[CompiledExprDynIndex], - offset: TypeIndex, - f: &mut impl FnMut(TypeIndex), - ) { - if let [next, rest @ ..] = dyn_indexes { - for next_offset in next.offsets() { - helper( - rest, - TypeIndex { - small_slots: StatePartIndex { - value: next_offset - .small_slots - .value - .checked_add(offset.small_slots.value) - .expect("array too big"), - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: next_offset - .big_slots - .value - .checked_add(offset.big_slots.value) - .expect("array too big"), - _phantom: PhantomData, - }, - }, - f, - ); - } - } else { - f(offset); - } - } - helper( - dyn_indexes, - TypeIndex { - small_slots: StatePartIndex { - value: 0, - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: 0, - _phantom: PhantomData, - }, - }, - &mut f, - ); + 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, - dyn_indexes: Interned<[CompiledExprDynIndex]>, + indexes: TypeArrayIndexes, } impl From> for CompiledExpr { fn from(static_part: CompiledValue) -> Self { Self { static_part, - dyn_indexes: Interned::default(), + indexes: TypeArrayIndexes::default(), } } } impl CompiledExpr { - fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledExpr { + fn map_ty(self, f: impl FnMut(T) -> U) -> CompiledExpr { let Self { static_part, - dyn_indexes, + indexes, } = self; CompiledExpr { - static_part: static_part.map(|CompiledTypeLayout { ty, layout, body }, range| { - ( - CompiledTypeLayout { - ty: f(ty), - layout, - body, - }, - range, - ) - }), - dyn_indexes, + static_part: static_part.map_ty(f), + indexes, } } fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { let Self { static_part, - dyn_indexes, + indexes, } = self; - CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + 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: _, - dyn_indexes, + indexes, } = self; self.add_target_without_indexes_to_set(inputs); - inputs.extend(dyn_indexes); + inputs.extend(indexes.as_ref().iter()); } } @@ -383,12 +360,59 @@ impl CompiledExpr { fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { let Self { static_part, - dyn_indexes, + indexes, } = self; - CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + indexes.as_ref().for_each_offset(|offset| { static_part.add_discriminant_to_set(offset, inputs); }); - inputs.extend(dyn_indexes); + 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, + } } } @@ -444,9 +468,23 @@ impl Extend for SlotSet { } } -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.index_slot)); +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)); } } @@ -475,6 +513,7 @@ struct Assignment { inputs: SlotSet, conditions: Interned<[Cond]>, insns: Vec, + source_location: SourceLocation, } #[derive(Debug, Default)] @@ -488,19 +527,59 @@ struct SlotAssignments { parts: TypeParts, } +impl SlotAssignments { + fn for_assignment(&mut self, assignment_index: usize) -> SlotAssignmentsForAssignment<'_> { + SlotAssignmentsForAssignment { + parts: &mut self.parts, + assignment_index, + } + } +} + impl StatePartsValue for SlotAssignments { type Value = StatePartAssignments; } +struct SlotAssignmentsForAssignment<'a> { + parts: &'a mut TypeParts, + assignment_index: usize, +} + +impl Extend> for SlotAssignmentsForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|slot| { + self.parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(slot) + .or_insert_with(Vec::new) + .push(self.assignment_index) + }); + } +} + +impl Extend> for SlotAssignmentsForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|slot| { + self.parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(slot) + .or_insert_with(Vec::new) + .push(self.assignment_index) + }); + } +} + #[derive(Debug)] pub struct Compiler { insns: Insns, base_module: Interned>, modules: HashMap, compiled_values: HashMap>, - compiled_exprs: HashMap<(Interned<[Cond]>, Expr), CompiledExpr>, - compiled_exprs_to_values: - HashMap<(Interned<[Cond]>, CompiledExpr), CompiledValue>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + decl_conditions: HashMap>, slots_assignments: SlotAssignments, } @@ -513,6 +592,7 @@ impl Compiler { compiled_values: HashMap::new(), compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), + decl_conditions: HashMap::new(), slots_assignments: SlotAssignments::default(), } } @@ -562,32 +642,11 @@ impl Compiler { }); 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(), - )), - ) - }) + parent.map_ty(Bundle::from_canonical).field_by_name(name) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { + parent.map_ty(Array::from_canonical).element(index) } - 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!(), } } @@ -597,32 +656,76 @@ impl Compiler { } fn compiled_expr_to_value( &mut self, - conditions: Interned<[Cond]>, expr: CompiledExpr, + source_location: SourceLocation, ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&(conditions, expr)) { + 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: mut retval, - dyn_indexes, + static_part, + indexes, } = expr; - for CompiledExprDynIndex { - index_slot, - len, - stride, - } in dyn_indexes - { - todo!(); - } - self.compiled_exprs_to_values - .insert((conditions, expr), retval); + 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)| { + if indexes.small_slots.is_empty() { + Insn::CopySmall { dest, src: base } + } else { + Insn::ReadSmallIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.small_slots, + }, + } + } + }) + .chain( + big_slots + .iter() + .zip(static_part.range.big_slots.iter()) + .map(|(dest, base)| { + if indexes.big_slots.is_empty() { + Insn::Copy { dest, src: base } + } else { + Insn::ReadIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.big_slots, + }, + } + } + }), + ), + source_location, + ); + 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); let assignment_index = self.slots_assignments.assignments.len(); @@ -630,28 +733,50 @@ impl Compiler { for insn in &insns { for InsnField { ty, kind } in insn.fields() { match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&small_slot)) => { - inputs.0.small_slots.insert(small_slot); + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { + inputs.extend([slot]); } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&big_slot)) => { - inputs.0.big_slots.insert(big_slot); + (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { + inputs.extend([slot]); } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&small_slot)) => self + ( + 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)) => self .slots_assignments - .parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(small_slot) - .or_insert_with(Vec::new) - .push(assignment_index), - (InsnFieldKind::Output, InsnFieldType::BigSlot(&big_slot)) => self + .for_assignment(assignment_index) + .extend([slot]), + (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => self .slots_assignments - .parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(big_slot) - .or_insert_with(Vec::new) - .push(assignment_index), + .for_assignment(assignment_index) + .extend([slot]), + ( + InsnFieldKind::Output, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + self.slots_assignments + .for_assignment(assignment_index) + .extend([slot]) + }); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| { + self.slots_assignments + .for_assignment(assignment_index) + .extend([slot]) + }); + inputs.extend(array_indexed.indexes); + } ( _, InsnFieldType::SmallUInt(_) @@ -669,11 +794,12 @@ impl Compiler { inputs, conditions, insns, + source_location, }); } fn simple_nary_big_expr( &mut self, - conditions: Interned<[Cond]>, + instantiated_module: InstantiatedModule, dest_ty: CanonicalType, inputs: [Expr; N], make_insns: impl FnOnce( @@ -682,8 +808,9 @@ impl Compiler { ) -> Vec, ) -> CompiledValue { let inputs = inputs.map(|input| { - let input = self.compile_expr(conditions, input); - let input = self.compiled_expr_to_value(conditions, 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, @@ -705,15 +832,19 @@ impl Compiler { range, write: None, }; - self.add_assignment(conditions, make_insns(big_slots.start, inputs)); + self.add_assignment( + Interned::default(), + make_insns(big_slots.start, inputs), + instantiated_module.leaf_module().source_location(), + ); retval } fn compile_expr( &mut self, - conditions: Interned<[Cond]>, + instantiated_module: InstantiatedModule, expr: Expr, ) -> CompiledExpr { - if let Some(&retval) = self.compiled_exprs.get(&(conditions, expr)) { + if let Some(&retval) = self.compiled_exprs.get(&expr) { return retval; } let mut cast_bit = |arg: Expr| { @@ -741,7 +872,7 @@ impl Compiler { CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, }; - self.simple_nary_big_expr(conditions, Expr::ty(expr), [arg], |dest, [src]| { + 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 }] @@ -762,23 +893,33 @@ impl Compiler { }; let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { ExprEnum::UIntLiteral(expr) => self - .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }) + .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(conditions, expr.ty().canonical(), [], |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }) + .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(conditions, Bool.canonical(), [], |dest, []| { + .simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| { vec![Insn::Const { dest, value: BigInt::from(expr).intern_sized(), @@ -791,7 +932,7 @@ impl Compiler { ExprEnum::Uninit(expr) => todo!(), ExprEnum::NotU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -805,7 +946,7 @@ impl Compiler { .into(), ExprEnum::NotS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::NotS { dest, src }], @@ -813,7 +954,7 @@ impl Compiler { .into(), ExprEnum::NotB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -827,7 +968,7 @@ impl Compiler { .into(), ExprEnum::Neg(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::Neg { dest, src }], @@ -835,7 +976,7 @@ impl Compiler { .into(), ExprEnum::BitAndU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -843,7 +984,7 @@ impl Compiler { .into(), ExprEnum::BitAndS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -851,7 +992,7 @@ impl Compiler { .into(), ExprEnum::BitAndB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -859,7 +1000,7 @@ impl Compiler { .into(), ExprEnum::BitOrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -867,7 +1008,7 @@ impl Compiler { .into(), ExprEnum::BitOrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -875,7 +1016,7 @@ impl Compiler { .into(), ExprEnum::BitOrB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -883,7 +1024,7 @@ impl Compiler { .into(), ExprEnum::BitXorU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -891,7 +1032,7 @@ impl Compiler { .into(), ExprEnum::BitXorS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -899,7 +1040,7 @@ impl Compiler { .into(), ExprEnum::BitXorB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -907,7 +1048,7 @@ impl Compiler { .into(), ExprEnum::AddU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], @@ -915,7 +1056,7 @@ impl Compiler { .into(), ExprEnum::AddS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], @@ -923,7 +1064,7 @@ impl Compiler { .into(), ExprEnum::SubU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| { @@ -938,7 +1079,7 @@ impl Compiler { .into(), ExprEnum::SubS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], @@ -946,7 +1087,7 @@ impl Compiler { .into(), ExprEnum::MulU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], @@ -954,7 +1095,7 @@ impl Compiler { .into(), ExprEnum::MulS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], @@ -962,7 +1103,7 @@ impl Compiler { .into(), ExprEnum::DivU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], @@ -970,7 +1111,7 @@ impl Compiler { .into(), ExprEnum::DivS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], @@ -978,7 +1119,7 @@ impl Compiler { .into(), ExprEnum::RemU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], @@ -986,7 +1127,7 @@ impl Compiler { .into(), ExprEnum::RemS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], @@ -994,7 +1135,7 @@ impl Compiler { .into(), ExprEnum::DynShlU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], @@ -1002,7 +1143,7 @@ impl Compiler { .into(), ExprEnum::DynShlS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], @@ -1010,7 +1151,7 @@ impl Compiler { .into(), ExprEnum::DynShrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], @@ -1018,7 +1159,7 @@ impl Compiler { .into(), ExprEnum::DynShrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], @@ -1026,7 +1167,7 @@ impl Compiler { .into(), ExprEnum::FixedShlU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1040,7 +1181,7 @@ impl Compiler { .into(), ExprEnum::FixedShlS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1054,7 +1195,7 @@ impl Compiler { .into(), ExprEnum::FixedShrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1068,7 +1209,7 @@ impl Compiler { .into(), ExprEnum::FixedShrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1082,7 +1223,7 @@ impl Compiler { .into(), ExprEnum::CmpLtB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1090,7 +1231,7 @@ impl Compiler { .into(), ExprEnum::CmpLeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1098,7 +1239,7 @@ impl Compiler { .into(), ExprEnum::CmpGtB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1107,7 +1248,7 @@ impl Compiler { .into(), ExprEnum::CmpGeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1116,7 +1257,7 @@ impl Compiler { .into(), ExprEnum::CmpEqB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1124,7 +1265,7 @@ impl Compiler { .into(), ExprEnum::CmpNeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1132,7 +1273,7 @@ impl Compiler { .into(), ExprEnum::CmpLtU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1140,7 +1281,7 @@ impl Compiler { .into(), ExprEnum::CmpLeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1148,7 +1289,7 @@ impl Compiler { .into(), ExprEnum::CmpGtU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1157,7 +1298,7 @@ impl Compiler { .into(), ExprEnum::CmpGeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1166,7 +1307,7 @@ impl Compiler { .into(), ExprEnum::CmpEqU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1174,7 +1315,7 @@ impl Compiler { .into(), ExprEnum::CmpNeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1182,7 +1323,7 @@ impl Compiler { .into(), ExprEnum::CmpLtS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1190,7 +1331,7 @@ impl Compiler { .into(), ExprEnum::CmpLeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1198,7 +1339,7 @@ impl Compiler { .into(), ExprEnum::CmpGtS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1207,7 +1348,7 @@ impl Compiler { .into(), ExprEnum::CmpGeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1216,7 +1357,7 @@ impl Compiler { .into(), ExprEnum::CmpEqS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1224,7 +1365,7 @@ impl Compiler { .into(), ExprEnum::CmpNeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1232,7 +1373,7 @@ impl Compiler { .into(), ExprEnum::CastUIntToUInt(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -1244,10 +1385,23 @@ impl Compiler { }, ) .into(), - ExprEnum::CastUIntToSInt(expr) => todo!(), + 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( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -1259,7 +1413,20 @@ impl Compiler { }, ) .into(), - ExprEnum::CastSIntToSInt(expr) => todo!(), + 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())), @@ -1287,34 +1454,190 @@ impl Compiler { 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) => todo!(), + 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) => 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) => todo!(), - ExprEnum::ReduceBitAndU(expr) => todo!(), - ExprEnum::ReduceBitAndS(expr) => todo!(), - ExprEnum::ReduceBitOrU(expr) => todo!(), - ExprEnum::ReduceBitOrS(expr) => todo!(), - ExprEnum::ReduceBitXorU(expr) => todo!(), - ExprEnum::ReduceBitXorS(expr) => todo!(), + 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) => todo!(), ExprEnum::SliceSInt(expr) => todo!(), ExprEnum::CastToBits(expr) => todo!(), ExprEnum::CastBitsTo(expr) => todo!(), - ExprEnum::ModuleIO(expr) => todo!(), - ExprEnum::Instance(expr) => todo!(), - ExprEnum::Wire(expr) => todo!(), - ExprEnum::Reg(expr) => todo!(), - ExprEnum::MemPort(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((conditions, expr), retval); + 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, - parent_module: Interned, - conditions: Interned<[Cond]>, + lhs_instantiated_module: InstantiatedModule, + lhs_conditions: Interned<[Cond]>, lhs: Expr, + rhs_instantiated_module: InstantiatedModule, + rhs_conditions: Interned<[Cond]>, mut rhs: Expr, source_location: SourceLocation, ) { @@ -1336,9 +1659,11 @@ impl Compiler { let rhs = Expr::::from_canonical(rhs); for index in 0..lhs_ty.len() { self.compile_connect( - parent_module, - conditions, + lhs_instantiated_module, + lhs_conditions, lhs[index], + rhs_instantiated_module, + rhs_conditions, rhs[index], source_location, ); @@ -1372,20 +1697,30 @@ impl Compiler { { assert_eq!(name, rhs_field.name); assert_eq!(flipped, rhs_field.flipped); - let mut lhs_expr = - crate::expr::ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); - let mut rhs_expr = - crate::expr::ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + 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 { - mem::swap(&mut lhs_expr, &mut rhs_expr); + // 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, + ); } - self.compile_connect( - parent_module, - conditions, - lhs_expr, - rhs_expr, - source_location, - ); } return; } @@ -1395,10 +1730,93 @@ impl Compiler { CanonicalType::Clock(_) => unreachable!(), } } - let lhs = self.compile_expr(conditions, lhs); - let rhs = self.compile_expr(conditions, rhs); - let rhs = self.compiled_expr_to_value(conditions, rhs); - todo!(); + 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]>, + ) { + 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!(), + } + } + } + } } fn compile_block( &mut self, @@ -1416,22 +1834,23 @@ impl Compiler { lhs, rhs, source_location, - }) => self.compile_connect(parent_module, conditions, lhs, rhs, source_location), - Stmt::Formal(StmtFormal { - kind, - clk, - pred, - en, - text, + }) => self.compile_connect( + *parent_module, + conditions, + lhs, + *parent_module, + conditions, + rhs, source_location, - }) => todo!("implement simulating formal statements"), + ), + Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"), Stmt::If(StmtIf { cond, source_location, blocks: [then_block, else_block], }) => { let cond = self - .compile_expr(conditions, Expr::canonical(cond)) + .compile_expr(*parent_module, Expr::canonical(cond)) .map_ty(Bool::from_canonical); self.compile_block( parent_module, @@ -1456,7 +1875,7 @@ impl Compiler { blocks, }) => { let enum_expr = self - .compile_expr(conditions, Expr::canonical(expr)) + .compile_expr(*parent_module, Expr::canonical(expr)) .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( @@ -1472,55 +1891,9 @@ impl Compiler { ); } } - Stmt::Declaration(declaration) => match declaration { - StmtDeclaration::Wire(StmtWire { annotations, wire }) => { - self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: wire.into(), - }); - } - StmtDeclaration::Reg(StmtReg { annotations, reg }) => { - self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: reg.into(), - }); - todo!(); - } - StmtDeclaration::Instance(StmtInstance { - annotations, - instance, - }) => { - let CompiledValue { - layout: - CompiledTypeLayout { - ty: value_ty, - layout: ty_layout, - body: CompiledTypeLayoutBody::Bundle { fields }, - }, - range: value_range, - write: None, - } = self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: instance.into(), - }) - else { - unreachable!(); - }; - let CompiledModule { module_io } = *self.compile_module( - InstantiatedModule::Child { - parent: parent_module, - instance: instance.intern_sized(), - } - .intern_sized(), - ); - for (module_io, CompiledBundleField { offset, ty }) in - module_io.into_iter().zip(fields) - { - todo!(); - } - todo!() - } - }, + Stmt::Declaration(declaration) => { + self.compile_declaration(declaration, parent_module, conditions); + } } } } @@ -1556,7 +1929,7 @@ impl Compiler { } pub fn compile(mut self) -> Compiled { self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - + todo!("handle self.slots_assignments"); Compiled { insns: Insns::from(self.insns).intern_sized(), modules: self.modules, diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 8fc9038..e89628c 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -109,6 +109,8 @@ 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>), @@ -787,6 +789,167 @@ macro_rules! make_state_part_kinds { *self.orig_pc = self.pc; } } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexes { + $(pub(crate) $type_field: Interned<[StatePartArrayIndex<$TypeKind>]>,)* + } + + impl TypeArrayIndexes { + pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { + TypeArrayIndexesRef { + $($type_field: &self.$type_field,)* + } + } + #[must_use] + pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_field: Interned::from_iter(self.$type_field.iter().copied().chain([next.$type_field])),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndex { + $(pub(crate) $type_field: StatePartArrayIndex<$TypeKind>,)* + } + + impl TypeArrayIndex { + pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { + Self { + $($type_field: StatePartArrayIndex { + index, + len, + stride: stride.$type_field, + },)* + } + } + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len; + $(assert_eq!(self.$type_field.len, len, "array length mismatch");)* + len + } + pub(crate) fn index(self) -> StatePartIndex { + let index = self.small_slots.index; + $(assert_eq!(self.$type_field.index, index, "array index mismatch");)* + index + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn stride(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.stride,)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexesRef<'a> { + $(pub(crate) $type_field: &'a [StatePartArrayIndex<$TypeKind>],)* + } + + impl<'a> TypeArrayIndexesRef<'a> { + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len(); + $(assert_eq!(self.$type_field.len(), len, "indexes count mismatch");)* + len + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn iter(self) -> impl Iterator + 'a { + (0..self.len()).map(move |i| TypeArrayIndex { + $($type_field: self.$type_field[i],)* + }) + } + pub(crate) fn for_each_offset( + self, + mut f: impl FnMut(TypeIndex), + ) { + self.for_each_offset2(TypeIndex { + $($type_field: StatePartIndex { + value: 0, + _phantom: PhantomData, + },)* + }, &mut f); + } + pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { + $(let $type_field = self.$type_field.split_first()?;)* + let next = TypeArrayIndex { + $($type_field: *$type_field.0,)* + }; + let rest = TypeArrayIndexesRef { + $($type_field: $type_field.1,)* + }; + Some((next, rest)) + } + pub(crate) fn for_each_offset2( + self, + base_offset: TypeIndex, + f: &mut (impl FnMut(TypeIndex) + ?Sized), + ) { + if let Some((next, rest)) = self.split_first() { + let stride = next.stride(); + for index in 0..next.len().try_into().expect("array too big") { + let mut offset = TypeIndex { + $($type_field: StatePartIndex { + value: stride + .$type_field + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + },)* + }; + $(offset.$type_field.value = + base_offset + .$type_field + .value + .checked_add(offset.$type_field.value) + .expect("array too big");)* + rest.for_each_offset2(offset, f); + } + } else { + $(assert!(self.$type_field.is_empty(), "indexes count mismatch");)* + f(base_offset); + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndexed { + $(pub(crate) $type_field: StatePartArrayIndexed<$TypeKind>,)* + } + + impl TypeArrayIndexed { + pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { + Self { + $($type_field: StatePartArrayIndexed { + base: base.$type_field, + indexes: indexes.$type_field, + },)* + } + } + pub(crate) fn base(self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.base,)* + } + } + pub(crate) fn indexes(self) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_field: self.$type_field.indexes,)* + } + } + } + + impl From for TypeArrayIndexed { + fn from(value: TypeIndex) -> Self { + TypeArrayIndexed { + $($type_field: value.$type_field.into(),)* + } + } + } }; } @@ -933,6 +1096,12 @@ impl SlotDebugData { ty: self.ty, } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + name: Interned::default(), + ty: self.ty, + } + } } impl, BK: InsnsBuildingKind> StatePartLayout { @@ -946,6 +1115,16 @@ impl, BK: InsnsBuildingKind> StatePa _phantom: PhantomData, } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_anonymized_debug_info()) + .collect(), + _phantom: PhantomData, + } + } } impl TypeLayout { @@ -955,6 +1134,63 @@ impl TypeLayout { big_slots: self.big_slots.with_prefixed_debug_names(prefix), } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + small_slots: self.small_slots.with_anonymized_debug_info(), + big_slots: self.big_slots.with_anonymized_debug_info(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePartArrayIndex { + pub(crate) index: StatePartIndex, + pub(crate) len: usize, + pub(crate) stride: StatePartLen, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePartArrayIndexed { + pub(crate) base: StatePartIndex, + pub(crate) indexes: Interned<[StatePartArrayIndex]>, +} + +impl From> for StatePartArrayIndexed { + fn from(base: StatePartIndex) -> Self { + Self { + base, + indexes: Interned::default(), + } + } +} + +impl StatePartArrayIndexed { + pub(crate) fn for_each_target2( + base: StatePartIndex, + indexes: &[StatePartArrayIndex], + f: &mut (impl FnMut(StatePartIndex) + ?Sized), + ) { + if let [next, rest @ ..] = indexes { + for i in 0..next.len.try_into().expect("array too big") { + Self::for_each_target2( + StatePartIndex { + value: base + .value + .checked_add(next.stride.value.checked_mul(i).expect("array too big")) + .expect("array too big"), + _phantom: PhantomData, + }, + rest, + f, + ); + } + } else { + f(base); + } + } + pub(crate) fn for_each_target(self, mut f: impl FnMut(StatePartIndex)) { + Self::for_each_target2(self.base, &self.indexes, &mut f); + } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1435,6 +1671,26 @@ impl State { } } +impl BorrowedState<'_> { + fn eval_array_indexed( + &self, + array_indexed: StatePartArrayIndexed, + ) -> Option> { + let StatePartArrayIndexed { + base: mut retval, + indexes, + } = array_indexed; + for StatePartArrayIndex { index, len, stride } in indexes { + let index = self.small_slots[index]; + if index >= len as SmallUInt { + return None; + } + retval.value += stride.value * index as u32; + } + Some(retval) + } +} + fn bigint_pow2(width: usize) -> Interned { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct MyMemoize; @@ -1515,6 +1771,69 @@ impl_insns! { } next!(); } + CopySmall { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + state.small_slots[dest] = state.small_slots[src]; + next!(); + } + ReadIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } + } else { + state.big_slots[dest] = BigInt::ZERO; + } + next!(); + } + ReadSmallIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + state.small_slots[dest] = state.small_slots[src]; + } else { + state.small_slots[dest] = 0; + } + next!(); + } + WriteIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } + } + next!(); + } + WriteSmallIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + state.small_slots[dest] = state.small_slots[src]; + } + next!(); + } CastToSInt { #[kind = Output] dest: StatePartIndex, @@ -1792,6 +2111,22 @@ impl_insns! { state.big_slots[dest] = value; next!(); } + ReduceBitXor { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + input_width: usize, + } => { + let src = &state.big_slots[src]; + let src = src & &*bigint_mask(input_width); + let value = BigInt::from( + src.to_biguint().expect("known to be non-negative").count_ones() % 2, + ); + state.big_slots[dest] = value; + next!(); + } Const { #[kind = Output] dest: StatePartIndex, From a6e40839ac64d751e807b28e89ef11bd2f956b40 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 13 Nov 2024 04:14:04 -0800 Subject: [PATCH 007/109] simulator WIP: use petgraph for topological sort over assignments --- Cargo.lock | 17 + Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/sim.rs | 959 +++++++++++++++++++++---- crates/fayalite/src/sim/interpreter.rs | 54 +- 5 files changed, 887 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c280158..c768882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,6 +315,7 @@ dependencies = [ "num-bigint", "num-traits", "os_pipe", + "petgraph", "serde", "serde_json", "tempfile", @@ -358,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" @@ -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" diff --git a/Cargo.toml b/Cargo.toml index add4bfe..a9cc312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ jobslot = "0.2.19" 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" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 8e90a74..2652792 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -25,6 +25,7 @@ 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 diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 8dc087b..b647302 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -21,16 +21,19 @@ use crate::{ prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, - StatePartIndexMap, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, - StatePartKindSmallSlots, StatePartLayout, StatePartsValue, TypeArrayIndex, - TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeParts, + SlotDebugData, SmallUInt, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, + StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, + TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, ty::StaticType, }; use hashbrown::HashMap; use num_bigint::BigInt; -use std::{collections::BTreeSet, fmt}; +use petgraph::visit::{ + GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, +}; +use std::{collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; mod interpreter; @@ -416,6 +419,385 @@ impl CompiledExpr { } } +#[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, Default)] +struct Assignments { + assignments: Vec, + slot_readers: Option, + slot_writers: Option, +} + +impl Assignments { + fn finalize(&mut self, slots_len: TypeLen) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); + for (assignment_index, assignment) in self.assignments.iter().enumerate() { + slot_readers + .keys_for_assignment(assignment_index) + .extend([&assignment.inputs]); + slot_writers + .keys_for_assignment(assignment_index) + .extend([&assignment.outputs]); + } + self.slot_readers = Some(slot_readers); + self.slot_writers = Some(slot_writers); + } + fn push(&mut self, v: Assignment) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + self.assignments.push(v); + } + fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_readers + .as_ref() + .expect("Assignments::finalize should have been called") + } + fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_writers + .as_ref() + .expect("Assignments::finalize should have been called") + } +} + +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, + } + } +} + +enum AssignmentsNeighborsDirected<'a> { + AssignmentIndexes(std::slice::Iter<'a, usize>), + Slots { + 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 { + match self { + AssignmentsNeighborsDirected::AssignmentIndexes(iter) => iter + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex), + AssignmentsNeighborsDirected::Slots { + small_slots, + big_slots, + } => small_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::SmallSlots) + .or_else(|| { + big_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::BigSlots) + }), + } + } +} + +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 SlotSet(TypeParts { + small_slots, + big_slots, + }) = match d { + Outgoing => &assignment.outputs, + Incoming => &assignment.inputs, + }; + AssignmentsNeighborsDirected::Slots { + small_slots: small_slots.iter(), + big_slots: big_slots.iter(), + } + } + AssignmentOrSlotIndex::SmallSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + AssignmentOrSlotIndex::BigSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + } + } +} + +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); @@ -427,6 +809,30 @@ impl SlotSet { }) = 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 { @@ -511,63 +917,221 @@ impl Extend for SlotSet { #[derive(Debug)] struct Assignment { inputs: SlotSet, + outputs: SlotSet, conditions: Interned<[Cond]>, insns: Vec, source_location: SourceLocation, } -#[derive(Debug, Default)] -struct StatePartAssignments { - written_slot_to_assignment_indexes_map: StatePartIndexMap>, +#[derive(Debug)] +struct SlotToAssignmentIndexFullMap(TypeParts); + +impl StatePartsValue for SlotToAssignmentIndexFullMap { + type Value = Box<[Vec]>; } -#[derive(Debug, Default)] -struct SlotAssignments { - assignments: Vec, - parts: TypeParts, -} - -impl SlotAssignments { - fn for_assignment(&mut self, assignment_index: usize) -> SlotAssignmentsForAssignment<'_> { - SlotAssignmentsForAssignment { - parts: &mut self.parts, +impl SlotToAssignmentIndexFullMap { + fn new(len: TypeLen) -> Self { + let TypeLen { + small_slots, + big_slots, + } = len; + const VEC_NEW: Vec = Vec::new(); + 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, } } -} - -impl StatePartsValue for SlotAssignments { - type Value = StatePartAssignments; -} - -struct SlotAssignmentsForAssignment<'a> { - parts: &'a mut TypeParts, - assignment_index: usize, -} - -impl Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(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 Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(self.assignment_index) - }); +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, + } } } @@ -580,7 +1144,9 @@ pub struct Compiler { compiled_exprs: HashMap, CompiledExpr>, compiled_exprs_to_values: HashMap, CompiledValue>, decl_conditions: HashMap>, - slots_assignments: SlotAssignments, + compiled_values_to_dyn_array_indexes: + HashMap, StatePartIndex>, + assignments: Assignments, } impl Compiler { @@ -593,7 +1159,8 @@ impl Compiler { compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), decl_conditions: HashMap::new(), - slots_assignments: SlotAssignments::default(), + compiled_values_to_dyn_array_indexes: HashMap::new(), + assignments: Assignments::default(), } } fn compile_value( @@ -728,74 +1295,8 @@ impl Compiler { source_location: SourceLocation, ) { let insns = Vec::from_iter(insns); - let assignment_index = self.slots_assignments.assignments.len(); - let mut inputs = 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)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - ( - InsnFieldKind::Output, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - ( - _, - InsnFieldType::SmallUInt(_) - | InsnFieldType::SmallSInt(_) - | InsnFieldType::InternedBigInt(_) - | InsnFieldType::U8(_) - | InsnFieldType::USize(_) - | InsnFieldType::Empty(_), - ) - | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} - } - } - } - self.slots_assignments.assignments.push(Assignment { - inputs, - conditions, - insns, - source_location, - }); + self.assignments + .push(Assignment::new(conditions, insns, source_location)); } fn simple_nary_big_expr( &mut self, @@ -839,6 +1340,70 @@ impl Compiler { ); 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 { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => compiled_value.range.small_slots.start, + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + } => { + 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, @@ -1463,7 +2028,21 @@ impl Compiler { .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Array::from_canonical) .element(expr.element_index()), - ExprEnum::DynArrayIndex(expr) => todo!(), + 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 { @@ -1534,8 +2113,36 @@ impl Compiler { }, ) .into(), - ExprEnum::SliceUInt(expr) => todo!(), - ExprEnum::SliceSInt(expr) => todo!(), + 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 @@ -1927,34 +2534,104 @@ impl Compiler { }; entry.insert(CompiledModule { module_io }) } - pub fn compile(mut self) -> Compiled { - self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - todo!("handle self.slots_assignments"); + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().len().ty); + let assignments_queue: 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, + ), + }, + }; + todo!(); + } + pub fn compile(mut self) -> Compiled { + let base_module = + *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + self.process_assignments(); Compiled { insns: Insns::from(self.insns).intern_sized(), - modules: self.modules, + base_module, + base_module_io_ty: self.base_module.io_ty(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct CompiledModule { + module_io: Interned<[CompiledValue]>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Compiled { + insns: Interned>, + base_module: CompiledModule, + base_module_io_ty: T, +} + +impl Compiled { + pub fn new(module: Module) -> Self { + Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) + } + pub fn canonical(self) -> Compiled { + let Self { + insns, + base_module, + base_module_io_ty, + } = self; + Compiled { + insns, + base_module, + base_module_io_ty: Bundle::from_canonical(base_module_io_ty.canonical()), + } + } + pub fn from_canonical(canonical: Compiled) -> Self { + let Compiled { + insns, + base_module, + base_module_io_ty, + } = canonical; + Self { + insns, + base_module, + base_module_io_ty: T::from_canonical(base_module_io_ty.canonical()), } } } #[derive(Debug)] -struct CompiledModule { - module_io: Interned<[CompiledValue]>, +pub struct Simulation { + state: interpreter::State, + compiled: Compiled, } -#[derive(Debug)] -pub struct Compiled { - insns: Interned>, - modules: HashMap, -} - -impl Compiled { - pub fn new(module: Module) -> Self { - Compiler::new(module.canonical().intern()).compile() +impl Simulation { + pub fn from_compiled(compiled: Compiled) -> Self { + Self { + state: interpreter::State::new(compiled.insns), + compiled, + } + } + pub fn settle_step(&mut self) { + self.state.setup_call(0); + self.state.run(); } } - -#[derive(Debug)] -pub struct Simulation { - state: interpreter::State, -} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index e89628c..5269131 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -362,6 +362,12 @@ pub(crate) struct Insns { state_layout: StateLayout, } +impl Insns { + pub(crate) fn state_layout(&self) -> &StateLayout { + &self.state_layout + } +} + struct InsnsDebug<'a> { insns: &'a [Insn], insn_source_locations: &'a [SourceLocation], @@ -564,13 +570,13 @@ macro_rules! make_state_part_kinds { impl Copy for StateLayout {} impl StateLayout { - pub(crate) fn len(self) -> StateLen { + pub(crate) fn len(&self) -> StateLen { StateLen { ty: self.ty.len(), $($state_field: self.$state_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.ty.is_empty() $(&& self.$state_field.is_empty())* } pub(crate) fn empty() -> Self { @@ -652,12 +658,12 @@ macro_rules! make_state_part_kinds { impl Copy for TypeLayout {} impl TypeLayout { - pub(crate) fn len(self) -> TypeLen { + pub(crate) fn len(&self) -> TypeLen { TypeLen { $($type_field: self.$type_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { $(self.$type_field.is_empty())&&+ } pub(crate) fn empty() -> Self { @@ -762,6 +768,14 @@ macro_rules! make_state_part_kinds { } impl State { + pub(crate) fn new(insns: Interned>) -> Self { + Self { + insns, + pc: 0, + $($state_field: StatePart::new(insns.state_layout.$state_field.len()),)* + $($type_field: StatePart::new(insns.state_layout.ty.$type_field.len()),)* + } + } pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { BorrowedState { orig_insns: self.insns, @@ -1494,6 +1508,13 @@ impl fmt::Debug for StatePartIndexMap { } } +impl Extend<(StatePartIndex, V)> for StatePartIndexMap { + fn extend, V)>>(&mut self, iter: T) { + self.map + .extend(iter.into_iter().map(|(k, v)| (k.as_usize(), v))); + } +} + impl StatePartIndexMap { pub(crate) fn new() -> Self { Self { @@ -1834,6 +1855,17 @@ impl_insns! { } next!(); } + CastBigToArrayIndex { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + let src = &state.big_slots[src]; + let value = src.try_into().unwrap_or(SmallUInt::MAX); + state.small_slots[dest] = value; + next!(); + } CastToSInt { #[kind = Output] dest: StatePartIndex, @@ -2055,6 +2087,20 @@ impl_insns! { state.big_slots[dest] = value; next!(); } + SliceInt { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + start: usize, + #[kind = Immediate] + len: usize, + } => { + let value = &state.big_slots[src] >> start; + state.big_slots[dest] = value & &*bigint_mask(len); + next!(); + } CmpEq { #[kind = Output] dest: StatePartIndex, From f54e55a14330d1e10e2b6da13510d77e016850bc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 13 Nov 2024 21:20:15 -0800 Subject: [PATCH 008/109] Simulation::settle_step() works for simple modules --- crates/fayalite/src/sim.rs | 485 +++++++++++++++-------- crates/fayalite/src/sim/interpreter.rs | 378 +++++++++++++++++- crates/fayalite/tests/sim.rs | 524 +++++++++++++++++++++++++ 3 files changed, 1221 insertions(+), 166 deletions(-) create mode 100644 crates/fayalite/tests/sim.rs diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b647302..7793dce 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -40,13 +40,13 @@ mod interpreter; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] enum CondBody { IfTrue { - cond: CompiledExpr, + cond: CompiledValue, }, IfFalse { - cond: CompiledExpr, + cond: CompiledValue, }, MatchArm { - enum_expr: CompiledExpr, + enum_expr: CompiledValue, variant_index: usize, }, } @@ -267,8 +267,8 @@ impl CompiledValue { } impl CompiledValue { - fn add_discriminant_to_set(self, offset: TypeIndex, inputs: &mut SlotSet) { - inputs.extend([self.range.offset(offset)]); + fn add_discriminant_to_set(self, inputs: &mut SlotSet) { + inputs.extend([self.range]); } } @@ -359,19 +359,6 @@ impl CompiledExpr { } } -impl CompiledExpr { - fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { - let Self { - static_part, - indexes, - } = self; - indexes.as_ref().for_each_offset(|offset| { - static_part.add_discriminant_to_set(offset, inputs); - }); - inputs.extend(indexes.as_ref().iter()); - } -} - impl CompiledExpr { fn field_by_index(self, field_index: usize) -> CompiledExpr { CompiledExpr { @@ -446,48 +433,118 @@ enum AssignmentIO { }, } -#[derive(Debug, Default)] -struct Assignments { - assignments: Vec, - slot_readers: Option, - slot_writers: Option, +#[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) { - assert!( - self.slot_readers.is_none() && self.slot_writers.is_none(), - "already finalized" - ); + 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); - for (assignment_index, assignment) in self.assignments.iter().enumerate() { + let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; + let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; + for (assignment_index, assignment) in assignments.iter().enumerate() { slot_readers .keys_for_assignment(assignment_index) .extend([&assignment.inputs]); - slot_writers - .keys_for_assignment(assignment_index) - .extend([&assignment.outputs]); + 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.slot_readers = Some(slot_readers); - self.slot_writers = Some(slot_writers); + *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) { - assert!( - self.slot_readers.is_none() && self.slot_writers.is_none(), - "already finalized" - ); - self.assignments.push(v); + 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 { - self.slot_readers - .as_ref() - .expect("Assignments::finalize should have been called") + let Self::Finalized { slot_readers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_readers } fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { - self.slot_writers - .as_ref() - .expect("Assignments::finalize should have been called") + 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 } } @@ -541,42 +598,47 @@ impl<'a> IntoNodeIdentifiers for &'a Assignments { big_slots, } = self.slot_readers().len(); AssignmentsNodeIdentifiers { - assignment_indexes: 0..self.assignments.len(), + assignment_indexes: 0..self.assignments().len(), small_slots: 0..small_slots.value, big_slots: 0..big_slots.value, } } } -enum AssignmentsNeighborsDirected<'a> { - AssignmentIndexes(std::slice::Iter<'a, usize>), - Slots { - small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - }, +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 { - match self { - AssignmentsNeighborsDirected::AssignmentIndexes(iter) => iter - .next() - .copied() - .map(AssignmentOrSlotIndex::AssignmentIndex), - AssignmentsNeighborsDirected::Slots { - small_slots, - big_slots, - } => small_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::SmallSlots) - .or_else(|| { - big_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::BigSlots) - }), + 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 } } } @@ -604,25 +666,39 @@ impl<'a> IntoNeighborsDirected for &'a Assignments { }; match n { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - let assignment = &self.assignments[assignment_index]; - let SlotSet(TypeParts { - small_slots, - big_slots, - }) = match d { - Outgoing => &assignment.outputs, - Incoming => &assignment.inputs, + 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::Slots { + AssignmentsNeighborsDirected { + assignment_indexes: assignment_indexes.iter(), small_slots: small_slots.iter(), big_slots: big_slots.iter(), } } - AssignmentOrSlotIndex::SmallSlots(slot) => { - AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) - } - AssignmentOrSlotIndex::BigSlots(slot) => { - AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].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(), + }, } } } @@ -659,7 +735,7 @@ impl Visitable for Assignments { fn visit_map(self: &Self) -> Self::Map { AssignmentsVisitMap { - assignments: vec![false; self.assignments.len()], + assignments: vec![false; self.assignments().len()], slots: DenseSlotSet::new(self.slot_readers().len()), } } @@ -667,7 +743,7 @@ impl Visitable for Assignments { fn reset_map(self: &Self, map: &mut Self::Map) { let AssignmentsVisitMap { assignments, slots } = map; assignments.clear(); - assignments.resize(self.assignments.len(), false); + assignments.resize(self.assignments().len(), false); if slots.len() != self.slot_readers().len() { *slots = DenseSlotSet::new(self.slot_readers().len()); } else { @@ -898,12 +974,12 @@ 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 } => { - cond.add_target_and_indexes_to_set(self); + self.extend([cond.range]); } CondBody::MatchArm { enum_expr, - variant_index, - } => enum_expr.add_discriminant_and_indexes_to_set(self), + variant_index: _, + } => enum_expr.add_discriminant_to_set(self), }) } } @@ -936,11 +1012,10 @@ impl SlotToAssignmentIndexFullMap { small_slots, big_slots, } = len; - const VEC_NEW: Vec = Vec::new(); Self(TypeParts { - small_slots: vec![VEC_NEW; small_slots.value.try_into().expect("length too big")] + 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")] + big_slots: vec![Vec::new(); big_slots.value.try_into().expect("length too big")] .into_boxed_slice(), }) } @@ -1237,54 +1312,51 @@ impl Compiler { static_part, indexes, } = expr; - let layout = static_part.layout.with_anonymized_debug_info(); - let retval = CompiledValue { - layout, - range: self.insns.allocate_variable(&layout.layout), - write: None, + 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 }; - 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)| { - if indexes.small_slots.is_empty() { - Insn::CopySmall { dest, src: base } - } else { - Insn::ReadSmallIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - } - } - }) - .chain( - big_slots - .iter() - .zip(static_part.range.big_slots.iter()) - .map(|(dest, base)| { - if indexes.big_slots.is_empty() { - Insn::Copy { dest, src: base } - } else { - Insn::ReadIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - } - } - }), - ), - source_location, - ); self.compiled_exprs_to_values.insert(expr, retval); retval } @@ -2456,9 +2528,9 @@ impl Compiler { source_location, blocks: [then_block, else_block], }) => { - let cond = self - .compile_expr(*parent_module, Expr::canonical(cond)) - .map_ty(Bool::from_canonical); + 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, @@ -2481,9 +2553,9 @@ impl Compiler { source_location, blocks, }) => { - let enum_expr = self - .compile_expr(*parent_module, Expr::canonical(expr)) - .map_ty(Enum::from_canonical); + 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, @@ -2514,10 +2586,12 @@ impl Compiler { annotations: _, module_io, }| { - self.compile_value(TargetInInstantiatedModule { + let target = TargetInInstantiatedModule { instantiated_module: *module, target: Target::from(module_io), - }) + }; + self.decl_conditions.insert(target, Interned::default()); + self.compile_value(target) }, ) .collect(); @@ -2537,8 +2611,7 @@ impl Compiler { fn process_assignments(&mut self) { self.assignments .finalize(self.insns.state_layout().len().ty); - let assignments_queue: Vec = match petgraph::algo::toposort(&self.assignments, None) - { + let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { Ok(nodes) => nodes .into_iter() .filter_map(|n| match n { @@ -2549,7 +2622,7 @@ impl Compiler { Err(e) => match e.node_id() { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( "combinatorial logic cycle detected at: {}", - self.assignments.assignments[assignment_index].source_location, + self.assignments.assignments()[assignment_index].source_location, ), AssignmentOrSlotIndex::SmallSlots(slot) => panic!( "combinatorial logic cycle detected through: {}", @@ -2561,12 +2634,113 @@ impl Compiler { ), }, }; - todo!(); + 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 { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => ( + 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 { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + } => ( + 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, @@ -2588,7 +2762,7 @@ pub struct Compiled { } impl Compiled { - pub fn new(module: Module) -> Self { + pub fn new(module: Interned>) -> Self { Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) } pub fn canonical(self) -> Compiled { @@ -2620,14 +2794,19 @@ impl Compiled { #[derive(Debug)] pub struct Simulation { state: interpreter::State, - compiled: Compiled, + base_module: CompiledModule, + base_module_io_ty: T, } impl Simulation { + pub fn new(module: Interned>) -> Self { + Self::from_compiled(Compiled::new(module)) + } pub fn from_compiled(compiled: Compiled) -> Self { Self { state: interpreter::State::new(compiled.insns), - compiled, + base_module: compiled.base_module, + base_module_io_ty: compiled.base_module_io_ty, } } pub fn settle_step(&mut self) { diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 5269131..e713300 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -7,6 +7,7 @@ use crate::{ ty::CanonicalType, util::get_many_mut, }; +use hashbrown::HashMap; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ @@ -166,6 +167,83 @@ fn make_array_into_iter( 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] @@ -191,7 +269,7 @@ macro_rules! impl_insns { })? => $block:block )* ) => { - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + #[derive(Copy, Clone, Eq, PartialEq, Hash)] $vis enum $Insn { $( $(#[$insn_meta])* @@ -249,6 +327,28 @@ macro_rules! impl_insns { )* } } + $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 { $( @@ -339,6 +439,8 @@ pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { + Default + Deref + FromIterator; + type Labels: Default; + fn labels(labels: &Self::Labels) -> Option<&Labels>; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] @@ -346,6 +448,10 @@ 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)] @@ -353,12 +459,27 @@ 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