From 42afd2da0e9c507232b8ed1d010208eceeadcdc7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 4 Dec 2024 20:58:39 -0800 Subject: [PATCH] sim: implement enums (except for connecting unequal enum types) --- crates/fayalite/src/sim.rs | 523 +++++++++++++++++++------ crates/fayalite/src/sim/interpreter.rs | 69 ++++ crates/fayalite/tests/sim.rs | 2 + 3 files changed, 475 insertions(+), 119 deletions(-) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 3751241..ea09e83 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -59,7 +59,7 @@ enum CondBody { cond: CompiledValue, }, MatchArm { - enum_expr: CompiledValue, + discriminant: StatePartIndex, variant_index: usize, }, } @@ -246,12 +246,6 @@ impl CompiledValue { } } -impl CompiledValue { - fn add_discriminant_to_set(self, inputs: &mut SlotSet) { - inputs.extend([self.range]); - } -} - impl CompiledValue { fn field_by_index(self, field_index: usize) -> CompiledValue { self.map(|layout, range| { @@ -957,9 +951,9 @@ impl Extend for SlotSet { self.extend([cond.range]); } CondBody::MatchArm { - enum_expr, + discriminant, variant_index: _, - } => enum_expr.add_discriminant_to_set(self), + } => self.extend([discriminant]), }) } } @@ -1271,11 +1265,14 @@ impl Compiler { } fn make_trace_scalar_helper( &mut self, - target: TargetInInstantiatedModule, + instantiated_module: InstantiatedModule, + target: Expr, + source_location: SourceLocation, small_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, big_kind: impl FnOnce(StatePartIndex) -> SimTraceKind, ) -> TraceScalarId { - let compiled_value = self.compile_value(target); + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); self.new_sim_trace(match compiled_value.range.len() { TypeLen::A_SMALL_SLOT => small_kind(compiled_value.range.small_slots.start), TypeLen::A_BIG_SLOT => big_kind(compiled_value.range.big_slots.start), @@ -1284,14 +1281,18 @@ impl Compiler { } fn make_trace_scalar( &mut self, - target: TargetInInstantiatedModule, + instantiated_module: InstantiatedModule, + target: Expr, name: Interned, + source_location: SourceLocation, ) -> TraceDecl { - let flow = target.target.flow(); - match target.target.canonical_ty() { + let flow = Expr::flow(target); + match Expr::ty(target) { CanonicalType::UInt(ty) => TraceUInt { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallUInt { index, ty }, |index| SimTraceKind::BigUInt { index, ty }, ), @@ -1302,7 +1303,9 @@ impl Compiler { .into(), CanonicalType::SInt(ty) => TraceSInt { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallSInt { index, ty }, |index| SimTraceKind::BigSInt { index, ty }, ), @@ -1313,7 +1316,9 @@ impl Compiler { .into(), CanonicalType::Bool(_) => TraceBool { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallBool { index }, |index| SimTraceKind::BigBool { index }, ), @@ -1324,10 +1329,11 @@ impl Compiler { CanonicalType::Array(_) => unreachable!(), CanonicalType::Enum(ty) => { assert_eq!(ty.discriminant_bit_width(), ty.type_properties().bit_width); - let compiled_value = self.compile_value(target); + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = self.compiled_expr_to_value(compiled_value, source_location); let discriminant = self.compile_enum_discriminant( compiled_value.map_ty(Enum::from_canonical), - target.target.base().source_location(), + source_location, ); TraceFieldlessEnum { id: self.new_sim_trace(SimTraceKind::EnumDiscriminant { @@ -1343,7 +1349,9 @@ impl Compiler { CanonicalType::Bundle(_) => unreachable!(), CanonicalType::AsyncReset(_) => TraceAsyncReset { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallAsyncReset { index }, |index| SimTraceKind::BigAsyncReset { index }, ), @@ -1353,7 +1361,9 @@ impl Compiler { .into(), CanonicalType::SyncReset(_) => TraceSyncReset { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallSyncReset { index }, |index| SimTraceKind::BigSyncReset { index }, ), @@ -1364,7 +1374,9 @@ impl Compiler { CanonicalType::Reset(_) => unreachable!(), CanonicalType::Clock(_) => TraceClock { id: self.make_trace_scalar_helper( + instantiated_module, target, + source_location, |index| SimTraceKind::SmallClock { index }, |index| SimTraceKind::BigClock { index }, ), @@ -1376,56 +1388,89 @@ impl Compiler { } fn make_trace_decl_child( &mut self, - target: TargetInInstantiatedModule, + instantiated_module: InstantiatedModule, + target: Expr, name: Interned, + source_location: SourceLocation, ) -> TraceDecl { - match target.target.canonical_ty() { + match Expr::ty(target) { CanonicalType::Array(ty) => { let elements = Interned::from_iter((0..ty.len()).map(|index| { self.make_trace_decl_child( - TargetInInstantiatedModule { - instantiated_module: target.instantiated_module, - target: target.target.join( - TargetPathElement::from(TargetPathArrayElement { index }) - .intern_sized(), - ), - }, + instantiated_module, + Expr::::from_canonical(target)[index], Intern::intern_owned(format!("[{index}]")), + source_location, ) })); TraceArray { name, elements, ty, - flow: target.target.flow(), + flow: Expr::flow(target), } .into() } CanonicalType::Enum(ty) => { if ty.variants().iter().all(|v| v.ty.is_none()) { - self.make_trace_scalar(target, name) + self.make_trace_scalar(instantiated_module, target, name, source_location) } else { - todo!() + let flow = Expr::flow(target); + let compiled_value = self.compile_expr(instantiated_module, target); + let compiled_value = + self.compiled_expr_to_value(compiled_value, source_location); + let discriminant = self.compile_enum_discriminant( + compiled_value.map_ty(Enum::from_canonical), + source_location, + ); + let discriminant = TraceEnumDiscriminant { + id: self.new_sim_trace(SimTraceKind::EnumDiscriminant { + index: discriminant, + ty, + }), + name, + ty, + flow, + }; + let base = Expr::::from_canonical(target); + let non_empty_fields = + Interned::from_iter(ty.variants().into_iter().enumerate().flat_map( + |(variant_index, variant)| { + variant.ty.map(|_| { + self.make_trace_decl_child( + instantiated_module, + ops::VariantAccess::new_by_index(base, variant_index) + .to_expr(), + variant.name, + source_location, + ) + }) + }, + )); + TraceEnumWithFields { + name, + discriminant, + non_empty_fields, + ty, + flow, + } + .into() } } CanonicalType::Bundle(ty) => { let fields = Interned::from_iter(ty.fields().iter().map(|field| { self.make_trace_decl_child( - TargetInInstantiatedModule { - instantiated_module: target.instantiated_module, - target: target.target.join( - TargetPathElement::from(TargetPathBundleField { name: field.name }) - .intern_sized(), - ), - }, + instantiated_module, + Expr::field(Expr::::from_canonical(target), &field.name), field.name, + source_location, ) })); TraceBundle { name, fields, ty, - flow: target.target.flow(), + flow: Expr::flow(target), } .into() } @@ -1435,7 +1480,9 @@ impl Compiler { | CanonicalType::AsyncReset(_) | CanonicalType::SyncReset(_) | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => self.make_trace_scalar(target, name), + | CanonicalType::Clock(_) => { + self.make_trace_scalar(instantiated_module, target, name, source_location) + } } } fn make_trace_decl( @@ -1443,15 +1490,17 @@ impl Compiler { instantiated_module: InstantiatedModule, target_base: TargetBase, ) -> TraceDecl { - let target = TargetInInstantiatedModule { - instantiated_module, - target: target_base.into(), - }; + let target = target_base.to_expr(); match target_base { TargetBase::ModuleIO(module_io) => TraceModuleIO { name: module_io.name(), child: self - .make_trace_decl_child(target, module_io.name()) + .make_trace_decl_child( + instantiated_module, + target, + module_io.name(), + module_io.source_location(), + ) .intern(), ty: module_io.ty(), flow: module_io.flow(), @@ -1459,9 +1508,12 @@ impl Compiler { .into(), TargetBase::MemPort(mem_port) => { let name = Intern::intern_owned(mem_port.port_name().to_string()); - let TraceDecl::Scope(TraceScope::Bundle(bundle)) = - self.make_trace_decl_child(target, name) - else { + let TraceDecl::Scope(TraceScope::Bundle(bundle)) = self.make_trace_decl_child( + instantiated_module, + target, + name, + mem_port.source_location(), + ) else { unreachable!() }; TraceMemPort { @@ -1473,36 +1525,67 @@ impl Compiler { } TargetBase::Reg(reg) => TraceReg { name: reg.name(), - child: self.make_trace_decl_child(target, reg.name()).intern(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), ty: reg.ty(), } .into(), TargetBase::RegSync(reg) => TraceReg { name: reg.name(), - child: self.make_trace_decl_child(target, reg.name()).intern(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), ty: reg.ty(), } .into(), TargetBase::RegAsync(reg) => TraceReg { name: reg.name(), - child: self.make_trace_decl_child(target, reg.name()).intern(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + reg.name(), + reg.source_location(), + ) + .intern(), ty: reg.ty(), } .into(), TargetBase::Wire(wire) => TraceWire { name: wire.name(), - child: self.make_trace_decl_child(target, wire.name()).intern(), + child: self + .make_trace_decl_child( + instantiated_module, + target, + wire.name(), + wire.source_location(), + ) + .intern(), ty: wire.ty(), } .into(), TargetBase::Instance(instance) => { - let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = - self.make_trace_decl_child(target, instance.name()) - else { + let TraceDecl::Scope(TraceScope::Bundle(instance_io)) = self.make_trace_decl_child( + instantiated_module, + target, + instance.name(), + instance.source_location(), + ) else { unreachable!() }; let compiled_module = &self.modules[&InstantiatedModule::Child { - parent: target.instantiated_module.intern(), + parent: instantiated_module.intern(), instance: instance.intern(), }]; TraceInstance { @@ -1647,6 +1730,49 @@ impl Compiler { self.assignments .push(Assignment::new(conditions, insns, source_location)); } + fn simple_big_expr_input( + &mut self, + instantiated_module: InstantiatedModule, + input: Expr, + ) -> StatePartIndex { + let input = self.compile_expr(instantiated_module, input); + let input = + self.compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); + assert_eq!(input.range.len(), TypeLen::A_BIG_SLOT); + input.range.big_slots.start + } + fn compile_expr_helper( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + make_insns: impl FnOnce(&mut Self, TypeIndexRange) -> Vec, + ) -> CompiledValue { + 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, range); + self.add_assignment( + Interned::default(), + insns, + instantiated_module.leaf_module().source_location(), + ); + retval + } + fn simple_nary_big_expr_helper( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + make_insns: impl FnOnce(StatePartIndex) -> Vec, + ) -> CompiledValue { + self.compile_expr_helper(instantiated_module, dest_ty, |_, dest| { + assert_eq!(dest.len(), TypeLen::A_BIG_SLOT); + make_insns(dest.big_slots.start) + }) + } fn simple_nary_big_expr( &mut self, instantiated_module: InstantiatedModule, @@ -1657,37 +1783,10 @@ impl Compiler { [StatePartIndex; N], ) -> Vec, ) -> CompiledValue { - let inputs = inputs.map(|input| { - let input = self.compile_expr(instantiated_module, input); - let input = self - .compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); - let TypeIndexRange { - small_slots, - big_slots, - } = input.range; - assert_eq!(small_slots.len.value, 0); - assert_eq!(big_slots.len.value, 1); - big_slots.start - }); - let layout = CompiledTypeLayout::get(dest_ty); - let range = self.insns.allocate_variable(&layout.layout); - let TypeIndexRange { - small_slots, - big_slots, - } = range; - assert_eq!(small_slots.len.value, 0); - assert_eq!(big_slots.len.value, 1); - let retval = CompiledValue { - layout, - range, - write: None, - }; - self.add_assignment( - Interned::default(), - make_insns(big_slots.start, inputs), - instantiated_module.leaf_module().source_location(), - ); - retval + let inputs = inputs.map(|input| self.simple_big_expr_input(instantiated_module, input)); + self.simple_nary_big_expr_helper(instantiated_module, dest_ty, |dest| { + make_insns(dest, inputs) + }) } fn compiled_value_to_dyn_array_index( &mut self, @@ -1773,6 +1872,153 @@ impl Compiler { .insert(compiled_value, retval); retval } + fn compile_cast_scalar_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + arg: Expr, + cast_fn: impl FnOnce(Expr) -> Expr, + ) -> CompiledValue { + let arg = Expr::::from_canonical(arg); + let retval = cast_fn(arg); + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + let retval = self + .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); + retval.map_ty(UInt::from_canonical) + } + fn compile_cast_aggregate_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + parts: impl IntoIterator>, + ) -> CompiledValue { + let retval = parts + .into_iter() + .map(|part| part.cast_to_bits()) + .reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width)) + .unwrap_or_else(|| UInt[0].zero().to_expr()); + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + let retval = self + .compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()); + retval.map_ty(UInt::from_canonical) + } + fn compile_cast_to_bits( + &mut self, + instantiated_module: InstantiatedModule, + expr: ops::CastToBits, + ) -> CompiledValue { + match Expr::ty(expr.arg()) { + CanonicalType::UInt(_) => { + self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg) + } + CanonicalType::SInt(ty) => self.compile_cast_scalar_to_bits( + instantiated_module, + expr.arg(), + |arg: Expr| arg.cast_to(ty.as_same_width_uint()), + ), + CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => self.compile_cast_scalar_to_bits( + instantiated_module, + expr.arg(), + |arg: Expr| arg.cast_to(UInt[1]), + ), + CanonicalType::Array(ty) => self.compile_cast_aggregate_to_bits( + instantiated_module, + (0..ty.len()).map(|index| Expr::::from_canonical(expr.arg())[index]), + ), + CanonicalType::Enum(ty) => self + .simple_nary_big_expr( + instantiated_module, + UInt[ty.type_properties().bit_width].canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Copy { dest, src }], + ) + .map_ty(UInt::from_canonical), + CanonicalType::Bundle(ty) => self.compile_cast_aggregate_to_bits( + instantiated_module, + ty.fields().iter().map(|field| { + Expr::field(Expr::::from_canonical(expr.arg()), &field.name) + }), + ), + } + } + fn compile_cast_bits_to( + &mut self, + instantiated_module: InstantiatedModule, + expr: ops::CastBitsTo, + ) -> CompiledValue { + let retval = match expr.ty() { + CanonicalType::UInt(_) => Expr::canonical(expr.arg()), + CanonicalType::SInt(ty) => Expr::canonical(expr.arg().cast_to(ty)), + CanonicalType::Bool(ty) => Expr::canonical(expr.arg().cast_to(ty)), + CanonicalType::Array(ty) => { + let stride = ty.element().bit_width(); + Expr::::canonical( + ops::ArrayLiteral::new( + ty.element(), + Interned::from_iter((0..ty.len()).map(|index| { + let start = stride * index; + let end = start + stride; + expr.arg()[start..end].cast_bits_to(ty.element()) + })), + ) + .to_expr(), + ) + } + ty @ CanonicalType::Enum(_) => { + return self.simple_nary_big_expr( + instantiated_module, + ty, + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Copy { dest, src }], + ); + } + CanonicalType::Bundle(ty) => Expr::canonical( + ops::BundleLiteral::new( + ty, + Interned::from_iter(ty.field_offsets().iter().zip(&ty.fields()).map( + |(&offset, &field)| { + let end = offset + field.ty.bit_width(); + expr.arg()[offset..end].cast_bits_to(field.ty) + }, + )), + ) + .to_expr(), + ), + CanonicalType::AsyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), + CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)), + }; + let retval = self.compile_expr(instantiated_module, Expr::canonical(retval)); + self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location()) + } + fn compile_aggregate_literal( + &mut self, + instantiated_module: InstantiatedModule, + dest_ty: CanonicalType, + inputs: Interned<[Expr]>, + ) -> CompiledValue { + self.compile_expr_helper(instantiated_module, dest_ty, |this, dest| { + let mut insns = Vec::new(); + let mut offset = TypeIndex::ZERO; + for input in inputs { + let input = this.compile_expr(instantiated_module, input); + let input = this + .compiled_expr_to_value( + input, + instantiated_module.leaf_module().source_location(), + ) + .range; + insns.extend( + input.insns_for_copy_to(dest.slice(TypeIndexRange::new(offset, input.len()))), + ); + offset = offset.offset(input.len().as_index()); + } + insns + }) + } fn compile_expr( &mut self, instantiated_module: InstantiatedModule, @@ -1860,10 +2106,44 @@ impl Compiler { }] }) .into(), - ExprEnum::BundleLiteral(expr) => todo!(), - ExprEnum::ArrayLiteral(expr) => todo!(), - ExprEnum::EnumLiteral(expr) => todo!(), - ExprEnum::Uninit(expr) => todo!(), + ExprEnum::BundleLiteral(literal) => self + .compile_aggregate_literal( + instantiated_module, + Expr::ty(expr), + literal.field_values(), + ) + .into(), + ExprEnum::ArrayLiteral(literal) => self + .compile_aggregate_literal( + instantiated_module, + Expr::ty(expr), + literal.element_values(), + ) + .into(), + ExprEnum::EnumLiteral(expr) => { + let enum_bits_ty = UInt[expr.ty().type_properties().bit_width]; + let enum_bits = if let Some(variant_value) = expr.variant_value() { + ( + UInt[expr.ty().discriminant_bit_width()] + .from_int_wrapping(expr.variant_index()), + variant_value, + ) + .cast_to_bits() + .cast_to(enum_bits_ty) + } else { + enum_bits_ty + .from_int_wrapping(expr.variant_index()) + .to_expr() + }; + self.compile_expr( + instantiated_module, + enum_bits.cast_bits_to(expr.ty().canonical()), + ) + } + ExprEnum::Uninit(expr) => self.compile_expr( + instantiated_module, + UInt[expr.ty().bit_width()].zero().cast_bits_to(expr.ty()), + ), ExprEnum::NotU(expr) => self .simple_nary_big_expr( instantiated_module, @@ -2392,7 +2672,12 @@ impl Compiler { .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Bundle::from_canonical) .field_by_index(expr.field_index()), - ExprEnum::VariantAccess(expr) => todo!(), + ExprEnum::VariantAccess(variant_access) => self.compile_expr( + instantiated_module, + variant_access.base().cast_to_bits() + [Expr::ty(variant_access.base()).discriminant_bit_width()..] + .cast_bits_to(Expr::ty(expr)), + ), ExprEnum::ArrayIndex(expr) => self .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Array::from_canonical) @@ -2512,8 +2797,13 @@ impl Compiler { }, ) .into(), - ExprEnum::CastToBits(expr) => todo!(), - ExprEnum::CastBitsTo(expr) => todo!(), + ExprEnum::CastToBits(expr) => self + .compile_cast_to_bits(instantiated_module, expr) + .map_ty(CanonicalType::UInt) + .into(), + ExprEnum::CastBitsTo(expr) => { + self.compile_cast_bits_to(instantiated_module, expr).into() + } ExprEnum::ModuleIO(expr) => self .compile_value(TargetInInstantiatedModule { instantiated_module, @@ -3061,13 +3351,14 @@ impl Compiler { 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); + let discriminant = self.compile_enum_discriminant(enum_expr, source_location); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, block, Interned::from_iter(conditions.iter().copied().chain([Cond { body: CondBody::MatchArm { - enum_expr, + discriminant, variant_index, }, source_location, @@ -3217,9 +3508,18 @@ impl Compiler { ); } CondBody::MatchArm { - enum_expr, + discriminant, variant_index, - } => todo!(), + } => { + self.insns.push( + Insn::BranchIfSmallNeImmediate { + target: end_label_index, + lhs: discriminant, + rhs: variant_index as _, + }, + cond.source_location, + ); + } } cond_stack.push(CondStackEntry { cond, @@ -3261,22 +3561,6 @@ impl Compiler { source_location, } in mem::take(&mut self.registers) { - let write_to_current_value = |value_to_write: CompiledValue| { - let TypeIndexRange { - small_slots, - big_slots, - } = value.range; - small_slots - .iter() - .zip(value_to_write.range.small_slots.iter()) - .map(|(base, src)| Insn::CopySmall { dest: base, src }) - .chain( - big_slots - .iter() - .zip(value_to_write.range.big_slots.iter()) - .map(|(base, src)| Insn::Copy { dest: base, src }), - ) - }; match reset { Some(RegisterReset { is_async, @@ -3300,15 +3584,15 @@ impl Compiler { self.insns.push(branch_if_not_triggered, source_location); self.insns.push(branch_if_reset, source_location); } - for insn in write_to_current_value(value.write_value()) { - self.insns.push(insn, source_location); - } + self.insns.extend( + value.range.insns_for_copy_from(value.write_value().range), + source_location, + ); self.insns .push(Insn::Branch { target: reg_end }, source_location); self.insns.define_label_at_next_insn(reg_reset); - for insn in write_to_current_value(init) { - self.insns.push(insn, source_location); - } + self.insns + .extend(value.range.insns_for_copy_from(init.range), source_location); self.insns.define_label_at_next_insn(reg_end); } None => { @@ -3320,9 +3604,10 @@ impl Compiler { }, source_location, ); - for insn in write_to_current_value(value.write_value()) { - self.insns.push(insn, source_location); - } + self.insns.extend( + value.range.insns_for_copy_from(value.write_value().range), + source_location, + ); self.insns.define_label_at_next_insn(reg_end); } } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index ceaa359..cb1f29b 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -913,12 +913,23 @@ macro_rules! make_state_part_kinds { $(pub(crate) $type_field: StatePartLen<$TypeKind>,)* } + impl TypeLen { + pub(crate) const fn as_index(self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.as_index(),)* + } + } + } + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeIndex { $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* } impl TypeIndex { + pub(crate) const ZERO: Self = Self { + $($type_field: StatePartIndex::ZERO,)* + }; pub(crate) fn offset(self, offset: TypeIndex) -> Self { Self { $($type_field: self.$type_field.offset(offset.$type_field),)* @@ -1470,6 +1481,10 @@ pub(crate) struct StatePartIndex { } impl StatePartIndex { + pub(crate) const ZERO: Self = Self { + value: 0, + _phantom: PhantomData, + }; pub(crate) fn as_usize(self) -> usize { self.value.try_into().expect("index too big") } @@ -1520,6 +1535,12 @@ impl StatePartLen { _phantom: PhantomData, } } + pub(crate) const fn as_index(self) -> StatePartIndex { + StatePartIndex { + value: self.value, + _phantom: PhantomData, + } + } } impl fmt::Debug for StatePartLen { @@ -1948,6 +1969,15 @@ impl Insns { self.insns.push(insn); self.insn_source_locations.push(source_location); } + pub(crate) fn extend( + &mut self, + insns: impl IntoIterator, + source_location: SourceLocation, + ) { + self.insns.extend(insns); + self.insn_source_locations + .resize(self.insns.len(), source_location); + } pub(crate) fn allocate_variable( &mut self, layout: &TypeLayout, @@ -2047,6 +2077,31 @@ impl BorrowedState<'_> { } } +impl TypeIndexRange { + #[must_use] + pub(crate) fn insns_for_copy_to(self, dest: TypeIndexRange) -> impl Iterator { + assert_eq!(self.len(), dest.len()); + let Self { + small_slots, + big_slots, + } = self; + small_slots + .iter() + .zip(dest.small_slots.iter()) + .map(|(src, dest)| Insn::CopySmall { dest, src }) + .chain( + big_slots + .iter() + .zip(dest.big_slots.iter()) + .map(|(src, dest)| Insn::Copy { dest, src }), + ) + } + #[must_use] + pub(crate) fn insns_for_copy_from(self, src: TypeIndexRange) -> impl Iterator { + src.insns_for_copy_to(self) + } +} + fn bigint_pow2(width: usize) -> Interned { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct MyMemoize; @@ -2633,6 +2688,20 @@ impl_insns! { branch!(target); } } + BranchIfSmallNeImmediate { + #[kind = BranchTarget] + target: usize, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Immediate] + rhs: SmallUInt, + } => { + if state.small_slots[lhs] != rhs { + branch!(target); + } else { + next!(); + } + } Return => { break; } diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index f34bb89..c837927 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -3793,3 +3793,5 @@ $end panic!(); } } + +// TODO: add tests for enums