From 277d3e0d4d37a36eaba12fe684225dae38b0163d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 10 Nov 2024 22:12:57 -0800 Subject: [PATCH] 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])) + } +}