sim: add .dot output for Assignments graph for debugging
All checks were successful
/ deps (push) Successful in 18s
/ test (push) Successful in 5m20s
/ deps (pull_request) Successful in 15s
/ test (pull_request) Successful in 5m21s

This commit is contained in:
Jacob Lifshay 2024-12-10 23:40:33 -08:00
parent 564ccb30bc
commit 6b31e6d515
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
2 changed files with 342 additions and 43 deletions

View file

@ -42,8 +42,12 @@ use bitvec::{bits, order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use num_bigint::BigInt; use num_bigint::BigInt;
use num_traits::{Signed, ToPrimitive, Zero}; use num_traits::{Signed, ToPrimitive, Zero};
use petgraph::visit::{ use petgraph::{
GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, 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}; use std::{borrow::Cow, collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut};
@ -384,8 +388,8 @@ impl CompiledExpr<Array> {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum AssignmentOrSlotIndex { enum AssignmentOrSlotIndex {
AssignmentIndex(usize), AssignmentIndex(usize),
SmallSlots(StatePartIndex<StatePartKindSmallSlots>), SmallSlot(StatePartIndex<StatePartKindSmallSlots>),
BigSlots(StatePartIndex<StatePartKindBigSlots>), BigSlot(StatePartIndex<StatePartKindBigSlots>),
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[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)] #[derive(Debug)]
enum Assignments { enum Assignments {
Accumulating { Accumulating {
@ -415,6 +428,7 @@ enum Assignments {
}, },
Finalized { Finalized {
assignments: Box<[Assignment]>, assignments: Box<[Assignment]>,
slots_layout: TypeLayout<InsnsBuildingDone>,
slot_readers: SlotToAssignmentIndexFullMap, slot_readers: SlotToAssignmentIndexFullMap,
slot_writers: SlotToAssignmentIndexFullMap, slot_writers: SlotToAssignmentIndexFullMap,
assignment_immediate_predecessors: Box<[Box<[usize]>]>, assignment_immediate_predecessors: Box<[Box<[usize]>]>,
@ -431,13 +445,13 @@ impl Default for Assignments {
} }
impl Assignments { impl Assignments {
fn finalize(&mut self, slots_len: TypeLen) { fn finalize(&mut self, slots_layout: TypeLayout<InsnsBuildingDone>) {
let Self::Accumulating { assignments } = self else { let Self::Accumulating { assignments } = self else {
unreachable!("already finalized"); unreachable!("already finalized");
}; };
let assignments = mem::take(assignments).into_boxed_slice(); let assignments = mem::take(assignments).into_boxed_slice();
let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_layout.len());
let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_layout.len());
let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()];
let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()];
for (assignment_index, assignment) in assignments.iter().enumerate() { for (assignment_index, assignment) in assignments.iter().enumerate() {
@ -465,6 +479,7 @@ impl Assignments {
} }
*self = Self::Finalized { *self = Self::Finalized {
assignments, assignments,
slots_layout,
slot_readers, slot_readers,
slot_writers, slot_writers,
assignment_immediate_predecessors: assignment_immediate_predecessors assignment_immediate_predecessors: assignment_immediate_predecessors
@ -489,6 +504,12 @@ impl Assignments {
}; };
assignments assignments
} }
fn slots_layout(&self) -> TypeLayout<InsnsBuildingDone> {
let Self::Finalized { slots_layout, .. } = self else {
unreachable!("Assignments::finalize should have been called");
};
*slots_layout
}
fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap {
let Self::Finalized { slot_readers, .. } = self else { let Self::Finalized { slot_readers, .. } = self else {
unreachable!("Assignments::finalize should have been called"); unreachable!("Assignments::finalize should have been called");
@ -521,46 +542,131 @@ impl Assignments {
}; };
assignment_immediate_successors 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 { impl GraphBase for Assignments {
type EdgeId = AssignmentIO; type EdgeId = AssignmentsEdge;
type NodeId = AssignmentOrSlotIndex; type NodeId = AssignmentOrSlotIndex;
} }
#[derive(Debug, Clone, Copy)]
enum AssignmentsNodeRef<'a> {
Assignment {
index: usize,
assignment: &'a Assignment,
},
SmallSlot(StatePartIndex<StatePartKindSmallSlots>, SlotDebugData),
BigSlot(StatePartIndex<StatePartKindBigSlots>, 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<AssignmentOrSlotIndex, usize>,
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<Self::Item> {
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 { struct AssignmentsNodeIdentifiers {
assignment_indexes: std::ops::Range<usize>, assignment_indexes: std::ops::Range<usize>,
small_slots: std::ops::Range<u32>, small_slots: std::ops::Range<u32>,
big_slots: std::ops::Range<u32>, big_slots: std::ops::Range<u32>,
} }
impl Iterator for AssignmentsNodeIdentifiers { impl AssignmentsNodeIdentifiers {
type Item = AssignmentOrSlotIndex; fn internal_iter<'a>(&'a mut self) -> impl Iterator<Item = AssignmentOrSlotIndex> + 'a {
fn next(&mut self) -> Option<Self::Item> {
let Self { let Self {
assignment_indexes, assignment_indexes,
small_slots, small_slots,
big_slots, big_slots,
} = self; } = self;
assignment_indexes assignment_indexes
.next()
.map(AssignmentOrSlotIndex::AssignmentIndex) .map(AssignmentOrSlotIndex::AssignmentIndex)
.or_else(|| { .chain(small_slots.map(|value| {
small_slots.next().map(|value| { AssignmentOrSlotIndex::SmallSlot(StatePartIndex {
AssignmentOrSlotIndex::SmallSlots(StatePartIndex { value,
value, _phantom: PhantomData,
_phantom: PhantomData,
})
}) })
}) }))
.or_else(|| { .chain(big_slots.map(|value| {
big_slots.next().map(|value| { AssignmentOrSlotIndex::BigSlot(StatePartIndex {
AssignmentOrSlotIndex::BigSlots(StatePartIndex { value,
value, _phantom: PhantomData,
_phantom: PhantomData,
})
}) })
}) }))
}
}
impl Iterator for AssignmentsNodeIdentifiers {
type Item = AssignmentOrSlotIndex;
fn next(&mut self) -> Option<Self::Item> {
self.internal_iter().next()
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
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::Item> {
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> { struct AssignmentsNeighborsDirected<'a> {
assignment_indexes: std::slice::Iter<'a, usize>, assignment_indexes: std::slice::Iter<'a, usize>,
small_slots: std::collections::btree_set::Iter<'a, StatePartIndex<StatePartKindSmallSlots>>, small_slots: std::collections::btree_set::Iter<'a, StatePartIndex<StatePartKindSmallSlots>>,
@ -603,13 +747,13 @@ impl Iterator for AssignmentsNeighborsDirected<'_> {
} else if let retval @ Some(_) = small_slots } else if let retval @ Some(_) = small_slots
.next() .next()
.copied() .copied()
.map(AssignmentOrSlotIndex::SmallSlots) .map(AssignmentOrSlotIndex::SmallSlot)
{ {
retval retval
} else if let retval @ Some(_) = big_slots } else if let retval @ Some(_) = big_slots
.next() .next()
.copied() .copied()
.map(AssignmentOrSlotIndex::BigSlots) .map(AssignmentOrSlotIndex::BigSlot)
{ {
retval retval
} else { } else {
@ -664,12 +808,12 @@ impl<'a> IntoNeighborsDirected for &'a Assignments {
big_slots: big_slots.iter(), big_slots: big_slots.iter(),
} }
} }
AssignmentOrSlotIndex::SmallSlots(slot) => AssignmentsNeighborsDirected { AssignmentOrSlotIndex::SmallSlot(slot) => AssignmentsNeighborsDirected {
assignment_indexes: slot_map[slot].iter(), assignment_indexes: slot_map[slot].iter(),
small_slots: Default::default(), small_slots: Default::default(),
big_slots: Default::default(), big_slots: Default::default(),
}, },
AssignmentOrSlotIndex::BigSlots(slot) => AssignmentsNeighborsDirected { AssignmentOrSlotIndex::BigSlot(slot) => AssignmentsNeighborsDirected {
assignment_indexes: slot_map[slot].iter(), assignment_indexes: slot_map[slot].iter(),
small_slots: Default::default(), small_slots: Default::default(),
big_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<Self::Item> {
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 { struct AssignmentsVisitMap {
assignments: Vec<bool>, assignments: Vec<bool>,
slots: DenseSlotSet, slots: DenseSlotSet,
@ -689,8 +976,8 @@ impl VisitMap<AssignmentOrSlotIndex> for AssignmentsVisitMap {
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => {
!mem::replace(&mut self.assignments[assignment_index], true) !mem::replace(&mut self.assignments[assignment_index], true)
} }
AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.insert(slot), AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.insert(slot),
AssignmentOrSlotIndex::BigSlots(slot) => self.slots.insert(slot), AssignmentOrSlotIndex::BigSlot(slot) => self.slots.insert(slot),
} }
} }
@ -699,8 +986,8 @@ impl VisitMap<AssignmentOrSlotIndex> for AssignmentsVisitMap {
AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => {
self.assignments[assignment_index] self.assignments[assignment_index]
} }
AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot), AssignmentOrSlotIndex::SmallSlot(slot) => self.slots.contains(slot),
AssignmentOrSlotIndex::BigSlots(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())) .compile_expr(instantiated_module, Expr::canonical(expr.base()))
.map_ty(Bundle::from_canonical) .map_ty(Bundle::from_canonical)
.field_by_index(expr.field_index()), .field_by_index(expr.field_index()),
ExprEnum::VariantAccess(variant_access) => self.compile_expr( ExprEnum::VariantAccess(variant_access) => {
instantiated_module, let start = Expr::ty(variant_access.base()).discriminant_bit_width();
variant_access.base().cast_to_bits() let len = Expr::ty(expr).bit_width();
[Expr::ty(variant_access.base()).discriminant_bit_width()..] self.compile_expr(
.cast_bits_to(Expr::ty(expr)), instantiated_module,
), variant_access.base().cast_to_bits()[start..start + len]
.cast_bits_to(Expr::ty(expr)),
)
}
ExprEnum::ArrayIndex(expr) => self ExprEnum::ArrayIndex(expr) => self
.compile_expr(instantiated_module, Expr::canonical(expr.base())) .compile_expr(instantiated_module, Expr::canonical(expr.base()))
.map_ty(Array::from_canonical) .map_ty(Array::from_canonical)
@ -4146,7 +4436,13 @@ impl Compiler {
} }
fn process_assignments(&mut self) { fn process_assignments(&mut self) {
self.assignments 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) { let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) {
Ok(nodes) => nodes Ok(nodes) => nodes
.into_iter() .into_iter()
@ -4160,11 +4456,11 @@ impl Compiler {
"combinatorial logic cycle detected at: {}", "combinatorial logic cycle detected at: {}",
self.assignments.assignments()[assignment_index].source_location, self.assignments.assignments()[assignment_index].source_location,
), ),
AssignmentOrSlotIndex::SmallSlots(slot) => panic!( AssignmentOrSlotIndex::SmallSlot(slot) => panic!(
"combinatorial logic cycle detected through: {}", "combinatorial logic cycle detected through: {}",
self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, 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: {}", "combinatorial logic cycle detected through: {}",
self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name,
), ),

View file

@ -1652,6 +1652,9 @@ impl<K: StatePartKind, BK: InsnsBuildingKind> StatePartLayout<K, BK> {
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
pub(crate) fn debug_data(&self, index: StatePartIndex<K>) -> &K::DebugData {
&self.debug_data[index.as_usize()]
}
} }
impl<K: StatePartKind> From<StatePartLayout<K, InsnsBuilding>> impl<K: StatePartKind> From<StatePartLayout<K, InsnsBuilding>>