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);