From 7851bf545cb33fd26b05fc72a2ebd009677d992e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 26 Nov 2024 00:07:11 -0800 Subject: [PATCH] working on deduce_resets.rs --- .../src/module/transform/deduce_resets.rs | 1413 +++++++++-------- 1 file changed, 707 insertions(+), 706 deletions(-) diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index 4308efa..e6b5987 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -1,37 +1,62 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information +#![cfg(todo)] + use hashbrown::{hash_map::Entry, HashMap}; +use num_bigint::BigInt; use petgraph::{ graph::{NodeIndex, UnGraph}, unionfind::UnionFind, }; use crate::{ + annotations::{Annotation, TargetedAnnotation}, bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, - expr::{ops, ExprEnum, Flow}, + expr::{ + ops, + target::{ + Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField, + TargetPathDynArrayElement, TargetPathElement, + }, + ExprEnum, Flow, + }, intern::{Intern, Interned, Memoize}, + memory::{DynPortType, MemPort}, module::{ - transform::visit::{Visit, Visitor}, - AnnotatedModuleIO, Block, ExternModuleBody, InstantiatedModule, ModuleBody, ModuleIO, - NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, - StmtMatch, StmtReg, StmtWire, TargetInInstantiatedModule, + transform::visit::{Folder, Visit, Visitor}, + AnnotatedModuleIO, Block, ExprInInstantiatedModule, ExternModuleBody, + ExternModuleParameter, ExternModuleParameterValue, InstantiatedModule, ModuleBody, + ModuleIO, NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, + StmtInstance, StmtMatch, StmtReg, StmtWire, }, prelude::*, + source_location, +}; +use std::{ + borrow::Borrow, + convert::Infallible, + fmt, + hash::{Hash, Hasher}, + marker::PhantomData, + mem, }; -use std::{convert::Infallible, fmt, mem}; #[derive(Debug)] pub enum DeduceResetsError { - ResetIsNotDrivenByAsyncOrSync { target: TargetInInstantiatedModule }, + ResetIsNotDrivenByAsyncOrSync { source_location: SourceLocation }, + ResetIsDrivenByBothAsyncAndSync { source_location: SourceLocation }, } impl fmt::Display for DeduceResetsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - DeduceResetsError::ResetIsNotDrivenByAsyncOrSync { target } => { - write!(f, "deduce_reset failed: Reset signal is not driven by any AsyncReset or SyncReset signals: {target:?}") + DeduceResetsError::ResetIsNotDrivenByAsyncOrSync { source_location } => { + write!(f, "deduce_reset failed: Reset signal is not driven by any AsyncReset or SyncReset signals: {source_location}") + } + DeduceResetsError::ResetIsDrivenByBothAsyncAndSync { source_location } => { + write!(f, "deduce_reset failed: Reset signal is driven by both AsyncReset and SyncReset signals: {source_location}") } } } @@ -46,767 +71,736 @@ impl From for std::io::Error { } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -enum ResetTarget { - Base { - base: Expr, +enum ResetsLayout { + NoResets, + Reset, + SyncReset, + AsyncReset, + Bundle { + fields: Interned<[ResetsLayout]>, + reset_count: usize, }, - BundleField { - parent: Interned, - field_ty: CanonicalType, - field_index: usize, + Enum { + variants: Interned<[ResetsLayout]>, + reset_count: usize, }, - EnumVariant { - parent: Interned, - variant_ty: CanonicalType, - variant_index: usize, - }, - /// Array's Elements: - /// deduce_resets requires all array elements to deduce to the same pattern of async/sync resets, - /// so we don't track individual array elements but instead track all of an array's elements together. - ArraysElements { - parent: Interned, - element_ty: CanonicalType, + Array { + element: Interned, + reset_count: usize, }, } -impl ResetTarget { - fn canonical_ty(self) -> CanonicalType { +impl ResetsLayout { + fn reset_count(self) -> usize { match self { - ResetTarget::Base { base } => Expr::ty(base), - ResetTarget::BundleField { field_ty, .. } => field_ty, - ResetTarget::EnumVariant { variant_ty, .. } => variant_ty, - ResetTarget::ArraysElements { element_ty, .. } => element_ty, + ResetsLayout::NoResets => 0, + ResetsLayout::Reset | ResetsLayout::SyncReset | ResetsLayout::AsyncReset => 1, + ResetsLayout::Bundle { reset_count, .. } + | ResetsLayout::Enum { reset_count, .. } + | ResetsLayout::Array { reset_count, .. } => reset_count, } } - fn parent(self) -> Option> { + fn new(ty: CanonicalType) -> Self { + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = ResetsLayout; + + fn inner(self, ty: &Self::Input) -> Self::Output { + match *ty { + CanonicalType::UInt(_) => ResetsLayout::NoResets, + CanonicalType::SInt(_) => ResetsLayout::NoResets, + CanonicalType::Bool(_) => ResetsLayout::NoResets, + CanonicalType::Array(ty) => { + let element = ResetsLayout::new(ty.element()).intern_sized(); + ResetsLayout::Array { + element, + reset_count: element.reset_count(), + } + } + CanonicalType::Enum(ty) => { + let mut reset_count = 0; + let variants = Interned::from_iter(ty.variants().iter().map(|variant| { + let resets_layout = + variant.ty.map_or(ResetsLayout::NoResets, ResetsLayout::new); + reset_count += resets_layout.reset_count(); + resets_layout + })); + ResetsLayout::Enum { + variants, + reset_count, + } + } + CanonicalType::Bundle(ty) => { + let mut reset_count = 0; + let fields = Interned::from_iter(ty.fields().iter().map(|field| { + let resets_layout = ResetsLayout::new(field.ty); + reset_count += resets_layout.reset_count(); + resets_layout + })); + ResetsLayout::Bundle { + fields, + reset_count, + } + } + CanonicalType::AsyncReset(_) => ResetsLayout::AsyncReset, + CanonicalType::SyncReset(_) => ResetsLayout::SyncReset, + CanonicalType::Reset(_) => ResetsLayout::Reset, + CanonicalType::Clock(_) => ResetsLayout::NoResets, + } + } + } + MyMemoize.get_owned(ty) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct ResetNodeIndex(usize); + +#[derive(Copy, Clone, Debug)] +struct ResetNode { + is_async: Option, + source_location: Option, +} + +impl ResetNode { + fn union( + self, + other: Self, + fallback_error_source_location: SourceLocation, + ) -> Result { + match (self.is_async, other.is_async) { + (None, None) => Ok(Self { + is_async: None, + source_location: self.source_location.or(other.source_location), + }), + (None, is_async @ Some(_)) => Ok(Self { + is_async, + // prioritize `other` + source_location: other.source_location.or(self.source_location), + }), + (is_async @ Some(_), None) => Ok(Self { + is_async, + // prioritize `self` + source_location: self.source_location.or(other.source_location), + }), + (Some(self_is_async), Some(other_is_async)) => { + if self_is_async == other_is_async { + Ok(Self { + is_async: Some(self_is_async), + source_location: self.source_location.or(other.source_location), + }) + } else { + Err(DeduceResetsError::ResetIsDrivenByBothAsyncAndSync { + source_location: self + .source_location + .or(other.source_location) + .unwrap_or(fallback_error_source_location), + }) + } + } + } + } +} + +#[derive(Debug, Default)] +struct ResetGraph { + union_find: UnionFind, + nodes: Vec, +} + +impl ResetGraph { + fn new_node( + &mut self, + is_async: Option, + source_location: Option, + ) -> ResetNodeIndex { + let index = self.union_find.new_set(); + assert_eq!(index, self.nodes.len()); + self.nodes.push(ResetNode { + is_async, + source_location, + }); + ResetNodeIndex(index) + } + fn union( + &mut self, + a: ResetNodeIndex, + b: ResetNodeIndex, + fallback_error_source_location: SourceLocation, + ) -> Result<(), DeduceResetsError> { + let a = self.union_find.find_mut(a.0); + let b = self.union_find.find_mut(b.0); + if a != b { + self.union_find.union(a, b); + let merged = self.union_find.find_mut(a); + self.nodes[merged] = + self.nodes[a].union(self.nodes[b], fallback_error_source_location)?; + } + Ok(()) + } + fn is_async( + &mut self, + node: ResetNodeIndex, + fallback_to_sync_reset: bool, + fallback_error_source_location: SourceLocation, + ) -> Result { + let ResetNode { + is_async, + source_location, + } = self.nodes[self.union_find.find_mut(node.0)]; + if let Some(is_async) = is_async { + Ok(is_async) + } else if fallback_to_sync_reset { + Ok(false) + } else { + Err(DeduceResetsError::ResetIsNotDrivenByAsyncOrSync { + source_location: source_location.unwrap_or(fallback_error_source_location), + }) + } + } +} + +#[derive(Debug)] +enum MaybeInterned<'a, T: ?Sized + Intern> { + Interned(Interned), + Borrowed(&'a T), +} + +impl<'a, T: ?Sized + Intern> Borrow for MaybeInterned<'a, T> { + fn borrow(&self) -> &T { + &**self + } +} + +impl<'a, T: ?Sized + Intern + PartialEq> PartialEq for MaybeInterned<'a, T> { + fn eq(&self, other: &Self) -> bool { + **self == **other + } +} + +impl<'a, T: ?Sized + Intern + Eq> Eq for MaybeInterned<'a, T> {} + +impl<'a, T: ?Sized + Intern + Hash> Hash for MaybeInterned<'a, T> { + fn hash(&self, state: &mut H) { + (**self).hash(state); + } +} + +impl<'a, T: ?Sized + Intern> Copy for MaybeInterned<'a, T> {} + +impl<'a, T: ?Sized + Intern> Clone for MaybeInterned<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T: ?Sized + Intern> std::ops::Deref for MaybeInterned<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { match self { - ResetTarget::Base { .. } => None, - ResetTarget::BundleField { parent, .. } - | ResetTarget::EnumVariant { parent, .. } - | ResetTarget::ArraysElements { parent, .. } => Some(parent), + MaybeInterned::Interned(v) => v, + MaybeInterned::Borrowed(v) => v, } } - fn base(mut self) -> Expr { - loop { - match self { - ResetTarget::Base { base } => break base, - ResetTarget::BundleField { parent, .. } - | ResetTarget::EnumVariant { parent, .. } - | ResetTarget::ArraysElements { parent, .. } => self = *parent, - } - } - } - fn bundle_field(self, field_index: usize) -> Self { - let field_ty = Bundle::from_canonical(self.canonical_ty()).fields()[field_index].ty; - Self::BundleField { - parent: self.intern_sized(), - field_ty, - field_index, - } - } - fn enum_variant(self, variant_index: usize) -> Self { - let variant_ty = Enum::from_canonical(self.canonical_ty()).variants()[variant_index] - .ty - .expect("known to have a variant field"); - Self::EnumVariant { - parent: self.intern_sized(), - variant_ty, - variant_index, - } - } - fn arrays_elements(self) -> Self { - let element_ty = ::from_canonical(self.canonical_ty()).element(); - Self::ArraysElements { - parent: self.intern_sized(), - element_ty, - } - } - fn for_each_child(self, mut f: impl FnMut(ResetTarget) -> Result<(), E>) -> Result<(), E> { - match self.canonical_ty() { - CanonicalType::UInt(_) - | CanonicalType::SInt(_) - | CanonicalType::Bool(_) - | CanonicalType::AsyncReset(_) - | CanonicalType::SyncReset(_) - | CanonicalType::Reset(_) - | CanonicalType::Clock(_) => Ok(()), - CanonicalType::Array(_) => f(self.arrays_elements()), - CanonicalType::Enum(ty) => { - for variant_index in 0..ty.variants().len() { - f(self.enum_variant(variant_index))?; - } - Ok(()) - } - CanonicalType::Bundle(ty) => { - for field_index in 0..ty.fields().len() { - f(self.bundle_field(field_index))?; - } - Ok(()) - } +} + +impl<'a, T: ?Sized + Intern> MaybeInterned<'a, T> { + fn to_interned(self) -> Interned { + match self { + MaybeInterned::Interned(v) => v, + MaybeInterned::Borrowed(v) => v.intern(), } } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -struct ResetTargetInInstantiatedModule { - instantiated_module: InstantiatedModule, - target: ResetTarget, +struct Resets<'a> { + ty: CanonicalType, + layout: ResetsLayout, + node_indexes: MaybeInterned<'a, [ResetNodeIndex]>, } -#[derive(Debug)] -struct Node { - target: ResetTargetInInstantiatedModule, - deduced_type: Option, +impl<'a> Resets<'a> { + fn into_interned(self) -> Resets<'static> { + let Self { + ty, + layout, + node_indexes, + } = self; + Resets { + ty, + layout, + node_indexes: MaybeInterned::Interned(node_indexes.to_interned()), + } + } } #[derive(Debug)] struct State { base_module: Interned>, - node_ids: HashMap>, - graph: UnGraph, + expr_resets: HashMap, Resets<'static>>, + reset_graph: ResetGraph, fallback_to_sync_reset: bool, } -fn type_contains_any_undeduced_resets(ty: CanonicalType) -> bool { - #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] - struct MyMemoize; - impl Memoize for MyMemoize { - type Input = CanonicalType; - type InputOwned = CanonicalType; - type Output = bool; +struct PassOutput(P::Output); - fn inner(self, input: &Self::Input) -> Self::Output { - match input { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => false, - CanonicalType::Bool(_) => false, - CanonicalType::Array(ty) => type_contains_any_undeduced_resets(ty.element()), - CanonicalType::Enum(ty) => { - ty.variants().iter().any(|EnumVariant { ty, .. }| { - ty.is_some_and(|ty| type_contains_any_undeduced_resets(ty)) - }) - } - CanonicalType::Bundle(ty) => ty - .fields() - .iter() - .any(|&BundleField { ty, .. }| type_contains_any_undeduced_resets(ty)), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => true, - CanonicalType::Clock(_) => false, +impl PassOutput { + fn new(v: T) -> Self { + P::output_new(v) + } + fn from_fn(f: impl FnOnce() -> T) -> Self { + PassOutput::new(()).map(|()| f()) + } + fn as_ref(&self) -> PassOutput<&T, P> { + P::output_as_ref(self) + } + fn as_mut(&mut self) -> PassOutput<&mut T, P> { + P::output_as_mut(self) + } + fn map(self, f: impl FnOnce(T) -> U) -> PassOutput { + P::map(self, f) + } +} + +trait PassOutputZip: Sized { + type Zipped; + fn zip(self) -> PassOutput; + fn call(self, f: impl FnOnce(Self::Zipped) -> U) -> PassOutput { + self.zip().map(f) + } +} + +impl PassOutputZip

for () { + type Zipped = (); + fn zip(self) -> PassOutput { + PassOutput::new(()) + } +} + +impl PassOutputZip

for (PassOutput,) { + type Zipped = (T,); + fn zip(self) -> PassOutput { + self.0.map(|v| (v,)) + } +} + +macro_rules! impl_zip { + ($first_arg:ident: $first_T:ident, $($arg:ident: $T:ident),* $(,)?) => { + impl_zip!(@step [], [($first_arg: $first_T) $(($arg: $T))*], (),); + }; + ( + @impl($first_arg:tt,), + $tuple_pat:tt, + ) => {}; + ( + @impl(($first_arg:ident: $first_T:ident), + $(($arg:ident: $T:ident),)*), + $tuple_pat:tt, + ) => { + impl<$first_T, $($T,)* P: Pass> PassOutputZip

for (PassOutput<$first_T, P>, $(PassOutput<$T, P>),*) { + type Zipped = ($first_T, $($T),*); + fn zip(self) -> PassOutput<($first_T, $($T),*), P> { + let (tuples, $($arg),*) = self; + $(let tuples = P::zip(tuples, $arg);)* + tuples.map(|$tuple_pat| ($first_arg, $($arg),*)) } } + }; + ( + @step [$($cur:tt)*], + [], + $tuple_pat:tt, + ) => {}; + ( + @step [$($cur:tt)*], + [($next_arg:ident: $next_T:ident) $($rest:tt)*], + (), + ) => { + impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), $next_arg,); + impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], $next_arg,); + }; + ( + @step [$($cur:tt)*], + [($next_arg:ident: $next_T:ident) $($rest:tt)*], + $tuple_pat:tt, + ) => { + impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), ($tuple_pat, $next_arg),); + impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], ($tuple_pat, $next_arg),); + }; +} + +impl_zip!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11); + +impl, P: Pass, A> FromIterator> for PassOutput { + fn from_iter>>(iter: I) -> Self { + P::output_from_iter(iter) } - MyMemoize.get_owned(ty) } -trait ProcessStep { - type Error; - type Processed; - fn processed_as_ref(v: &Self::Processed) -> Self::Processed<&T>; - fn processed_as_mut(v: &mut Self::Processed) -> Self::Processed<&mut T>; - fn processed_zip(t: Self::Processed, u: Self::Processed) - -> Self::Processed<(T, U)>; - fn processed_map(v: Self::Processed, f: impl FnOnce(T) -> U) -> Self::Processed; - fn processed_make(t: T) -> Self::Processed; - fn processed_from_iter>( - iter: impl IntoIterator>, - ) -> Self::Processed; +trait Pass: Sized { + type Output; + fn output_new(v: T) -> PassOutput; + fn output_as_ref(v: &PassOutput) -> PassOutput<&T, Self>; + fn output_as_mut(v: &mut PassOutput) -> PassOutput<&mut T, Self>; + fn output_from_iter, A>( + iter: impl IntoIterator>, + ) -> PassOutput; + fn map(v: PassOutput, f: impl FnOnce(T) -> U) -> PassOutput; + fn zip(t: PassOutput, u: PassOutput) -> PassOutput<(T, U), Self>; } -macro_rules! impl_process_step_with_empty_processed { - () => { - type Processed = (); - fn processed_as_ref(v: &Self::Processed) -> Self::Processed<&T> { - *v +struct BuildResetGraph; + +impl Pass for BuildResetGraph { + type Output = (); + + fn output_new(_v: T) -> PassOutput { + PassOutput(()) + } + + fn output_as_ref(_v: &PassOutput) -> PassOutput<&T, Self> { + PassOutput(()) + } + + fn output_as_mut(_v: &mut PassOutput) -> PassOutput<&mut T, Self> { + PassOutput(()) + } + + fn output_from_iter, A>( + iter: impl IntoIterator>, + ) -> PassOutput { + iter.into_iter().for_each(|_| {}); + PassOutput(()) + } + + fn map(_v: PassOutput, _f: impl FnOnce(T) -> U) -> PassOutput { + PassOutput(()) + } + + fn zip(_t: PassOutput, _u: PassOutput) -> PassOutput<(T, U), Self> { + PassOutput(()) + } +} + +struct SubstituteResets; + +impl Pass for SubstituteResets { + type Output = T; + + fn output_new(v: T) -> PassOutput { + PassOutput(v) + } + + fn output_as_ref(v: &PassOutput) -> PassOutput<&T, Self> { + PassOutput(&v.0) + } + + fn output_as_mut(v: &mut PassOutput) -> PassOutput<&mut T, Self> { + PassOutput(&mut v.0) + } + + fn output_from_iter, A>( + iter: impl IntoIterator>, + ) -> PassOutput { + PassOutput(T::from_iter(iter.into_iter().map(|PassOutput(v)| v))) + } + + fn map(v: PassOutput, f: impl FnOnce(T) -> U) -> PassOutput { + PassOutput(f(v.0)) + } + + fn zip(t: PassOutput, u: PassOutput) -> PassOutput<(T, U), Self> { + PassOutput((t.0, u.0)) + } +} + +struct PassArgs<'a, P: Pass> { + state: &'a mut State, + instantiated_module: InstantiatedModule, + fallback_error_source_location: SourceLocation, + _phantom: PhantomData

, +} + +impl<'a, P: Pass> fmt::Debug for PassArgs<'a, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + state, + instantiated_module, + fallback_error_source_location, + _phantom: _, + } = self; + f.debug_struct("PassArgs") + .field("state", state) + .field("instantiated_module", instantiated_module) + .field( + "fallback_error_source_location", + fallback_error_source_location, + ) + .finish() + } +} + +impl<'a, P: Pass> PassArgs<'a, P> { + fn as_mut(&mut self) -> PassArgs<'_, P> { + let PassArgs { + ref mut state, + instantiated_module, + fallback_error_source_location, + _phantom: _, + } = *self; + PassArgs { + state: &mut **state, + instantiated_module, + fallback_error_source_location, + _phantom: PhantomData, } - fn processed_as_mut(v: &mut Self::Processed) -> Self::Processed<&mut T> { - *v - } - fn processed_zip( - t: Self::Processed, - u: Self::Processed, - ) -> Self::Processed<(T, U)> { - let _ = t; - let _ = u; - () - } - fn processed_map( - v: Self::Processed, - f: impl FnOnce(T) -> U, - ) -> Self::Processed { - let _ = f; - v - } - fn processed_make(t: T) -> Self::Processed { - let _ = t; - () - } - fn processed_from_iter>( - iter: impl IntoIterator>, - ) -> Self::Processed { - FromIterator::from_iter(iter) + } +} + +trait RunPass: Sized { + fn run_pass( + &self, + pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError>; +} + +impl + Intern + Clone, P: Pass> RunPass

for Interned { + fn run_pass( + &self, + pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + Ok(T::run_pass(self, pass_args)?.map(Intern::intern_sized)) + } +} + +impl + Clone, P: Pass> RunPass

for Interned<[T]> +where + [T]: Intern, +{ + fn run_pass( + &self, + mut pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + Result::from_iter(self.iter().map(|v| v.run_pass(pass_args.as_mut()))) + } +} + +macro_rules! impl_run_pass_copy { + ([$($generics:tt)*] $ty:ty) => { + impl RunPass

for $ty { + fn run_pass( + &self, + _pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + Ok(PassOutput::new(*self)) + } } }; } -struct Processed(Step::Processed); - -impl Processed { - fn as_ref(&self) -> Processed<&T, Step> { - Processed(Step::processed_as_ref(&self.0)) - } - fn as_mut(&mut self) -> Processed<&mut T, Step> { - Processed(Step::processed_as_mut(&mut self.0)) - } - fn zip(self, u: Processed) -> Processed<(T, U), Step> { - Processed(Step::processed_zip(self.0, u.0)) - } - fn new(v: T) -> Self { - Processed(Step::processed_make(v)) - } - fn map(self, f: impl FnOnce(T) -> U) -> Processed { - Processed(Step::processed_map(self.0, f)) - } -} - -impl, A, Step: ProcessStep> FromIterator> - for Processed -{ - fn from_iter>>(iter: T) -> Self { - Processed(Step::processed_from_iter(iter.into_iter().map(|v| v.0))) - } -} - -fn for_each_subexpr_exclusive( - expr: Expr, - f: impl FnMut(Expr) -> Result<(), E>, -) -> Result<(), E> { - struct MyVisitor { - f: F, - } - impl) -> Result<(), E>, E> Visitor for MyVisitor { - type Error = E; - fn visit_expr_enum(&mut self, v: &ExprEnum) -> Result<(), Self::Error> { - (self.f)(v.to_expr())?; - v.default_visit(self) - } - } - Expr::expr_enum(expr).default_visit(&mut MyVisitor { f }) -} - -struct AddNodesToGraphStep; - -impl ProcessStep for AddNodesToGraphStep { - type Error = Infallible; - impl_process_step_with_empty_processed!(); -} - -struct AddEdgesToGraphStep { - union_find: UnionFind, -} - -impl ProcessStep for AddEdgesToGraphStep { - type Error = Infallible; - impl_process_step_with_empty_processed!(); -} - -trait RunProcessStep: Sized { - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error>; -} - -impl RunProcessStep for ResetTarget { - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut AddNodesToGraphStep, - ) -> Result, Infallible> { - let target = ResetTargetInInstantiatedModule { - instantiated_module, - target: self, - }; - let Entry::Vacant(entry) = state.node_ids.entry(target) else { - return Ok(Processed(())); - }; - let ty = self.canonical_ty(); - let node_id = state.graph.add_node(Node { - target, - deduced_type: type_contains_any_undeduced_resets(ty).then_some(ty), - }); - entry.insert(node_id); - self.for_each_child(|target| -> Result<(), Infallible> { - target.run_process_step(instantiated_module, state, step)?; - Ok(()) - })?; - Ok(Processed(())) - } -} - -impl RunProcessStep for ResetTarget { - fn run_process_step( - self, - _instantiated_module: InstantiatedModule, - _state: &mut State, - _step: &mut AddEdgesToGraphStep, - ) -> Result, Infallible> { - Ok(Processed(())) - } -} - -impl RunProcessStep for Expr -where - ResetTarget: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let retval = ResetTarget::Base { base: self } - .run_process_step(instantiated_module, state, step)? - .map(|target| match target { - ResetTarget::Base { base } => base, - _ => unreachable!(), - }); - for_each_subexpr_exclusive(self, |base| { - ResetTarget::Base { base }.run_process_step(instantiated_module, state, step)?; - Ok(()) - })?; - Ok(retval) - } -} - -struct ConnectAndLhsInstantiatedModule { - lhs_instantiated_module: InstantiatedModule, - lhs: Expr, - rhs: Expr, - source_location: SourceLocation, -} - -impl RunProcessStep for ConnectAndLhsInstantiatedModule { - fn run_process_step( - self, - _instantiated_module: InstantiatedModule, - _state: &mut State, - _step: &mut AddNodesToGraphStep, - ) -> Result, Infallible> { - Ok(Processed(())) - } -} - -impl RunProcessStep for ConnectAndLhsInstantiatedModule { - fn run_process_step( - self, - rhs_instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut AddEdgesToGraphStep, - ) -> Result, Infallible> { - let Self { - lhs_instantiated_module, - lhs, - rhs, - source_location, - } = self; - match Expr::ty(lhs) { - CanonicalType::UInt(_) => {} - CanonicalType::SInt(_) => {} - CanonicalType::Bool(_) => {} - CanonicalType::AsyncReset(ty) => todo!(), - CanonicalType::SyncReset(ty) => todo!(), - CanonicalType::Reset(ty) => todo!(), - CanonicalType::Clock(_) => {} - CanonicalType::Array(ty) => todo!(), - CanonicalType::Enum(ty) => { - for (variant_index, EnumVariant { ty, .. }) in ty.variants().into_iter().enumerate() - { - if ty.is_none() { - continue; - } - let lhs = - ops::VariantAccess::new_by_index(Expr::from_canonical(lhs), variant_index) - .to_expr(); - let rhs = - ops::VariantAccess::new_by_index(Expr::from_canonical(rhs), variant_index) - .to_expr(); - ConnectAndLhsInstantiatedModule { - lhs_instantiated_module, - lhs, - rhs, - source_location, - } - .run_process_step(rhs_instantiated_module, state, step)?; - } +macro_rules! impl_run_pass_clone { + ([$($generics:tt)*] $ty:ty) => { + impl RunPass

for $ty { + fn run_pass( + &self, + _pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + Ok(PassOutput::from_fn(|| self.clone())) } - CanonicalType::Bundle(ty) => { - for (field_index, BundleField { flipped, .. }) in - ty.fields().into_iter().enumerate() - { - let mut lhs = - ops::FieldAccess::new_by_index(Expr::from_canonical(lhs), field_index) - .to_expr(); - let mut rhs = - ops::FieldAccess::new_by_index(Expr::from_canonical(rhs), field_index) - .to_expr(); - let mut lhs_instantiated_module = lhs_instantiated_module; - let mut rhs_instantiated_module = rhs_instantiated_module; - if flipped { - mem::swap(&mut lhs, &mut rhs); - mem::swap(&mut lhs_instantiated_module, &mut rhs_instantiated_module); - } - ConnectAndLhsInstantiatedModule { - lhs_instantiated_module, - lhs, - rhs, - source_location, - } - .run_process_step(rhs_instantiated_module, state, step)?; + } + }; +} + +impl_run_pass_copy!([] Interned); +impl_run_pass_copy!([] usize); +impl_run_pass_copy!([] bool); +impl_run_pass_clone!([] BigInt); + +macro_rules! impl_run_pass_for_struct { + ( + $(#[adjust_pass_args = $adjust_pass_args:expr])? + #[constructor = $constructor:expr] + impl[$($generics:tt)*] $RunPass:ident for $ty:ty { + $($field:ident $(($($args:tt)*))?: _,)* + } + ) => { + impl $RunPass

for $ty { + #[allow(unused_mut, unused_variables)] + fn run_pass( + &self, + mut pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + $($adjust_pass_args(self, &mut pass_args);)? + Ok(($(self.$field$(($($args)*))?.run_pass(pass_args.as_mut())?,)*).call(|($($field,)*)| $constructor)) + } + } + }; + ( + $(#[adjust_pass_args = $adjust_pass_args:expr])? + impl[$($generics:tt)*] $RunPass:ident for $ty:ty { + $($field:ident: _,)* + } + ) => { + impl_run_pass_for_struct! { + $(#[adjust_pass_args = $adjust_pass_args])? + #[constructor = { + type Ty = T; + Ty::<$ty> { $($field,)* } + }] + impl[$($generics)*] $RunPass for $ty { + $($field: _,)* + } + } + }; +} + +macro_rules! impl_run_pass_for_enum { + (impl[$($generics:tt)*] $RunPass:ident for $ty:ty { + $($variant:ident($arg:ident),)* + }) => { + impl $RunPass

for $ty { + fn run_pass( + &self, + pass_args: PassArgs<'_, P>, + ) -> Result, DeduceResetsError> { + type Ty = T; + match self { + $(Ty::<$ty>::$variant($arg) => Ok($arg.run_pass(pass_args)?.map(<$ty>::$variant)),)* } } } - Ok(Processed(())) + }; +} + +impl_run_pass_for_struct! { + impl[] RunPass for Block { + memories: _, + stmts: _, } } -impl RunProcessStep for StmtConnect -where - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let StmtConnect { - lhs, - rhs, - source_location, - } = self; - Ok(ConnectAndLhsInstantiatedModule { - lhs_instantiated_module: instantiated_module, - lhs, - rhs, - source_location, - } - .run_process_step(instantiated_module, state, step)? - .map( - |ConnectAndLhsInstantiatedModule { - lhs_instantiated_module: _, - lhs, - rhs, - source_location, - }| StmtConnect { - lhs, - rhs, - source_location, - }, - )) +impl_run_pass_for_struct! { + impl[] RunPass for NormalModuleBody { + body: _, } } -impl RunProcessStep for StmtFormal { - fn run_process_step( - self, - _instantiated_module: InstantiatedModule, - _state: &mut State, - _step: &mut Step, - ) -> Result, Step::Error> { - // no inputs are Reset - Ok(Processed::new(self)) +impl_run_pass_copy!([] ExternModuleBody); +impl_run_pass_copy!([] NameId); +impl_run_pass_copy!([] SourceLocation); +impl_run_pass_copy!([] DontTouchAnnotation); +impl_run_pass_copy!([] SVAttributeAnnotation); +impl_run_pass_copy!([] BlackBoxInlineAnnotation); +impl_run_pass_copy!([] BlackBoxPathAnnotation); +impl_run_pass_copy!([] DocStringAnnotation); +impl_run_pass_copy!([] CustomFirrtlAnnotation); +impl_run_pass_copy!([] MemPort); // Mem can't contain any `Reset` types +impl_run_pass_copy!([] Mem); // Mem can't contain any `Reset` types + +impl_run_pass_for_enum! { + impl[] RunPass for TargetBase { + ModuleIO(v), + MemPort(v), + Reg(v), + Wire(v), + Instance(v), } } -impl RunProcessStep for StmtIf { - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - todo!() +impl_run_pass_for_struct! { + impl[] RunPass for TargetPathBundleField { + name: _, } } -impl RunProcessStep for StmtMatch { - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - todo!() +impl_run_pass_for_struct! { + impl[] RunPass for TargetPathArrayElement { + index: _, } } -impl RunProcessStep for ModuleIO -where - ResetTarget: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - Ok(self - .to_expr() - .run_process_step(instantiated_module, state, step)? - .map(|expr| match *Expr::expr_enum(expr) { - ExprEnum::ModuleIO(module_io) => module_io, - _ => unreachable!(), - })) +impl_run_pass_for_struct! { + impl[] RunPass for TargetPathDynArrayElement {} +} + +impl_run_pass_for_enum! { + impl[] RunPass for TargetPathElement { + BundleField(v), + ArrayElement(v), + DynArrayElement(v), } } -impl RunProcessStep for StmtWire -where - ResetTarget: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let Self { annotations, wire } = self; - Ok(wire - .to_expr() - .run_process_step(instantiated_module, state, step)? - .map(|expr| match *Expr::expr_enum(expr) { - ExprEnum::Wire(wire) => Self { annotations, wire }, - _ => unreachable!(), - })) +impl_run_pass_for_enum! { + impl[] RunPass for Target { + Base(v), + Child(v), } } -impl RunProcessStep for StmtReg -where - ResetTarget: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let Self { annotations, reg } = self; - Ok(reg - .to_expr() - .run_process_step(instantiated_module, state, step)? - .map(|expr| match *Expr::expr_enum(expr) { - ExprEnum::Reg(reg) => Self { annotations, reg }, - _ => unreachable!(), - })) +impl_run_pass_for_struct! { + #[constructor = TargetChild::new(parent, path_element)] + impl[] RunPass for TargetChild { + parent(): _, + path_element(): _, } } -impl RunProcessStep for StmtInstance -where - ResetTarget: RunProcessStep, - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let Self { - annotations, - instance, - } = self; - let child_instantiated_module = InstantiatedModule::Child { - parent: instantiated_module.intern_sized(), - instance: instance.intern_sized(), - }; - instance - .instantiated() - .run_process_step(child_instantiated_module, state, step)?; - for (field_index, AnnotatedModuleIO { module_io, .. }) in - instance.instantiated().module_io().into_iter().enumerate() - { - let (lhs_instantiated_module, lhs, rhs_instantiated_module, rhs) = match module_io - .flow() - { - Flow::Source => { - // connect to submodule's input from instance input - ( - child_instantiated_module, - module_io.to_expr(), - instantiated_module, - ops::FieldAccess::new_by_index(instance.to_expr(), field_index).to_expr(), - ) - } - Flow::Sink => { - // connect to instance output from submodule's output - ( - instantiated_module, - ops::FieldAccess::new_by_index(instance.to_expr(), field_index).to_expr(), - child_instantiated_module, - module_io.to_expr(), - ) - } - Flow::Duplex => unreachable!(), - }; - ConnectAndLhsInstantiatedModule { - lhs_instantiated_module, - lhs, - rhs, - source_location: instance.source_location(), - } - .run_process_step(rhs_instantiated_module, state, step)?; - } - Ok(Expr::canonical(instance.to_expr()) - .run_process_step(instantiated_module, state, step)? - .map(|expr| match *Expr::expr_enum(expr) { - ExprEnum::Instance(instance) => Self { - annotations, - instance, - }, - _ => unreachable!(), - })) +impl_run_pass_for_enum! { + impl[] RunPass for Annotation { + DontTouch(v), + SVAttribute(v), + BlackBoxInline(v), + BlackBoxPath(v), + DocString(v), + CustomFirrtl(v), } } -impl RunProcessStep for StmtDeclaration -where - ResetTarget: RunProcessStep, - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - Ok(match self { - StmtDeclaration::Wire(decl) => decl - .run_process_step(instantiated_module, state, step)? - .map(StmtDeclaration::from), - StmtDeclaration::Reg(decl) => decl - .run_process_step(instantiated_module, state, step)? - .map(StmtDeclaration::from), - StmtDeclaration::Instance(decl) => decl - .run_process_step(instantiated_module, state, step)? - .map(StmtDeclaration::from), - }) +impl_run_pass_for_enum! { + impl[] RunPass for ModuleBody { + Normal(v), + Extern(v), } } -impl RunProcessStep for Stmt -where - ResetTarget: RunProcessStep, - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - Ok(match self { - Stmt::Connect(stmt) => stmt - .run_process_step(instantiated_module, state, step)? - .map(Stmt::from), - Stmt::Formal(stmt) => stmt - .run_process_step(instantiated_module, state, step)? - .map(Stmt::from), - Stmt::If(stmt) => stmt - .run_process_step(instantiated_module, state, step)? - .map(Stmt::from), - Stmt::Match(stmt) => stmt - .run_process_step(instantiated_module, state, step)? - .map(Stmt::from), - Stmt::Declaration(stmt) => stmt - .run_process_step(instantiated_module, state, step)? - .map(Stmt::from), - }) +impl_run_pass_for_struct! { + #[constructor = TargetedAnnotation::new(target, annotation)] + impl[] RunPass for TargetedAnnotation { + target(): _, + annotation(): _, } } -impl RunProcessStep for Block -where - ResetTarget: RunProcessStep, - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let Block { memories, stmts } = self; - // memories and memory ports won't ever contain any Reset values, - // so always just use the old `memories` value. we add the ports to the graph anyway to make the other logic easier. - for memory in memories { - for port in memory.ports() { - Expr::canonical(port.to_expr()).run_process_step( - instantiated_module, - state, - step, - )?; - } - } - let stmts = Result::, _>::from_iter( - stmts - .iter() - .map(|stmt| stmt.run_process_step(instantiated_module, state, step)), - )?; - Ok(stmts.map(|stmts| Block { memories, stmts })) +impl_run_pass_for_struct! { + impl[] RunPass for AnnotatedModuleIO { + annotations: _, + module_io: _, } } -impl RunProcessStep for Module -where - ResetTarget: RunProcessStep, - ConnectAndLhsInstantiatedModule: RunProcessStep, -{ - fn run_process_step( - self, - instantiated_module: InstantiatedModule, - state: &mut State, - step: &mut Step, - ) -> Result, Step::Error> { - let module = *instantiated_module.leaf_module(); - let module_io = - Result::, _>, _>::from_iter(module.module_io().iter().map( - |&AnnotatedModuleIO { - annotations, - module_io, - }| { - Ok(module_io - .run_process_step(instantiated_module, state, step)? - .map(|module_io| AnnotatedModuleIO { - annotations, - module_io, - })) - }, - ))?; - let body = match module.body() { - ModuleBody::Normal(NormalModuleBody { body }) => body - .run_process_step(instantiated_module, state, step)? - .map(|body| ModuleBody::Normal(NormalModuleBody { body })), - body @ ModuleBody::Extern(ExternModuleBody { - verilog_name: _, - parameters: _, - }) => Processed::new(body), - }; - Ok(module_io.zip(body).map(|(module_io, body)| { - Module::new_unchecked( - module.name_id(), - module.source_location(), - body, - module_io, - module.module_annotations(), - ) - })) +impl_run_pass_for_struct! { + #[adjust_pass_args = |module: &Module<_>, pass_args: &mut PassArgs<'_, _>| { + pass_args.fallback_error_source_location = module.source_location(); + }] + #[constructor = Module::new_unchecked( + name_id, + source_location, + body, + module_io, + module_annotations, + )] + impl[] RunPass for Module { + name_id(): _, + source_location(): _, + body(): _, + module_io(): _, + module_annotations(): _, } } @@ -816,21 +810,28 @@ pub fn deduce_resets( ) -> Result>, DeduceResetsError> { let mut state = State { base_module: module, - node_ids: HashMap::new(), - graph: UnGraph::new_undirected(), + expr_resets: HashMap::new(), + reset_graph: ResetGraph::default(), fallback_to_sync_reset, }; - let Ok(Processed(())) = module.run_process_step( - InstantiatedModule::Base(module), - &mut state, - &mut AddNodesToGraphStep, - ); - let mut step = AddEdgesToGraphStep { - union_find: UnionFind::new(state.graph.node_count()), - }; - let Ok(Processed(())) = - module.run_process_step(InstantiatedModule::Base(module), &mut state, &mut step); - let AddEdgesToGraphStep { union_find } = step; - todo!("deduce types"); - Ok(todo!("transform module")) + RunPass::::run_pass( + &*module, + PassArgs { + state: &mut state, + instantiated_module: InstantiatedModule::Base(module), + fallback_error_source_location: module.source_location(), + _phantom: PhantomData, + }, + )?; + Ok(RunPass::::run_pass( + &*module, + PassArgs { + state: &mut state, + instantiated_module: InstantiatedModule::Base(module), + fallback_error_source_location: module.source_location(), + _phantom: PhantomData, + }, + )? + .0 + .intern_sized()) }