diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index d2906e1..5d37baf 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -42,8 +42,12 @@ use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use hashbrown::{HashMap, HashSet}; use num_bigint::BigInt; use num_traits::{Signed, ToPrimitive, Zero}; -use petgraph::visit::{ - GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, +use petgraph::{ + data::FromElements, + visit::{ + EdgeRef, GraphBase, IntoEdgeReferences, IntoNeighbors, IntoNeighborsDirected, + IntoNodeIdentifiers, IntoNodeReferences, NodeRef, VisitMap, Visitable, + }, }; use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; @@ -384,8 +388,8 @@ impl CompiledExpr { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AssignmentOrSlotIndex { AssignmentIndex(usize), - SmallSlots(StatePartIndex), - BigSlots(StatePartIndex), + SmallSlot(StatePartIndex), + BigSlot(StatePartIndex), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -408,6 +412,15 @@ enum AssignmentIO { }, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentsEdge { + IO(AssignmentIO), + AssignmentImmediatePredecessor { + predecessor_assignment_index: usize, + assignment_index: usize, + }, +} + #[derive(Debug)] enum Assignments { Accumulating { @@ -415,6 +428,7 @@ enum Assignments { }, Finalized { assignments: Box<[Assignment]>, + slots_layout: TypeLayout, slot_readers: SlotToAssignmentIndexFullMap, slot_writers: SlotToAssignmentIndexFullMap, assignment_immediate_predecessors: Box<[Box<[usize]>]>, @@ -431,13 +445,13 @@ impl Default for Assignments { } impl Assignments { - fn finalize(&mut self, slots_len: TypeLen) { + fn finalize(&mut self, slots_layout: TypeLayout) { let Self::Accumulating { assignments } = self else { unreachable!("already finalized"); }; let assignments = mem::take(assignments).into_boxed_slice(); - let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); - let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len()); let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; for (assignment_index, assignment) in assignments.iter().enumerate() { @@ -465,6 +479,7 @@ impl Assignments { } *self = Self::Finalized { assignments, + slots_layout, slot_readers, slot_writers, assignment_immediate_predecessors: assignment_immediate_predecessors @@ -489,6 +504,12 @@ impl Assignments { }; assignments } + fn slots_layout(&self) -> TypeLayout { + let Self::Finalized { slots_layout, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + *slots_layout + } fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { let Self::Finalized { slot_readers, .. } = self else { unreachable!("Assignments::finalize should have been called"); @@ -521,46 +542,131 @@ impl Assignments { }; assignment_immediate_successors } + fn elements(&self) -> AssignmentsElements<'_> { + let SlotToAssignmentIndexFullMap(TypeParts { + small_slots, + big_slots, + }) = self.slot_readers(); + AssignmentsElements { + node_indexes: HashMap::with_capacity( + self.assignments().len() + small_slots.len() + big_slots.len(), + ), + nodes: self.node_references(), + edges: self.edge_references(), + } + } } impl GraphBase for Assignments { - type EdgeId = AssignmentIO; + type EdgeId = AssignmentsEdge; type NodeId = AssignmentOrSlotIndex; } +#[derive(Debug, Clone, Copy)] +enum AssignmentsNodeRef<'a> { + Assignment { + index: usize, + assignment: &'a Assignment, + }, + SmallSlot(StatePartIndex, SlotDebugData), + BigSlot(StatePartIndex, SlotDebugData), +} + +impl<'a> NodeRef for AssignmentsNodeRef<'a> { + type NodeId = AssignmentOrSlotIndex; + type Weight = AssignmentsNodeRef<'a>; + + fn id(&self) -> Self::NodeId { + match *self { + AssignmentsNodeRef::Assignment { + index, + assignment: _, + } => AssignmentOrSlotIndex::AssignmentIndex(index), + AssignmentsNodeRef::SmallSlot(slot, _) => AssignmentOrSlotIndex::SmallSlot(slot), + AssignmentsNodeRef::BigSlot(slot, _) => AssignmentOrSlotIndex::BigSlot(slot), + } + } + + fn weight(&self) -> &Self::Weight { + self + } +} + +impl<'a> petgraph::visit::Data for &'a Assignments { + type NodeWeight = AssignmentsNodeRef<'a>; + type EdgeWeight = AssignmentsEdge; +} + +struct AssignmentsElements<'a> { + node_indexes: HashMap, + nodes: AssignmentsNodes<'a>, + edges: AssignmentsEdges<'a>, +} + +impl<'a> Iterator for AssignmentsElements<'a> { + type Item = petgraph::data::Element< + <&'a Assignments as petgraph::visit::Data>::NodeWeight, + <&'a Assignments as petgraph::visit::Data>::EdgeWeight, + >; + + fn next(&mut self) -> Option { + let Self { + node_indexes, + nodes, + edges, + } = self; + if let Some(node) = nodes.next() { + node_indexes.insert(node.id(), node_indexes.len()); + return Some(petgraph::data::Element::Node { weight: node }); + } + let edge = edges.next()?; + Some(petgraph::data::Element::Edge { + source: node_indexes[&edge.source()], + target: node_indexes[&edge.target()], + weight: *edge.weight(), + }) + } +} + +#[derive(Clone)] 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 { +impl AssignmentsNodeIdentifiers { + fn internal_iter<'a>(&'a mut self) -> impl Iterator + 'a { 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, - }) + .chain(small_slots.map(|value| { + AssignmentOrSlotIndex::SmallSlot(StatePartIndex { + value, + _phantom: PhantomData, }) - }) - .or_else(|| { - big_slots.next().map(|value| { - AssignmentOrSlotIndex::BigSlots(StatePartIndex { - value, - _phantom: PhantomData, - }) + })) + .chain(big_slots.map(|value| { + AssignmentOrSlotIndex::BigSlot(StatePartIndex { + value, + _phantom: PhantomData, }) - }) + })) + } +} + +impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + self.internal_iter().next() + } + + fn nth(&mut self, n: usize) -> Option { + self.internal_iter().nth(n) } } @@ -580,6 +686,44 @@ impl<'a> IntoNodeIdentifiers for &'a Assignments { } } +struct AssignmentsNodes<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, +} + +impl<'a> Iterator for AssignmentsNodes<'a> { + type Item = AssignmentsNodeRef<'a>; + + fn next(&mut self) -> Option { + self.nodes.next().map(|node| match node { + AssignmentOrSlotIndex::AssignmentIndex(index) => AssignmentsNodeRef::Assignment { + index, + assignment: &self.assignments.assignments()[index], + }, + AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNodeRef::SmallSlot( + slot, + *self.assignments.slots_layout().small_slots.debug_data(slot), + ), + AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNodeRef::BigSlot( + slot, + *self.assignments.slots_layout().big_slots.debug_data(slot), + ), + }) + } +} + +impl<'a> IntoNodeReferences for &'a Assignments { + type NodeRef = AssignmentsNodeRef<'a>; + type NodeReferences = AssignmentsNodes<'a>; + + fn node_references(self) -> Self::NodeReferences { + AssignmentsNodes { + assignments: self, + nodes: self.node_identifiers(), + } + } +} + struct AssignmentsNeighborsDirected<'a> { assignment_indexes: std::slice::Iter<'a, usize>, small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, @@ -603,13 +747,13 @@ impl Iterator for AssignmentsNeighborsDirected<'_> { } else if let retval @ Some(_) = small_slots .next() .copied() - .map(AssignmentOrSlotIndex::SmallSlots) + .map(AssignmentOrSlotIndex::SmallSlot) { retval } else if let retval @ Some(_) = big_slots .next() .copied() - .map(AssignmentOrSlotIndex::BigSlots) + .map(AssignmentOrSlotIndex::BigSlot) { retval } else { @@ -664,12 +808,12 @@ impl<'a> IntoNeighborsDirected for &'a Assignments { big_slots: big_slots.iter(), } } - AssignmentOrSlotIndex::SmallSlots(slot) => AssignmentsNeighborsDirected { + AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNeighborsDirected { assignment_indexes: slot_map[slot].iter(), small_slots: Default::default(), big_slots: Default::default(), }, - AssignmentOrSlotIndex::BigSlots(slot) => AssignmentsNeighborsDirected { + AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNeighborsDirected { assignment_indexes: slot_map[slot].iter(), small_slots: Default::default(), big_slots: Default::default(), @@ -678,6 +822,149 @@ impl<'a> IntoNeighborsDirected for &'a Assignments { } } +impl EdgeRef for AssignmentsEdge { + type NodeId = AssignmentOrSlotIndex; + type EdgeId = AssignmentsEdge; + type Weight = AssignmentsEdge; + + fn source(&self) -> Self::NodeId { + match *self { + AssignmentsEdge::IO(AssignmentIO::BigInput { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::BigSlot(slot), + AssignmentsEdge::IO(AssignmentIO::SmallInput { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::SmallSlot(slot), + AssignmentsEdge::IO(AssignmentIO::BigOutput { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentsEdge::IO(AssignmentIO::SmallOutput { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index: _, + } => AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + } + } + + fn target(&self) -> Self::NodeId { + match *self { + AssignmentsEdge::IO(AssignmentIO::BigInput { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentsEdge::IO(AssignmentIO::SmallInput { + assignment_index, + slot: _, + }) => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentsEdge::IO(AssignmentIO::BigOutput { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::BigSlot(slot), + AssignmentsEdge::IO(AssignmentIO::SmallOutput { + assignment_index: _, + slot, + }) => AssignmentOrSlotIndex::SmallSlot(slot), + AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index: _, + assignment_index, + } => AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + } + } + + fn weight(&self) -> &Self::Weight { + self + } + + fn id(&self) -> Self::EdgeId { + *self + } +} + +struct AssignmentsEdges<'a> { + assignments: &'a Assignments, + nodes: AssignmentsNodeIdentifiers, + outgoing_neighbors: Option<(AssignmentOrSlotIndex, AssignmentsNeighborsDirected<'a>)>, +} + +impl Iterator for AssignmentsEdges<'_> { + type Item = AssignmentsEdge; + + fn next(&mut self) -> Option { + loop { + if let Some((node, outgoing_neighbors)) = &mut self.outgoing_neighbors { + if let Some(outgoing_neighbor) = outgoing_neighbors.next() { + return Some(match (*node, outgoing_neighbor) { + ( + AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), + AssignmentOrSlotIndex::SmallSlot(_) | AssignmentOrSlotIndex::BigSlot(_), + ) => unreachable!(), + ( + AssignmentOrSlotIndex::AssignmentIndex(predecessor_assignment_index), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::AssignmentImmediatePredecessor { + predecessor_assignment_index, + assignment_index, + }, + ( + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentOrSlotIndex::SmallSlot(slot), + ) => AssignmentsEdge::IO(AssignmentIO::SmallOutput { + assignment_index, + slot, + }), + ( + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + AssignmentOrSlotIndex::BigSlot(slot), + ) => AssignmentsEdge::IO(AssignmentIO::BigOutput { + assignment_index, + slot, + }), + ( + AssignmentOrSlotIndex::SmallSlot(slot), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::IO(AssignmentIO::SmallInput { + assignment_index, + slot, + }), + ( + AssignmentOrSlotIndex::BigSlot(slot), + AssignmentOrSlotIndex::AssignmentIndex(assignment_index), + ) => AssignmentsEdge::IO(AssignmentIO::BigInput { + assignment_index, + slot, + }), + }); + } + } + let node = self.nodes.next()?; + self.outgoing_neighbors = Some(( + node, + self.assignments + .neighbors_directed(node, petgraph::Direction::Outgoing), + )); + } + } +} + +impl<'a> IntoEdgeReferences for &'a Assignments { + type EdgeRef = AssignmentsEdge; + type EdgeReferences = AssignmentsEdges<'a>; + + fn edge_references(self) -> Self::EdgeReferences { + AssignmentsEdges { + assignments: self, + nodes: self.node_identifiers(), + outgoing_neighbors: None, + } + } +} + struct AssignmentsVisitMap { assignments: Vec, slots: DenseSlotSet, @@ -689,8 +976,8 @@ impl VisitMap for AssignmentsVisitMap { 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), + AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.insert(slot), + AssignmentOrSlotIndex::BigSlot(slot) => self.slots.insert(slot), } } @@ -699,8 +986,8 @@ impl VisitMap for AssignmentsVisitMap { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { self.assignments[assignment_index] } - AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot), - AssignmentOrSlotIndex::BigSlots(slot) => self.slots.contains(slot), + AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.contains(slot), + AssignmentOrSlotIndex::BigSlot(slot) => self.slots.contains(slot), } } } @@ -2725,12 +3012,15 @@ impl Compiler { .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Bundle::from_canonical) .field_by_index(expr.field_index()), - ExprEnum::VariantAccess(variant_access) => self.compile_expr( - instantiated_module, - variant_access.base().cast_to_bits() - [Expr::ty(variant_access.base()).discriminant_bit_width()..] - .cast_bits_to(Expr::ty(expr)), - ), + ExprEnum::VariantAccess(variant_access) => { + let start = Expr::ty(variant_access.base()).discriminant_bit_width(); + let len = Expr::ty(expr).bit_width(); + self.compile_expr( + instantiated_module, + variant_access.base().cast_to_bits()[start..start + len] + .cast_bits_to(Expr::ty(expr)), + ) + } ExprEnum::ArrayIndex(expr) => self .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Array::from_canonical) @@ -4146,7 +4436,13 @@ impl Compiler { } fn process_assignments(&mut self) { self.assignments - .finalize(self.insns.state_layout().len().ty); + .finalize(self.insns.state_layout().ty.clone().into()); + println!( + "{:#?}", + petgraph::dot::Dot::new(&petgraph::graph::DiGraph::<_, _, usize>::from_elements( + self.assignments.elements() + )) + ); let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { Ok(nodes) => nodes .into_iter() @@ -4160,11 +4456,11 @@ impl Compiler { "combinatorial logic cycle detected at: {}", self.assignments.assignments()[assignment_index].source_location, ), - AssignmentOrSlotIndex::SmallSlots(slot) => panic!( + AssignmentOrSlotIndex::SmallSlot(slot) => panic!( "combinatorial logic cycle detected through: {}", self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, ), - AssignmentOrSlotIndex::BigSlots(slot) => panic!( + AssignmentOrSlotIndex::BigSlot(slot) => panic!( "combinatorial logic cycle detected through: {}", self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, ), diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 23d1275..aabf6e2 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -1652,6 +1652,9 @@ impl StatePartLayout { _phantom: PhantomData, } } + pub(crate) fn debug_data(&self, index: StatePartIndex) -> &K::DebugData { + &self.debug_data[index.as_usize()] + } } impl From>