From 4bd6db3de82be168237684f2d6b7dbdb00be0a7e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 10 Jun 2026 01:24:37 -0700 Subject: [PATCH] add deduce_structural_eq_flags transform --- crates/fayalite/src/array.rs | 12 +- crates/fayalite/src/build/graph.rs | 6 +- crates/fayalite/src/bundle.rs | 12 +- crates/fayalite/src/expr/target.rs | 2 +- crates/fayalite/src/module.rs | 6 +- crates/fayalite/src/module/transform.rs | 1 + .../transform/deduce_structural_eq_flags.rs | 1319 ++++++++++ .../src/module/transform/simplify_enums.rs | 7 +- crates/fayalite/src/util.rs | 7 +- crates/fayalite/src/util/indented_print.rs | 117 + crates/fayalite/src/util/map_trait.rs | 463 ++++ crates/fayalite/src/util/union_find_map.rs | 352 +++ .../tests/deduce_structural_eq_flags.rs | 2117 +++++++++++++++++ crates/fayalite/tests/module.rs | 34 +- 14 files changed, 4418 insertions(+), 37 deletions(-) create mode 100644 crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs create mode 100644 crates/fayalite/src/util/indented_print.rs create mode 100644 crates/fayalite/src/util/map_trait.rs create mode 100644 crates/fayalite/src/util/union_find_map.rs create mode 100644 crates/fayalite/tests/deduce_structural_eq_flags.rs diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 4e93093..95ecb8b 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -4,7 +4,7 @@ use crate::{ expr::{ CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless, - ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator}, + ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, StructuralEq}, }, int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType}, intern::{Intern, Interned, LazyInterned}, @@ -389,6 +389,11 @@ where } fn cmp_expr_eq(lhs: Expr, rhs: Expr>) -> Expr { assert_eq!(lhs.ty().len(), rhs.ty().len()); + if Self::TRY_STRUCTURAL_EQ { + if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) { + return retval.to_expr(); + } + } lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_eq(r)) @@ -398,6 +403,11 @@ where } fn cmp_expr_ne(lhs: Expr, rhs: Expr>) -> Expr { assert_eq!(lhs.ty().len(), rhs.ty().len()); + if Self::TRY_STRUCTURAL_EQ { + if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) { + return !retval.to_expr(); + } + } lhs.into_iter() .zip(rhs) .map(|(l, r)| l.cmp_ne(r)) diff --git a/crates/fayalite/src/build/graph.rs b/crates/fayalite/src/build/graph.rs index bed8829..e46f0a4 100644 --- a/crates/fayalite/src/build/graph.rs +++ b/crates/fayalite/src/build/graph.rs @@ -12,7 +12,7 @@ use crate::{ use eyre::{ContextCompat, eyre}; use petgraph::{ algo::{DfsSpace, kosaraju_scc, toposort}, - graph::DiGraph, + graph::{DiGraph, NodeIndex}, visit::{GraphBase, Visitable}, }; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error, ser::SerializeSeq}; @@ -465,7 +465,7 @@ impl JobGraph { } }) .expect("we know there's a cycle"); - let cycle_set = HashSet::from_iter(cycle.iter().copied()); + let cycle_set = HashSet::::from_iter(cycle.iter().copied()); let job = cycle .into_iter() .find_map(|node_id| { @@ -701,7 +701,7 @@ impl JobGraph { job: DynJob, thread: ScopedJoinHandle<'scope, eyre::Result>>, } - let mut running_jobs = HashMap::default(); + let mut running_jobs = HashMap::::default(); let (finished_jobs_sender, finished_jobs_receiver) = mpsc::channel(); let mut next_finished_job = None; loop { diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 70eb798..5a97944 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -5,7 +5,7 @@ use crate::{ expr::{ CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType, Valueless, - ops::{ArrayLiteral, BundleLiteral}, + ops::{ArrayLiteral, BundleLiteral, StructuralEq}, value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue}, }, int::{Bool, DynSize}, @@ -727,6 +727,11 @@ macro_rules! impl_tuples { #[track_caller] fn cmp_expr_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + if Self::TRY_STRUCTURAL_EQ { + if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) { + return retval.to_expr(); + } + } let ($($lhs_var,)*) = *lhs; let ($($rhs_var,)*) = *rhs; ArrayLiteral::::new( @@ -739,6 +744,11 @@ macro_rules! impl_tuples { #[track_caller] fn cmp_expr_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + if Self::TRY_STRUCTURAL_EQ { + if let Ok(retval) = StructuralEq::try_new(Expr::canonical(lhs), Expr::canonical(rhs)) { + return !retval.to_expr(); + } + } let ($($lhs_var,)*) = *lhs; let ($($rhs_var,)*) = *rhs; ArrayLiteral::::new( diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index d7775ec..84d3ace 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -63,7 +63,7 @@ pub struct TargetPathToTraceAsString { impl fmt::Display for TargetPathToTraceAsString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, ".to_trace_as_string(...)") + write!(f, ".to_trace_as_string()") } } diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 0968be6..e899fd7 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -2248,7 +2248,7 @@ impl Module { clocks_for_past, simulation: Some(simulation), }) => { - let mut clocks_for_past_set = HashSet::default(); + let mut clocks_for_past_set = HashSet::::default(); *clocks_for_past = clocks_for_past .iter() .copied() @@ -2269,7 +2269,9 @@ impl Module { } if simulation.sim_io_to_generator_map.len() > module_io.len() { // if sim_io_to_generator_map is bigger, then there must be a key that's not in module_io - let module_io_set = HashSet::from_iter(module_io.iter().map(|v| v.module_io)); + let module_io_set = HashSet::>::from_iter( + module_io.iter().map(|v| v.module_io), + ); for (sim_io, generator_io) in simulation.sim_io_to_generator_map.iter() { if !module_io_set.contains(&**sim_io) { panic!( diff --git a/crates/fayalite/src/module/transform.rs b/crates/fayalite/src/module/transform.rs index 063a1a3..7eec4cf 100644 --- a/crates/fayalite/src/module/transform.rs +++ b/crates/fayalite/src/module/transform.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information pub mod deduce_resets; +pub mod deduce_structural_eq_flags; pub mod simplify_enums; pub mod simplify_memories; pub mod visit; diff --git a/crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs b/crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs new file mode 100644 index 0000000..8d03e19 --- /dev/null +++ b/crates/fayalite/src/module/transform/deduce_structural_eq_flags.rs @@ -0,0 +1,1319 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::{ + bundle::BundleType, + enum_::EnumType, + expr::{ + ExprEnum, + ops::{ + ArrayIndex, FieldAccess, StructuralEq, StructuralEqFlags, TraceAsStringAsInner, + VariantAccess, + }, + target::TargetBase, + }, + intern::{Intern, InternSlice, Interned, Memoize}, + module::{ + ModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtInstance, StmtReg, StmtWire, + transform::visit::{Fold, Folder, Visit, Visitor}, + }, + prelude::*, + util::{ + HashMap, + indented_print::{PushIndent, indented_println}, + union_find_map::{Entry, UnionFindMap}, + }, +}; +use std::{convert::Infallible, fmt}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +enum FlagsTree { + Enum { + variants: Interned<[Option>]>, + /// invariant -- if this is true all children must also have [`FlagsTree::assume_padding_is_zeroed()`] return true + assume_padding_is_zeroed: bool, + }, + Bundle { + fields: Interned<[Interned]>, + /// invariant -- if this is true all children must also have [`FlagsTree::assume_padding_is_zeroed()`] return true + assume_padding_is_zeroed: bool, + }, + NoPadding, +} + +impl fmt::Debug for FlagsTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Enum { + variants, + assume_padding_is_zeroed, + } => f + .debug_struct("Enum") + .field( + "variants", + &fmt::from_fn(|f| { + f.debug_list() + .entries(variants.iter().map(|variant| { + fmt::from_fn(move |f| match variant { + Some(variant) => variant.fmt(f), + None => f.write_str("None"), + }) + })) + .finish() + }), + ) + .field("assume_padding_is_zeroed", assume_padding_is_zeroed) + .finish(), + Self::Bundle { + fields, + assume_padding_is_zeroed, + } => f + .debug_struct("Bundle") + .field("fields", fields) + .field("assume_padding_is_zeroed", assume_padding_is_zeroed) + .finish(), + Self::NoPadding => f.write_str("NoPadding"), + } + } +} + +impl FlagsTree { + fn contains_padding(&self) -> bool { + match self { + Self::NoPadding => false, + Self::Enum { .. } | Self::Bundle { .. } => true, + } + } + fn assume_padding_is_zeroed(&self) -> bool { + match *self { + Self::Enum { + assume_padding_is_zeroed, + .. + } + | Self::Bundle { + assume_padding_is_zeroed, + .. + } => assume_padding_is_zeroed, + Self::NoPadding => true, + } + } + fn new_inner(ty: CanonicalType, assume_padding_is_zeroed: bool) -> Interned { + match ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => Self::NoPadding.intern_sized(), + CanonicalType::Array(ty) => { + if ty.is_empty() { + Self::NoPadding.intern_sized() + } else { + Self::new(ty.element(), assume_padding_is_zeroed) + } + } + CanonicalType::Enum(ty) => { + let mut expected_bit_width = None; + let mut variants = Vec::with_capacity(ty.variants().len()); + let mut contains_padding = false; + for variant in ty.variants() { + let variant_flags_tree = + variant.ty.map(|ty| Self::new(ty, assume_padding_is_zeroed)); + variants.push(variant_flags_tree); + contains_padding |= variant_flags_tree.is_some_and(|v| v.contains_padding()); + let bit_width = if let Some(ty) = variant.ty { + ty.bit_width() + } else { + 0 + }; + if expected_bit_width + .replace(bit_width) + .is_some_and(|v| v != bit_width) + { + contains_padding = true; + } + } + if contains_padding { + Self::Enum { + variants: variants.intern_slice(), + assume_padding_is_zeroed, + } + .intern_sized() + } else { + Self::NoPadding.intern_sized() + } + } + CanonicalType::Bundle(ty) => { + let mut contains_padding = false; + let fields = Vec::from_iter(ty.fields().iter().map(|field| { + let flags_tree = Self::new(field.ty, assume_padding_is_zeroed); + contains_padding |= flags_tree.contains_padding(); + flags_tree + })); + if contains_padding { + Self::Bundle { + fields: fields.intern_slice(), + assume_padding_is_zeroed, + } + .intern_sized() + } else { + Self::NoPadding.intern_sized() + } + } + CanonicalType::TraceAsString(ty) => Self::new(ty.inner_ty(), assume_padding_is_zeroed), + } + } + fn new(ty: CanonicalType, assume_padding_is_zeroed: bool) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize { + assume_padding_is_zeroed: bool, + } + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + let Self { + assume_padding_is_zeroed, + } = self; + let retval = FlagsTree::new_inner(*input, assume_padding_is_zeroed); + retval + } + } + MyMemoize { + assume_padding_is_zeroed, + } + .get_owned(ty) + } + #[must_use] + fn merged(self, other: Interned) -> Interned { + if self == *other { + return other; + } + match (self, *other) { + ( + Self::Enum { + variants: l_variants, + assume_padding_is_zeroed: l_assume_padding_is_zeroed, + }, + Self::Enum { + variants: r_variants, + assume_padding_is_zeroed: r_assume_padding_is_zeroed, + }, + ) => { + let variants = Interned::from_iter(l_variants.iter().zip(&r_variants).map( + |(&l_variant, &r_variant)| { + l_variant + .zip(r_variant) + .map(|(l_variant, r_variant)| l_variant.merged(r_variant)) + }, + )); + let assume_padding_is_zeroed = + l_assume_padding_is_zeroed & r_assume_padding_is_zeroed; + Self::Enum { + variants, + assume_padding_is_zeroed, + } + .intern_sized() + } + (Self::Enum { .. }, _) => unreachable!("mismatched types"), + ( + Self::Bundle { + fields: l_fields, + assume_padding_is_zeroed: l_assume_padding_is_zeroed, + }, + Self::Bundle { + fields: r_fields, + assume_padding_is_zeroed: r_assume_padding_is_zeroed, + }, + ) => { + let fields = Interned::from_iter( + l_fields + .iter() + .zip(&r_fields) + .map(|(&l_field, &r_field)| l_field.merged(r_field)), + ); + let assume_padding_is_zeroed = + l_assume_padding_is_zeroed & r_assume_padding_is_zeroed; + Self::Bundle { + fields, + assume_padding_is_zeroed, + } + .intern_sized() + } + (Self::Bundle { .. }, _) => unreachable!("mismatched types"), + (Self::NoPadding, _) => { + unreachable!("NoPadding is always caught by the early return above") + } + } + } +} + +#[derive(Copy, Clone)] +enum ExprOrUnknown { + Expr(Expr), + Unknown(T), +} + +impl fmt::Debug for ExprOrUnknown { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Expr(expr) => expr.fmt(f), + Self::Unknown(ty) => f.debug_tuple("Unknown").field(ty).finish(), + } + } +} + +fn write_expr_path(expr: impl ToExpr, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write_expr_enum_path(&Expr::expr_enum(expr.to_expr()), f) +} + +fn write_expr_enum_path(expr: &ExprEnum, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_target_base( + target_base: impl Into, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(f, "{:?}", target_base.into().target_name()) + } + match expr { + ExprEnum::FieldAccess(expr) => { + write_expr_path(expr.base(), f)?; + write!(f, ".{}", expr.field_name()) + } + ExprEnum::VariantAccess(expr) => { + write_expr_path(expr.base(), f)?; + write!(f, ".<{}>", expr.variant_name()) + } + ExprEnum::ArrayIndex(expr) => { + write_expr_path(expr.base(), f)?; + write!(f, "[{}]", expr.element_index()) + } + ExprEnum::DynArrayIndex(expr) => { + write_expr_path(expr.base(), f)?; + write!(f, "[...]") + } + ExprEnum::ToTraceAsString(expr) => { + write_expr_path(expr.inner(), f)?; + write!(f, ".to_trace_as_string()") + } + ExprEnum::TraceAsStringAsInner(expr) => { + write_expr_path(expr.arg(), f)?; + write!(f, ".") + } + ExprEnum::ModuleIO(expr) => fmt_target_base(*expr, f), + ExprEnum::Instance(expr) => fmt_target_base(*expr, f), + ExprEnum::Wire(expr) => fmt_target_base(*expr, f), + ExprEnum::Reg(expr) => fmt_target_base(*expr, f), + ExprEnum::RegSync(expr) => fmt_target_base(*expr, f), + ExprEnum::RegAsync(expr) => fmt_target_base(*expr, f), + ExprEnum::MemPort(expr) => fmt_target_base(*expr, f), + ExprEnum::FormalInput(expr) => fmt_target_base(*expr, f), + + ExprEnum::UIntLiteral(_) + | ExprEnum::SIntLiteral(_) + | ExprEnum::BoolLiteral(_) + | ExprEnum::PhantomConst(_) + | ExprEnum::BundleLiteral(_) + | ExprEnum::ArrayLiteral(_) + | ExprEnum::EnumLiteral(_) + | ExprEnum::Uninit(_) + | ExprEnum::NotU(_) + | ExprEnum::NotS(_) + | ExprEnum::NotB(_) + | ExprEnum::Neg(_) + | ExprEnum::BitAndU(_) + | ExprEnum::BitAndS(_) + | ExprEnum::BitAndB(_) + | ExprEnum::BitOrU(_) + | ExprEnum::BitOrS(_) + | ExprEnum::BitOrB(_) + | ExprEnum::BitXorU(_) + | ExprEnum::BitXorS(_) + | ExprEnum::BitXorB(_) + | ExprEnum::AddU(_) + | ExprEnum::AddS(_) + | ExprEnum::SubU(_) + | ExprEnum::SubS(_) + | ExprEnum::MulU(_) + | ExprEnum::MulS(_) + | ExprEnum::DivU(_) + | ExprEnum::DivS(_) + | ExprEnum::RemU(_) + | ExprEnum::RemS(_) + | ExprEnum::DynShlU(_) + | ExprEnum::DynShlS(_) + | ExprEnum::DynShrU(_) + | ExprEnum::DynShrS(_) + | ExprEnum::FixedShlU(_) + | ExprEnum::FixedShlS(_) + | ExprEnum::FixedShrU(_) + | ExprEnum::FixedShrS(_) + | ExprEnum::CmpLtB(_) + | ExprEnum::CmpLeB(_) + | ExprEnum::CmpGtB(_) + | ExprEnum::CmpGeB(_) + | ExprEnum::CmpEqB(_) + | ExprEnum::CmpNeB(_) + | ExprEnum::CmpLtU(_) + | ExprEnum::CmpLeU(_) + | ExprEnum::CmpGtU(_) + | ExprEnum::CmpGeU(_) + | ExprEnum::CmpEqU(_) + | ExprEnum::CmpNeU(_) + | ExprEnum::CmpLtS(_) + | ExprEnum::CmpLeS(_) + | ExprEnum::CmpGtS(_) + | ExprEnum::CmpGeS(_) + | ExprEnum::CmpEqS(_) + | ExprEnum::CmpNeS(_) + | ExprEnum::CastUIntToUInt(_) + | ExprEnum::CastUIntToSInt(_) + | ExprEnum::CastSIntToUInt(_) + | ExprEnum::CastSIntToSInt(_) + | ExprEnum::CastBoolToUInt(_) + | ExprEnum::CastBoolToSInt(_) + | ExprEnum::CastUIntToBool(_) + | ExprEnum::CastSIntToBool(_) + | ExprEnum::CastBoolToSyncReset(_) + | ExprEnum::CastUIntToSyncReset(_) + | ExprEnum::CastSIntToSyncReset(_) + | ExprEnum::CastBoolToAsyncReset(_) + | ExprEnum::CastUIntToAsyncReset(_) + | ExprEnum::CastSIntToAsyncReset(_) + | ExprEnum::CastSyncResetToBool(_) + | ExprEnum::CastSyncResetToUInt(_) + | ExprEnum::CastSyncResetToSInt(_) + | ExprEnum::CastSyncResetToReset(_) + | ExprEnum::CastAsyncResetToBool(_) + | ExprEnum::CastAsyncResetToUInt(_) + | ExprEnum::CastAsyncResetToSInt(_) + | ExprEnum::CastAsyncResetToReset(_) + | ExprEnum::CastResetToBool(_) + | ExprEnum::CastResetToUInt(_) + | ExprEnum::CastResetToSInt(_) + | ExprEnum::CastBoolToClock(_) + | ExprEnum::CastUIntToClock(_) + | ExprEnum::CastSIntToClock(_) + | ExprEnum::CastClockToBool(_) + | ExprEnum::CastClockToUInt(_) + | ExprEnum::CastClockToSInt(_) + | ExprEnum::ReduceBitAndU(_) + | ExprEnum::ReduceBitAndS(_) + | ExprEnum::ReduceBitOrU(_) + | ExprEnum::ReduceBitOrS(_) + | ExprEnum::ReduceBitXorU(_) + | ExprEnum::ReduceBitXorS(_) + | ExprEnum::SliceUInt(_) + | ExprEnum::SliceSInt(_) + | ExprEnum::CastToBits(_) + | ExprEnum::CastBitsTo(_) + | ExprEnum::StructuralEq(_) + | ExprEnum::SimIoForGlobal(_) => write!(f, "..."), + } +} + +fn display_expr_enum_path(expr: ExprEnum) -> impl fmt::Display { + fmt::from_fn(move |f| write_expr_enum_path(&expr, f)) +} + +fn display_expr_path(expr: impl ToExpr) -> impl fmt::Display { + display_expr_enum_path(*Expr::expr_enum(expr.to_expr())) +} + +impl fmt::Display for ExprOrUnknown { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_target_base( + target_base: impl Into, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + write!(f, "{:?}", target_base.into().target_name()) + } + match *self { + Self::Expr(expr) => match *Expr::expr_enum(expr) { + ExprEnum::FieldAccess(expr) => { + ExprOrUnknown::Expr(expr.base()).fmt(f)?; + write!(f, ".{}", expr.field_name()) + } + ExprEnum::VariantAccess(expr) => { + ExprOrUnknown::Expr(expr.base()).fmt(f)?; + write!(f, ".<{}>", expr.variant_name()) + } + ExprEnum::ArrayIndex(expr) => { + ExprOrUnknown::Expr(expr.base()).fmt(f)?; + write!(f, "[{}]", expr.element_index()) + } + ExprEnum::DynArrayIndex(expr) => { + ExprOrUnknown::Expr(expr.base()).fmt(f)?; + write!(f, "[...]") + } + ExprEnum::ToTraceAsString(expr) => { + ExprOrUnknown::Expr(expr.inner()).fmt(f)?; + write!(f, ".to_trace_as_string()") + } + ExprEnum::TraceAsStringAsInner(expr) => { + ExprOrUnknown::Expr(expr.arg()).fmt(f)?; + write!(f, ".") + } + ExprEnum::ModuleIO(expr) => fmt_target_base(expr, f), + ExprEnum::Instance(expr) => fmt_target_base(expr, f), + ExprEnum::Wire(expr) => fmt_target_base(expr, f), + ExprEnum::Reg(expr) => fmt_target_base(expr, f), + ExprEnum::RegSync(expr) => fmt_target_base(expr, f), + ExprEnum::RegAsync(expr) => fmt_target_base(expr, f), + ExprEnum::MemPort(expr) => fmt_target_base(expr, f), + ExprEnum::FormalInput(expr) => fmt_target_base(expr, f), + + ExprEnum::UIntLiteral(_) + | ExprEnum::SIntLiteral(_) + | ExprEnum::BoolLiteral(_) + | ExprEnum::PhantomConst(_) + | ExprEnum::BundleLiteral(_) + | ExprEnum::ArrayLiteral(_) + | ExprEnum::EnumLiteral(_) + | ExprEnum::Uninit(_) + | ExprEnum::NotU(_) + | ExprEnum::NotS(_) + | ExprEnum::NotB(_) + | ExprEnum::Neg(_) + | ExprEnum::BitAndU(_) + | ExprEnum::BitAndS(_) + | ExprEnum::BitAndB(_) + | ExprEnum::BitOrU(_) + | ExprEnum::BitOrS(_) + | ExprEnum::BitOrB(_) + | ExprEnum::BitXorU(_) + | ExprEnum::BitXorS(_) + | ExprEnum::BitXorB(_) + | ExprEnum::AddU(_) + | ExprEnum::AddS(_) + | ExprEnum::SubU(_) + | ExprEnum::SubS(_) + | ExprEnum::MulU(_) + | ExprEnum::MulS(_) + | ExprEnum::DivU(_) + | ExprEnum::DivS(_) + | ExprEnum::RemU(_) + | ExprEnum::RemS(_) + | ExprEnum::DynShlU(_) + | ExprEnum::DynShlS(_) + | ExprEnum::DynShrU(_) + | ExprEnum::DynShrS(_) + | ExprEnum::FixedShlU(_) + | ExprEnum::FixedShlS(_) + | ExprEnum::FixedShrU(_) + | ExprEnum::FixedShrS(_) + | ExprEnum::CmpLtB(_) + | ExprEnum::CmpLeB(_) + | ExprEnum::CmpGtB(_) + | ExprEnum::CmpGeB(_) + | ExprEnum::CmpEqB(_) + | ExprEnum::CmpNeB(_) + | ExprEnum::CmpLtU(_) + | ExprEnum::CmpLeU(_) + | ExprEnum::CmpGtU(_) + | ExprEnum::CmpGeU(_) + | ExprEnum::CmpEqU(_) + | ExprEnum::CmpNeU(_) + | ExprEnum::CmpLtS(_) + | ExprEnum::CmpLeS(_) + | ExprEnum::CmpGtS(_) + | ExprEnum::CmpGeS(_) + | ExprEnum::CmpEqS(_) + | ExprEnum::CmpNeS(_) + | ExprEnum::CastUIntToUInt(_) + | ExprEnum::CastUIntToSInt(_) + | ExprEnum::CastSIntToUInt(_) + | ExprEnum::CastSIntToSInt(_) + | ExprEnum::CastBoolToUInt(_) + | ExprEnum::CastBoolToSInt(_) + | ExprEnum::CastUIntToBool(_) + | ExprEnum::CastSIntToBool(_) + | ExprEnum::CastBoolToSyncReset(_) + | ExprEnum::CastUIntToSyncReset(_) + | ExprEnum::CastSIntToSyncReset(_) + | ExprEnum::CastBoolToAsyncReset(_) + | ExprEnum::CastUIntToAsyncReset(_) + | ExprEnum::CastSIntToAsyncReset(_) + | ExprEnum::CastSyncResetToBool(_) + | ExprEnum::CastSyncResetToUInt(_) + | ExprEnum::CastSyncResetToSInt(_) + | ExprEnum::CastSyncResetToReset(_) + | ExprEnum::CastAsyncResetToBool(_) + | ExprEnum::CastAsyncResetToUInt(_) + | ExprEnum::CastAsyncResetToSInt(_) + | ExprEnum::CastAsyncResetToReset(_) + | ExprEnum::CastResetToBool(_) + | ExprEnum::CastResetToUInt(_) + | ExprEnum::CastResetToSInt(_) + | ExprEnum::CastBoolToClock(_) + | ExprEnum::CastUIntToClock(_) + | ExprEnum::CastSIntToClock(_) + | ExprEnum::CastClockToBool(_) + | ExprEnum::CastClockToUInt(_) + | ExprEnum::CastClockToSInt(_) + | ExprEnum::ReduceBitAndU(_) + | ExprEnum::ReduceBitAndS(_) + | ExprEnum::ReduceBitOrU(_) + | ExprEnum::ReduceBitOrS(_) + | ExprEnum::ReduceBitXorU(_) + | ExprEnum::ReduceBitXorS(_) + | ExprEnum::SliceUInt(_) + | ExprEnum::SliceSInt(_) + | ExprEnum::CastToBits(_) + | ExprEnum::CastBitsTo(_) + | ExprEnum::StructuralEq(_) + | ExprEnum::SimIoForGlobal(_) => write!(f, "..."), + }, + Self::Unknown(_ty) => write!(f, "Unknown(...)"), + } + } +} + +impl From> for ExprOrUnknown { + fn from(expr: Expr) -> Self { + Self::Expr(expr) + } +} + +impl ExprOrUnknown { + fn ty(self) -> T { + match self { + Self::Expr(expr) => expr.ty(), + Self::Unknown(ty) => ty, + } + } + fn map( + self, + map_ty: impl FnOnce(T) -> U, + map_expr: impl FnOnce(Expr) -> Expr, + ) -> ExprOrUnknown { + match self { + Self::Expr(expr) => ExprOrUnknown::Expr(map_expr(expr)), + Self::Unknown(ty) => ExprOrUnknown::Unknown(map_ty(ty)), + } + } + fn map_unwrap(self, map_ty: impl FnOnce(T) -> U, map_expr: impl FnOnce(Expr) -> U) -> U { + match self { + Self::Expr(expr) => map_expr(expr), + Self::Unknown(ty) => map_ty(ty), + } + } + fn from_canonical(v: ExprOrUnknown) -> Self { + match v { + ExprOrUnknown::Expr(expr) => Self::Expr(Expr::from_canonical(expr)), + ExprOrUnknown::Unknown(ty) => Self::Unknown(T::from_canonical(ty)), + } + } +} + +impl ExprOrUnknown { + fn field_at_index(self, field_index: usize) -> ExprOrUnknown { + self.map( + |ty| ty.fields()[field_index].ty, + |expr| FieldAccess::new_by_index(expr, field_index).to_expr(), + ) + } +} + +impl ExprOrUnknown { + fn variant_at_index(self, variant_index: usize) -> Option> { + match self { + Self::Expr(expr) => { + expr.ty().variants()[variant_index].ty?; + Some(ExprOrUnknown::Expr( + VariantAccess::new_by_index(expr, variant_index).to_expr(), + )) + } + Self::Unknown(ty) => Some(ExprOrUnknown::Unknown(ty.variants()[variant_index].ty?)), + } + } +} + +impl ExprOrUnknown { + fn element(self, element_index: usize) -> ExprOrUnknown { + match self { + Self::Expr(expr) => ExprOrUnknown::Expr(ArrayIndex::new(expr, element_index).to_expr()), + Self::Unknown(ty) => ExprOrUnknown::Unknown(ty.element()), + } + } +} + +impl ExprOrUnknown { + fn inner(self) -> ExprOrUnknown { + match self { + Self::Expr(expr) => ExprOrUnknown::Expr(TraceAsStringAsInner::new(expr).to_expr()), + Self::Unknown(ty) => ExprOrUnknown::Unknown(ty.inner_ty()), + } + } +} + +#[derive(Debug)] +struct State { + root_module: Interned>, + debug_trace: bool, + any_changes: bool, + expr_flags: UnionFindMap, Interned>, + exprs_visited: HashMap, bool>, + modules_visited: HashMap>, bool>, +} + +impl State { + fn new(root_module: Interned>, debug_trace: bool) -> Self { + Self { + root_module, + debug_trace, + any_changes: false, + expr_flags: UnionFindMap::default(), + exprs_visited: HashMap::default(), + modules_visited: HashMap::default(), + } + } + fn merge_expr_flags( + &mut self, + expr: Interned, + new_flags: FlagsTree, + ) -> Interned { + match self.expr_flags.entry(expr) { + Entry::Vacant(entry) => { + let new_flags = new_flags.intern_sized(); + entry.insert(new_flags); + self.any_changes = true; + new_flags + } + Entry::Occupied(mut entry) => { + let merged_flags = new_flags.merged(*entry.get()); + if merged_flags != *entry.get() { + if self.debug_trace { + indented_println!( + "merge_expr_flags({}):\n\ + old flags: {:#?}\n\ + new_flags: {new_flags:#?}\n\ + merged_flags: {merged_flags:#?}", + display_expr_enum_path(*expr), + entry.get(), + ); + } + self.any_changes = true; + } else if self.debug_trace { + if *merged_flags != new_flags { + indented_println!( + "merge_expr_flags({}):\n\ + flags (unchanged): {:#?}\n\ + new_flags: {new_flags:#?}", + display_expr_enum_path(*expr), + entry.get(), + ); + } else { + indented_println!( + "merge_expr_flags({}):\n\ + flags (unchanged): {:#?}", + display_expr_enum_path(*expr), + entry.get(), + ); + } + } + entry.insert(merged_flags); + merged_flags + } + } + } + fn union_exprs( + &mut self, + expr1: Interned, + expr2: Interned, + ) -> Interned { + let (unioned, value) = self + .expr_flags + .union(&expr1, &expr2, |_, v1, _, v2| v1.merged(v2)); + self.any_changes |= unioned; + *value + } + fn visit_expr_or_unknown(&mut self, expr: ExprOrUnknown) -> Interned { + match expr { + ExprOrUnknown::Expr(expr) => self.visit_canonical_expr(expr), + ExprOrUnknown::Unknown(ty) => FlagsTree::new(ty, false), + } + } + fn connect( + &mut self, + lhs: impl Into>, + rhs: impl Into>, + ) -> (Interned, Interned) { + let lhs = lhs.into(); + let rhs = rhs.into(); + let _push_indent; + if self.debug_trace { + indented_println!("connect({lhs}, {rhs}):"); + _push_indent = PushIndent::new(); + indented_println!("lhs: {lhs:?}"); + indented_println!("rhs: {rhs:?}"); + } + let lhs_ty = lhs.ty(); + let lhs_flags = self.visit_expr_or_unknown(lhs); + let rhs_flags = self.visit_expr_or_unknown(rhs); + if lhs_flags == rhs_flags { + return (lhs_flags, rhs_flags); + } + let (mut lhs_flags, mut rhs_flags) = match lhs_ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => { + unreachable!("flags are always FlagsTree::NoPadding") + } + CanonicalType::Array(lhs_ty) => { + let lhs = ExprOrUnknown::from_canonical(lhs); + let rhs = ExprOrUnknown::::from_canonical(rhs); + assert_eq!(lhs_ty.len(), rhs.ty().len()); + assert!(lhs_ty.len() > 0); + // FlagsTree treats arrays transparently, so the returned flags don't need to be adjusted. + // All array indexing operations are unioned together so just arbitrarily use index 0 + self.connect(lhs.element(0), rhs.element(0)) + } + CanonicalType::Enum(lhs_ty) => { + let lhs = ExprOrUnknown::from_canonical(lhs); + let rhs = ExprOrUnknown::::from_canonical(rhs); + assert_eq!(lhs_ty.variants().len(), rhs.ty().variants().len()); + let mut lhs_assume_padding_is_zeroed = + lhs_flags.assume_padding_is_zeroed() & rhs_flags.assume_padding_is_zeroed(); + let mut lhs_variants = Vec::with_capacity(lhs_ty.variants().len()); + for variant_index in 0..lhs_ty.variants().len() { + let lhs_variant = lhs.variant_at_index(variant_index); + let rhs_variant = rhs.variant_at_index(variant_index); + assert_eq!(lhs_variant.is_some(), rhs_variant.is_some()); + if let Some(lhs_variant) = lhs_variant + && let Some(rhs_variant) = rhs_variant + { + let (lhs_field_flags, _rhs_field_flags) = + self.connect(lhs_variant, rhs_variant); + lhs_variants.push(Some(lhs_field_flags)); + lhs_assume_padding_is_zeroed &= lhs_field_flags.assume_padding_is_zeroed(); + } else { + lhs_variants.push(None); + } + } + let lhs_flags = FlagsTree::Enum { + variants: lhs_variants.intern_slice(), + assume_padding_is_zeroed: lhs_assume_padding_is_zeroed, + } + .intern_sized(); + (lhs_flags, rhs_flags) + } + CanonicalType::Bundle(lhs_ty) => { + let lhs = ExprOrUnknown::from_canonical(lhs); + let rhs = ExprOrUnknown::::from_canonical(rhs); + assert_eq!(lhs_ty.fields().len(), rhs.ty().fields().len()); + let mut lhs_assume_padding_is_zeroed = lhs_flags.assume_padding_is_zeroed(); + let mut rhs_assume_padding_is_zeroed = rhs_flags.assume_padding_is_zeroed(); + let mut lhs_fields = Vec::with_capacity(lhs_ty.fields().len()); + let mut rhs_fields = Vec::with_capacity(lhs_ty.fields().len()); + for (field_index, (lhs_field, rhs_field)) in lhs_ty + .fields() + .into_iter() + .zip(rhs.ty().fields()) + .enumerate() + { + assert_eq!(lhs_field.flipped, rhs_field.flipped); + let lhs_field_flags; + let rhs_field_flags; + if lhs_field.flipped { + // flipped, so exchange lhs/rhs when recursively calling connect + (rhs_field_flags, lhs_field_flags) = self.connect( + rhs.field_at_index(field_index), + lhs.field_at_index(field_index), + ); + } else { + (lhs_field_flags, rhs_field_flags) = self.connect( + lhs.field_at_index(field_index), + rhs.field_at_index(field_index), + ); + } + lhs_fields.push(lhs_field_flags); + rhs_fields.push(rhs_field_flags); + lhs_assume_padding_is_zeroed &= lhs_field_flags.assume_padding_is_zeroed(); + rhs_assume_padding_is_zeroed &= rhs_field_flags.assume_padding_is_zeroed(); + } + let lhs_flags = FlagsTree::Bundle { + fields: lhs_fields.intern_slice(), + assume_padding_is_zeroed: lhs_assume_padding_is_zeroed, + } + .intern_sized(); + let rhs_flags = FlagsTree::Bundle { + fields: rhs_fields.intern_slice(), + assume_padding_is_zeroed: rhs_assume_padding_is_zeroed, + } + .intern_sized(); + (lhs_flags, rhs_flags) + } + CanonicalType::TraceAsString(_) => { + let lhs = ExprOrUnknown::from_canonical(lhs); + let rhs = ExprOrUnknown::from_canonical(rhs); + // FlagsTree treats TraceAsString transparently, so the returned flags don't need to be adjusted. + // this expression and the inner expression are unioned together + self.connect(lhs.inner(), rhs.inner()) + } + }; + if let ExprOrUnknown::Expr(lhs) = lhs { + lhs_flags = self.merge_expr_flags(Expr::expr_enum(lhs), *lhs_flags); + } + if let ExprOrUnknown::Expr(rhs) = rhs { + rhs_flags = self.merge_expr_flags(Expr::expr_enum(rhs), *rhs_flags); + } + let retval = (lhs_flags, rhs_flags); + if self.debug_trace { + indented_println!("returned: {retval:#?}"); + } + retval + } + fn visit_canonical_expr(&mut self, expr: Expr) -> Interned { + let expr_enum = Expr::expr_enum(expr); + let ty = expr.ty(); + let visited = self.exprs_visited.entry(expr_enum).or_insert(false); + let flags = *self.expr_flags.entry(expr_enum).or_insert_with(|| { + self.any_changes = true; + FlagsTree::new(ty, true) + }); + if std::mem::replace(visited, true) { + return flags; + } + let handle_array_index = |this: &mut Self, base: Expr| -> FlagsTree { + this.visit_canonical_expr(Expr::canonical(base)); + // FlagsTree treats arrays transparently, so just union them together. + *this.union_exprs(Expr::expr_enum(base), expr_enum) + }; + let handle_reg = |this: &mut Self, init: Option>| -> FlagsTree { + let init = match init { + Some(init) => ExprOrUnknown::Expr(init), + None => ExprOrUnknown::Unknown(ty), + }; + let (flags, _) = this.connect(ExprOrUnknown::Expr(expr), init); + *flags + }; + let flags = match *expr_enum { + ExprEnum::UIntLiteral(_) + | ExprEnum::SIntLiteral(_) + | ExprEnum::BoolLiteral(_) + | ExprEnum::PhantomConst(_) => *flags, + ExprEnum::BundleLiteral(bundle_literal) => { + let expr = Expr::::from_canonical(expr); + let ty = bundle_literal.ty(); + let mut assume_padding_is_zeroed = flags.assume_padding_is_zeroed(); + let mut fields = Vec::with_capacity(ty.fields().len()); + for (field_index, field) in ty.fields().into_iter().enumerate() { + assert!(!field.flipped); + let (field_flags, _) = self.connect( + FieldAccess::new_by_index(expr, field_index).to_expr(), + bundle_literal.field_values()[field_index], + ); + fields.push(field_flags); + assume_padding_is_zeroed &= field_flags.assume_padding_is_zeroed(); + } + if let FlagsTree::NoPadding = *flags { + *flags + } else { + FlagsTree::Bundle { + fields: fields.intern_slice(), + assume_padding_is_zeroed, + } + } + } + ExprEnum::ArrayLiteral(array_literal) => { + let expr = Expr::::from_canonical(expr); + for (element_index, element_value) in + array_literal.element_values().into_iter().enumerate() + { + let array_index = ArrayIndex::new(expr, element_index).to_expr(); + self.visit_canonical_expr(array_index); + self.union_exprs(expr_enum, Expr::expr_enum(array_index)); + self.connect(array_index, element_value); + } + *flags + } + ExprEnum::EnumLiteral(enum_literal) => { + let expr = Expr::::from_canonical(expr); + let variant_access = + VariantAccess::new_by_index(expr, enum_literal.variant_index()).to_expr(); + self.visit_canonical_expr(variant_access); + if let Some(variant_value) = enum_literal.variant_value() { + self.connect(variant_access, variant_value); + } + *flags + } + ExprEnum::Uninit(_) => *FlagsTree::new(ty, false), + ExprEnum::NotU(_) + | ExprEnum::NotS(_) + | ExprEnum::NotB(_) + | ExprEnum::Neg(_) + | ExprEnum::BitAndU(_) + | ExprEnum::BitAndS(_) + | ExprEnum::BitAndB(_) + | ExprEnum::BitOrU(_) + | ExprEnum::BitOrS(_) + | ExprEnum::BitOrB(_) + | ExprEnum::BitXorU(_) + | ExprEnum::BitXorS(_) + | ExprEnum::BitXorB(_) + | ExprEnum::AddU(_) + | ExprEnum::AddS(_) + | ExprEnum::SubU(_) + | ExprEnum::SubS(_) + | ExprEnum::MulU(_) + | ExprEnum::MulS(_) + | ExprEnum::DivU(_) + | ExprEnum::DivS(_) + | ExprEnum::RemU(_) + | ExprEnum::RemS(_) + | ExprEnum::DynShlU(_) + | ExprEnum::DynShlS(_) + | ExprEnum::DynShrU(_) + | ExprEnum::DynShrS(_) + | ExprEnum::FixedShlU(_) + | ExprEnum::FixedShlS(_) + | ExprEnum::FixedShrU(_) + | ExprEnum::FixedShrS(_) + | ExprEnum::CmpLtB(_) + | ExprEnum::CmpLeB(_) + | ExprEnum::CmpGtB(_) + | ExprEnum::CmpGeB(_) + | ExprEnum::CmpEqB(_) + | ExprEnum::CmpNeB(_) + | ExprEnum::CmpLtU(_) + | ExprEnum::CmpLeU(_) + | ExprEnum::CmpGtU(_) + | ExprEnum::CmpGeU(_) + | ExprEnum::CmpEqU(_) + | ExprEnum::CmpNeU(_) + | ExprEnum::CmpLtS(_) + | ExprEnum::CmpLeS(_) + | ExprEnum::CmpGtS(_) + | ExprEnum::CmpGeS(_) + | ExprEnum::CmpEqS(_) + | ExprEnum::CmpNeS(_) + | ExprEnum::CastUIntToUInt(_) + | ExprEnum::CastUIntToSInt(_) + | ExprEnum::CastSIntToUInt(_) + | ExprEnum::CastSIntToSInt(_) + | ExprEnum::CastBoolToUInt(_) + | ExprEnum::CastBoolToSInt(_) + | ExprEnum::CastUIntToBool(_) + | ExprEnum::CastSIntToBool(_) + | ExprEnum::CastBoolToSyncReset(_) + | ExprEnum::CastUIntToSyncReset(_) + | ExprEnum::CastSIntToSyncReset(_) + | ExprEnum::CastBoolToAsyncReset(_) + | ExprEnum::CastUIntToAsyncReset(_) + | ExprEnum::CastSIntToAsyncReset(_) + | ExprEnum::CastSyncResetToBool(_) + | ExprEnum::CastSyncResetToUInt(_) + | ExprEnum::CastSyncResetToSInt(_) + | ExprEnum::CastSyncResetToReset(_) + | ExprEnum::CastAsyncResetToBool(_) + | ExprEnum::CastAsyncResetToUInt(_) + | ExprEnum::CastAsyncResetToSInt(_) + | ExprEnum::CastAsyncResetToReset(_) + | ExprEnum::CastResetToBool(_) + | ExprEnum::CastResetToUInt(_) + | ExprEnum::CastResetToSInt(_) + | ExprEnum::CastBoolToClock(_) + | ExprEnum::CastUIntToClock(_) + | ExprEnum::CastSIntToClock(_) + | ExprEnum::CastClockToBool(_) + | ExprEnum::CastClockToUInt(_) + | ExprEnum::CastClockToSInt(_) => *flags, + ExprEnum::FieldAccess(field_access) => { + let base_flags = self.visit_canonical_expr(Expr::canonical(field_access.base())); + match *base_flags { + FlagsTree::Enum { .. } => unreachable!(), + FlagsTree::Bundle { + fields, + assume_padding_is_zeroed, + } => { + let field_flags = fields[field_access.field_index()]; + let flags = field_flags.merged(flags); + if flags != field_flags { + self.merge_expr_flags( + Expr::expr_enum(field_access.base()), + FlagsTree::Bundle { + fields: fields + .iter() + .enumerate() + .map(|(i, field)| { + if i == field_access.field_index() { + flags + } else { + *field + } + }) + .collect(), + assume_padding_is_zeroed: assume_padding_is_zeroed + & flags.assume_padding_is_zeroed(), + }, + ); + } + *flags + } + FlagsTree::NoPadding => *flags, + } + } + ExprEnum::VariantAccess(variant_access) => { + let base_flags = self.visit_canonical_expr(Expr::canonical(variant_access.base())); + match *base_flags { + FlagsTree::Enum { + variants, + assume_padding_is_zeroed, + } => { + if let Some(variant_flags) = variants[variant_access.variant_index()] { + let flags = variant_flags.merged(flags); + if flags != variant_flags { + self.merge_expr_flags( + Expr::expr_enum(variant_access.base()), + FlagsTree::Enum { + variants: variants + .iter() + .enumerate() + .map(|(i, field)| { + if i == variant_access.variant_index() { + Some(flags) + } else { + *field + } + }) + .collect(), + assume_padding_is_zeroed: assume_padding_is_zeroed + & flags.assume_padding_is_zeroed(), + }, + ); + } + *flags + } else { + *flags + } + } + FlagsTree::Bundle { .. } => unreachable!(), + FlagsTree::NoPadding => *flags, + } + } + ExprEnum::ArrayIndex(expr) => handle_array_index(self, expr.base()), + ExprEnum::DynArrayIndex(expr) => handle_array_index(self, expr.base()), + ExprEnum::ReduceBitAndU(_) + | ExprEnum::ReduceBitAndS(_) + | ExprEnum::ReduceBitOrU(_) + | ExprEnum::ReduceBitOrS(_) + | ExprEnum::ReduceBitXorU(_) + | ExprEnum::ReduceBitXorS(_) + | ExprEnum::SliceUInt(_) + | ExprEnum::SliceSInt(_) + | ExprEnum::CastToBits(_) => *flags, + ExprEnum::CastBitsTo(_) => *FlagsTree::new(ty, false), + ExprEnum::ToTraceAsString(expr) => { + self.visit_canonical_expr(Expr::canonical(expr.inner())); + // FlagsTree treats TraceAsString transparently, so just union them together. + *self.union_exprs(Expr::expr_enum(expr.inner()), expr_enum) + } + ExprEnum::TraceAsStringAsInner(expr) => { + self.visit_canonical_expr(Expr::canonical(expr.arg())); + // FlagsTree treats TraceAsString transparently, so just union them together. + *self.union_exprs(Expr::expr_enum(expr.arg()), expr_enum) + } + ExprEnum::StructuralEq(_) => *flags, + ExprEnum::ModuleIO(_) => *flags, + ExprEnum::Instance(instance) => { + let expr = Expr::::from_canonical(expr); + for (field_index, module_io) in + instance.instantiated().module_io().into_iter().enumerate() + { + let module_io = module_io.module_io.to_expr(); + let field_access = FieldAccess::new_by_index(expr, field_index).to_expr(); + self.visit_canonical_expr(module_io); + self.visit_canonical_expr(field_access); + self.union_exprs(Expr::expr_enum(field_access), Expr::expr_enum(module_io)); + } + *flags + } + ExprEnum::Wire(_) => *flags, + ExprEnum::Reg(reg) => handle_reg(self, reg.init()), + ExprEnum::RegSync(reg) => handle_reg(self, reg.init()), + ExprEnum::RegAsync(reg) => handle_reg(self, reg.init()), + ExprEnum::MemPort(_) => *FlagsTree::new(ty, false), + ExprEnum::FormalInput(_) => *FlagsTree::new(ty, false), + ExprEnum::SimIoForGlobal(_) => { + unreachable!("Module is known to not contain SimIoForGlobal from validation") + } + }; + self.merge_expr_flags(expr_enum, flags); + let Ok(()) = expr_enum.default_visit(self); + *self.expr_flags.find_mut(&expr_enum) + } +} + +impl Visitor for State { + type Error = Infallible; + + fn visit_expr_enum(&mut self, expr_enum: &ExprEnum) -> Result<(), Self::Error> { + self.visit_canonical_expr(expr_enum.to_expr()); + Ok(()) + } + + fn visit_stmt_declaration(&mut self, stmt: &StmtDeclaration) -> Result<(), Self::Error> { + match stmt { + StmtDeclaration::Wire(StmtWire { + annotations: _, + wire, + }) => self.visit_canonical_expr(wire.to_expr()), + StmtDeclaration::Reg(StmtReg { + annotations: _, + reg, + }) => self.visit_canonical_expr(reg.to_expr()), + StmtDeclaration::RegSync(StmtReg { + annotations: _, + reg, + }) => self.visit_canonical_expr(reg.to_expr()), + StmtDeclaration::RegAsync(StmtReg { + annotations: _, + reg, + }) => self.visit_canonical_expr(reg.to_expr()), + StmtDeclaration::Instance(StmtInstance { + annotations: _, + instance, + }) => self.visit_canonical_expr(Expr::canonical(instance.to_expr())), + }; + Ok(()) + } + + fn visit_stmt(&mut self, stmt: &Stmt) -> Result<(), Self::Error> { + match stmt { + &Stmt::Connect(StmtConnect { + lhs, + rhs, + source_location: _, + }) => { + self.connect(lhs, rhs); + } + Stmt::Formal(_) | Stmt::If(_) | Stmt::Match(_) => stmt.default_visit(self)?, + Stmt::Declaration(stmt) => self.visit_stmt_declaration(stmt)?, + } + Ok(()) + } + + fn visit_module(&mut self, module: &Module) -> Result<(), Self::Error> { + let module = module.canonical().intern_sized(); + let visited = self.modules_visited.entry(module).or_insert(false); + if std::mem::replace(visited, true) { + return Ok(()); + } + let external = match module.body() { + ModuleBody::Normal(_) => false, + ModuleBody::Extern(_) => true, + }; + let is_root_module = self.root_module == module; + if external || is_root_module { + for module_io in module.module_io() { + let expr = module_io.module_io.to_expr(); + let mut connect_unknown = |is_lhs| { + if is_lhs { + self.connect(expr, ExprOrUnknown::Unknown(expr.ty())); + } else { + self.connect(ExprOrUnknown::Unknown(expr.ty()), expr); + } + }; + // all main/external module I/O can have non-zeroed padding + if external { + // outputs are unknown + connect_unknown(module_io.module_io.is_output()); + } + // note: the root module can be external, so we don't use `else if` here + if is_root_module { + // inputs are unknown + connect_unknown(module_io.module_io.is_input()); + } + } + } + module.default_visit(self) + } +} + +impl Folder for State { + type Error = Infallible; + fn fold_structural_eq(&mut self, v: StructuralEq) -> Result { + let lhs_flags = *self.expr_flags.find_mut(&Expr::expr_enum(v.lhs())); + let rhs_flags = *self.expr_flags.find_mut(&Expr::expr_enum(v.rhs())); + let inputs_assume_padding_is_zeroed = + lhs_flags.assume_padding_is_zeroed() & rhs_flags.assume_padding_is_zeroed(); + let new_lhs = v.lhs().fold(self)?; + let new_rhs = v.rhs().fold(self)?; + let StructuralEqFlags { + assume_padding_is_zeroed: orig_assume_padding_is_zeroed, + } = v.flags(); + Ok(StructuralEq::with_flags( + new_lhs, + new_rhs, + StructuralEqFlags { + assume_padding_is_zeroed: inputs_assume_padding_is_zeroed + | orig_assume_padding_is_zeroed, + }, + )) + } +} + +pub fn deduce_structural_eq_flags(module: Interned>) -> Interned> { + deduce_structural_eq_flags_with_debug_tracing(module, false) +} + +pub fn deduce_structural_eq_flags_with_debug_tracing( + module: Interned>, + debug_trace: bool, +) -> Interned> { + static CACHE: std::sync::Mutex< + Option>, Interned>>>, + > = std::sync::Mutex::new(None); + let cache = CACHE.lock().expect("not poisoned"); + if let Some(retval) = cache.as_ref().and_then(|cache| cache.get(&module)) { + return *retval; + } + drop(cache); + // the algorithm proceeds in two stages: + // 1. Visitor for State: a fixed-point data-flow algorithm to determine what flags should be + // 2. Folder for State: transforming the StructuralEq operations to have the deduced flags + let mut state = State::new(module, debug_trace); + let mut loops = 0; + const LOOP_LIMIT: u32 = 10000; + loop { + loops += 1; + let Ok(()) = module.visit(&mut state); + if loops > LOOP_LIMIT { + panic!("deduce_structural_eq_flags: looped too many times! state:\n{state:#?}"); + } + let State { + root_module: _, + debug_trace: _, + any_changes, + expr_flags: _, + exprs_visited: expr_visited, + modules_visited: module_visited, + } = &mut state; + if !std::mem::replace(any_changes, false) { + break; + } + expr_visited.values_mut().for_each(|v| *v = false); + module_visited.values_mut().for_each(|v| *v = false); + } + if debug_trace { + indented_println!("{state:#?}"); + } + let Ok(retval) = module.fold(&mut state); + CACHE + .lock() + .expect("not poisoned") + .get_or_insert_default() + .extend([(module, retval), (retval, retval)]); + retval +} diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 67b02b0..6c137ca 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -11,7 +11,10 @@ use crate::{ memory::{DynPortType, MemPort}, module::{ Block, Id, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, - transform::visit::{Fold, Folder}, + transform::{ + deduce_structural_eq_flags::deduce_structural_eq_flags, + visit::{Fold, Folder}, + }, }, prelude::*, util::HashMap, @@ -1262,7 +1265,7 @@ pub fn simplify_enums( module: Interned>, kind: SimplifyEnumsKind, ) -> Result>, SimplifyEnumsError> { - // TODO: deduce StructuralEq's assume_padding_is_zeroed + let module = deduce_structural_eq_flags(module); module.fold(&mut State { enum_types: HashMap::default(), replacement_mem_ports: HashMap::default(), diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 6845d3c..fae3a3c 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -16,8 +16,8 @@ pub type DefaultBuildHasher = test_hasher::DefaultBuildHasher; #[cfg(not(feature = "unstable-test-hasher"))] pub(crate) type DefaultBuildHasher = hashbrown::DefaultHashBuilder; -pub(crate) type HashMap = hashbrown::HashMap; -pub(crate) type HashSet = hashbrown::HashSet; +pub(crate) type HashMap = hashbrown::HashMap; +pub(crate) type HashSet = hashbrown::HashSet; #[doc(inline)] pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; @@ -43,7 +43,10 @@ pub use misc::{ }; pub(crate) use misc::{InternedStrCompareAsStr, chain, copy_le_bytes_to_bitslice}; +pub(crate) mod indented_print; pub mod job_server; +pub mod map_trait; pub mod prefix_sum; pub mod ready_valid; pub(crate) mod serde_by_id; +pub mod union_find_map; diff --git a/crates/fayalite/src/util/indented_print.rs b/crates/fayalite/src/util/indented_print.rs new file mode 100644 index 0000000..7243578 --- /dev/null +++ b/crates/fayalite/src/util/indented_print.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use std::{ + fmt::{self, Write as _}, + marker::PhantomData, +}; + +struct IndentState { + indent: usize, + need_indent: bool, + buf: String, +} + +thread_local! { + static INDENT_STATE: std::cell::RefCell = const { + std::cell::RefCell::new(IndentState { + indent: 0, + need_indent: true, + buf: String::new(), + }) + }; +} + +struct IndentedOut; + +impl fmt::Write for IndentedOut { + fn write_str(&mut self, s: &str) -> fmt::Result { + INDENT_STATE.with_borrow_mut(|state| { + let IndentState { + indent, + need_indent, + buf, + } = state; + buf.clear(); + for ch in s.chars() { + if ch == '\n' { + *need_indent = true; + } else { + if *need_indent { + *need_indent = false; + for _ in 0..*indent { + buf.push_str(" "); + } + } + } + buf.push(ch) + } + std::print!("{buf}"); + }); + Ok(()) + } +} + +#[allow(unused)] +pub(crate) struct PushIndent(PhantomData<*const ()>); + +impl Drop for PushIndent { + fn drop(&mut self) { + let _ = INDENT_STATE.try_with(|state| state.borrow_mut().indent -= 1); + } +} + +impl PushIndent { + #[allow(unused)] + pub(crate) fn new() -> Self { + INDENT_STATE.with_borrow_mut(|state| state.indent += 1); + Self(PhantomData) + } +} + +#[allow(unused)] +pub(crate) fn indented_print_fmt(args: fmt::Arguments<'_>) { + if LN { + writeln!(IndentedOut, "{args}").expect("writing can't fail") + } else { + IndentedOut.write_fmt(args).expect("writing can't fail") + } +} + +#[allow(unused)] +macro_rules! indented_print { + ($($args:tt)*) => { + $crate::util::indented_print::indented_print_fmt::($crate::__std::format_args!($($args)*)) + }; +} + +#[allow(unused)] +pub(crate) use indented_print; + +#[allow(unused)] +macro_rules! indented_println { + ($($args:tt)*) => { + $crate::util::indented_print::indented_print_fmt::($crate::__std::format_args!($($args)*)) + }; +} + +#[allow(unused)] +pub(crate) use indented_println; + +#[allow(unused)] +macro_rules! indented_dbg { + ($expr:expr) => {{ + let v = $expr; + $crate::util::indented_print::indented_println!( + "[{}:{}:{}] {} = {v:#?}", + file!(), + line!(), + column!(), + stringify!($expr), + ); + v + }}; +} + +#[allow(unused)] +pub(crate) use indented_dbg; diff --git a/crates/fayalite/src/util/map_trait.rs b/crates/fayalite/src/util/map_trait.rs new file mode 100644 index 0000000..6fc065c --- /dev/null +++ b/crates/fayalite/src/util/map_trait.rs @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use std::fmt; + +pub enum Entry<'a, M: Map + 'a> { + Vacant(M::VacantEntry<'a>), + Occupied(M::OccupiedEntry<'a>), +} + +impl<'a, M: Map + 'a> Entry<'a, M> { + pub fn and_modify(mut self, f: F) -> Self { + if let Self::Occupied(entry) = &mut self { + f(entry.get_mut()); + } + self + } + pub fn insert_entry(self, v: M::Value) -> M::OccupiedEntry<'a> { + match self { + Self::Vacant(entry) => entry.insert_entry(v), + Self::Occupied(mut entry) => { + entry.insert(v); + entry + } + } + } + pub fn key(&self) -> &M::Key { + match self { + Self::Vacant(entry) => entry.key(), + Self::Occupied(entry) => entry.key(), + } + } + pub fn or_default(self) -> &'a mut M::Value + where + M::Value: Default, + { + self.or_insert_with(Default::default) + } + pub fn or_insert(self, v: M::Value) -> &'a mut M::Value { + match self { + Self::Vacant(entry) => entry.insert(v), + Self::Occupied(entry) => entry.into_mut(), + } + } + pub fn or_insert_with M::Value>(self, f: F) -> &'a mut M::Value { + match self { + Self::Vacant(entry) => entry.insert(f()), + Self::Occupied(entry) => entry.into_mut(), + } + } + pub fn or_insert_with_key M::Value>(self, f: F) -> &'a mut M::Value { + match self { + Self::Vacant(entry) => { + let v = f(entry.key()); + entry.insert(v) + } + Self::Occupied(entry) => entry.into_mut(), + } + } +} + +impl<'a, M: Map: fmt::Debug, VacantEntry<'a>: fmt::Debug> + 'a> fmt::Debug + for Entry<'a, M> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Vacant(v) => f.debug_tuple("Vacant").field(v).finish(), + Self::Occupied(v) => f.debug_tuple("Occupied").field(v).finish(), + } + } +} + +pub trait VacantEntry<'a>: Sized { + type Map: Map = Self> + 'a; + fn insert(self, v: ::Value) -> &'a mut ::Value; + fn insert_entry(self, v: ::Value) -> ::OccupiedEntry<'a>; + fn into_key(self) -> ::Key; + fn key(&self) -> &::Key; +} + +pub trait OccupiedEntry<'a>: Sized { + type Map: Map = Self> + 'a; + fn get(&self) -> &::Value; + fn get_mut(&mut self) -> &mut ::Value; + fn insert(&mut self, v: ::Value) -> ::Value; + fn into_mut(self) -> &'a mut ::Value; + fn key(&self) -> &::Key; + fn remove(self) -> ::Value; + fn remove_entry(self) -> (::Key, ::Value); +} + +pub trait Map: + Sized + + IntoIterator::Key, ::Value)> + + Extend<(::Key, ::Value)> + + FromIterator<(::Key, ::Value)> +{ + type Key; + type Value; + type IntoKeys: Iterator; + type IntoValues: Iterator; + type Iter<'a>: Iterator + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type IterMut<'a>: Iterator + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type Keys<'a>: Iterator + where + Self: 'a, + Self::Key: 'a; + type Values<'a>: Iterator + where + Self: 'a, + Self::Value: 'a; + type ValuesMut<'a>: Iterator + where + Self: 'a, + Self::Value: 'a; + type OccupiedEntry<'a>: OccupiedEntry<'a, Map = Self> + where + Self: 'a; + type VacantEntry<'a>: VacantEntry<'a, Map = Self> + where + Self: 'a; + fn clear(&mut self); + fn entry(&mut self, k: Self::Key) -> Entry<'_, Self>; + fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option; + fn into_keys(self) -> Self::IntoKeys; + fn into_values(self) -> Self::IntoValues; + fn is_empty(&self) -> bool; + fn iter(&self) -> Self::Iter<'_>; + fn iter_mut(&mut self) -> Self::IterMut<'_>; + fn keys(&self) -> Self::Keys<'_>; + fn len(&self) -> usize; + fn retain bool>(&mut self, f: F); + fn values(&self) -> Self::Values<'_>; + fn values_mut(&mut self) -> Self::ValuesMut<'_>; +} + +pub trait MapGet: Map { + fn contains_key(&self, k: &Q) -> bool; + fn get(&self, k: &Q) -> Option<&Self::Value>; + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value>; + fn remove(&mut self, k: &Q) -> Option; + fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)>; +} + +mod hash_map { + use super::*; + use crate::util::HashMap; + use hashbrown::{Equivalent, hash_map}; + use std::hash::{BuildHasher, Hash}; + + impl Map for HashMap { + type Key = K; + type Value = V; + type IntoKeys = hash_map::IntoKeys; + type IntoValues = hash_map::IntoValues; + type Iter<'a> + = hash_map::Iter<'a, K, V> + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type IterMut<'a> + = hash_map::IterMut<'a, K, V> + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type Keys<'a> + = hash_map::Keys<'a, K, V> + where + Self: 'a, + Self::Key: 'a; + type Values<'a> + = hash_map::Values<'a, K, V> + where + Self: 'a, + Self::Value: 'a; + type ValuesMut<'a> + = hash_map::ValuesMut<'a, K, V> + where + Self: 'a, + Self::Value: 'a; + type OccupiedEntry<'a> + = hash_map::OccupiedEntry<'a, K, V, H> + where + Self: 'a; + type VacantEntry<'a> + = hash_map::VacantEntry<'a, K, V, H> + where + Self: 'a; + fn clear(&mut self) { + self.clear(); + } + fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> { + use hash_map::Entry::*; + match self.entry(k) { + Occupied(entry) => Entry::Occupied(entry), + Vacant(entry) => Entry::Vacant(entry), + } + } + fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option { + self.insert(k, v) + } + fn into_keys(self) -> Self::IntoKeys { + self.into_keys() + } + fn into_values(self) -> Self::IntoValues { + self.into_values() + } + fn is_empty(&self) -> bool { + self.is_empty() + } + fn iter(&self) -> Self::Iter<'_> { + self.iter() + } + fn iter_mut(&mut self) -> Self::IterMut<'_> { + self.iter_mut() + } + fn keys(&self) -> Self::Keys<'_> { + self.keys() + } + fn len(&self) -> usize { + self.len() + } + fn retain bool>(&mut self, f: F) { + self.retain(f); + } + fn values(&self) -> Self::Values<'_> { + self.values() + } + fn values_mut(&mut self) -> Self::ValuesMut<'_> { + self.values_mut() + } + } + + impl> MapGet + for HashMap + { + fn contains_key(&self, k: &Q) -> bool { + self.contains_key(k) + } + fn get(&self, k: &Q) -> Option<&Self::Value> { + self.get(k) + } + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> { + self.get_mut(k) + } + fn remove(&mut self, k: &Q) -> Option { + self.remove(k) + } + fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> { + self.remove_entry(k) + } + } + + impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> VacantEntry<'a> + for hash_map::VacantEntry<'a, K, V, H> + { + type Map = HashMap; + fn insert(self, v: ::Value) -> &'a mut ::Value { + self.insert(v) + } + fn insert_entry( + self, + v: ::Value, + ) -> ::OccupiedEntry<'a> { + self.insert_entry(v) + } + fn into_key(self) -> ::Key { + self.into_key() + } + fn key(&self) -> &::Key { + self.key() + } + } + + impl<'a, K: Eq + Hash, V, H: BuildHasher + Default> OccupiedEntry<'a> + for hash_map::OccupiedEntry<'a, K, V, H> + { + type Map = HashMap; + fn get(&self) -> &::Value { + self.get() + } + fn get_mut(&mut self) -> &mut ::Value { + self.get_mut() + } + fn insert(&mut self, v: ::Value) -> ::Value { + self.insert(v) + } + fn into_mut(self) -> &'a mut ::Value { + self.into_mut() + } + fn key(&self) -> &::Key { + self.key() + } + fn remove(self) -> ::Value { + self.remove() + } + fn remove_entry(self) -> (::Key, ::Value) { + self.remove_entry() + } + } +} + +mod btree_map { + use super::*; + use std::collections::{BTreeMap, btree_map}; + + impl Map for BTreeMap { + type Key = K; + type Value = V; + type IntoKeys = btree_map::IntoKeys; + type IntoValues = btree_map::IntoValues; + type Iter<'a> + = btree_map::Iter<'a, K, V> + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type IterMut<'a> + = btree_map::IterMut<'a, K, V> + where + Self: 'a, + Self::Key: 'a, + Self::Value: 'a; + type Keys<'a> + = btree_map::Keys<'a, K, V> + where + Self: 'a, + Self::Key: 'a; + type Values<'a> + = btree_map::Values<'a, K, V> + where + Self: 'a, + Self::Value: 'a; + type ValuesMut<'a> + = btree_map::ValuesMut<'a, K, V> + where + Self: 'a, + Self::Value: 'a; + type OccupiedEntry<'a> + = btree_map::OccupiedEntry<'a, K, V> + where + Self: 'a; + type VacantEntry<'a> + = btree_map::VacantEntry<'a, K, V> + where + Self: 'a; + fn clear(&mut self) { + self.clear(); + } + fn entry(&mut self, k: Self::Key) -> Entry<'_, Self> { + use btree_map::Entry::*; + match self.entry(k) { + Occupied(entry) => Entry::Occupied(entry), + Vacant(entry) => Entry::Vacant(entry), + } + } + fn insert(&mut self, k: Self::Key, v: Self::Value) -> Option { + self.insert(k, v) + } + fn into_keys(self) -> Self::IntoKeys { + self.into_keys() + } + fn into_values(self) -> Self::IntoValues { + self.into_values() + } + fn is_empty(&self) -> bool { + self.is_empty() + } + fn iter(&self) -> Self::Iter<'_> { + self.iter() + } + fn iter_mut(&mut self) -> Self::IterMut<'_> { + self.iter_mut() + } + fn keys(&self) -> Self::Keys<'_> { + self.keys() + } + fn len(&self) -> usize { + self.len() + } + fn retain bool>(&mut self, f: F) { + self.retain(f); + } + fn values(&self) -> Self::Values<'_> { + self.values() + } + fn values_mut(&mut self) -> Self::ValuesMut<'_> { + self.values_mut() + } + } + + impl, V, Q: ?Sized + Ord> MapGet for BTreeMap { + fn contains_key(&self, k: &Q) -> bool { + self.contains_key(k) + } + fn get(&self, k: &Q) -> Option<&Self::Value> { + self.get(k) + } + fn get_mut(&mut self, k: &Q) -> Option<&mut Self::Value> { + self.get_mut(k) + } + fn remove(&mut self, k: &Q) -> Option { + self.remove(k) + } + fn remove_entry(&mut self, k: &Q) -> Option<(Self::Key, Self::Value)> { + self.remove_entry(k) + } + } + + impl<'a, K: Ord, V> VacantEntry<'a> for btree_map::VacantEntry<'a, K, V> { + type Map = BTreeMap; + fn insert(self, v: ::Value) -> &'a mut ::Value { + self.insert(v) + } + fn insert_entry( + self, + v: ::Value, + ) -> ::OccupiedEntry<'a> { + self.insert_entry(v) + } + fn into_key(self) -> ::Key { + self.into_key() + } + fn key(&self) -> &::Key { + self.key() + } + } + + impl<'a, K: Ord, V> OccupiedEntry<'a> for btree_map::OccupiedEntry<'a, K, V> { + type Map = BTreeMap; + fn get(&self) -> &::Value { + self.get() + } + fn get_mut(&mut self) -> &mut ::Value { + self.get_mut() + } + fn insert(&mut self, v: ::Value) -> ::Value { + self.insert(v) + } + fn into_mut(self) -> &'a mut ::Value { + self.into_mut() + } + fn key(&self) -> &::Key { + self.key() + } + fn remove(self) -> ::Value { + self.remove() + } + fn remove_entry(self) -> (::Key, ::Value) { + self.remove_entry() + } + } +} diff --git a/crates/fayalite/src/util/union_find_map.rs b/crates/fayalite/src/util/union_find_map.rs new file mode 100644 index 0000000..d197a61 --- /dev/null +++ b/crates/fayalite/src/util/union_find_map.rs @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use crate::util::{ + HashMap, + map_trait::{self, Map, MapGet, OccupiedEntry as _, VacantEntry as _}, +}; +use petgraph::unionfind::UnionFind; +use std::{collections::BTreeMap, fmt, marker::PhantomData}; + +pub struct UnionFindMap> { + uf: UnionFind, + keys_to_indexes: M, + values: Vec>, + _phantom: PhantomData, +} + +impl> fmt::Debug + for UnionFindMap +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut indexes_to_keys = vec![None; self.len()]; + for (k, &index) in self.keys_to_indexes.iter() { + indexes_to_keys[index] = Some(k); + } + let mut debug_map = f.debug_map(); + for (index, key) in indexes_to_keys.into_iter().enumerate() { + if let Some(key) = key { + debug_map.key(key); + } else { + debug_map.key(&fmt::from_fn(|f| { + f.write_str("<>") + })); + } + let set_index = self.uf.find(index); + debug_map.value(&fmt::from_fn(|f| { + write!(f, "@{set_index} ")?; + if set_index == index { + let Some(value) = &self.values[index] else { + unreachable!(); + }; + value.fmt(f) + } else { + Ok(()) + } + })); + } + debug_map.finish() + } +} + +impl> UnionFindMap { + /// returns the number of keys, not the number of sets/values + pub fn len(&self) -> usize { + self.values.len() + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + pub fn capacity(&self) -> usize { + self.values.capacity() + } + #[track_caller] + pub fn equiv(&self, k1: &K1, k2: &K2) -> bool + where + M: MapGet + MapGet, + { + self.try_equiv(k1, k2).expect("key not found") + } + pub fn try_equiv(&self, k1: &K1, k2: &K2) -> Option + where + M: MapGet + MapGet, + { + let &index1 = self.keys_to_indexes.get(k1)?; + let &index2 = self.keys_to_indexes.get(k2)?; + Some(self.uf.equiv(index1, index2)) + } + #[track_caller] + pub fn find(&self, k: &Q) -> &V + where + M: MapGet, + { + self.try_find(k).expect("key not found") + } + pub fn try_find(&self, k: &Q) -> Option<&V> + where + M: MapGet, + { + let &index = self.keys_to_indexes.get(k)?; + self.values[self.uf.find(index)].as_ref() + } + #[track_caller] + pub fn find_mut(&mut self, k: &Q) -> &mut V + where + M: MapGet, + { + self.try_find_mut(k).expect("key not found") + } + pub fn try_find_mut(&mut self, k: &Q) -> Option<&mut V> + where + M: MapGet, + { + let &index = self.keys_to_indexes.get(k)?; + self.values[self.uf.find_mut(index)].as_mut() + } + /// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key + pub fn insert(&mut self, k: K, v: V) -> Option { + match self.entry(k) { + Entry::Vacant(entry) => { + entry.insert(v); + None + } + Entry::Occupied(mut entry) => Some(entry.insert(v)), + } + } + pub fn entry(&mut self, k: K) -> Entry<'_, K, V, M> { + match self.keys_to_indexes.entry(k) { + map_trait::Entry::Vacant(keys_to_indexes_entry) => Entry::Vacant(VacantEntry { + keys_to_indexes_entry, + uf: &mut self.uf, + values: &mut self.values, + }), + map_trait::Entry::Occupied(keys_to_indexes_entry) => { + let set_index = self.uf.find_mut(*keys_to_indexes_entry.get()); + Entry::Occupied(OccupiedEntry { + keys_to_indexes_entry, + set_index, + values: &mut self.values, + }) + } + } + } + /// Unify the two sets containing `k1` and `k2`. + /// If the sets were the same, returns `Some((false, value))`, + /// otherwise calling `merge` to merge their values and returning `Some((true, value))`. + /// Returns `None` if either of the keys weren't found. + pub fn try_union( + &mut self, + k1: &K1, + k2: &K2, + merge: F, + ) -> Option<(bool, &mut V)> + where + M: MapGet + MapGet, + F: FnOnce(&K1, V, &K2, V) -> V, + { + let &index1 = self.keys_to_indexes.get(k1)?; + let &index2 = self.keys_to_indexes.get(k2)?; + let index1 = self.uf.find_mut(index1); + let index2 = self.uf.find_mut(index2); + if index1 == index2 { + return Some((false, self.values[index1].as_mut()?)); + } + assert!(self.uf.union(index1, index2)); + let v1 = self.values[index1].take().expect("known to be Some"); + let v2 = self.values[index2].take().expect("known to be Some"); + let dest = &mut self.values[self.uf.find_mut(index1)]; + let dest = dest.insert(merge(k1, v1, k2, v2)); + Some((true, dest)) + } + /// Unify the two sets containing `k1` and `k2`. + /// If the sets were the same, returns `(false, value)`, + /// otherwise calling `merge` to merge their values and returning `(true, value)`. + /// panics if either of the keys weren't found. + #[track_caller] + pub fn union(&mut self, k1: &K1, k2: &K2, merge: F) -> (bool, &mut V) + where + M: MapGet + MapGet, + F: FnOnce(&K1, V, &K2, V) -> V, + { + self.try_union(k1, k2, merge).expect("key not found") + } +} + +impl UnionFindMap { + pub fn new() -> Self { + Self::with_hasher(Default::default()) + } + pub fn with_capacity(capacity: usize) -> Self { + Self::with_capacity_and_hasher(capacity, Default::default()) + } +} + +impl UnionFindMap> { + pub const fn new_btree() -> Self { + Self { + uf: UnionFind::new_empty(), + keys_to_indexes: BTreeMap::new(), + values: Vec::new(), + _phantom: PhantomData, + } + } +} + +impl UnionFindMap> { + pub const fn with_hasher(hash_builder: H) -> Self { + Self { + uf: UnionFind::new_empty(), + keys_to_indexes: HashMap::with_hasher(hash_builder), + values: Vec::new(), + _phantom: PhantomData, + } + } + pub fn with_capacity_and_hasher(capacity: usize, hash_builder: H) -> Self { + Self { + uf: UnionFind::with_capacity(capacity), + keys_to_indexes: HashMap::with_capacity_and_hasher(capacity, hash_builder), + values: Vec::with_capacity(capacity), + _phantom: PhantomData, + } + } +} + +impl Default for UnionFindMap { + fn default() -> Self { + Self { + uf: UnionFind::new_empty(), + keys_to_indexes: M::default(), + values: Vec::new(), + _phantom: PhantomData, + } + } +} + +pub struct OccupiedEntry<'a, K, V, M: Map + 'a> { + keys_to_indexes_entry: M::OccupiedEntry<'a>, + set_index: usize, + values: &'a mut [Option], +} + +impl<'a, K, V, M: Map + 'a> OccupiedEntry<'a, K, V, M> { + pub fn get(&self) -> &V { + let Some(v) = &self.values[self.set_index] else { + unreachable!() + }; + v + } + pub fn get_mut(&mut self) -> &mut V { + let Some(v) = &mut self.values[self.set_index] else { + unreachable!() + }; + v + } + /// replaces the value for this set + pub fn insert(&mut self, v: V) -> V { + std::mem::replace(self.get_mut(), v) + } + pub fn into_mut(self) -> &'a mut V { + let Some(v) = &mut self.values[self.set_index] else { + unreachable!() + }; + v + } + pub fn key(&self) -> &K { + self.keys_to_indexes_entry.key() + } +} + +pub struct VacantEntry<'a, K, V, M: Map + 'a> { + keys_to_indexes_entry: M::VacantEntry<'a>, + uf: &'a mut UnionFind, + values: &'a mut Vec>, +} + +impl<'a, K, V, M: Map + 'a> VacantEntry<'a, K, V, M> { + /// inserts a new key as a new set + pub fn insert(self, v: V) -> &'a mut V { + self.insert_entry(v).into_mut() + } + /// inserts a new key as a new set + pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> { + let Self { + keys_to_indexes_entry, + uf, + values, + } = self; + let set_index = uf.new_set(); + values.push(Some(v)); + OccupiedEntry { + keys_to_indexes_entry: keys_to_indexes_entry.insert_entry(set_index), + set_index, + values, + } + } + pub fn into_key(self) -> K { + self.keys_to_indexes_entry.into_key() + } + pub fn key(&self) -> &K { + self.keys_to_indexes_entry.key() + } +} + +pub enum Entry<'a, K, V, M: Map + 'a> { + Vacant(VacantEntry<'a, K, V, M>), + Occupied(OccupiedEntry<'a, K, V, M>), +} + +impl<'a, K, V, M: Map + 'a> Entry<'a, K, V, M> { + pub fn and_modify(mut self, f: F) -> Self { + if let Self::Occupied(entry) = &mut self { + f(entry.get_mut()); + } + self + } + /// inserts a new key as a new set, otherwise replaces the value for the set containing the passed-in key + pub fn insert_entry(self, v: V) -> OccupiedEntry<'a, K, V, M> { + match self { + Self::Vacant(entry) => entry.insert_entry(v), + Self::Occupied(mut entry) => { + entry.insert(v); + entry + } + } + } + pub fn key(&self) -> &K { + match self { + Self::Vacant(entry) => entry.key(), + Self::Occupied(entry) => entry.key(), + } + } + /// inserts a new key as a new set + pub fn or_default(self) -> &'a mut V + where + V: Default, + { + self.or_insert_with(V::default) + } + /// inserts a new key as a new set + pub fn or_insert(self, v: V) -> &'a mut V { + match self { + Self::Vacant(entry) => entry.insert(v), + Self::Occupied(entry) => entry.into_mut(), + } + } + /// inserts a new key as a new set + pub fn or_insert_with V>(self, f: F) -> &'a mut V { + match self { + Self::Vacant(entry) => entry.insert(f()), + Self::Occupied(entry) => entry.into_mut(), + } + } + /// inserts a new key as a new set + pub fn or_insert_with_key V>(self, f: F) -> &'a mut V { + match self { + Self::Vacant(entry) => { + let v = f(entry.key()); + entry.insert(v) + } + Self::Occupied(entry) => entry.into_mut(), + } + } +} diff --git a/crates/fayalite/tests/deduce_structural_eq_flags.rs b/crates/fayalite/tests/deduce_structural_eq_flags.rs new file mode 100644 index 0000000..e2390c8 --- /dev/null +++ b/crates/fayalite/tests/deduce_structural_eq_flags.rs @@ -0,0 +1,2117 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use fayalite::{ + assert_export_firrtl, + expr::{ExprEnum, ops::StructuralEq, target::GetTarget}, + firrtl::ExportOptions, + intern::Intern, + module::{ + StmtConnect, + transform::{ + deduce_structural_eq_flags::deduce_structural_eq_flags_with_debug_tracing, + simplify_enums::{SimplifyEnumsKind, simplify_enums}, + visit::{Visit, Visitor}, + }, + }, + prelude::*, +}; +use std::convert::Infallible; + +#[hdl(outline_generated)] +struct CheckStructuralEqOut { + opt_unit_flip: Bool, + opt_unit: Bool, + opt_bool_flip: Bool, + opt_bool: Bool, + opt_opt_unit_flip: Bool, + opt_opt_unit: Bool, + array_opt_bool_flip: Bool, + array_opt_bool: Bool, + struct_opt_bool_flip: Bool, + struct_opt_bool: Bool, +} + +#[hdl(outline_generated)] +struct CheckStructuralEqModuleIO { + #[hdl(flip)] + opt_unit_flip: HdlOption<()>, + opt_unit: HdlOption<()>, + #[hdl(flip)] + opt_bool_flip: HdlOption, + opt_bool: HdlOption, + #[hdl(flip)] + opt_opt_unit_flip: HdlOption>, + opt_opt_unit: HdlOption>, + #[hdl(flip)] + array_opt_bool_flip: Array, 2>, + array_opt_bool: Array, 2>, + #[hdl(flip)] + struct_opt_bool_flip: (HdlOption, Bool), + struct_opt_bool: (HdlOption, Bool), +} + +#[hdl_module(extern, outline_generated)] +fn check_deduce_structural_eq_flags_extern_child() { + #[hdl] + let io: CheckStructuralEqModuleIO = m.output(); +} + +#[hdl_module(outline_generated)] +fn check_deduce_structural_eq_flags_child() { + #[hdl] + let io: CheckStructuralEqModuleIO = m.output(); + #[hdl] + let CheckStructuralEqModuleIO { + opt_unit_flip, + opt_unit, + opt_bool_flip, + opt_bool, + opt_opt_unit_flip, + opt_opt_unit, + array_opt_bool_flip, + array_opt_bool, + struct_opt_bool_flip, + struct_opt_bool, + } = io; + connect(opt_unit, opt_unit_flip); + connect(opt_bool, opt_bool_flip); + connect(opt_opt_unit, opt_opt_unit_flip); + connect(array_opt_bool, array_opt_bool_flip); + connect(struct_opt_bool, struct_opt_bool_flip); +} + +#[hdl_module(outline_generated)] +fn check_deduce_structural_eq_flags_parent() { + #[hdl] + let io: CheckStructuralEqModuleIO = m.input(); + + #[hdl] + let parent_out: CheckStructuralEqOut = m.output(); + #[hdl] + let extern_child_out: CheckStructuralEqOut = m.output(); + #[hdl] + let child_out: CheckStructuralEqOut = m.output(); + + #[hdl] + let extern_child = instance(check_deduce_structural_eq_flags_extern_child()); + #[hdl] + let child = instance(check_deduce_structural_eq_flags_child()); + + let connect_io = |io_in: Expr, out: Expr| { + #[hdl] + let CheckStructuralEqModuleIO { + opt_unit_flip, + opt_unit, + opt_bool_flip, + opt_bool, + opt_opt_unit_flip, + opt_opt_unit, + array_opt_bool_flip, + array_opt_bool, + struct_opt_bool_flip, + struct_opt_bool, + } = io_in; + macro_rules! connect_pair { + ($field_flip:ident, $field:ident, $literal:expr $(,)?) => { + let literal = $literal.to_expr(); + connect($field_flip, literal); + connect( + out.$field, + StructuralEq::new(Expr::canonical($field), Expr::canonical(literal)), + ); + connect( + out.$field_flip, + StructuralEq::new(Expr::canonical($field_flip), Expr::canonical(literal)), + ); + }; + } + connect_pair!(opt_unit_flip, opt_unit, HdlSome(())); + connect_pair!(opt_bool_flip, opt_bool, HdlSome(true)); + connect_pair!(opt_opt_unit_flip, opt_opt_unit, HdlSome(HdlSome(()))); + connect_pair!( + array_opt_bool_flip, + array_opt_bool, + [HdlSome(false), HdlSome(true)], + ); + connect_pair!(struct_opt_bool_flip, struct_opt_bool, (HdlSome(true), true)); + }; + + connect_io(io, parent_out); + connect_io(extern_child.io, extern_child_out); + connect_io(child.io, child_out); +} + +#[test] +fn test_deduce_structural_eq_flags() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_deduce_structural_eq_flags_parent(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, + "/test/check_deduce_structural_eq_flags_parent.fir": r"FIRRTL version 3.2.0 +circuit check_deduce_structural_eq_flags_parent: + type Ty0 = {} + type Ty1 = {|HdlNone, HdlSome: Ty0|} + type Ty2 = {|HdlNone, HdlSome: UInt<1>|} + type Ty3 = {|HdlNone, HdlSome: Ty1|} + type Ty4 = {`0`: Ty2, `1`: UInt<1>} + type Ty5 = {flip opt_unit_flip: Ty1, opt_unit: Ty1, flip opt_bool_flip: Ty2, opt_bool: Ty2, flip opt_opt_unit_flip: Ty3, opt_opt_unit: Ty3, flip array_opt_bool_flip: Ty2[2], array_opt_bool: Ty2[2], flip struct_opt_bool_flip: Ty4, struct_opt_bool: Ty4} + type Ty6 = {opt_unit_flip: UInt<1>, opt_unit: UInt<1>, opt_bool_flip: UInt<1>, opt_bool: UInt<1>, opt_opt_unit_flip: UInt<1>, opt_opt_unit: UInt<1>, array_opt_bool_flip: UInt<1>, array_opt_bool: UInt<1>, struct_opt_bool_flip: UInt<1>, struct_opt_bool: UInt<1>} + type Ty7 = {io: Ty5} + module check_deduce_structural_eq_flags_parent: @[module-XXXXXXXXXX.rs 1:1] + input io: Ty5 @[module-XXXXXXXXXX.rs 2:1] + output parent_out: Ty6 @[module-XXXXXXXXXX.rs 3:1] + output extern_child_out: Ty6 @[module-XXXXXXXXXX.rs 4:1] + output child_out: Ty6 @[module-XXXXXXXXXX.rs 5:1] + inst extern_child of check_deduce_structural_eq_flags_extern_child @[module-XXXXXXXXXX.rs 6:1] + inst child of check_deduce_structural_eq_flags_child @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_literal_expr: Ty0 + invalidate _bundle_literal_expr + connect io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr: UInt<1> + match io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome): + connect _cast_enum_to_bits_expr, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + wire _cast_enum_to_bits_expr_1: UInt<1> + match {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr): + HdlNone: + connect _cast_enum_to_bits_expr_1, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_1): + connect _cast_enum_to_bits_expr_1, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect parent_out.opt_unit, eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_2: UInt<1> + match io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_2, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_2): + connect _cast_enum_to_bits_expr_2, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect parent_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_2, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_3: UInt<2> + match io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_3, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_3): + connect _cast_enum_to_bits_expr_3, pad(cat(_cast_enum_to_bits_expr_HdlSome_3, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_4: UInt<2> + match {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)): + HdlNone: + connect _cast_enum_to_bits_expr_4, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_4): + connect _cast_enum_to_bits_expr_4, pad(cat(_cast_enum_to_bits_expr_HdlSome_4, UInt<1>(1)), 2) + connect parent_out.opt_bool, eq(_cast_enum_to_bits_expr_3, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_5: UInt<2> + match io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_5, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_5): + connect _cast_enum_to_bits_expr_5, pad(cat(_cast_enum_to_bits_expr_HdlSome_5, UInt<1>(1)), 2) + connect parent_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_5, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_6: UInt<2> + match io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_6, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_6): + wire _cast_enum_to_bits_expr_7: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_6: + HdlNone: + connect _cast_enum_to_bits_expr_7, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_7): + connect _cast_enum_to_bits_expr_7, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_6, pad(cat(_cast_enum_to_bits_expr_7, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_8: UInt<2> + match {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)): + HdlNone: + connect _cast_enum_to_bits_expr_8, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_8): + wire _cast_enum_to_bits_expr_9: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_8: + HdlNone: + connect _cast_enum_to_bits_expr_9, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_9): + connect _cast_enum_to_bits_expr_9, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_8, pad(cat(_cast_enum_to_bits_expr_9, UInt<1>(1)), 2) + connect parent_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_6, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_10: UInt<2> + match io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_10, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_10): + wire _cast_enum_to_bits_expr_11: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_10: + HdlNone: + connect _cast_enum_to_bits_expr_11, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_11): + connect _cast_enum_to_bits_expr_11, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_10, pad(cat(_cast_enum_to_bits_expr_11, UInt<1>(1)), 2) + connect parent_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_10, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _array_literal_expr: Ty2[2] + connect _array_literal_expr[0], {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h0)) + connect _array_literal_expr[1], {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) + connect io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_12: UInt<2> + match io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_12, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_12): + connect _cast_enum_to_bits_expr_12, pad(cat(_cast_enum_to_bits_expr_HdlSome_12, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_13: UInt<2> + match _array_literal_expr[0]: + HdlNone: + connect _cast_enum_to_bits_expr_13, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_13): + connect _cast_enum_to_bits_expr_13, pad(cat(_cast_enum_to_bits_expr_HdlSome_13, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_14: UInt<2> + match io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_14, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_14): + connect _cast_enum_to_bits_expr_14, pad(cat(_cast_enum_to_bits_expr_HdlSome_14, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_15: UInt<2> + match _array_literal_expr[1]: + HdlNone: + connect _cast_enum_to_bits_expr_15, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_15): + connect _cast_enum_to_bits_expr_15, pad(cat(_cast_enum_to_bits_expr_HdlSome_15, UInt<1>(1)), 2) + wire _array_structural_eq: UInt<1> + connect _array_structural_eq, and(eq(_cast_enum_to_bits_expr_12, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_14, _cast_enum_to_bits_expr_15)) + connect parent_out.array_opt_bool, _array_structural_eq @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_16: UInt<2> + match io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_16, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_16): + connect _cast_enum_to_bits_expr_16, pad(cat(_cast_enum_to_bits_expr_HdlSome_16, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_17: UInt<2> + match io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_17, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_17): + connect _cast_enum_to_bits_expr_17, pad(cat(_cast_enum_to_bits_expr_HdlSome_17, UInt<1>(1)), 2) + wire _array_structural_eq_1: UInt<1> + connect _array_structural_eq_1, and(eq(_cast_enum_to_bits_expr_16, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_17, _cast_enum_to_bits_expr_15)) + connect parent_out.array_opt_bool_flip, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_literal_expr_1: Ty4 + connect _bundle_literal_expr_1.`0`, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) + connect _bundle_literal_expr_1.`1`, UInt<1>(0h1) + connect io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_18: UInt<2> + match io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_18, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_18): + connect _cast_enum_to_bits_expr_18, pad(cat(_cast_enum_to_bits_expr_HdlSome_18, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_19: UInt<2> + match _bundle_literal_expr_1.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_19, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_19): + connect _cast_enum_to_bits_expr_19, pad(cat(_cast_enum_to_bits_expr_HdlSome_19, UInt<1>(1)), 2) + wire _bundle_structural_eq: UInt<1> + connect _bundle_structural_eq, and(eq(_cast_enum_to_bits_expr_18, _cast_enum_to_bits_expr_19), eq(io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect parent_out.struct_opt_bool, _bundle_structural_eq @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_20: UInt<2> + match io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_20, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_20): + connect _cast_enum_to_bits_expr_20, pad(cat(_cast_enum_to_bits_expr_HdlSome_20, UInt<1>(1)), 2) + wire _bundle_structural_eq_1: UInt<1> + connect _bundle_structural_eq_1, and(eq(_cast_enum_to_bits_expr_20, _cast_enum_to_bits_expr_19), eq(io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect parent_out.struct_opt_bool_flip, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 13:1] + connect extern_child.io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_21: UInt<1> + match extern_child.io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_21, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_21): + connect _cast_enum_to_bits_expr_21, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect extern_child_out.opt_unit, eq(_cast_enum_to_bits_expr_21, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_22: UInt<1> + match extern_child.io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_22, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_22): + connect _cast_enum_to_bits_expr_22, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect extern_child_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_22, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect extern_child.io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_23: UInt<2> + match extern_child.io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_23, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_23): + connect _cast_enum_to_bits_expr_23, pad(cat(_cast_enum_to_bits_expr_HdlSome_23, UInt<1>(1)), 2) + connect extern_child_out.opt_bool, eq(_cast_enum_to_bits_expr_23, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_24: UInt<2> + match extern_child.io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_24, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_24): + connect _cast_enum_to_bits_expr_24, pad(cat(_cast_enum_to_bits_expr_HdlSome_24, UInt<1>(1)), 2) + connect extern_child_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_24, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect extern_child.io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_25: UInt<2> + match extern_child.io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_25, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_25): + wire _cast_enum_to_bits_expr_26: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_25: + HdlNone: + connect _cast_enum_to_bits_expr_26, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_26): + connect _cast_enum_to_bits_expr_26, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_25, pad(cat(_cast_enum_to_bits_expr_26, UInt<1>(1)), 2) + connect extern_child_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_25, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_27: UInt<2> + match extern_child.io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_27, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_27): + wire _cast_enum_to_bits_expr_28: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_27: + HdlNone: + connect _cast_enum_to_bits_expr_28, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_28): + connect _cast_enum_to_bits_expr_28, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_27, pad(cat(_cast_enum_to_bits_expr_28, UInt<1>(1)), 2) + connect extern_child_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_27, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + connect extern_child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_29: UInt<2> + match extern_child.io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_29, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_29): + connect _cast_enum_to_bits_expr_29, pad(cat(_cast_enum_to_bits_expr_HdlSome_29, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_30: UInt<2> + match extern_child.io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_30, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_30): + connect _cast_enum_to_bits_expr_30, pad(cat(_cast_enum_to_bits_expr_HdlSome_30, UInt<1>(1)), 2) + wire _array_structural_eq_2: UInt<1> + connect _array_structural_eq_2, and(eq(_cast_enum_to_bits_expr_29, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_30, _cast_enum_to_bits_expr_15)) + connect extern_child_out.array_opt_bool, _array_structural_eq_2 @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_31: UInt<2> + match extern_child.io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_31, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_31): + connect _cast_enum_to_bits_expr_31, pad(cat(_cast_enum_to_bits_expr_HdlSome_31, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_32: UInt<2> + match extern_child.io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_32, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_32): + connect _cast_enum_to_bits_expr_32, pad(cat(_cast_enum_to_bits_expr_HdlSome_32, UInt<1>(1)), 2) + wire _array_structural_eq_3: UInt<1> + connect _array_structural_eq_3, and(eq(_cast_enum_to_bits_expr_31, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_32, _cast_enum_to_bits_expr_15)) + connect extern_child_out.array_opt_bool_flip, _array_structural_eq_3 @[module-XXXXXXXXXX.rs 12:1] + connect extern_child.io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_33: UInt<2> + match extern_child.io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_33, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_33): + connect _cast_enum_to_bits_expr_33, pad(cat(_cast_enum_to_bits_expr_HdlSome_33, UInt<1>(1)), 2) + wire _bundle_structural_eq_2: UInt<1> + connect _bundle_structural_eq_2, and(eq(_cast_enum_to_bits_expr_33, _cast_enum_to_bits_expr_19), eq(extern_child.io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect extern_child_out.struct_opt_bool, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_34: UInt<2> + match extern_child.io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_34, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_34): + connect _cast_enum_to_bits_expr_34, pad(cat(_cast_enum_to_bits_expr_HdlSome_34, UInt<1>(1)), 2) + wire _bundle_structural_eq_3: UInt<1> + connect _bundle_structural_eq_3, and(eq(_cast_enum_to_bits_expr_34, _cast_enum_to_bits_expr_19), eq(extern_child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect extern_child_out.struct_opt_bool_flip, _bundle_structural_eq_3 @[module-XXXXXXXXXX.rs 13:1] + connect child.io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_35: UInt<1> + match child.io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_35, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_35): + connect _cast_enum_to_bits_expr_35, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect child_out.opt_unit, eq(_cast_enum_to_bits_expr_35, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_36: UInt<1> + match child.io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_36, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_36): + connect _cast_enum_to_bits_expr_36, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect child_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_36, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect child.io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_37: UInt<2> + match child.io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_37, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_37): + connect _cast_enum_to_bits_expr_37, pad(cat(_cast_enum_to_bits_expr_HdlSome_37, UInt<1>(1)), 2) + connect child_out.opt_bool, eq(_cast_enum_to_bits_expr_37, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_38: UInt<2> + match child.io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_38, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_38): + connect _cast_enum_to_bits_expr_38, pad(cat(_cast_enum_to_bits_expr_HdlSome_38, UInt<1>(1)), 2) + connect child_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_38, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect child.io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_39: UInt<2> + match child.io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_39, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_39): + wire _cast_enum_to_bits_expr_40: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_39: + HdlNone: + connect _cast_enum_to_bits_expr_40, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_40): + connect _cast_enum_to_bits_expr_40, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_39, pad(cat(_cast_enum_to_bits_expr_40, UInt<1>(1)), 2) + connect child_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_39, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_41: UInt<2> + match child.io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_41, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_41): + wire _cast_enum_to_bits_expr_42: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_41: + HdlNone: + connect _cast_enum_to_bits_expr_42, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_42): + connect _cast_enum_to_bits_expr_42, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_41, pad(cat(_cast_enum_to_bits_expr_42, UInt<1>(1)), 2) + connect child_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_41, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + connect child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_43: UInt<2> + match child.io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_43, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_43): + connect _cast_enum_to_bits_expr_43, pad(cat(_cast_enum_to_bits_expr_HdlSome_43, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_44: UInt<2> + match child.io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_44, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_44): + connect _cast_enum_to_bits_expr_44, pad(cat(_cast_enum_to_bits_expr_HdlSome_44, UInt<1>(1)), 2) + wire _array_structural_eq_4: UInt<1> + connect _array_structural_eq_4, and(eq(_cast_enum_to_bits_expr_43, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_44, _cast_enum_to_bits_expr_15)) + connect child_out.array_opt_bool, _array_structural_eq_4 @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_45: UInt<2> + match child.io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_45, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_45): + connect _cast_enum_to_bits_expr_45, pad(cat(_cast_enum_to_bits_expr_HdlSome_45, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_46: UInt<2> + match child.io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_46, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_46): + connect _cast_enum_to_bits_expr_46, pad(cat(_cast_enum_to_bits_expr_HdlSome_46, UInt<1>(1)), 2) + wire _array_structural_eq_5: UInt<1> + connect _array_structural_eq_5, and(eq(_cast_enum_to_bits_expr_45, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_46, _cast_enum_to_bits_expr_15)) + connect child_out.array_opt_bool_flip, _array_structural_eq_5 @[module-XXXXXXXXXX.rs 12:1] + connect child.io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_47: UInt<2> + match child.io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_47, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_47): + connect _cast_enum_to_bits_expr_47, pad(cat(_cast_enum_to_bits_expr_HdlSome_47, UInt<1>(1)), 2) + wire _bundle_structural_eq_4: UInt<1> + connect _bundle_structural_eq_4, and(eq(_cast_enum_to_bits_expr_47, _cast_enum_to_bits_expr_19), eq(child.io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect child_out.struct_opt_bool, _bundle_structural_eq_4 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_48: UInt<2> + match child.io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_48, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_48): + connect _cast_enum_to_bits_expr_48, pad(cat(_cast_enum_to_bits_expr_HdlSome_48, UInt<1>(1)), 2) + wire _bundle_structural_eq_5: UInt<1> + connect _bundle_structural_eq_5, and(eq(_cast_enum_to_bits_expr_48, _cast_enum_to_bits_expr_19), eq(child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect child_out.struct_opt_bool_flip, _bundle_structural_eq_5 @[module-XXXXXXXXXX.rs 13:1] + extmodule check_deduce_structural_eq_flags_extern_child: @[module-XXXXXXXXXX-2.rs 1:1] + output io: Ty5 @[module-XXXXXXXXXX-2.rs 2:1] + defname = check_deduce_structural_eq_flags_extern_child + module check_deduce_structural_eq_flags_child: @[module-XXXXXXXXXX-3.rs 1:1] + output io: Ty5 @[module-XXXXXXXXXX-3.rs 2:1] + connect io.opt_unit, io.opt_unit_flip @[module-XXXXXXXXXX-3.rs 4:1] + connect io.opt_bool, io.opt_bool_flip @[module-XXXXXXXXXX-3.rs 5:1] + connect io.opt_opt_unit, io.opt_opt_unit_flip @[module-XXXXXXXXXX-3.rs 6:1] + connect io.array_opt_bool, io.array_opt_bool_flip @[module-XXXXXXXXXX-3.rs 7:1] + connect io.struct_opt_bool, io.struct_opt_bool_flip @[module-XXXXXXXXXX-3.rs 8:1] +", + }; + let m = deduce_structural_eq_flags_with_debug_tracing(m.canonical().intern_sized(), true); + dbg!(m); + struct MyVisitor(Vec<(String, bool)>); + macro_rules! connects { + {$($key:literal: $value:literal),* $(,)?} => { + const KNOWN_CONNECTS: &[(&str, bool)] = &[$(($key, $value)),*]; + }; + } + + connects! { + "check_deduce_structural_eq_flags_parent::parent_out.opt_unit": true, + "check_deduce_structural_eq_flags_parent::parent_out.opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::parent_out.opt_bool": false, + "check_deduce_structural_eq_flags_parent::parent_out.opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::parent_out.opt_opt_unit": false, + "check_deduce_structural_eq_flags_parent::parent_out.opt_opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::parent_out.array_opt_bool": false, + "check_deduce_structural_eq_flags_parent::parent_out.array_opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::parent_out.struct_opt_bool": false, + "check_deduce_structural_eq_flags_parent::parent_out.struct_opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_unit": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_bool": false, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_opt_unit": false, + "check_deduce_structural_eq_flags_parent::extern_child_out.opt_opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.array_opt_bool": false, + "check_deduce_structural_eq_flags_parent::extern_child_out.array_opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::extern_child_out.struct_opt_bool": false, + "check_deduce_structural_eq_flags_parent::extern_child_out.struct_opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_unit": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_bool": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_opt_unit": true, + "check_deduce_structural_eq_flags_parent::child_out.opt_opt_unit_flip": true, + "check_deduce_structural_eq_flags_parent::child_out.array_opt_bool": true, + "check_deduce_structural_eq_flags_parent::child_out.array_opt_bool_flip": true, + "check_deduce_structural_eq_flags_parent::child_out.struct_opt_bool": true, + "check_deduce_structural_eq_flags_parent::child_out.struct_opt_bool_flip": true, + } + + impl Visitor for MyVisitor { + type Error = Infallible; + fn visit_stmt_connect(&mut self, v: &StmtConnect) -> Result<(), Self::Error> { + let ExprEnum::StructuralEq(structural_eq) = *Expr::expr_enum(v.rhs) else { + return v.default_visit(self); + }; + let Some(lhs_target) = v.lhs.target() else { + panic!("connect lhs must have a Target: {v:#?}"); + }; + let lhs_target_str = lhs_target.to_string(); + let assume_padding_is_zeroed = structural_eq.flags().assume_padding_is_zeroed; + if let Some(key) = KNOWN_CONNECTS + .iter() + .copied() + .find(|&(key, _)| key == lhs_target_str) + { + if assume_padding_is_zeroed != key.1 { + println!("{key:?}: {v:#?}"); + panic!( + "{key:?}: assume_padding_is_zeroed ({assume_padding_is_zeroed}) is not as expected" + ); + } + } else { + let k = (lhs_target_str, assume_padding_is_zeroed); + println!("{k:?}: {v:#?}"); + self.0.push(k); + } + Ok(()) + } + } + let mut visitor = MyVisitor(vec![]); + let Ok(()) = visitor.visit_module(&m); + if !visitor.0.is_empty() { + panic!( + "unknown connects:\nconnects! {:#?}", + std::fmt::from_fn(|f| { f.debug_map().entries(visitor.0.iter().cloned()).finish() }) + ) + } + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, + "/test/check_deduce_structural_eq_flags_parent.fir": r"FIRRTL version 3.2.0 +circuit check_deduce_structural_eq_flags_parent: + type Ty0 = {} + type Ty1 = {|HdlNone, HdlSome: Ty0|} + type Ty2 = {|HdlNone, HdlSome: UInt<1>|} + type Ty3 = {|HdlNone, HdlSome: Ty1|} + type Ty4 = {`0`: Ty2, `1`: UInt<1>} + type Ty5 = {flip opt_unit_flip: Ty1, opt_unit: Ty1, flip opt_bool_flip: Ty2, opt_bool: Ty2, flip opt_opt_unit_flip: Ty3, opt_opt_unit: Ty3, flip array_opt_bool_flip: Ty2[2], array_opt_bool: Ty2[2], flip struct_opt_bool_flip: Ty4, struct_opt_bool: Ty4} + type Ty6 = {opt_unit_flip: UInt<1>, opt_unit: UInt<1>, opt_bool_flip: UInt<1>, opt_bool: UInt<1>, opt_opt_unit_flip: UInt<1>, opt_opt_unit: UInt<1>, array_opt_bool_flip: UInt<1>, array_opt_bool: UInt<1>, struct_opt_bool_flip: UInt<1>, struct_opt_bool: UInt<1>} + type Ty7 = {io: Ty5} + module check_deduce_structural_eq_flags_parent: @[module-XXXXXXXXXX.rs 1:1] + input io: Ty5 @[module-XXXXXXXXXX.rs 2:1] + output parent_out: Ty6 @[module-XXXXXXXXXX.rs 3:1] + output extern_child_out: Ty6 @[module-XXXXXXXXXX.rs 4:1] + output child_out: Ty6 @[module-XXXXXXXXXX.rs 5:1] + inst extern_child of check_deduce_structural_eq_flags_extern_child @[module-XXXXXXXXXX.rs 6:1] + inst child of check_deduce_structural_eq_flags_child @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_literal_expr: Ty0 + invalidate _bundle_literal_expr + connect io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr: UInt<1> + match io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome): + connect _cast_enum_to_bits_expr, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + wire _cast_enum_to_bits_expr_1: UInt<1> + match {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr): + HdlNone: + connect _cast_enum_to_bits_expr_1, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_1): + connect _cast_enum_to_bits_expr_1, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect parent_out.opt_unit, eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_2: UInt<1> + match io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_2, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_2): + connect _cast_enum_to_bits_expr_2, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect parent_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_2, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_3: UInt<2> + match io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_3, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_3): + connect _cast_enum_to_bits_expr_3, pad(cat(_cast_enum_to_bits_expr_HdlSome_3, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_4: UInt<2> + match {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)): + HdlNone: + connect _cast_enum_to_bits_expr_4, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_4): + connect _cast_enum_to_bits_expr_4, pad(cat(_cast_enum_to_bits_expr_HdlSome_4, UInt<1>(1)), 2) + connect parent_out.opt_bool, eq(_cast_enum_to_bits_expr_3, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_5: UInt<2> + match io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_5, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_5): + connect _cast_enum_to_bits_expr_5, pad(cat(_cast_enum_to_bits_expr_HdlSome_5, UInt<1>(1)), 2) + connect parent_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_5, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_6: UInt<2> + match io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_6, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_6): + wire _cast_enum_to_bits_expr_7: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_6: + HdlNone: + connect _cast_enum_to_bits_expr_7, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_7): + connect _cast_enum_to_bits_expr_7, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_6, pad(cat(_cast_enum_to_bits_expr_7, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_8: UInt<2> + match {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)): + HdlNone: + connect _cast_enum_to_bits_expr_8, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_8): + wire _cast_enum_to_bits_expr_9: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_8: + HdlNone: + connect _cast_enum_to_bits_expr_9, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_9): + connect _cast_enum_to_bits_expr_9, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_8, pad(cat(_cast_enum_to_bits_expr_9, UInt<1>(1)), 2) + connect parent_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_6, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_10: UInt<2> + match io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_10, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_10): + wire _cast_enum_to_bits_expr_11: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_10: + HdlNone: + connect _cast_enum_to_bits_expr_11, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_11): + connect _cast_enum_to_bits_expr_11, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_10, pad(cat(_cast_enum_to_bits_expr_11, UInt<1>(1)), 2) + connect parent_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_10, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _array_literal_expr: Ty2[2] + connect _array_literal_expr[0], {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h0)) + connect _array_literal_expr[1], {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) + connect io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_12: UInt<2> + match io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_12, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_12): + connect _cast_enum_to_bits_expr_12, pad(cat(_cast_enum_to_bits_expr_HdlSome_12, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_13: UInt<2> + match _array_literal_expr[0]: + HdlNone: + connect _cast_enum_to_bits_expr_13, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_13): + connect _cast_enum_to_bits_expr_13, pad(cat(_cast_enum_to_bits_expr_HdlSome_13, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_14: UInt<2> + match io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_14, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_14): + connect _cast_enum_to_bits_expr_14, pad(cat(_cast_enum_to_bits_expr_HdlSome_14, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_15: UInt<2> + match _array_literal_expr[1]: + HdlNone: + connect _cast_enum_to_bits_expr_15, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_15): + connect _cast_enum_to_bits_expr_15, pad(cat(_cast_enum_to_bits_expr_HdlSome_15, UInt<1>(1)), 2) + wire _array_structural_eq: UInt<1> + connect _array_structural_eq, and(eq(_cast_enum_to_bits_expr_12, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_14, _cast_enum_to_bits_expr_15)) + connect parent_out.array_opt_bool, _array_structural_eq @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_16: UInt<2> + match io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_16, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_16): + connect _cast_enum_to_bits_expr_16, pad(cat(_cast_enum_to_bits_expr_HdlSome_16, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_17: UInt<2> + match io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_17, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_17): + connect _cast_enum_to_bits_expr_17, pad(cat(_cast_enum_to_bits_expr_HdlSome_17, UInt<1>(1)), 2) + wire _array_structural_eq_1: UInt<1> + connect _array_structural_eq_1, and(eq(_cast_enum_to_bits_expr_16, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_17, _cast_enum_to_bits_expr_15)) + connect parent_out.array_opt_bool_flip, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_literal_expr_1: Ty4 + connect _bundle_literal_expr_1.`0`, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) + connect _bundle_literal_expr_1.`1`, UInt<1>(0h1) + connect io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_18: UInt<2> + match io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_18, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_18): + connect _cast_enum_to_bits_expr_18, pad(cat(_cast_enum_to_bits_expr_HdlSome_18, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_19: UInt<2> + match _bundle_literal_expr_1.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_19, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_19): + connect _cast_enum_to_bits_expr_19, pad(cat(_cast_enum_to_bits_expr_HdlSome_19, UInt<1>(1)), 2) + wire _bundle_structural_eq: UInt<1> + connect _bundle_structural_eq, and(eq(_cast_enum_to_bits_expr_18, _cast_enum_to_bits_expr_19), eq(io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect parent_out.struct_opt_bool, _bundle_structural_eq @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_20: UInt<2> + match io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_20, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_20): + connect _cast_enum_to_bits_expr_20, pad(cat(_cast_enum_to_bits_expr_HdlSome_20, UInt<1>(1)), 2) + wire _bundle_structural_eq_1: UInt<1> + connect _bundle_structural_eq_1, and(eq(_cast_enum_to_bits_expr_20, _cast_enum_to_bits_expr_19), eq(io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect parent_out.struct_opt_bool_flip, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 13:1] + connect extern_child.io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_21: UInt<1> + match extern_child.io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_21, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_21): + connect _cast_enum_to_bits_expr_21, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect extern_child_out.opt_unit, eq(_cast_enum_to_bits_expr_21, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_22: UInt<1> + match extern_child.io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_22, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_22): + connect _cast_enum_to_bits_expr_22, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect extern_child_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_22, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect extern_child.io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_23: UInt<2> + match extern_child.io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_23, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_23): + connect _cast_enum_to_bits_expr_23, pad(cat(_cast_enum_to_bits_expr_HdlSome_23, UInt<1>(1)), 2) + connect extern_child_out.opt_bool, eq(_cast_enum_to_bits_expr_23, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_24: UInt<2> + match extern_child.io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_24, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_24): + connect _cast_enum_to_bits_expr_24, pad(cat(_cast_enum_to_bits_expr_HdlSome_24, UInt<1>(1)), 2) + connect extern_child_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_24, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect extern_child.io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_25: UInt<2> + match extern_child.io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_25, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_25): + wire _cast_enum_to_bits_expr_26: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_25: + HdlNone: + connect _cast_enum_to_bits_expr_26, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_26): + connect _cast_enum_to_bits_expr_26, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_25, pad(cat(_cast_enum_to_bits_expr_26, UInt<1>(1)), 2) + connect extern_child_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_25, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_27: UInt<2> + match extern_child.io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_27, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_27): + wire _cast_enum_to_bits_expr_28: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_27: + HdlNone: + connect _cast_enum_to_bits_expr_28, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_28): + connect _cast_enum_to_bits_expr_28, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_27, pad(cat(_cast_enum_to_bits_expr_28, UInt<1>(1)), 2) + connect extern_child_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_27, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + connect extern_child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_29: UInt<2> + match extern_child.io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_29, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_29): + connect _cast_enum_to_bits_expr_29, pad(cat(_cast_enum_to_bits_expr_HdlSome_29, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_30: UInt<2> + match extern_child.io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_30, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_30): + connect _cast_enum_to_bits_expr_30, pad(cat(_cast_enum_to_bits_expr_HdlSome_30, UInt<1>(1)), 2) + wire _array_structural_eq_2: UInt<1> + connect _array_structural_eq_2, and(eq(_cast_enum_to_bits_expr_29, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_30, _cast_enum_to_bits_expr_15)) + connect extern_child_out.array_opt_bool, _array_structural_eq_2 @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_31: UInt<2> + match extern_child.io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_31, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_31): + connect _cast_enum_to_bits_expr_31, pad(cat(_cast_enum_to_bits_expr_HdlSome_31, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_32: UInt<2> + match extern_child.io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_32, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_32): + connect _cast_enum_to_bits_expr_32, pad(cat(_cast_enum_to_bits_expr_HdlSome_32, UInt<1>(1)), 2) + wire _array_structural_eq_3: UInt<1> + connect _array_structural_eq_3, and(eq(_cast_enum_to_bits_expr_31, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_32, _cast_enum_to_bits_expr_15)) + connect extern_child_out.array_opt_bool_flip, _array_structural_eq_3 @[module-XXXXXXXXXX.rs 12:1] + connect extern_child.io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_33: UInt<2> + match extern_child.io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_33, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_33): + connect _cast_enum_to_bits_expr_33, pad(cat(_cast_enum_to_bits_expr_HdlSome_33, UInt<1>(1)), 2) + wire _bundle_structural_eq_2: UInt<1> + connect _bundle_structural_eq_2, and(eq(_cast_enum_to_bits_expr_33, _cast_enum_to_bits_expr_19), eq(extern_child.io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect extern_child_out.struct_opt_bool, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_34: UInt<2> + match extern_child.io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_34, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_34): + connect _cast_enum_to_bits_expr_34, pad(cat(_cast_enum_to_bits_expr_HdlSome_34, UInt<1>(1)), 2) + wire _bundle_structural_eq_3: UInt<1> + connect _bundle_structural_eq_3, and(eq(_cast_enum_to_bits_expr_34, _cast_enum_to_bits_expr_19), eq(extern_child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect extern_child_out.struct_opt_bool_flip, _bundle_structural_eq_3 @[module-XXXXXXXXXX.rs 13:1] + connect child.io.opt_unit_flip, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_35: UInt<1> + match child.io.opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_35, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_35): + connect _cast_enum_to_bits_expr_35, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect child_out.opt_unit, eq(_cast_enum_to_bits_expr_35, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_36: UInt<1> + match child.io.opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_36, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_36): + connect _cast_enum_to_bits_expr_36, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect child_out.opt_unit_flip, eq(_cast_enum_to_bits_expr_36, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 9:1] + connect child.io.opt_bool_flip, {|HdlNone, HdlSome: UInt<1>|}(HdlSome, UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_37: UInt<2> + match child.io.opt_bool: + HdlNone: + connect _cast_enum_to_bits_expr_37, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_37): + connect _cast_enum_to_bits_expr_37, pad(cat(_cast_enum_to_bits_expr_HdlSome_37, UInt<1>(1)), 2) + connect child_out.opt_bool, eq(_cast_enum_to_bits_expr_37, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_38: UInt<2> + match child.io.opt_bool_flip: + HdlNone: + connect _cast_enum_to_bits_expr_38, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_38): + connect _cast_enum_to_bits_expr_38, pad(cat(_cast_enum_to_bits_expr_HdlSome_38, UInt<1>(1)), 2) + connect child_out.opt_bool_flip, eq(_cast_enum_to_bits_expr_38, _cast_enum_to_bits_expr_4) @[module-XXXXXXXXXX.rs 10:1] + connect child.io.opt_opt_unit_flip, {|HdlNone, HdlSome: Ty1|}(HdlSome, {|HdlNone, HdlSome: Ty0|}(HdlSome, _bundle_literal_expr)) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_39: UInt<2> + match child.io.opt_opt_unit: + HdlNone: + connect _cast_enum_to_bits_expr_39, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_39): + wire _cast_enum_to_bits_expr_40: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_39: + HdlNone: + connect _cast_enum_to_bits_expr_40, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_40): + connect _cast_enum_to_bits_expr_40, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_39, pad(cat(_cast_enum_to_bits_expr_40, UInt<1>(1)), 2) + connect child_out.opt_opt_unit, eq(_cast_enum_to_bits_expr_39, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_41: UInt<2> + match child.io.opt_opt_unit_flip: + HdlNone: + connect _cast_enum_to_bits_expr_41, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_41): + wire _cast_enum_to_bits_expr_42: UInt<1> + match _cast_enum_to_bits_expr_HdlSome_41: + HdlNone: + connect _cast_enum_to_bits_expr_42, UInt<1>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_42): + connect _cast_enum_to_bits_expr_42, pad(cat(UInt<0>(0), UInt<1>(1)), 1) + connect _cast_enum_to_bits_expr_41, pad(cat(_cast_enum_to_bits_expr_42, UInt<1>(1)), 2) + connect child_out.opt_opt_unit_flip, eq(_cast_enum_to_bits_expr_41, _cast_enum_to_bits_expr_8) @[module-XXXXXXXXXX.rs 11:1] + connect child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_43: UInt<2> + match child.io.array_opt_bool[0]: + HdlNone: + connect _cast_enum_to_bits_expr_43, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_43): + connect _cast_enum_to_bits_expr_43, pad(cat(_cast_enum_to_bits_expr_HdlSome_43, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_44: UInt<2> + match child.io.array_opt_bool[1]: + HdlNone: + connect _cast_enum_to_bits_expr_44, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_44): + connect _cast_enum_to_bits_expr_44, pad(cat(_cast_enum_to_bits_expr_HdlSome_44, UInt<1>(1)), 2) + wire _array_structural_eq_4: UInt<1> + connect _array_structural_eq_4, and(eq(_cast_enum_to_bits_expr_43, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_44, _cast_enum_to_bits_expr_15)) + connect child_out.array_opt_bool, _array_structural_eq_4 @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_45: UInt<2> + match child.io.array_opt_bool_flip[0]: + HdlNone: + connect _cast_enum_to_bits_expr_45, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_45): + connect _cast_enum_to_bits_expr_45, pad(cat(_cast_enum_to_bits_expr_HdlSome_45, UInt<1>(1)), 2) + wire _cast_enum_to_bits_expr_46: UInt<2> + match child.io.array_opt_bool_flip[1]: + HdlNone: + connect _cast_enum_to_bits_expr_46, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_46): + connect _cast_enum_to_bits_expr_46, pad(cat(_cast_enum_to_bits_expr_HdlSome_46, UInt<1>(1)), 2) + wire _array_structural_eq_5: UInt<1> + connect _array_structural_eq_5, and(eq(_cast_enum_to_bits_expr_45, _cast_enum_to_bits_expr_13), eq(_cast_enum_to_bits_expr_46, _cast_enum_to_bits_expr_15)) + connect child_out.array_opt_bool_flip, _array_structural_eq_5 @[module-XXXXXXXXXX.rs 12:1] + connect child.io.struct_opt_bool_flip, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_47: UInt<2> + match child.io.struct_opt_bool.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_47, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_47): + connect _cast_enum_to_bits_expr_47, pad(cat(_cast_enum_to_bits_expr_HdlSome_47, UInt<1>(1)), 2) + wire _bundle_structural_eq_4: UInt<1> + connect _bundle_structural_eq_4, and(eq(_cast_enum_to_bits_expr_47, _cast_enum_to_bits_expr_19), eq(child.io.struct_opt_bool.`1`, _bundle_literal_expr_1.`1`)) + connect child_out.struct_opt_bool, _bundle_structural_eq_4 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_48: UInt<2> + match child.io.struct_opt_bool_flip.`0`: + HdlNone: + connect _cast_enum_to_bits_expr_48, UInt<2>(0) + HdlSome(_cast_enum_to_bits_expr_HdlSome_48): + connect _cast_enum_to_bits_expr_48, pad(cat(_cast_enum_to_bits_expr_HdlSome_48, UInt<1>(1)), 2) + wire _bundle_structural_eq_5: UInt<1> + connect _bundle_structural_eq_5, and(eq(_cast_enum_to_bits_expr_48, _cast_enum_to_bits_expr_19), eq(child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_1.`1`)) + connect child_out.struct_opt_bool_flip, _bundle_structural_eq_5 @[module-XXXXXXXXXX.rs 13:1] + extmodule check_deduce_structural_eq_flags_extern_child: @[module-XXXXXXXXXX-2.rs 1:1] + output io: Ty5 @[module-XXXXXXXXXX-2.rs 2:1] + defname = check_deduce_structural_eq_flags_extern_child + module check_deduce_structural_eq_flags_child: @[module-XXXXXXXXXX-3.rs 1:1] + output io: Ty5 @[module-XXXXXXXXXX-3.rs 2:1] + connect io.opt_unit, io.opt_unit_flip @[module-XXXXXXXXXX-3.rs 4:1] + connect io.opt_bool, io.opt_bool_flip @[module-XXXXXXXXXX-3.rs 5:1] + connect io.opt_opt_unit, io.opt_opt_unit_flip @[module-XXXXXXXXXX-3.rs 6:1] + connect io.array_opt_bool, io.array_opt_bool_flip @[module-XXXXXXXXXX-3.rs 7:1] + connect io.struct_opt_bool, io.struct_opt_bool_flip @[module-XXXXXXXXXX-3.rs 8:1] +", + }; + let m_no_body = simplify_enums(m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); + dbg!(m_no_body); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m_no_body => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, + "/test/check_deduce_structural_eq_flags_parent.fir": r"FIRRTL version 3.2.0 +circuit check_deduce_structural_eq_flags_parent: + type Ty0 = {|HdlNone, HdlSome|} + type Ty1 = {tag: Ty0, body: UInt<0>} + type Ty2 = {tag: Ty0, body: UInt<1>} + type Ty3 = {`0`: Ty2, `1`: UInt<1>} + type Ty4 = {flip opt_unit_flip: Ty1, opt_unit: Ty1, flip opt_bool_flip: Ty2, opt_bool: Ty2, flip opt_opt_unit_flip: Ty2, opt_opt_unit: Ty2, flip array_opt_bool_flip: Ty2[2], array_opt_bool: Ty2[2], flip struct_opt_bool_flip: Ty3, struct_opt_bool: Ty3} + type Ty5 = {opt_unit_flip: UInt<1>, opt_unit: UInt<1>, opt_bool_flip: UInt<1>, opt_bool: UInt<1>, opt_opt_unit_flip: UInt<1>, opt_opt_unit: UInt<1>, array_opt_bool_flip: UInt<1>, array_opt_bool: UInt<1>, struct_opt_bool_flip: UInt<1>, struct_opt_bool: UInt<1>} + type Ty6 = {} + type Ty7 = {tag: UInt<1>, body: UInt<0>} + type Ty8 = {io: Ty4} + module check_deduce_structural_eq_flags_parent: @[module-XXXXXXXXXX.rs 1:1] + input io: Ty4 @[module-XXXXXXXXXX.rs 2:1] + output parent_out: Ty5 @[module-XXXXXXXXXX.rs 3:1] + output extern_child_out: Ty5 @[module-XXXXXXXXXX.rs 4:1] + output child_out: Ty5 @[module-XXXXXXXXXX.rs 5:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_1: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_2: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_3: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_4: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_5: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_6: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_7: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_8: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_9: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + inst extern_child of check_deduce_structural_eq_flags_extern_child @[module-XXXXXXXXXX.rs 6:1] + inst child of check_deduce_structural_eq_flags_child @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_literal_expr: Ty1 + connect _bundle_literal_expr.tag, {|HdlNone, HdlSome|}(HdlSome) + wire _bundle_literal_expr_1: Ty6 + invalidate _bundle_literal_expr_1 + connect _bundle_literal_expr.body, UInt<0>(0) + connect io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr: UInt<1> + match io.opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr, UInt<1>(1) + wire _cast_enum_to_bits_expr_1: UInt<1> + match _bundle_literal_expr.tag: + HdlNone: + connect _cast_enum_to_bits_expr_1, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_1, UInt<1>(1) + wire _bundle_structural_eq: UInt<1> + connect _bundle_structural_eq, and(eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1), eq(io.opt_unit.body, _bundle_literal_expr.body)) + connect parent_out.opt_unit, _bundle_structural_eq @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_2: UInt<1> + match io.opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_2, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_2, UInt<1>(1) + wire _bundle_structural_eq_1: UInt<1> + connect _bundle_structural_eq_1, and(eq(_cast_enum_to_bits_expr_2, _cast_enum_to_bits_expr_1), eq(io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect parent_out.opt_unit_flip, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_2: Ty2 + connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome) + connect _bundle_literal_expr_2.body, UInt<1>(0h1) + connect io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + connect parent_out.opt_bool, __enum_structural_eq @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_3: UInt<1> + match io.opt_bool_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_3, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_3, UInt<1>(1) + wire _cast_enum_to_bits_expr_4: UInt<1> + match _bundle_literal_expr_2.tag: + HdlNone: + connect _cast_enum_to_bits_expr_4, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_4, UInt<1>(1) + wire _bundle_structural_eq_2: UInt<1> + connect _bundle_structural_eq_2, and(eq(_cast_enum_to_bits_expr_3, _cast_enum_to_bits_expr_4), eq(io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect parent_out.opt_bool_flip, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_literal_expr_3: Ty2 + connect _bundle_literal_expr_3.tag, {|HdlNone, HdlSome|}(HdlSome) + wire _cast_bundle_to_bits_expr: Ty7 + connect _cast_bundle_to_bits_expr.tag, _cast_enum_to_bits_expr_1 + connect _cast_bundle_to_bits_expr.body, _bundle_literal_expr.body + wire _cast_to_bits_expr: UInt<1> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect _bundle_literal_expr_3.body, _cast_to_bits_expr + connect io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + connect parent_out.opt_opt_unit, __enum_structural_eq_1 @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_5: UInt<1> + match io.opt_opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_5, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_5, UInt<1>(1) + wire _cast_enum_to_bits_expr_6: UInt<1> + match _bundle_literal_expr_3.tag: + HdlNone: + connect _cast_enum_to_bits_expr_6, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_6, UInt<1>(1) + wire _bundle_structural_eq_3: UInt<1> + connect _bundle_structural_eq_3, and(eq(_cast_enum_to_bits_expr_5, _cast_enum_to_bits_expr_6), eq(io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect parent_out.opt_opt_unit_flip, _bundle_structural_eq_3 @[module-XXXXXXXXXX.rs 11:1] + wire _array_literal_expr: Ty2[2] + wire _bundle_literal_expr_4: Ty2 + connect _bundle_literal_expr_4.tag, {|HdlNone, HdlSome|}(HdlSome) + connect _bundle_literal_expr_4.body, UInt<1>(0h0) + connect _array_literal_expr[0], _bundle_literal_expr_4 + connect _array_literal_expr[1], _bundle_literal_expr_2 + connect io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect parent_out.array_opt_bool, and(__enum_structural_eq_2, __enum_structural_eq_3) @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_7: UInt<1> + match io.array_opt_bool_flip[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_7, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_7, UInt<1>(1) + wire _cast_enum_to_bits_expr_8: UInt<1> + match _array_literal_expr[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_8, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_8, UInt<1>(1) + wire _bundle_structural_eq_4: UInt<1> + connect _bundle_structural_eq_4, and(eq(_cast_enum_to_bits_expr_7, _cast_enum_to_bits_expr_8), eq(io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _cast_enum_to_bits_expr_9: UInt<1> + match io.array_opt_bool_flip[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_9, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_9, UInt<1>(1) + wire _cast_enum_to_bits_expr_10: UInt<1> + match _array_literal_expr[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_10, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_10, UInt<1>(1) + wire _bundle_structural_eq_5: UInt<1> + connect _bundle_structural_eq_5, and(eq(_cast_enum_to_bits_expr_9, _cast_enum_to_bits_expr_10), eq(io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect parent_out.array_opt_bool_flip, and(_bundle_structural_eq_4, _bundle_structural_eq_5) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_literal_expr_5: Ty3 + connect _bundle_literal_expr_5.`0`, _bundle_literal_expr_2 + connect _bundle_literal_expr_5.`1`, UInt<1>(0h1) + connect io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect parent_out.struct_opt_bool, and(__enum_structural_eq_4, eq(io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_11: UInt<1> + match io.struct_opt_bool_flip.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_11, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_11, UInt<1>(1) + wire _cast_enum_to_bits_expr_12: UInt<1> + match _bundle_literal_expr_5.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_12, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_12, UInt<1>(1) + wire _bundle_structural_eq_6: UInt<1> + connect _bundle_structural_eq_6, and(eq(_cast_enum_to_bits_expr_11, _cast_enum_to_bits_expr_12), eq(io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect parent_out.struct_opt_bool_flip, and(_bundle_structural_eq_6, eq(io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect extern_child.io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_13: UInt<1> + match extern_child.io.opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr_13, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_13, UInt<1>(1) + wire _bundle_structural_eq_7: UInt<1> + connect _bundle_structural_eq_7, and(eq(_cast_enum_to_bits_expr_13, _cast_enum_to_bits_expr_1), eq(extern_child.io.opt_unit.body, _bundle_literal_expr.body)) + connect extern_child_out.opt_unit, _bundle_structural_eq_7 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_14: UInt<1> + match extern_child.io.opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_14, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_14, UInt<1>(1) + wire _bundle_structural_eq_8: UInt<1> + connect _bundle_structural_eq_8, and(eq(_cast_enum_to_bits_expr_14, _cast_enum_to_bits_expr_1), eq(extern_child.io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect extern_child_out.opt_unit_flip, _bundle_structural_eq_8 @[module-XXXXXXXXXX.rs 9:1] + connect extern_child.io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child_out.opt_bool, __enum_structural_eq_5 @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_15: UInt<1> + match extern_child.io.opt_bool_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_15, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_15, UInt<1>(1) + wire _bundle_structural_eq_9: UInt<1> + connect _bundle_structural_eq_9, and(eq(_cast_enum_to_bits_expr_15, _cast_enum_to_bits_expr_4), eq(extern_child.io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect extern_child_out.opt_bool_flip, _bundle_structural_eq_9 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child.io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child_out.opt_opt_unit, __enum_structural_eq_6 @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_16: UInt<1> + match extern_child.io.opt_opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_16, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_16, UInt<1>(1) + wire _bundle_structural_eq_10: UInt<1> + connect _bundle_structural_eq_10, and(eq(_cast_enum_to_bits_expr_16, _cast_enum_to_bits_expr_6), eq(extern_child.io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect extern_child_out.opt_opt_unit_flip, _bundle_structural_eq_10 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect extern_child_out.array_opt_bool, and(__enum_structural_eq_7, __enum_structural_eq_8) @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_17: UInt<1> + match extern_child.io.array_opt_bool_flip[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_17, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_17, UInt<1>(1) + wire _bundle_structural_eq_11: UInt<1> + connect _bundle_structural_eq_11, and(eq(_cast_enum_to_bits_expr_17, _cast_enum_to_bits_expr_8), eq(extern_child.io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _cast_enum_to_bits_expr_18: UInt<1> + match extern_child.io.array_opt_bool_flip[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_18, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_18, UInt<1>(1) + wire _bundle_structural_eq_12: UInt<1> + connect _bundle_structural_eq_12, and(eq(_cast_enum_to_bits_expr_18, _cast_enum_to_bits_expr_10), eq(extern_child.io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect extern_child_out.array_opt_bool_flip, and(_bundle_structural_eq_11, _bundle_structural_eq_12) @[module-XXXXXXXXXX.rs 12:1] + connect extern_child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect extern_child_out.struct_opt_bool, and(__enum_structural_eq_9, eq(extern_child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_19: UInt<1> + match extern_child.io.struct_opt_bool_flip.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_19, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_19, UInt<1>(1) + wire _bundle_structural_eq_13: UInt<1> + connect _bundle_structural_eq_13, and(eq(_cast_enum_to_bits_expr_19, _cast_enum_to_bits_expr_12), eq(extern_child.io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect extern_child_out.struct_opt_bool_flip, and(_bundle_structural_eq_13, eq(extern_child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect child.io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_20: UInt<1> + match child.io.opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr_20, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_20, UInt<1>(1) + wire _bundle_structural_eq_14: UInt<1> + connect _bundle_structural_eq_14, and(eq(_cast_enum_to_bits_expr_20, _cast_enum_to_bits_expr_1), eq(child.io.opt_unit.body, _bundle_literal_expr.body)) + connect child_out.opt_unit, _bundle_structural_eq_14 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_enum_to_bits_expr_21: UInt<1> + match child.io.opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_21, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_21, UInt<1>(1) + wire _bundle_structural_eq_15: UInt<1> + connect _bundle_structural_eq_15, and(eq(_cast_enum_to_bits_expr_21, _cast_enum_to_bits_expr_1), eq(child.io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect child_out.opt_unit_flip, _bundle_structural_eq_15 @[module-XXXXXXXXXX.rs 9:1] + connect child.io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_22: UInt<1> + match child.io.opt_bool.tag: + HdlNone: + connect _cast_enum_to_bits_expr_22, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_22, UInt<1>(1) + wire _bundle_structural_eq_16: UInt<1> + connect _bundle_structural_eq_16, and(eq(_cast_enum_to_bits_expr_22, _cast_enum_to_bits_expr_4), eq(child.io.opt_bool.body, _bundle_literal_expr_2.body)) + connect child_out.opt_bool, _bundle_structural_eq_16 @[module-XXXXXXXXXX.rs 10:1] + wire _cast_enum_to_bits_expr_23: UInt<1> + match child.io.opt_bool_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_23, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_23, UInt<1>(1) + wire _bundle_structural_eq_17: UInt<1> + connect _bundle_structural_eq_17, and(eq(_cast_enum_to_bits_expr_23, _cast_enum_to_bits_expr_4), eq(child.io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect child_out.opt_bool_flip, _bundle_structural_eq_17 @[module-XXXXXXXXXX.rs 10:1] + connect child.io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_24: UInt<1> + match child.io.opt_opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr_24, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_24, UInt<1>(1) + wire _bundle_structural_eq_18: UInt<1> + connect _bundle_structural_eq_18, and(eq(_cast_enum_to_bits_expr_24, _cast_enum_to_bits_expr_6), eq(child.io.opt_opt_unit.body, _bundle_literal_expr_3.body)) + connect child_out.opt_opt_unit, _bundle_structural_eq_18 @[module-XXXXXXXXXX.rs 11:1] + wire _cast_enum_to_bits_expr_25: UInt<1> + match child.io.opt_opt_unit_flip.tag: + HdlNone: + connect _cast_enum_to_bits_expr_25, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_25, UInt<1>(1) + wire _bundle_structural_eq_19: UInt<1> + connect _bundle_structural_eq_19, and(eq(_cast_enum_to_bits_expr_25, _cast_enum_to_bits_expr_6), eq(child.io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect child_out.opt_opt_unit_flip, _bundle_structural_eq_19 @[module-XXXXXXXXXX.rs 11:1] + connect child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_26: UInt<1> + match child.io.array_opt_bool[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_26, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_26, UInt<1>(1) + wire _bundle_structural_eq_20: UInt<1> + connect _bundle_structural_eq_20, and(eq(_cast_enum_to_bits_expr_26, _cast_enum_to_bits_expr_8), eq(child.io.array_opt_bool[0].body, _array_literal_expr[0].body)) + wire _cast_enum_to_bits_expr_27: UInt<1> + match child.io.array_opt_bool[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_27, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_27, UInt<1>(1) + wire _bundle_structural_eq_21: UInt<1> + connect _bundle_structural_eq_21, and(eq(_cast_enum_to_bits_expr_27, _cast_enum_to_bits_expr_10), eq(child.io.array_opt_bool[1].body, _array_literal_expr[1].body)) + connect child_out.array_opt_bool, and(_bundle_structural_eq_20, _bundle_structural_eq_21) @[module-XXXXXXXXXX.rs 12:1] + wire _cast_enum_to_bits_expr_28: UInt<1> + match child.io.array_opt_bool_flip[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_28, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_28, UInt<1>(1) + wire _bundle_structural_eq_22: UInt<1> + connect _bundle_structural_eq_22, and(eq(_cast_enum_to_bits_expr_28, _cast_enum_to_bits_expr_8), eq(child.io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _cast_enum_to_bits_expr_29: UInt<1> + match child.io.array_opt_bool_flip[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_29, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_29, UInt<1>(1) + wire _bundle_structural_eq_23: UInt<1> + connect _bundle_structural_eq_23, and(eq(_cast_enum_to_bits_expr_29, _cast_enum_to_bits_expr_10), eq(child.io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect child_out.array_opt_bool_flip, and(_bundle_structural_eq_22, _bundle_structural_eq_23) @[module-XXXXXXXXXX.rs 12:1] + connect child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_30: UInt<1> + match child.io.struct_opt_bool.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_30, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_30, UInt<1>(1) + wire _bundle_structural_eq_24: UInt<1> + connect _bundle_structural_eq_24, and(eq(_cast_enum_to_bits_expr_30, _cast_enum_to_bits_expr_12), eq(child.io.struct_opt_bool.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect child_out.struct_opt_bool, and(_bundle_structural_eq_24, eq(child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _cast_enum_to_bits_expr_31: UInt<1> + match child.io.struct_opt_bool_flip.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_31, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_31, UInt<1>(1) + wire _bundle_structural_eq_25: UInt<1> + connect _bundle_structural_eq_25, and(eq(_cast_enum_to_bits_expr_31, _cast_enum_to_bits_expr_12), eq(child.io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect child_out.struct_opt_bool_flip, and(_bundle_structural_eq_25, eq(child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_32: UInt<1> + match io.opt_bool.tag: + HdlNone: + connect _cast_enum_to_bits_expr_32, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_32, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_32, _cast_enum_to_bits_expr_4): @[module-XXXXXXXXXX.rs 1:1] + match io.opt_bool.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq, eq(bits(io.opt_bool.body, 0, 0), bits(_bundle_literal_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_33: UInt<1> + match io.opt_opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr_33, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_33, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_33, _cast_enum_to_bits_expr_6): @[module-XXXXXXXXXX.rs 1:1] + match io.opt_opt_unit.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_1, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + wire __enum_structural_eq_10: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, __enum_structural_eq_10 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_10, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_bits_to_bundle_expr: Ty1 + wire _cast_bits_to_bundle_expr_flattened: Ty7 + connect _cast_bits_to_bundle_expr_flattened.tag, bits(bits(io.opt_opt_unit.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr: Ty0 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened.tag, 0)): + connect _cast_bits_to_enum_expr, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr.tag, _cast_bits_to_enum_expr + connect _cast_bits_to_bundle_expr_flattened.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr.body, _cast_bits_to_bundle_expr_flattened.body + wire _cast_bits_to_bundle_expr_1: Ty1 + wire _cast_bits_to_bundle_expr_flattened_1: Ty7 + connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(_bundle_literal_expr_3.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr_1: Ty0 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)): + connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1 + connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + wire _cast_enum_to_bits_expr_34: UInt<1> + match _cast_bits_to_bundle_expr.tag: + HdlNone: + connect _cast_enum_to_bits_expr_34, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_34, UInt<1>(1) + wire _cast_enum_to_bits_expr_35: UInt<1> + match _cast_bits_to_bundle_expr_1.tag: + HdlNone: + connect _cast_enum_to_bits_expr_35, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_35, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_34, _cast_enum_to_bits_expr_35): @[module-XXXXXXXXXX.rs 1:1] + match _cast_bits_to_bundle_expr.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + wire _cast_bits_to_bundle_expr_2: Ty6 + invalidate _cast_bits_to_bundle_expr_2 + wire _cast_bits_to_bundle_expr_3: Ty6 + invalidate _cast_bits_to_bundle_expr_3 + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_2, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_36: UInt<1> + match io.array_opt_bool[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_36, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_36, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_36, _cast_enum_to_bits_expr_8): @[module-XXXXXXXXXX.rs 1:1] + match io.array_opt_bool[0].tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_2, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_2, eq(bits(io.array_opt_bool[0].body, 0, 0), bits(_array_literal_expr[0].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_3, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_37: UInt<1> + match io.array_opt_bool[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_37, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_37, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_37, _cast_enum_to_bits_expr_10): @[module-XXXXXXXXXX.rs 1:1] + match io.array_opt_bool[1].tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_3, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_3, eq(bits(io.array_opt_bool[1].body, 0, 0), bits(_array_literal_expr[1].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_4, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_38: UInt<1> + match io.struct_opt_bool.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_38, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_38, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_38, _cast_enum_to_bits_expr_12): @[module-XXXXXXXXXX.rs 1:1] + match io.struct_opt_bool.`0`.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_4, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_4, eq(bits(io.struct_opt_bool.`0`.body, 0, 0), bits(_bundle_literal_expr_5.`0`.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_5, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_39: UInt<1> + match extern_child.io.opt_bool.tag: + HdlNone: + connect _cast_enum_to_bits_expr_39, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_39, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_39, _cast_enum_to_bits_expr_4): @[module-XXXXXXXXXX.rs 1:1] + match extern_child.io.opt_bool.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_5, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_5, eq(bits(extern_child.io.opt_bool.body, 0, 0), bits(_bundle_literal_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_40: UInt<1> + match extern_child.io.opt_opt_unit.tag: + HdlNone: + connect _cast_enum_to_bits_expr_40, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_40, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_40, _cast_enum_to_bits_expr_6): @[module-XXXXXXXXXX.rs 1:1] + match extern_child.io.opt_opt_unit.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_6, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + wire __enum_structural_eq_11: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, __enum_structural_eq_11 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_11, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_bits_to_bundle_expr_4: Ty1 + wire _cast_bits_to_bundle_expr_flattened_2: Ty7 + connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(extern_child.io.opt_opt_unit.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr_2: Ty0 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)): + connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_enum_expr_2 + connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_2.body + wire _cast_bits_to_bundle_expr_5: Ty1 + wire _cast_bits_to_bundle_expr_flattened_3: Ty7 + connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(_bundle_literal_expr_3.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr_3: Ty0 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)): + connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_enum_expr_3 + connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_3.body + wire _cast_enum_to_bits_expr_41: UInt<1> + match _cast_bits_to_bundle_expr_4.tag: + HdlNone: + connect _cast_enum_to_bits_expr_41, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_41, UInt<1>(1) + wire _cast_enum_to_bits_expr_42: UInt<1> + match _cast_bits_to_bundle_expr_5.tag: + HdlNone: + connect _cast_enum_to_bits_expr_42, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_42, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_41, _cast_enum_to_bits_expr_42): @[module-XXXXXXXXXX.rs 1:1] + match _cast_bits_to_bundle_expr_4.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + wire _cast_bits_to_bundle_expr_6: Ty6 + invalidate _cast_bits_to_bundle_expr_6 + wire _cast_bits_to_bundle_expr_7: Ty6 + invalidate _cast_bits_to_bundle_expr_7 + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_7, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_43: UInt<1> + match extern_child.io.array_opt_bool[0].tag: + HdlNone: + connect _cast_enum_to_bits_expr_43, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_43, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_43, _cast_enum_to_bits_expr_8): @[module-XXXXXXXXXX.rs 1:1] + match extern_child.io.array_opt_bool[0].tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_7, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_7, eq(bits(extern_child.io.array_opt_bool[0].body, 0, 0), bits(_array_literal_expr[0].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_8, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_44: UInt<1> + match extern_child.io.array_opt_bool[1].tag: + HdlNone: + connect _cast_enum_to_bits_expr_44, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_44, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_44, _cast_enum_to_bits_expr_10): @[module-XXXXXXXXXX.rs 1:1] + match extern_child.io.array_opt_bool[1].tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_8, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_8, eq(bits(extern_child.io.array_opt_bool[1].body, 0, 0), bits(_array_literal_expr[1].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_9, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr_45: UInt<1> + match extern_child.io.struct_opt_bool.`0`.tag: + HdlNone: + connect _cast_enum_to_bits_expr_45, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_45, UInt<1>(1) + when eq(_cast_enum_to_bits_expr_45, _cast_enum_to_bits_expr_12): @[module-XXXXXXXXXX.rs 1:1] + match extern_child.io.struct_opt_bool.`0`.tag: @[module-XXXXXXXXXX.rs 1:1] + HdlNone: + connect __enum_structural_eq_9, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + HdlSome: + connect __enum_structural_eq_9, eq(bits(extern_child.io.struct_opt_bool.`0`.body, 0, 0), bits(_bundle_literal_expr_5.`0`.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + extmodule check_deduce_structural_eq_flags_extern_child: @[module-XXXXXXXXXX-2.rs 1:1] + output io: Ty4 @[module-XXXXXXXXXX-2.rs 2:1] + defname = check_deduce_structural_eq_flags_extern_child + module check_deduce_structural_eq_flags_child: @[module-XXXXXXXXXX-3.rs 1:1] + output io: Ty4 @[module-XXXXXXXXXX-3.rs 2:1] + connect io.opt_unit, io.opt_unit_flip @[module-XXXXXXXXXX-3.rs 4:1] + connect io.opt_bool, io.opt_bool_flip @[module-XXXXXXXXXX-3.rs 5:1] + connect io.opt_opt_unit, io.opt_opt_unit_flip @[module-XXXXXXXXXX-3.rs 6:1] + connect io.array_opt_bool, io.array_opt_bool_flip @[module-XXXXXXXXXX-3.rs 7:1] + connect io.struct_opt_bool, io.struct_opt_bool_flip @[module-XXXXXXXXXX-3.rs 8:1] +", + }; + let m_bundle_of_uints = simplify_enums(m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); + dbg!(m_bundle_of_uints); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m_bundle_of_uints => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, + "/test/check_deduce_structural_eq_flags_parent.fir": r"FIRRTL version 3.2.0 +circuit check_deduce_structural_eq_flags_parent: + type Ty0 = {tag: UInt<1>, body: UInt<0>} + type Ty1 = {tag: UInt<1>, body: UInt<1>} + type Ty2 = {`0`: Ty1, `1`: UInt<1>} + type Ty3 = {flip opt_unit_flip: Ty0, opt_unit: Ty0, flip opt_bool_flip: Ty1, opt_bool: Ty1, flip opt_opt_unit_flip: Ty1, opt_opt_unit: Ty1, flip array_opt_bool_flip: Ty1[2], array_opt_bool: Ty1[2], flip struct_opt_bool_flip: Ty2, struct_opt_bool: Ty2} + type Ty4 = {opt_unit_flip: UInt<1>, opt_unit: UInt<1>, opt_bool_flip: UInt<1>, opt_bool: UInt<1>, opt_opt_unit_flip: UInt<1>, opt_opt_unit: UInt<1>, array_opt_bool_flip: UInt<1>, array_opt_bool: UInt<1>, struct_opt_bool_flip: UInt<1>, struct_opt_bool: UInt<1>} + type Ty5 = {} + type Ty6 = {io: Ty3} + module check_deduce_structural_eq_flags_parent: @[module-XXXXXXXXXX.rs 1:1] + input io: Ty3 @[module-XXXXXXXXXX.rs 2:1] + output parent_out: Ty4 @[module-XXXXXXXXXX.rs 3:1] + output extern_child_out: Ty4 @[module-XXXXXXXXXX.rs 4:1] + output child_out: Ty4 @[module-XXXXXXXXXX.rs 5:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_1: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_2: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_3: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_4: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_5: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_6: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_7: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_8: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_9: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + inst extern_child of check_deduce_structural_eq_flags_extern_child @[module-XXXXXXXXXX.rs 6:1] + inst child of check_deduce_structural_eq_flags_child @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_literal_expr: Ty0 + connect _bundle_literal_expr.tag, UInt<1>(0h1) + wire _bundle_literal_expr_1: Ty5 + invalidate _bundle_literal_expr_1 + connect _bundle_literal_expr.body, UInt<0>(0) + connect io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq: UInt<1> + connect _bundle_structural_eq, and(eq(io.opt_unit.tag, _bundle_literal_expr.tag), eq(io.opt_unit.body, _bundle_literal_expr.body)) + connect parent_out.opt_unit, _bundle_structural_eq @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq_1: UInt<1> + connect _bundle_structural_eq_1, and(eq(io.opt_unit_flip.tag, _bundle_literal_expr.tag), eq(io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect parent_out.opt_unit_flip, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_2: Ty1 + connect _bundle_literal_expr_2.tag, UInt<1>(0h1) + connect _bundle_literal_expr_2.body, UInt<1>(0h1) + connect io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + connect parent_out.opt_bool, __enum_structural_eq @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_structural_eq_2: UInt<1> + connect _bundle_structural_eq_2, and(eq(io.opt_bool_flip.tag, _bundle_literal_expr_2.tag), eq(io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect parent_out.opt_bool_flip, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_literal_expr_3: Ty1 + connect _bundle_literal_expr_3.tag, UInt<1>(0h1) + wire _cast_bundle_to_bits_expr: Ty0 + connect _cast_bundle_to_bits_expr.tag, _bundle_literal_expr.tag + connect _cast_bundle_to_bits_expr.body, _bundle_literal_expr.body + wire _cast_to_bits_expr: UInt<1> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect _bundle_literal_expr_3.body, _cast_to_bits_expr + connect io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + connect parent_out.opt_opt_unit, __enum_structural_eq_1 @[module-XXXXXXXXXX.rs 11:1] + wire _bundle_structural_eq_3: UInt<1> + connect _bundle_structural_eq_3, and(eq(io.opt_opt_unit_flip.tag, _bundle_literal_expr_3.tag), eq(io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect parent_out.opt_opt_unit_flip, _bundle_structural_eq_3 @[module-XXXXXXXXXX.rs 11:1] + wire _array_literal_expr: Ty1[2] + wire _bundle_literal_expr_4: Ty1 + connect _bundle_literal_expr_4.tag, UInt<1>(0h1) + connect _bundle_literal_expr_4.body, UInt<1>(0h0) + connect _array_literal_expr[0], _bundle_literal_expr_4 + connect _array_literal_expr[1], _bundle_literal_expr_2 + connect io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect parent_out.array_opt_bool, and(__enum_structural_eq_2, __enum_structural_eq_3) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_structural_eq_4: UInt<1> + connect _bundle_structural_eq_4, and(eq(io.array_opt_bool_flip[0].tag, _array_literal_expr[0].tag), eq(io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _bundle_structural_eq_5: UInt<1> + connect _bundle_structural_eq_5, and(eq(io.array_opt_bool_flip[1].tag, _array_literal_expr[1].tag), eq(io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect parent_out.array_opt_bool_flip, and(_bundle_structural_eq_4, _bundle_structural_eq_5) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_literal_expr_5: Ty2 + connect _bundle_literal_expr_5.`0`, _bundle_literal_expr_2 + connect _bundle_literal_expr_5.`1`, UInt<1>(0h1) + connect io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect parent_out.struct_opt_bool, and(__enum_structural_eq_4, eq(io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _bundle_structural_eq_6: UInt<1> + connect _bundle_structural_eq_6, and(eq(io.struct_opt_bool_flip.`0`.tag, _bundle_literal_expr_5.`0`.tag), eq(io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect parent_out.struct_opt_bool_flip, and(_bundle_structural_eq_6, eq(io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect extern_child.io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq_7: UInt<1> + connect _bundle_structural_eq_7, and(eq(extern_child.io.opt_unit.tag, _bundle_literal_expr.tag), eq(extern_child.io.opt_unit.body, _bundle_literal_expr.body)) + connect extern_child_out.opt_unit, _bundle_structural_eq_7 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq_8: UInt<1> + connect _bundle_structural_eq_8, and(eq(extern_child.io.opt_unit_flip.tag, _bundle_literal_expr.tag), eq(extern_child.io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect extern_child_out.opt_unit_flip, _bundle_structural_eq_8 @[module-XXXXXXXXXX.rs 9:1] + connect extern_child.io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child_out.opt_bool, __enum_structural_eq_5 @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_structural_eq_9: UInt<1> + connect _bundle_structural_eq_9, and(eq(extern_child.io.opt_bool_flip.tag, _bundle_literal_expr_2.tag), eq(extern_child.io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect extern_child_out.opt_bool_flip, _bundle_structural_eq_9 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child.io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child_out.opt_opt_unit, __enum_structural_eq_6 @[module-XXXXXXXXXX.rs 11:1] + wire _bundle_structural_eq_10: UInt<1> + connect _bundle_structural_eq_10, and(eq(extern_child.io.opt_opt_unit_flip.tag, _bundle_literal_expr_3.tag), eq(extern_child.io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect extern_child_out.opt_opt_unit_flip, _bundle_structural_eq_10 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect extern_child_out.array_opt_bool, and(__enum_structural_eq_7, __enum_structural_eq_8) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_structural_eq_11: UInt<1> + connect _bundle_structural_eq_11, and(eq(extern_child.io.array_opt_bool_flip[0].tag, _array_literal_expr[0].tag), eq(extern_child.io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _bundle_structural_eq_12: UInt<1> + connect _bundle_structural_eq_12, and(eq(extern_child.io.array_opt_bool_flip[1].tag, _array_literal_expr[1].tag), eq(extern_child.io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect extern_child_out.array_opt_bool_flip, and(_bundle_structural_eq_11, _bundle_structural_eq_12) @[module-XXXXXXXXXX.rs 12:1] + connect extern_child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect extern_child_out.struct_opt_bool, and(__enum_structural_eq_9, eq(extern_child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _bundle_structural_eq_13: UInt<1> + connect _bundle_structural_eq_13, and(eq(extern_child.io.struct_opt_bool_flip.`0`.tag, _bundle_literal_expr_5.`0`.tag), eq(extern_child.io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect extern_child_out.struct_opt_bool_flip, and(_bundle_structural_eq_13, eq(extern_child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect child.io.opt_unit_flip, _bundle_literal_expr @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq_14: UInt<1> + connect _bundle_structural_eq_14, and(eq(child.io.opt_unit.tag, _bundle_literal_expr.tag), eq(child.io.opt_unit.body, _bundle_literal_expr.body)) + connect child_out.opt_unit, _bundle_structural_eq_14 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_structural_eq_15: UInt<1> + connect _bundle_structural_eq_15, and(eq(child.io.opt_unit_flip.tag, _bundle_literal_expr.tag), eq(child.io.opt_unit_flip.body, _bundle_literal_expr.body)) + connect child_out.opt_unit_flip, _bundle_structural_eq_15 @[module-XXXXXXXXXX.rs 9:1] + connect child.io.opt_bool_flip, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_structural_eq_16: UInt<1> + connect _bundle_structural_eq_16, and(eq(child.io.opt_bool.tag, _bundle_literal_expr_2.tag), eq(child.io.opt_bool.body, _bundle_literal_expr_2.body)) + connect child_out.opt_bool, _bundle_structural_eq_16 @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_structural_eq_17: UInt<1> + connect _bundle_structural_eq_17, and(eq(child.io.opt_bool_flip.tag, _bundle_literal_expr_2.tag), eq(child.io.opt_bool_flip.body, _bundle_literal_expr_2.body)) + connect child_out.opt_bool_flip, _bundle_structural_eq_17 @[module-XXXXXXXXXX.rs 10:1] + connect child.io.opt_opt_unit_flip, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 11:1] + wire _bundle_structural_eq_18: UInt<1> + connect _bundle_structural_eq_18, and(eq(child.io.opt_opt_unit.tag, _bundle_literal_expr_3.tag), eq(child.io.opt_opt_unit.body, _bundle_literal_expr_3.body)) + connect child_out.opt_opt_unit, _bundle_structural_eq_18 @[module-XXXXXXXXXX.rs 11:1] + wire _bundle_structural_eq_19: UInt<1> + connect _bundle_structural_eq_19, and(eq(child.io.opt_opt_unit_flip.tag, _bundle_literal_expr_3.tag), eq(child.io.opt_opt_unit_flip.body, _bundle_literal_expr_3.body)) + connect child_out.opt_opt_unit_flip, _bundle_structural_eq_19 @[module-XXXXXXXXXX.rs 11:1] + connect child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_structural_eq_20: UInt<1> + connect _bundle_structural_eq_20, and(eq(child.io.array_opt_bool[0].tag, _array_literal_expr[0].tag), eq(child.io.array_opt_bool[0].body, _array_literal_expr[0].body)) + wire _bundle_structural_eq_21: UInt<1> + connect _bundle_structural_eq_21, and(eq(child.io.array_opt_bool[1].tag, _array_literal_expr[1].tag), eq(child.io.array_opt_bool[1].body, _array_literal_expr[1].body)) + connect child_out.array_opt_bool, and(_bundle_structural_eq_20, _bundle_structural_eq_21) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_structural_eq_22: UInt<1> + connect _bundle_structural_eq_22, and(eq(child.io.array_opt_bool_flip[0].tag, _array_literal_expr[0].tag), eq(child.io.array_opt_bool_flip[0].body, _array_literal_expr[0].body)) + wire _bundle_structural_eq_23: UInt<1> + connect _bundle_structural_eq_23, and(eq(child.io.array_opt_bool_flip[1].tag, _array_literal_expr[1].tag), eq(child.io.array_opt_bool_flip[1].body, _array_literal_expr[1].body)) + connect child_out.array_opt_bool_flip, and(_bundle_structural_eq_22, _bundle_structural_eq_23) @[module-XXXXXXXXXX.rs 12:1] + connect child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + wire _bundle_structural_eq_24: UInt<1> + connect _bundle_structural_eq_24, and(eq(child.io.struct_opt_bool.`0`.tag, _bundle_literal_expr_5.`0`.tag), eq(child.io.struct_opt_bool.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect child_out.struct_opt_bool, and(_bundle_structural_eq_24, eq(child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + wire _bundle_structural_eq_25: UInt<1> + connect _bundle_structural_eq_25, and(eq(child.io.struct_opt_bool_flip.`0`.tag, _bundle_literal_expr_5.`0`.tag), eq(child.io.struct_opt_bool_flip.`0`.body, _bundle_literal_expr_5.`0`.body)) + connect child_out.struct_opt_bool_flip, and(_bundle_structural_eq_25, eq(child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(io.opt_bool.tag, _bundle_literal_expr_2.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(io.opt_bool.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq, eq(bits(io.opt_bool.body, 0, 0), bits(_bundle_literal_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(io.opt_opt_unit.tag, _bundle_literal_expr_3.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(io.opt_opt_unit.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire __enum_structural_eq_10: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, __enum_structural_eq_10 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_10, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_bits_to_bundle_expr: Ty0 + wire _cast_bits_to_bundle_expr_flattened: Ty0 + connect _cast_bits_to_bundle_expr_flattened.tag, bits(bits(io.opt_opt_unit.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr.tag, _cast_bits_to_bundle_expr_flattened.tag + connect _cast_bits_to_bundle_expr_flattened.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr.body, _cast_bits_to_bundle_expr_flattened.body + wire _cast_bits_to_bundle_expr_1: Ty0 + wire _cast_bits_to_bundle_expr_flattened_1: Ty0 + connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(_bundle_literal_expr_3.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag + connect _cast_bits_to_bundle_expr_flattened_1.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + when eq(_cast_bits_to_bundle_expr.tag, _cast_bits_to_bundle_expr_1.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(_cast_bits_to_bundle_expr.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_bundle_expr_2: Ty5 + invalidate _cast_bits_to_bundle_expr_2 + wire _cast_bits_to_bundle_expr_3: Ty5 + invalidate _cast_bits_to_bundle_expr_3 + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_2, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(io.array_opt_bool[0].tag, _array_literal_expr[0].tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(io.array_opt_bool[0].tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_2, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_2, eq(bits(io.array_opt_bool[0].body, 0, 0), bits(_array_literal_expr[0].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_3, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(io.array_opt_bool[1].tag, _array_literal_expr[1].tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(io.array_opt_bool[1].tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_3, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_3, eq(bits(io.array_opt_bool[1].body, 0, 0), bits(_array_literal_expr[1].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_4, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(io.struct_opt_bool.`0`.tag, _bundle_literal_expr_5.`0`.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(io.struct_opt_bool.`0`.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_4, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_4, eq(bits(io.struct_opt_bool.`0`.body, 0, 0), bits(_bundle_literal_expr_5.`0`.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_5, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.opt_bool.tag, _bundle_literal_expr_2.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.opt_bool.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_5, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_5, eq(bits(extern_child.io.opt_bool.body, 0, 0), bits(_bundle_literal_expr_2.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.opt_opt_unit.tag, _bundle_literal_expr_3.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.opt_opt_unit.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire __enum_structural_eq_11: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, __enum_structural_eq_11 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_11, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_bits_to_bundle_expr_4: Ty0 + wire _cast_bits_to_bundle_expr_flattened_2: Ty0 + connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(extern_child.io.opt_opt_unit.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_flattened_2.tag + connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_2.body + wire _cast_bits_to_bundle_expr_5: Ty0 + wire _cast_bits_to_bundle_expr_flattened_3: Ty0 + connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(_bundle_literal_expr_3.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_bundle_expr_flattened_3.tag + connect _cast_bits_to_bundle_expr_flattened_3.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_3.body + when eq(_cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_5.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(_cast_bits_to_bundle_expr_4.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_bundle_expr_6: Ty5 + invalidate _cast_bits_to_bundle_expr_6 + wire _cast_bits_to_bundle_expr_7: Ty5 + invalidate _cast_bits_to_bundle_expr_7 + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_7, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.array_opt_bool[0].tag, _array_literal_expr[0].tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.array_opt_bool[0].tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_7, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_7, eq(bits(extern_child.io.array_opt_bool[0].body, 0, 0), bits(_array_literal_expr[0].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_8, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.array_opt_bool[1].tag, _array_literal_expr[1].tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.array_opt_bool[1].tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_8, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_8, eq(bits(extern_child.io.array_opt_bool[1].body, 0, 0), bits(_array_literal_expr[1].body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_9, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.struct_opt_bool.`0`.tag, _bundle_literal_expr_5.`0`.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(extern_child.io.struct_opt_bool.`0`.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_9, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_9, eq(bits(extern_child.io.struct_opt_bool.`0`.body, 0, 0), bits(_bundle_literal_expr_5.`0`.body, 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + extmodule check_deduce_structural_eq_flags_extern_child: @[module-XXXXXXXXXX-2.rs 1:1] + output io: Ty3 @[module-XXXXXXXXXX-2.rs 2:1] + defname = check_deduce_structural_eq_flags_extern_child + module check_deduce_structural_eq_flags_child: @[module-XXXXXXXXXX-3.rs 1:1] + output io: Ty3 @[module-XXXXXXXXXX-3.rs 2:1] + connect io.opt_unit, io.opt_unit_flip @[module-XXXXXXXXXX-3.rs 4:1] + connect io.opt_bool, io.opt_bool_flip @[module-XXXXXXXXXX-3.rs 5:1] + connect io.opt_opt_unit, io.opt_opt_unit_flip @[module-XXXXXXXXXX-3.rs 6:1] + connect io.array_opt_bool, io.array_opt_bool_flip @[module-XXXXXXXXXX-3.rs 7:1] + connect io.struct_opt_bool, io.struct_opt_bool_flip @[module-XXXXXXXXXX-3.rs 8:1] +", + }; + let m_uints = simplify_enums(m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); + dbg!(m_uints); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m_uints => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, + "/test/check_deduce_structural_eq_flags_parent.fir": r"FIRRTL version 3.2.0 +circuit check_deduce_structural_eq_flags_parent: + type Ty0 = {`0`: UInt<2>, `1`: UInt<1>} + type Ty1 = {flip opt_unit_flip: UInt<1>, opt_unit: UInt<1>, flip opt_bool_flip: UInt<2>, opt_bool: UInt<2>, flip opt_opt_unit_flip: UInt<2>, opt_opt_unit: UInt<2>, flip array_opt_bool_flip: UInt<2>[2], array_opt_bool: UInt<2>[2], flip struct_opt_bool_flip: Ty0, struct_opt_bool: Ty0} + type Ty2 = {opt_unit_flip: UInt<1>, opt_unit: UInt<1>, opt_bool_flip: UInt<1>, opt_bool: UInt<1>, opt_opt_unit_flip: UInt<1>, opt_opt_unit: UInt<1>, array_opt_bool_flip: UInt<1>, array_opt_bool: UInt<1>, struct_opt_bool_flip: UInt<1>, struct_opt_bool: UInt<1>} + type Ty3 = {tag: UInt<1>, body: UInt<0>} + type Ty4 = {} + type Ty5 = {tag: UInt<1>, body: UInt<1>} + type Ty6 = {io: Ty1} + module check_deduce_structural_eq_flags_parent: @[module-XXXXXXXXXX.rs 1:1] + input io: Ty1 @[module-XXXXXXXXXX.rs 2:1] + output parent_out: Ty2 @[module-XXXXXXXXXX.rs 3:1] + output extern_child_out: Ty2 @[module-XXXXXXXXXX.rs 4:1] + output child_out: Ty2 @[module-XXXXXXXXXX.rs 5:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_1: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_2: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_3: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_4: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_5: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_6: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_7: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_8: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + wire __enum_structural_eq_9: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + inst extern_child of check_deduce_structural_eq_flags_extern_child @[module-XXXXXXXXXX.rs 6:1] + inst child of check_deduce_structural_eq_flags_child @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_literal_expr: Ty3 + connect _bundle_literal_expr.tag, UInt<1>(0h1) + wire _bundle_literal_expr_1: Ty4 + invalidate _bundle_literal_expr_1 + connect _bundle_literal_expr.body, UInt<0>(0) + wire _cast_bundle_to_bits_expr: Ty3 + connect _cast_bundle_to_bits_expr.tag, _bundle_literal_expr.tag + connect _cast_bundle_to_bits_expr.body, _bundle_literal_expr.body + wire _cast_to_bits_expr: UInt<1> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect io.opt_unit_flip, _cast_to_bits_expr @[module-XXXXXXXXXX.rs 9:1] + connect parent_out.opt_unit, eq(io.opt_unit, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + connect parent_out.opt_unit_flip, eq(io.opt_unit_flip, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_2: Ty5 + connect _bundle_literal_expr_2.tag, UInt<1>(0h1) + connect _bundle_literal_expr_2.body, UInt<1>(0h1) + wire _cast_bundle_to_bits_expr_1: Ty5 + connect _cast_bundle_to_bits_expr_1.tag, _bundle_literal_expr_2.tag + connect _cast_bundle_to_bits_expr_1.body, _bundle_literal_expr_2.body + wire _cast_to_bits_expr_1: UInt<2> + connect _cast_to_bits_expr_1, cat(_cast_bundle_to_bits_expr_1.body, _cast_bundle_to_bits_expr_1.tag) + connect io.opt_bool_flip, _cast_to_bits_expr_1 @[module-XXXXXXXXXX.rs 10:1] + connect parent_out.opt_bool, __enum_structural_eq @[module-XXXXXXXXXX.rs 10:1] + connect parent_out.opt_bool_flip, eq(io.opt_bool_flip, _cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 10:1] + wire _bundle_literal_expr_3: Ty5 + connect _bundle_literal_expr_3.tag, UInt<1>(0h1) + connect _bundle_literal_expr_3.body, _cast_to_bits_expr + wire _cast_bundle_to_bits_expr_2: Ty5 + connect _cast_bundle_to_bits_expr_2.tag, _bundle_literal_expr_3.tag + connect _cast_bundle_to_bits_expr_2.body, _bundle_literal_expr_3.body + wire _cast_to_bits_expr_2: UInt<2> + connect _cast_to_bits_expr_2, cat(_cast_bundle_to_bits_expr_2.body, _cast_bundle_to_bits_expr_2.tag) + connect io.opt_opt_unit_flip, _cast_to_bits_expr_2 @[module-XXXXXXXXXX.rs 11:1] + connect parent_out.opt_opt_unit, __enum_structural_eq_1 @[module-XXXXXXXXXX.rs 11:1] + connect parent_out.opt_opt_unit_flip, eq(io.opt_opt_unit_flip, _cast_to_bits_expr_2) @[module-XXXXXXXXXX.rs 11:1] + wire _array_literal_expr: UInt<2>[2] + wire _bundle_literal_expr_4: Ty5 + connect _bundle_literal_expr_4.tag, UInt<1>(0h1) + connect _bundle_literal_expr_4.body, UInt<1>(0h0) + wire _cast_bundle_to_bits_expr_3: Ty5 + connect _cast_bundle_to_bits_expr_3.tag, _bundle_literal_expr_4.tag + connect _cast_bundle_to_bits_expr_3.body, _bundle_literal_expr_4.body + wire _cast_to_bits_expr_3: UInt<2> + connect _cast_to_bits_expr_3, cat(_cast_bundle_to_bits_expr_3.body, _cast_bundle_to_bits_expr_3.tag) + connect _array_literal_expr[0], _cast_to_bits_expr_3 + connect _array_literal_expr[1], _cast_to_bits_expr_1 + connect io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect parent_out.array_opt_bool, and(__enum_structural_eq_2, __enum_structural_eq_3) @[module-XXXXXXXXXX.rs 12:1] + connect parent_out.array_opt_bool_flip, and(eq(io.array_opt_bool_flip[0], _array_literal_expr[0]), eq(io.array_opt_bool_flip[1], _array_literal_expr[1])) @[module-XXXXXXXXXX.rs 12:1] + wire _bundle_literal_expr_5: Ty0 + connect _bundle_literal_expr_5.`0`, _cast_to_bits_expr_1 + connect _bundle_literal_expr_5.`1`, UInt<1>(0h1) + connect io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect parent_out.struct_opt_bool, and(__enum_structural_eq_4, eq(io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect parent_out.struct_opt_bool_flip, and(eq(io.struct_opt_bool_flip.`0`, _bundle_literal_expr_5.`0`), eq(io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect extern_child.io.opt_unit_flip, _cast_to_bits_expr @[module-XXXXXXXXXX.rs 9:1] + connect extern_child_out.opt_unit, eq(extern_child.io.opt_unit, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + connect extern_child_out.opt_unit_flip, eq(extern_child.io.opt_unit_flip, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + connect extern_child.io.opt_bool_flip, _cast_to_bits_expr_1 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child_out.opt_bool, __enum_structural_eq_5 @[module-XXXXXXXXXX.rs 10:1] + connect extern_child_out.opt_bool_flip, eq(extern_child.io.opt_bool_flip, _cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 10:1] + connect extern_child.io.opt_opt_unit_flip, _cast_to_bits_expr_2 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child_out.opt_opt_unit, __enum_structural_eq_6 @[module-XXXXXXXXXX.rs 11:1] + connect extern_child_out.opt_opt_unit_flip, eq(extern_child.io.opt_opt_unit_flip, _cast_to_bits_expr_2) @[module-XXXXXXXXXX.rs 11:1] + connect extern_child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect extern_child_out.array_opt_bool, and(__enum_structural_eq_7, __enum_structural_eq_8) @[module-XXXXXXXXXX.rs 12:1] + connect extern_child_out.array_opt_bool_flip, and(eq(extern_child.io.array_opt_bool_flip[0], _array_literal_expr[0]), eq(extern_child.io.array_opt_bool_flip[1], _array_literal_expr[1])) @[module-XXXXXXXXXX.rs 12:1] + connect extern_child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect extern_child_out.struct_opt_bool, and(__enum_structural_eq_9, eq(extern_child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect extern_child_out.struct_opt_bool_flip, and(eq(extern_child.io.struct_opt_bool_flip.`0`, _bundle_literal_expr_5.`0`), eq(extern_child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect child.io.opt_unit_flip, _cast_to_bits_expr @[module-XXXXXXXXXX.rs 9:1] + connect child_out.opt_unit, eq(child.io.opt_unit, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + connect child_out.opt_unit_flip, eq(child.io.opt_unit_flip, _cast_to_bits_expr) @[module-XXXXXXXXXX.rs 9:1] + connect child.io.opt_bool_flip, _cast_to_bits_expr_1 @[module-XXXXXXXXXX.rs 10:1] + connect child_out.opt_bool, eq(child.io.opt_bool, _cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 10:1] + connect child_out.opt_bool_flip, eq(child.io.opt_bool_flip, _cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 10:1] + connect child.io.opt_opt_unit_flip, _cast_to_bits_expr_2 @[module-XXXXXXXXXX.rs 11:1] + connect child_out.opt_opt_unit, eq(child.io.opt_opt_unit, _cast_to_bits_expr_2) @[module-XXXXXXXXXX.rs 11:1] + connect child_out.opt_opt_unit_flip, eq(child.io.opt_opt_unit_flip, _cast_to_bits_expr_2) @[module-XXXXXXXXXX.rs 11:1] + connect child.io.array_opt_bool_flip, _array_literal_expr @[module-XXXXXXXXXX.rs 12:1] + connect child_out.array_opt_bool, and(eq(child.io.array_opt_bool[0], _array_literal_expr[0]), eq(child.io.array_opt_bool[1], _array_literal_expr[1])) @[module-XXXXXXXXXX.rs 12:1] + connect child_out.array_opt_bool_flip, and(eq(child.io.array_opt_bool_flip[0], _array_literal_expr[0]), eq(child.io.array_opt_bool_flip[1], _array_literal_expr[1])) @[module-XXXXXXXXXX.rs 12:1] + connect child.io.struct_opt_bool_flip, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 13:1] + connect child_out.struct_opt_bool, and(eq(child.io.struct_opt_bool.`0`, _bundle_literal_expr_5.`0`), eq(child.io.struct_opt_bool.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect child_out.struct_opt_bool_flip, and(eq(child.io.struct_opt_bool_flip.`0`, _bundle_literal_expr_5.`0`), eq(child.io.struct_opt_bool_flip.`1`, _bundle_literal_expr_5.`1`)) @[module-XXXXXXXXXX.rs 13:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.opt_bool, 0, 0), bits(_cast_to_bits_expr_1, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.opt_bool, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq, eq(bits(bits(io.opt_bool, 1, 1), 0, 0), bits(bits(_cast_to_bits_expr_1, 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.opt_opt_unit, 0, 0), bits(_cast_to_bits_expr_2, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.opt_opt_unit, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire __enum_structural_eq_10: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_1, __enum_structural_eq_10 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_10, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(bits(bits(io.opt_opt_unit, 1, 1), 0, 0), 0, 0), bits(bits(bits(_cast_to_bits_expr_2, 1, 1), 0, 0), 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(bits(bits(io.opt_opt_unit, 1, 1), 0, 0), 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_bundle_expr: Ty4 + invalidate _cast_bits_to_bundle_expr + wire _cast_bits_to_bundle_expr_1: Ty4 + invalidate _cast_bits_to_bundle_expr_1 + connect __enum_structural_eq_10, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_2, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.array_opt_bool[0], 0, 0), bits(_array_literal_expr[0], 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.array_opt_bool[0], 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_2, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_2, eq(bits(bits(io.array_opt_bool[0], 1, 1), 0, 0), bits(bits(_array_literal_expr[0], 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_3, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.array_opt_bool[1], 0, 0), bits(_array_literal_expr[1], 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.array_opt_bool[1], 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_3, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_3, eq(bits(bits(io.array_opt_bool[1], 1, 1), 0, 0), bits(bits(_array_literal_expr[1], 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_4, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.struct_opt_bool.`0`, 0, 0), bits(_bundle_literal_expr_5.`0`, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(io.struct_opt_bool.`0`, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_4, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_4, eq(bits(bits(io.struct_opt_bool.`0`, 1, 1), 0, 0), bits(bits(_bundle_literal_expr_5.`0`, 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_5, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.opt_bool, 0, 0), bits(_cast_to_bits_expr_1, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.opt_bool, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_5, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_5, eq(bits(bits(extern_child.io.opt_bool, 1, 1), 0, 0), bits(bits(_cast_to_bits_expr_1, 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.opt_opt_unit, 0, 0), bits(_cast_to_bits_expr_2, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.opt_opt_unit, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire __enum_structural_eq_11: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_6, __enum_structural_eq_11 @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_11, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(bits(bits(extern_child.io.opt_opt_unit, 1, 1), 0, 0), 0, 0), bits(bits(bits(_cast_to_bits_expr_2, 1, 1), 0, 0), 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(bits(bits(extern_child.io.opt_opt_unit, 1, 1), 0, 0), 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_bundle_expr_2: Ty4 + invalidate _cast_bits_to_bundle_expr_2 + wire _cast_bits_to_bundle_expr_3: Ty4 + invalidate _cast_bits_to_bundle_expr_3 + connect __enum_structural_eq_11, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_7, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.array_opt_bool[0], 0, 0), bits(_array_literal_expr[0], 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.array_opt_bool[0], 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_7, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_7, eq(bits(bits(extern_child.io.array_opt_bool[0], 1, 1), 0, 0), bits(bits(_array_literal_expr[0], 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_8, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.array_opt_bool[1], 0, 0), bits(_array_literal_expr[1], 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.array_opt_bool[1], 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_8, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_8, eq(bits(bits(extern_child.io.array_opt_bool[1], 1, 1), 0, 0), bits(bits(_array_literal_expr[1], 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_9, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.struct_opt_bool.`0`, 0, 0), bits(_bundle_literal_expr_5.`0`, 0, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(extern_child.io.struct_opt_bool.`0`, 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq_9, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else: + connect __enum_structural_eq_9, eq(bits(bits(extern_child.io.struct_opt_bool.`0`, 1, 1), 0, 0), bits(bits(_bundle_literal_expr_5.`0`, 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 1:1] + extmodule check_deduce_structural_eq_flags_extern_child: @[module-XXXXXXXXXX-2.rs 1:1] + output io: Ty1 @[module-XXXXXXXXXX-2.rs 2:1] + defname = check_deduce_structural_eq_flags_extern_child + module check_deduce_structural_eq_flags_child: @[module-XXXXXXXXXX-3.rs 1:1] + output io: Ty1 @[module-XXXXXXXXXX-3.rs 2:1] + connect io.opt_unit, io.opt_unit_flip @[module-XXXXXXXXXX-3.rs 4:1] + connect io.opt_bool, io.opt_bool_flip @[module-XXXXXXXXXX-3.rs 5:1] + connect io.opt_opt_unit, io.opt_opt_unit_flip @[module-XXXXXXXXXX-3.rs 6:1] + connect io.array_opt_bool, io.array_opt_bool_flip @[module-XXXXXXXXXX-3.rs 7:1] + connect io.struct_opt_bool, io.struct_opt_bool_flip @[module-XXXXXXXXXX-3.rs 8:1] +", + }; +} diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index a6699ed..7d12810 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -4863,32 +4863,16 @@ circuit check_struct_cmp_eq: input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] - wire _array_literal_expr: UInt<1>[3] - connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) - connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) - connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) - wire _cast_array_to_bits_expr: UInt<1>[3] - connect _cast_array_to_bits_expr[0], _array_literal_expr[0] - connect _cast_array_to_bits_expr[1], _array_literal_expr[1] - connect _cast_array_to_bits_expr[2], _array_literal_expr[2] - wire _cast_to_bits_expr: UInt<3> - connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) - connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] - wire _array_literal_expr_1: UInt<1>[3] - connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) - connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) - connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) - wire _cast_array_to_bits_expr_1: UInt<1>[3] - connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] - connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] - connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] - wire _cast_to_bits_expr_1: UInt<3> - connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) - connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] wire _bundle_structural_eq: UInt<1> - connect _bundle_structural_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) - connect test_struct_cmp_eq, _bundle_structural_eq @[module-XXXXXXXXXX.rs 11:1] - connect test_struct_cmp_ne, not(_bundle_structural_eq) @[module-XXXXXXXXXX.rs 13:1] + connect _bundle_structural_eq, and(eq(tuple_lhs.`0`, tuple_rhs.`0`), eq(tuple_lhs.`1`, tuple_rhs.`1`)) + wire _bundle_structural_eq_1: UInt<1> + connect _bundle_structural_eq_1, and(_bundle_structural_eq, eq(tuple_lhs.`2`, tuple_rhs.`2`)) + connect tuple_cmp_eq, _bundle_structural_eq_1 @[module-XXXXXXXXXX.rs 5:1] + connect tuple_cmp_ne, not(_bundle_structural_eq_1) @[module-XXXXXXXXXX.rs 7:1] + wire _bundle_structural_eq_2: UInt<1> + connect _bundle_structural_eq_2, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) + connect test_struct_cmp_eq, _bundle_structural_eq_2 @[module-XXXXXXXXXX.rs 11:1] + connect test_struct_cmp_ne, not(_bundle_structural_eq_2) @[module-XXXXXXXXXX.rs 13:1] connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1] connect test_struct_2_cmp_ne, not(eq(test_struct_2_lhs.v, test_struct_2_rhs.v)) @[module-XXXXXXXXXX.rs 19:1] connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]