From a6e40839ac64d751e807b28e89ef11bd2f956b40 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 13 Nov 2024 04:14:04 -0800 Subject: [PATCH] simulator WIP: use petgraph for topological sort over assignments --- Cargo.lock | 17 + Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/sim.rs | 959 +++++++++++++++++++++---- crates/fayalite/src/sim/interpreter.rs | 54 +- 5 files changed, 887 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c280158..c768882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,6 +315,7 @@ dependencies = [ "num-bigint", "num-traits", "os_pipe", + "petgraph", "serde", "serde_json", "tempfile", @@ -358,6 +359,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "funty" version = "2.0.0" @@ -515,6 +522,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "prettyplease" version = "0.2.20" diff --git a/Cargo.toml b/Cargo.toml index add4bfe..a9cc312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ jobslot = "0.2.19" num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" +petgraph = "0.6.5" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 8e90a74..2652792 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -25,6 +25,7 @@ jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true os_pipe.workspace = true +petgraph.workspace = true serde_json.workspace = true serde.workspace = true tempfile.workspace = true diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 8dc087b..b647302 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -21,16 +21,19 @@ use crate::{ prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, - StatePartIndexMap, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, - StatePartKindSmallSlots, StatePartLayout, StatePartsValue, TypeArrayIndex, - TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeParts, + SlotDebugData, SmallUInt, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, + StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, + TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, ty::StaticType, }; use hashbrown::HashMap; use num_bigint::BigInt; -use std::{collections::BTreeSet, fmt}; +use petgraph::visit::{ + GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, +}; +use std::{collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; mod interpreter; @@ -416,6 +419,385 @@ impl CompiledExpr { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentOrSlotIndex { + AssignmentIndex(usize), + SmallSlots(StatePartIndex), + BigSlots(StatePartIndex), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentIO { + BigInput { + assignment_index: usize, + slot: StatePartIndex, + }, + SmallInput { + assignment_index: usize, + slot: StatePartIndex, + }, + BigOutput { + assignment_index: usize, + slot: StatePartIndex, + }, + SmallOutput { + assignment_index: usize, + slot: StatePartIndex, + }, +} + +#[derive(Debug, Default)] +struct Assignments { + assignments: Vec, + slot_readers: Option, + slot_writers: Option, +} + +impl Assignments { + fn finalize(&mut self, slots_len: TypeLen) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); + for (assignment_index, assignment) in self.assignments.iter().enumerate() { + slot_readers + .keys_for_assignment(assignment_index) + .extend([&assignment.inputs]); + slot_writers + .keys_for_assignment(assignment_index) + .extend([&assignment.outputs]); + } + self.slot_readers = Some(slot_readers); + self.slot_writers = Some(slot_writers); + } + fn push(&mut self, v: Assignment) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + self.assignments.push(v); + } + fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_readers + .as_ref() + .expect("Assignments::finalize should have been called") + } + fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_writers + .as_ref() + .expect("Assignments::finalize should have been called") + } +} + +impl GraphBase for Assignments { + type EdgeId = AssignmentIO; + type NodeId = AssignmentOrSlotIndex; +} + +struct AssignmentsNodeIdentifiers { + assignment_indexes: std::ops::Range, + small_slots: std::ops::Range, + big_slots: std::ops::Range, +} + +impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + let Self { + assignment_indexes, + small_slots, + big_slots, + } = self; + assignment_indexes + .next() + .map(AssignmentOrSlotIndex::AssignmentIndex) + .or_else(|| { + small_slots.next().map(|value| { + AssignmentOrSlotIndex::SmallSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + .or_else(|| { + big_slots.next().map(|value| { + AssignmentOrSlotIndex::BigSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + } +} + +impl<'a> IntoNodeIdentifiers for &'a Assignments { + type NodeIdentifiers = AssignmentsNodeIdentifiers; + + fn node_identifiers(self) -> Self::NodeIdentifiers { + let TypeLen { + small_slots, + big_slots, + } = self.slot_readers().len(); + AssignmentsNodeIdentifiers { + assignment_indexes: 0..self.assignments.len(), + small_slots: 0..small_slots.value, + big_slots: 0..big_slots.value, + } + } +} + +enum AssignmentsNeighborsDirected<'a> { + AssignmentIndexes(std::slice::Iter<'a, usize>), + Slots { + small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, + big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, + }, +} + +impl Iterator for AssignmentsNeighborsDirected<'_> { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + match self { + AssignmentsNeighborsDirected::AssignmentIndexes(iter) => iter + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex), + AssignmentsNeighborsDirected::Slots { + small_slots, + big_slots, + } => small_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::SmallSlots) + .or_else(|| { + big_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::BigSlots) + }), + } + } +} + +impl<'a> IntoNeighbors for &'a Assignments { + type Neighbors = AssignmentsNeighborsDirected<'a>; + + fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { + self.neighbors_directed(n, petgraph::Direction::Outgoing) + } +} + +impl<'a> IntoNeighborsDirected for &'a Assignments { + type NeighborsDirected = AssignmentsNeighborsDirected<'a>; + + fn neighbors_directed( + self, + n: Self::NodeId, + d: petgraph::Direction, + ) -> Self::NeighborsDirected { + use petgraph::Direction::*; + let slot_map = match d { + Outgoing => self.slot_readers(), + Incoming => self.slot_writers(), + }; + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + let assignment = &self.assignments[assignment_index]; + let SlotSet(TypeParts { + small_slots, + big_slots, + }) = match d { + Outgoing => &assignment.outputs, + Incoming => &assignment.inputs, + }; + AssignmentsNeighborsDirected::Slots { + small_slots: small_slots.iter(), + big_slots: big_slots.iter(), + } + } + AssignmentOrSlotIndex::SmallSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + AssignmentOrSlotIndex::BigSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + } + } +} + +struct AssignmentsVisitMap { + assignments: Vec, + slots: DenseSlotSet, +} + +impl VisitMap for AssignmentsVisitMap { + fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + !mem::replace(&mut self.assignments[assignment_index], true) + } + AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.insert(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.insert(slot), + } + } + + fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { + match *n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + self.assignments[assignment_index] + } + AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.contains(slot), + } + } +} + +impl Visitable for Assignments { + type Map = AssignmentsVisitMap; + + fn visit_map(self: &Self) -> Self::Map { + AssignmentsVisitMap { + assignments: vec![false; self.assignments.len()], + slots: DenseSlotSet::new(self.slot_readers().len()), + } + } + + fn reset_map(self: &Self, map: &mut Self::Map) { + let AssignmentsVisitMap { assignments, slots } = map; + assignments.clear(); + assignments.resize(self.assignments.len(), false); + if slots.len() != self.slot_readers().len() { + *slots = DenseSlotSet::new(self.slot_readers().len()); + } else { + slots.clear(); + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +struct DenseSlotSet(TypeParts); + +impl DenseSlotSet { + fn new(len: TypeLen) -> Self { + let TypeLen { + small_slots, + big_slots, + } = len; + Self(TypeParts { + small_slots: vec![false; small_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + big_slots: vec![false; big_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + }) + } + fn len(&self) -> TypeLen { + TypeLen { + small_slots: StatePartLen { + value: self.0.small_slots.len() as _, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: self.0.big_slots.len() as _, + _phantom: PhantomData, + }, + } + } + fn clear(&mut self) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.fill(false); + big_slots.fill(false); + } +} + +impl StatePartsValue for DenseSlotSet { + type Value = Box<[bool]>; +} + +trait DenseSlotSetMethods: Extend> { + fn contains(&self, k: StatePartIndex) -> bool; + fn remove(&mut self, k: StatePartIndex) -> bool { + self.take(k).is_some() + } + fn take(&mut self, k: StatePartIndex) -> Option>; + fn replace(&mut self, k: StatePartIndex) -> Option>; + fn insert(&mut self, k: StatePartIndex) -> bool { + self.replace(k).is_none() + } +} + +impl Extend> for DenseSlotSet +where + Self: DenseSlotSetMethods, +{ + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|v| { + self.insert(v); + }); + } +} + +impl DenseSlotSetMethods for DenseSlotSet { + fn contains(&self, k: StatePartIndex) -> bool { + self.0.small_slots[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(self.0.small_slots.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(&mut self.0.small_slots[k.as_usize()], true).then_some(k) + } +} + +impl DenseSlotSetMethods for DenseSlotSet { + fn contains(&self, k: StatePartIndex) -> bool { + self.0.big_slots[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(self.0.big_slots.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(&mut self.0.big_slots[k.as_usize()], true).then_some(k) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +struct SlotVec(TypeParts); + +impl SlotVec { + fn is_empty(&self) -> bool { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.is_empty() && big_slots.is_empty() + } +} + +impl StatePartsValue for SlotVec { + type Value = Vec>; +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] struct SlotSet(TypeParts); @@ -427,6 +809,30 @@ impl SlotSet { }) = self; small_slots.is_empty() && big_slots.is_empty() } + fn for_each( + &self, + small_slots_fn: impl FnMut(StatePartIndex), + big_slots_fn: impl FnMut(StatePartIndex), + ) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().copied().for_each(small_slots_fn); + big_slots.iter().copied().for_each(big_slots_fn); + } + fn all( + &self, + small_slots_fn: impl FnMut(StatePartIndex) -> bool, + big_slots_fn: impl FnMut(StatePartIndex) -> bool, + ) -> bool { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().copied().all(small_slots_fn) + && big_slots.iter().copied().all(big_slots_fn) + } } impl StatePartsValue for SlotSet { @@ -511,63 +917,221 @@ impl Extend for SlotSet { #[derive(Debug)] struct Assignment { inputs: SlotSet, + outputs: SlotSet, conditions: Interned<[Cond]>, insns: Vec, source_location: SourceLocation, } -#[derive(Debug, Default)] -struct StatePartAssignments { - written_slot_to_assignment_indexes_map: StatePartIndexMap>, +#[derive(Debug)] +struct SlotToAssignmentIndexFullMap(TypeParts); + +impl StatePartsValue for SlotToAssignmentIndexFullMap { + type Value = Box<[Vec]>; } -#[derive(Debug, Default)] -struct SlotAssignments { - assignments: Vec, - parts: TypeParts, -} - -impl SlotAssignments { - fn for_assignment(&mut self, assignment_index: usize) -> SlotAssignmentsForAssignment<'_> { - SlotAssignmentsForAssignment { - parts: &mut self.parts, +impl SlotToAssignmentIndexFullMap { + fn new(len: TypeLen) -> Self { + let TypeLen { + small_slots, + big_slots, + } = len; + const VEC_NEW: Vec = Vec::new(); + Self(TypeParts { + small_slots: vec![VEC_NEW; small_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + big_slots: vec![VEC_NEW; big_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + }) + } + fn len(&self) -> TypeLen { + TypeLen { + small_slots: StatePartLen { + value: self.0.small_slots.len() as _, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: self.0.big_slots.len() as _, + _phantom: PhantomData, + }, + } + } + fn keys_for_assignment( + &mut self, + assignment_index: usize, + ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + SlotToAssignmentIndexFullMapKeysForAssignment { + map: self, assignment_index, } } -} - -impl StatePartsValue for SlotAssignments { - type Value = StatePartAssignments; -} - -struct SlotAssignmentsForAssignment<'a> { - parts: &'a mut TypeParts, - assignment_index: usize, -} - -impl Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(self.assignment_index) + fn for_each( + &self, + mut small_slots_fn: impl FnMut(StatePartIndex, &[usize]), + mut big_slots_fn: impl FnMut(StatePartIndex, &[usize]), + ) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().enumerate().for_each(|(k, v)| { + small_slots_fn( + StatePartIndex { + value: k as _, + _phantom: PhantomData, + }, + v, + ) + }); + big_slots.iter().enumerate().for_each(|(k, v)| { + big_slots_fn( + StatePartIndex { + value: k as _, + _phantom: PhantomData, + }, + v, + ) }); } } -impl Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(self.assignment_index) - }); +impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.0.small_slots[index.as_usize()] + } +} + +impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.0.small_slots[index.as_usize()] + } +} + +impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.0.big_slots[index.as_usize()] + } +} + +impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.0.big_slots[index.as_usize()] + } +} + +struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { + map: &'a mut SlotToAssignmentIndexFullMap, + assignment_index: usize, +} + +impl<'a, K: StatePartKind> Extend<&'a StatePartIndex> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> +where + Self: Extend>, +{ + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()); + } +} + +impl Extend> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> +where + SlotToAssignmentIndexFullMap: IndexMut, Output = Vec>, +{ + fn extend>>(&mut self, iter: T) { + iter.into_iter() + .for_each(|slot| self.map[slot].push(self.assignment_index)); + } +} + +impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |SlotSet(TypeParts { + small_slots, + big_slots, + })| { + self.extend(small_slots); + self.extend(big_slots); + }, + ); + } +} + +impl Assignment { + fn new( + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + ) -> Self { + let mut inputs = SlotSet::default(); + let mut outputs = SlotSet::default(); + for insn in &insns { + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { + inputs.extend([slot]); + } + (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { + inputs.extend([slot]); + } + ( + InsnFieldKind::Input, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => { + outputs.extend([slot]); + } + (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => { + outputs.extend([slot]); + } + ( + InsnFieldKind::Output, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + } + ( + _, + InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} + } + } + } + Self { + inputs, + outputs, + conditions, + insns, + source_location, + } } } @@ -580,7 +1144,9 @@ pub struct Compiler { compiled_exprs: HashMap, CompiledExpr>, compiled_exprs_to_values: HashMap, CompiledValue>, decl_conditions: HashMap>, - slots_assignments: SlotAssignments, + compiled_values_to_dyn_array_indexes: + HashMap, StatePartIndex>, + assignments: Assignments, } impl Compiler { @@ -593,7 +1159,8 @@ impl Compiler { compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), decl_conditions: HashMap::new(), - slots_assignments: SlotAssignments::default(), + compiled_values_to_dyn_array_indexes: HashMap::new(), + assignments: Assignments::default(), } } fn compile_value( @@ -728,74 +1295,8 @@ impl Compiler { source_location: SourceLocation, ) { let insns = Vec::from_iter(insns); - let assignment_index = self.slots_assignments.assignments.len(); - let mut inputs = SlotSet::default(); - for insn in &insns { - for InsnField { ty, kind } in insn.fields() { - match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { - inputs.extend([slot]); - } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { - inputs.extend([slot]); - } - ( - InsnFieldKind::Input, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - ( - InsnFieldKind::Output, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - ( - _, - InsnFieldType::SmallUInt(_) - | InsnFieldType::SmallSInt(_) - | InsnFieldType::InternedBigInt(_) - | InsnFieldType::U8(_) - | InsnFieldType::USize(_) - | InsnFieldType::Empty(_), - ) - | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} - } - } - } - self.slots_assignments.assignments.push(Assignment { - inputs, - conditions, - insns, - source_location, - }); + self.assignments + .push(Assignment::new(conditions, insns, source_location)); } fn simple_nary_big_expr( &mut self, @@ -839,6 +1340,70 @@ impl Compiler { ); retval } + fn compiled_value_to_dyn_array_index( + &mut self, + compiled_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self + .compiled_values_to_dyn_array_indexes + .get(&compiled_value) + { + return retval; + } + let retval = match compiled_value.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => compiled_value.range.small_slots.start, + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + } => { + let debug_data = SlotDebugData { + name: Interned::default(), + ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(), + }; + let dest = self + .insns + .allocate_variable(&TypeLayout { + small_slots: StatePartLayout::scalar(debug_data), + big_slots: StatePartLayout::empty(), + }) + .small_slots + .start; + self.add_assignment( + Interned::default(), + vec![Insn::CastBigToArrayIndex { + dest, + src: compiled_value.range.big_slots.start, + }], + source_location, + ); + dest + } + _ => unreachable!(), + }; + self.compiled_values_to_dyn_array_indexes + .insert(compiled_value, retval); + retval + } fn compile_expr( &mut self, instantiated_module: InstantiatedModule, @@ -1463,7 +2028,21 @@ impl Compiler { .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Array::from_canonical) .element(expr.element_index()), - ExprEnum::DynArrayIndex(expr) => todo!(), + ExprEnum::DynArrayIndex(expr) => { + let element_index = + self.compile_expr(instantiated_module, Expr::canonical(expr.element_index())); + let element_index = self.compiled_expr_to_value( + element_index, + instantiated_module.leaf_module().source_location(), + ); + let index_slot = self.compiled_value_to_dyn_array_index( + element_index.map_ty(UInt::from_canonical), + instantiated_module.leaf_module().source_location(), + ); + self.compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element_dyn(index_slot) + } ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) } else { @@ -1534,8 +2113,36 @@ impl Compiler { }, ) .into(), - ExprEnum::SliceUInt(expr) => todo!(), - ExprEnum::SliceSInt(expr) => todo!(), + ExprEnum::SliceUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::SliceSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), ExprEnum::CastToBits(expr) => todo!(), ExprEnum::CastBitsTo(expr) => todo!(), ExprEnum::ModuleIO(expr) => self @@ -1927,34 +2534,104 @@ impl Compiler { }; entry.insert(CompiledModule { module_io }) } - pub fn compile(mut self) -> Compiled { - self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - todo!("handle self.slots_assignments"); + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().len().ty); + let assignments_queue: Vec = match petgraph::algo::toposort(&self.assignments, None) + { + Ok(nodes) => nodes + .into_iter() + .filter_map(|n| match n { + AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), + _ => None, + }) + .collect(), + Err(e) => match e.node_id() { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( + "combinatorial logic cycle detected at: {}", + self.assignments.assignments[assignment_index].source_location, + ), + AssignmentOrSlotIndex::SmallSlots(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, + ), + AssignmentOrSlotIndex::BigSlots(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, + ), + }, + }; + todo!(); + } + pub fn compile(mut self) -> Compiled { + let base_module = + *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + self.process_assignments(); Compiled { insns: Insns::from(self.insns).intern_sized(), - modules: self.modules, + base_module, + base_module_io_ty: self.base_module.io_ty(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct CompiledModule { + module_io: Interned<[CompiledValue]>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Compiled { + insns: Interned>, + base_module: CompiledModule, + base_module_io_ty: T, +} + +impl Compiled { + pub fn new(module: Module) -> Self { + Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) + } + pub fn canonical(self) -> Compiled { + let Self { + insns, + base_module, + base_module_io_ty, + } = self; + Compiled { + insns, + base_module, + base_module_io_ty: Bundle::from_canonical(base_module_io_ty.canonical()), + } + } + pub fn from_canonical(canonical: Compiled) -> Self { + let Compiled { + insns, + base_module, + base_module_io_ty, + } = canonical; + Self { + insns, + base_module, + base_module_io_ty: T::from_canonical(base_module_io_ty.canonical()), } } } #[derive(Debug)] -struct CompiledModule { - module_io: Interned<[CompiledValue]>, +pub struct Simulation { + state: interpreter::State, + compiled: Compiled, } -#[derive(Debug)] -pub struct Compiled { - insns: Interned>, - modules: HashMap, -} - -impl Compiled { - pub fn new(module: Module) -> Self { - Compiler::new(module.canonical().intern()).compile() +impl Simulation { + pub fn from_compiled(compiled: Compiled) -> Self { + Self { + state: interpreter::State::new(compiled.insns), + compiled, + } + } + pub fn settle_step(&mut self) { + self.state.setup_call(0); + self.state.run(); } } - -#[derive(Debug)] -pub struct Simulation { - state: interpreter::State, -} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index e89628c..5269131 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -362,6 +362,12 @@ pub(crate) struct Insns { state_layout: StateLayout, } +impl Insns { + pub(crate) fn state_layout(&self) -> &StateLayout { + &self.state_layout + } +} + struct InsnsDebug<'a> { insns: &'a [Insn], insn_source_locations: &'a [SourceLocation], @@ -564,13 +570,13 @@ macro_rules! make_state_part_kinds { impl Copy for StateLayout {} impl StateLayout { - pub(crate) fn len(self) -> StateLen { + pub(crate) fn len(&self) -> StateLen { StateLen { ty: self.ty.len(), $($state_field: self.$state_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.ty.is_empty() $(&& self.$state_field.is_empty())* } pub(crate) fn empty() -> Self { @@ -652,12 +658,12 @@ macro_rules! make_state_part_kinds { impl Copy for TypeLayout {} impl TypeLayout { - pub(crate) fn len(self) -> TypeLen { + pub(crate) fn len(&self) -> TypeLen { TypeLen { $($type_field: self.$type_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { $(self.$type_field.is_empty())&&+ } pub(crate) fn empty() -> Self { @@ -762,6 +768,14 @@ macro_rules! make_state_part_kinds { } impl State { + pub(crate) fn new(insns: Interned>) -> Self { + Self { + insns, + pc: 0, + $($state_field: StatePart::new(insns.state_layout.$state_field.len()),)* + $($type_field: StatePart::new(insns.state_layout.ty.$type_field.len()),)* + } + } pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { BorrowedState { orig_insns: self.insns, @@ -1494,6 +1508,13 @@ impl fmt::Debug for StatePartIndexMap { } } +impl Extend<(StatePartIndex, V)> for StatePartIndexMap { + fn extend, V)>>(&mut self, iter: T) { + self.map + .extend(iter.into_iter().map(|(k, v)| (k.as_usize(), v))); + } +} + impl StatePartIndexMap { pub(crate) fn new() -> Self { Self { @@ -1834,6 +1855,17 @@ impl_insns! { } next!(); } + CastBigToArrayIndex { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + let src = &state.big_slots[src]; + let value = src.try_into().unwrap_or(SmallUInt::MAX); + state.small_slots[dest] = value; + next!(); + } CastToSInt { #[kind = Output] dest: StatePartIndex, @@ -2055,6 +2087,20 @@ impl_insns! { state.big_slots[dest] = value; next!(); } + SliceInt { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + start: usize, + #[kind = Immediate] + len: usize, + } => { + let value = &state.big_slots[src] >> start; + state.big_slots[dest] = value & &*bigint_mask(len); + next!(); + } CmpEq { #[kind = Output] dest: StatePartIndex,