diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f7ad68d..451bc7e 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -1133,6 +1133,7 @@ impl ToTokens for ParsedBundle { let mut fields_expr_ne = vec![]; let mut fields_valueless_eq = vec![]; let mut fields_valueless_ne = vec![]; + let mut fields_structural_eq = vec![]; for field in fields.named() { let field_ident = field.ident(); let field_ty = field.ty(); @@ -1141,6 +1142,9 @@ impl ToTokens for ParsedBundle { .push(parse_quote_spanned! {cmp_eq.span=> #field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty> }); + fields_structural_eq.push(quote_spanned! {cmp_eq.span=> + <#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ + }); fields_value_eq.push(quote_spanned! {span=> ::fayalite::expr::HdlPartialEqImpl::cmp_value_eq( __lhs.#field_ident, @@ -1188,6 +1192,7 @@ impl ToTokens for ParsedBundle { let expr_ne_body; let valueless_eq_body; let valueless_ne_body; + let structural_eq; if fields_len == 0 { value_eq_body = quote_spanned! {span=> true @@ -1207,6 +1212,9 @@ impl ToTokens for ParsedBundle { valueless_ne_body = quote_spanned! {span=> ::fayalite::expr::Valueless::new(::fayalite::int::Bool) }; + structural_eq = quote_spanned! {span=> + true + }; } else { value_eq_body = quote_spanned! {span=> #(#fields_value_eq)&* @@ -1230,12 +1238,17 @@ impl ToTokens for ParsedBundle { let __rhs = ::fayalite::expr::ValueType::ty(&__rhs); #(#fields_valueless_ne)|* }; + structural_eq = quote_spanned! {span=> + #(#fields_structural_eq)&&* + }; }; quote_spanned! {span=> #[automatically_derived] impl #impl_generics ::fayalite::expr::HdlPartialEqImpl for #target #type_generics #cmp_eq_where_clause { + const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq; + #[track_caller] fn cmp_value_eq( __lhs: Self, @@ -1261,6 +1274,16 @@ impl ToTokens for ParsedBundle { __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + if >::TRY_STRUCTURAL_EQ { + if let ::fayalite::__std::result::Result::Ok(__retval) = + ::fayalite::expr::ops::StructuralEq::try_new( + ::fayalite::expr::Expr::canonical(__lhs), + ::fayalite::expr::Expr::canonical(__rhs), + ) + { + return ::fayalite::expr::ToExpr::to_expr(&__retval); + } + } #expr_eq_body } @@ -1269,6 +1292,14 @@ impl ToTokens for ParsedBundle { __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + if >::TRY_STRUCTURAL_EQ { + return !::fayalite::expr::ToExpr::to_expr( + &::fayalite::expr::ops::StructuralEq::new( + ::fayalite::expr::Expr::canonical(__lhs), + ::fayalite::expr::Expr::canonical(__rhs), + ), + ); + } #expr_ne_body } diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index e9f013b..68fcc02 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -643,7 +643,9 @@ impl ToTokens for ParsedEnum { #where_clause { #[allow(non_snake_case, dead_code)] - #vis fn #ident(#self_token) -> ::fayalite::sim::value::SimValue<#target #type_generics> { + #vis fn #ident( + #self_token, + ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { ::fayalite::sim::value::SimValue::from_value( #self_token.#sim_builder_ty_field_ident, #sim_value_ident::#ident(::fayalite::enum_::EnumPaddingSimValue::new()), @@ -929,8 +931,14 @@ impl ToTokens for ParsedEnum { impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics #where_clause { - fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { - <#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f) + fn fmt( + &self, + f: &mut ::fayalite::__std::fmt::Formatter<'_>, + ) -> ::fayalite::__std::fmt::Result { + <#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display( + self, + f, + ) } } }.to_tokens(tokens); @@ -946,6 +954,7 @@ impl ToTokens for ParsedEnum { let mut variants_value_eq = vec![]; let mut variants_expr_eq = vec![]; let mut fields_valueless_eq = vec![]; + let mut structural_eq: Option = None; for ( variant_index, ParsedVariant { @@ -971,8 +980,23 @@ impl ToTokens for ParsedEnum { .push(parse_quote_spanned! {cmp_eq.span=> #field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty> }); + match &mut structural_eq { + Some(structural_eq) => { + structural_eq.extend(quote_spanned! {cmp_eq.span=> + && <#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ + }); + } + None => { + structural_eq = Some(quote_spanned! {cmp_eq.span=> + <#field_ty as ::fayalite::expr::HdlPartialEqImpl<#field_ty>>::TRY_STRUCTURAL_EQ + }); + } + } variants_value_eq.push(quote_spanned! {span=> - (#sim_value_ident::#variant_ident(__lhs_field, _), #sim_value_ident::#variant_ident(__rhs_field, _)) => { + ( + #sim_value_ident::#variant_ident(__lhs_field, _), + #sim_value_ident::#variant_ident(__rhs_field, _), + ) => { ::fayalite::expr::HdlPartialEqImpl::cmp_value_eq( __lhs.#variant_ident, ::fayalite::__std::borrow::Cow::Borrowed(__lhs_field), @@ -1002,7 +1026,10 @@ impl ToTokens for ParsedEnum { else { ::fayalite::__std::unreachable!(); }; - ::fayalite::module::connect(__retval, ::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(__lhs, __rhs)); + ::fayalite::module::connect( + __retval, + ::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(__lhs, __rhs), + ); } }); fields_valueless_eq.push(quote_spanned! {span=> @@ -1043,7 +1070,10 @@ impl ToTokens for ParsedEnum { } if let Some(sim_value_unknown_variant_name) = &sim_value_unknown_variant_name { variants_value_eq.push(quote_spanned! {span=> - (#sim_value_ident::#sim_value_unknown_variant_name(__lhs_unknown), #sim_value_ident::#sim_value_unknown_variant_name(__rhs_unknown)) => { + ( + #sim_value_ident::#sim_value_unknown_variant_name(__lhs_unknown), + #sim_value_ident::#sim_value_unknown_variant_name(__rhs_unknown), + ) => { __lhs_unknown == __rhs_unknown } }); @@ -1060,17 +1090,26 @@ impl ToTokens for ParsedEnum { } }; let cmp_expr_eq_wire_name = format!("{ident}_cmp_eq"); + let structural_eq = structural_eq.unwrap_or_else(|| { + quote_spanned! {span=> + true + } + }); quote_spanned! {span=> #[automatically_derived] impl #impl_generics ::fayalite::expr::HdlPartialEqImpl for #target #type_generics #cmp_eq_where_clause { + const TRY_STRUCTURAL_EQ: ::fayalite::__std::primitive::bool = #structural_eq; + #[track_caller] fn cmp_value_eq( __lhs: Self, - __lhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __lhs_value: ::fayalite::__std::borrow::Cow<'_, + ::SimValue>, __rhs: Self, - __rhs_value: ::fayalite::__std::borrow::Cow<'_, ::SimValue>, + __rhs_value: ::fayalite::__std::borrow::Cow<'_, + ::SimValue>, ) -> ::fayalite::__std::primitive::bool { match (&*__lhs_value, &*__rhs_value) { #(#variants_value_eq)* @@ -1083,7 +1122,20 @@ impl ToTokens for ParsedEnum { __lhs: ::fayalite::expr::Expr, __rhs: ::fayalite::expr::Expr, ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { - let __retval = ::fayalite::module::wire(::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name), ::fayalite::int::Bool); + if >::TRY_STRUCTURAL_EQ { + if let ::fayalite::__std::result::Result::Ok(__retval) = + ::fayalite::expr::ops::StructuralEq::try_new( + ::fayalite::expr::Expr::canonical(__lhs), + ::fayalite::expr::Expr::canonical(__rhs), + ) + { + return ::fayalite::expr::ToExpr::to_expr(&__retval); + } + } + let __retval = ::fayalite::module::wire( + ::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name), + ::fayalite::int::Bool, + ); ::fayalite::module::connect(__retval, false); let mut __lhs_match_variant_iter = ::fayalite::module::match_(__lhs); #(#variants_expr_eq)* @@ -1112,7 +1164,8 @@ impl ToTokens for ParsedEnum { type SimValue = #sim_value_ident #type_generics; type MatchVariant = #match_variant_ident #type_generics; type MatchActiveScope = ::fayalite::module::Scope; - type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope; + type MatchVariantAndInactiveScope = + ::fayalite::enum_::EnumMatchVariantAndInactiveScope; type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter; fn match_variants( @@ -1125,7 +1178,9 @@ impl ToTokens for ParsedEnum { ::fayalite::int::Bool } fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { - ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token))) + ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new( + ::fayalite::enum_::EnumType::variants(#self_token), + )) } #[track_caller] #[allow(non_snake_case)] @@ -1134,7 +1189,11 @@ impl ToTokens for ParsedEnum { ::fayalite::__std::panic!("expected enum"); }; let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_); - ::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants"); + ::fayalite::__std::assert_eq!( + #variants_token.len(), + #variants_len, + "enum has wrong number of variants", + ); Self { #(#from_canonical_body_fields)* } @@ -1180,7 +1239,10 @@ impl ToTokens for ParsedEnum { type SimBuilder = #sim_builder_ident #type_generics; fn match_activate_scope( v: ::MatchVariantAndInactiveScope, - ) -> (::MatchVariant, ::MatchActiveScope) { + ) -> ( + ::MatchVariant, + ::MatchActiveScope, + ) { let (#variant_access_token, scope) = v.activate(); ( match #variant_access_token.variant_index() { @@ -1200,7 +1262,10 @@ impl ToTokens for ParsedEnum { impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics #where_clause { - fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + fn fmt( + &self, + f: &mut ::fayalite::__std::fmt::Formatter<'_>, + ) -> ::fayalite::__std::fmt::Result { <#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f) } } @@ -1213,7 +1278,10 @@ impl ToTokens for ParsedEnum { &self, ty: #target #type_generics, ) -> ::fayalite::sim::value::SimValue<#target #type_generics> { - ::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self)) + ::fayalite::sim::value::SimValue::from_value( + ty, + ::fayalite::__std::clone::Clone::clone(self), + ) } fn into_sim_value_with_type( self, diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index fa754fd..4e93093 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -367,6 +367,8 @@ impl HdlPartialEqImpl> for where Lhs: HdlPartialEqImpl, { + const TRY_STRUCTURAL_EQ: bool = >::TRY_STRUCTURAL_EQ; + fn cmp_value_eq( lhs: Self, lhs_value: Cow<'_, Self::SimValue>, diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 5fad35c..70eb798 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -708,6 +708,8 @@ macro_rules! impl_tuples { } } impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) { + const TRY_STRUCTURAL_EQ: bool = true $(&& <$Lhs as HdlPartialEqImpl<$Rhs>>::TRY_STRUCTURAL_EQ)*; + #[track_caller] fn cmp_value_eq( lhs: Self, diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index eb4bf0f..f9f239f 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -221,6 +221,7 @@ expr_enum! { CastBitsTo(ops::CastBitsTo), ToTraceAsString(ops::ToTraceAsString), TraceAsStringAsInner(ops::TraceAsStringAsInner), + StructuralEq(ops::StructuralEq), ModuleIO(ModuleIO), Instance(Instance), Wire(Wire), @@ -701,6 +702,7 @@ macro_rules! impl_hdl_cmp { impl_helper = $HdlCmpImplHelper:ident, $(impl_helper_base = $HdlCmpImplHelperBase:ident,)? impl_helper_sealed = $HdlCmpImplHelperSealed:ident, + $(try_structural_eq = $TRY_STRUCTURAL_EQ:ident,)? ] $vis:vis trait $HdlCmp:ident<$Rhs:ident: ValueType>: ValueType $(+ $HdlCmpImplBase:ident)?> $(+ $HdlCmpBase:ident)? @@ -729,6 +731,8 @@ macro_rules! impl_hdl_cmp { } $vis trait $HdlCmpImpl<$Rhs: Type>: Type $(+ $HdlCmpImplBase<$Rhs>)? { + $(const $TRY_STRUCTURAL_EQ: bool;)? + $(#[track_caller] fn $cmp_value_fn( $cmp_value_lhs: Self, @@ -912,6 +916,7 @@ impl_hdl_cmp! { #[ impl_helper = HdlPartialEqImplHelper, impl_helper_sealed = HdlPartialEqImplHelperSealed, + try_structural_eq = TRY_STRUCTURAL_EQ, ] pub trait HdlPartialEq: ValueType > diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 5d335a5..7c5af38 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -22,7 +22,7 @@ use crate::{ Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue, }, - intern::{Intern, Interned}, + intern::{Intern, Interned, MemoizeGeneric}, phantom_const::{PhantomConst, PhantomConstValue}, reset::{ AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset, @@ -2984,6 +2984,7 @@ macro_rules! impl_compare_op { #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] #[to_cmp_value($lhs_compare_value:ident => $lhs_compare_value_expr:expr, $rhs_compare_value:ident => $rhs_compare_value_expr:expr)] #[type($Lhs:ty, $Rhs:ty)] + $(#[try_structural_eq = $TRY_STRUCTURAL_EQ:ident])? #[trait($Trait:ident)] $( struct $name:ident; @@ -3045,6 +3046,7 @@ macro_rules! impl_compare_op { })* impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs { + $(const $TRY_STRUCTURAL_EQ: bool = true;)? $(fn $value_method( _lhs: Self, $lhs_compare_value: Cow<'_, ::SimValue>, @@ -3065,6 +3067,7 @@ impl_compare_op! { #[to_dyn_type(lhs => lhs, rhs => rhs)] #[to_cmp_value(lhs_value => &*lhs_value, rhs_value => &*rhs_value)] #[type(Bool, Bool)] + #[try_structural_eq = TRY_STRUCTURAL_EQ] #[trait(HdlPartialEqImpl)] struct CmpEqB; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); struct CmpNeB; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); @@ -3088,6 +3091,7 @@ impl_compare_op! { #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(UIntType, UIntType)] + #[try_structural_eq = TRY_STRUCTURAL_EQ] #[trait(HdlPartialEqImpl)] struct CmpEqU; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); struct CmpNeU; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); @@ -3112,6 +3116,7 @@ impl_compare_op! { #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[to_cmp_value(lhs_value => &lhs_value.to_bigint(), rhs_value => &rhs_value.to_bigint())] #[type(SIntType, SIntType)] + #[try_structural_eq = TRY_STRUCTURAL_EQ] #[trait(HdlPartialEqImpl)] struct CmpEqS; fn cmp_value_eq(); fn cmp_expr_eq(); PartialEq::eq(); struct CmpNeS; fn cmp_value_ne(); fn cmp_expr_ne(); PartialEq::ne(); @@ -3133,6 +3138,8 @@ impl_compare_op! { macro_rules! impl_compare_forwards_to_bool { ($ty:ident) => { impl HdlPartialEqImpl for $ty { + const TRY_STRUCTURAL_EQ: bool = true; + #[track_caller] fn cmp_value_eq( _lhs: Self, @@ -4948,3 +4955,270 @@ impl ToExpr for SimIoForGlobal { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub struct StructuralEqFlags { + pub assume_padding_is_zeroed: bool, +} + +impl StructuralEqFlags { + pub const DEFAULT: Self = Self { + assume_padding_is_zeroed: false, + }; +} + +impl Default for StructuralEqFlags { + fn default() -> Self { + Self::DEFAULT + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[non_exhaustive] +pub enum StructuralEqError { + TypesAreNotEqual { + lhs: CanonicalType, + rhs: CanonicalType, + }, + TypeContainsSimOnly { + ty: CanonicalType, + }, +} + +impl fmt::Display for StructuralEqError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::TypesAreNotEqual { lhs, rhs } => write!( + f, + "StructuralEq lhs type must be the same as rhs type:\nlhs: {lhs:#?}\nrhs: {rhs:#?}\n", + ), + Self::TypeContainsSimOnly { ty } => write!( + f, + "StructuralEq input type must not contain SimOnly type: {ty:#?}", + ), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct StructuralEq { + lhs: Expr, + rhs: Expr, + flags: StructuralEqFlags, + literal_bits: Result, NotALiteralExpr>, +} + +impl StructuralEq { + fn literal_eq( + ty: CanonicalType, + l: Interned, + r: Interned, + ) -> Result { + enum PairRefOrInternedBitSlice<'a> { + Ref(&'a BitSlice, &'a BitSlice), + Interned(Interned, Interned), + } + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize(CanonicalType); + impl MemoizeGeneric for MyMemoize { + type InputRef<'a> = (&'a BitSlice, &'a BitSlice); + type InputOwned = (Interned, Interned); + type InputCow<'a> = PairRefOrInternedBitSlice<'a>; + type Output = Result; + + fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { + a == b + } + + fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { + let (l, r) = input; + (l, r) + } + + fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { + match input { + PairRefOrInternedBitSlice::Ref(l, r) => (l.intern(), r.intern()), + PairRefOrInternedBitSlice::Interned(l, r) => (l, r), + } + } + + fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { + match input { + PairRefOrInternedBitSlice::Ref(l, r) => (l, r), + PairRefOrInternedBitSlice::Interned(l, r) => (l, r), + } + } + + fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { + let (l, r) = input; + PairRefOrInternedBitSlice::Interned(l, r) + } + + fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { + let (l, r) = input; + PairRefOrInternedBitSlice::Ref(l, r) + } + + fn inner(self, input: Self::InputRef<'_>) -> Self::Output { + let (mut l, mut r) = input; + match self.0 { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => Ok(l == r), + CanonicalType::Array(ty) => { + let element_ty = ty.element(); + let element_bit_width = element_ty.bit_width(); + let mut eq = true; + for element_index in 0..ty.len() { + if !Self(element_ty).get(( + &l[element_bit_width * element_index..][..element_bit_width], + &r[element_bit_width * element_index..][..element_bit_width], + ))? { + eq = false; + break; + } + } + Ok(eq) + } + CanonicalType::Enum(ty) => { + let discriminant_bit_width = ty.discriminant_bit_width(); + let (l_discriminant_bits, l_body_bits) = l.split_at(discriminant_bit_width); + let (r_discriminant_bits, r_body_bits) = r.split_at(discriminant_bit_width); + if l_discriminant_bits != r_discriminant_bits { + return Ok(false); + } + let mut discriminant = 0usize; + discriminant.view_bits_mut::()[..l_discriminant_bits.len()] + .copy_from_bitslice(l_discriminant_bits); + match ty.variants().get(discriminant) { + Some(&EnumVariant { + name: _, + ty: Some(variant_ty), + }) => { + let variant_bit_width = variant_ty.bit_width(); + Self(variant_ty).get(( + &l_body_bits[..variant_bit_width], + &r_body_bits[..variant_bit_width], + )) + } + Some(EnumVariant { name: _, ty: None }) => Ok(true), + None => Err(NotALiteralExpr), + } + } + CanonicalType::Bundle(ty) => { + let mut eq = true; + for field in &ty.fields() { + let field_bit_width = field.ty.bit_width(); + let l_field; + let r_field; + (l_field, l) = l.split_at(field_bit_width); + (r_field, r) = r.split_at(field_bit_width); + if !Self(field.ty).get((l_field, r_field))? { + eq = false; + break; + } + } + Ok(eq) + } + CanonicalType::DynSimOnly(_) => unreachable!("doesn't have literal_bits"), + CanonicalType::TraceAsString(ty) => Self(ty.inner_ty()).get((l, r)), + } + } + } + MyMemoize(ty).get_owned((l, r)) + } + #[track_caller] + pub fn new(lhs: Expr, rhs: Expr) -> Self { + Self::with_flags(lhs, rhs, StructuralEqFlags::DEFAULT) + } + #[track_caller] + pub fn with_flags( + lhs: Expr, + rhs: Expr, + flags: StructuralEqFlags, + ) -> Self { + match Self::try_with_flags(lhs, rhs, flags) { + Ok(retval) => retval, + Err(e) => panic!("{e}"), + } + } + pub fn try_new( + lhs: Expr, + rhs: Expr, + ) -> Result { + Self::try_with_flags(lhs, rhs, StructuralEqFlags::DEFAULT) + } + pub fn try_with_flags( + lhs: Expr, + rhs: Expr, + flags: StructuralEqFlags, + ) -> Result { + let ty = lhs.ty(); + let rhs_ty = rhs.ty(); + if ty != rhs_ty { + return Err(StructuralEqError::TypesAreNotEqual { + lhs: ty, + rhs: rhs_ty, + }); + } + if ty.contains_sim_only() { + return Err(StructuralEqError::TypeContainsSimOnly { ty }); + } + Ok(Self { + lhs, + rhs, + flags, + literal_bits: lhs.to_literal_bits().and_then(|lhs| { + let rhs = rhs.to_literal_bits()?; + if flags.assume_padding_is_zeroed { + lhs == rhs + } else { + Self::literal_eq(ty, lhs, rhs)? + } + .to_literal_bits() + }), + }) + } + pub fn lhs(self) -> Expr { + self.lhs + } + pub fn rhs(self) -> Expr { + self.rhs + } + pub fn flags(self) -> StructuralEqFlags { + self.flags + } +} + +impl ToLiteralBits for StructuralEq { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + self.literal_bits + } +} + +impl_get_target_none!([] StructuralEq); + +impl ValueType for StructuralEq { + type Type = Bool; + type ValueCategory = ValueCategoryExpr; + + fn ty(&self) -> Self::Type { + Bool + } +} + +impl ToExpr for StructuralEq { + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::StructuralEq(*self).intern(), + __ty: Bool, + __flow: Flow::Source, + } + } +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 60cc0d1..cad55a8 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -7,7 +7,7 @@ use crate::{ bundle::{BundleField, BundleType}, enum_::{EnumType, EnumVariant}, expr::{ - ExprEnum, + CastToImpl, ExprEnum, ops::{self, VariantAccess}, target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, @@ -389,6 +389,7 @@ struct BlockDefinitionsCache { cast_bits_to_array_exprs: RefCell>, cast_bits_to_phantom_const_exprs: RefCell>, per_module_formal_inputs: RefCell>, + structural_eq_exprs: RefCell>, } struct BlockDefinitions<'a> { @@ -1774,6 +1775,143 @@ endmodule }, ) } + fn expr_structural_eq_bit>( + &mut self, + lhs: Expr, + rhs: Expr, + definitions: &BlockDefinitions<'_>, + const_ty: bool, + ) -> Result { + self.expr_binary( + "eq", + Expr::::from_canonical(lhs).cast_to(Bool), + Expr::::from_canonical(rhs).cast_to(Bool), + definitions, + const_ty, + ) + } + fn expr_structural_eq( + &mut self, + ty: CanonicalType, + lhs: String, + rhs: String, + definitions: &BlockDefinitions<'_>, + const_ty: bool, + ) -> Result { + match ty { + CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_) => { + Ok(format!("eq({lhs}, {rhs})")) + } + CanonicalType::Array(ty) => { + if ty.len() == 1 { + return self.expr_structural_eq( + ty.element(), + lhs + "[0]", + rhs + "[0]", + definitions, + const_ty, + ); + } else if ty.is_empty() { + return Ok(self.bool_literal(true)); + } + definitions.get_or_write_definition( + (lhs, rhs), + |c| &c.structural_eq_exprs, + |definitions, (lhs, rhs)| { + let mut retval = None; + for array_index in 0..ty.len() { + let element_eq = self.expr_structural_eq( + ty.element(), + format!("{lhs}[{array_index}]"), + format!("{rhs}[{array_index}]"), + &definitions, + const_ty, + )?; + retval = match retval { + Some(old_eq) => { + let ident = self.module.ns.make_new("_array_structural_eq"); + definitions + .add_definition_line(format_args!("wire {ident}: UInt<1>")); + definitions.add_definition_line(format_args!( + "connect {ident}, and({old_eq}, {element_eq})" + )); + Some(ident.to_string()) + } + None => Some(element_eq), + }; + } + Ok(retval.expect("known to be Some")) + }, + ) + } + CanonicalType::Enum(ty) => { + let lhs = self.expr_cast_enum_to_bits( + lhs, + ty, + definitions, + Indent { + indent_depth: &Cell::new(0), + indent: self.indent.indent, + }, + )?; + let rhs = self.expr_cast_enum_to_bits( + rhs, + ty, + definitions, + Indent { + indent_depth: &Cell::new(0), + indent: self.indent.indent, + }, + )?; + Ok(format!("eq({lhs}, {rhs})")) + } + CanonicalType::Bundle(ty) => { + let fields = ty.fields(); + if fields.is_empty() { + return Ok(self.bool_literal(true)); + } + definitions.get_or_write_definition( + (lhs, rhs), + |c| &c.structural_eq_exprs, + |definitions, (lhs, rhs)| { + let mut retval = None; + for field in fields { + let field_ident = self.type_state.get_bundle_field(ty, field.name)?; + let field_eq = self.expr_structural_eq( + field.ty, + format!("{lhs}.{field_ident}"), + format!("{rhs}.{field_ident}"), + &definitions, + const_ty, + )?; + retval = match retval { + Some(old_eq) => { + let ident = self.module.ns.make_new("_bundle_structural_eq"); + definitions + .add_definition_line(format_args!("wire {ident}: UInt<1>")); + definitions.add_definition_line(format_args!( + "connect {ident}, and({old_eq}, {field_eq})" + )); + Some(ident.to_string()) + } + None => Some(field_eq), + }; + } + Ok(retval.expect("known to be Some")) + }, + ) + } + CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => Ok(format!("eq(asUInt({lhs}), asUInt({rhs}))")), + CanonicalType::PhantomConst(_) => Ok(self.bool_literal(true)), + CanonicalType::DynSimOnly(_) => Err(FirrtlError::SimOnlyValuesAreNotPermitted.into()), + CanonicalType::TraceAsString(ty) => { + self.expr_structural_eq(ty.inner_ty(), lhs, rhs, definitions, const_ty) + } + } + } fn expr( &mut self, expr: Expr, @@ -2117,6 +2255,13 @@ endmodule ExprEnum::TraceAsStringAsInner(expr) => { self.expr(Expr::canonical(expr.arg()), definitions, const_ty) } + ExprEnum::StructuralEq(expr) => { + let ty = expr.lhs().ty(); + assert_eq!(ty, expr.rhs().ty()); + let lhs = self.expr(expr.lhs(), definitions, const_ty)?; + let rhs = self.expr(expr.rhs(), definitions, const_ty)?; + self.expr_structural_eq(ty, lhs, rhs, definitions, const_ty) + } ExprEnum::ModuleIO(expr) => Ok(self.module.ns.get(expr.name_id()).to_string()), ExprEnum::Instance(expr) => { assert!(!const_ty, "not a constant"); diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index edf2e25..4292d08 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -177,6 +177,8 @@ impl CastToImpl for Bool { } impl HdlPartialEqImpl for UIntInRangeMaskType { + const TRY_STRUCTURAL_EQ: bool = true; + #[track_caller] fn cmp_value_eq( _lhs: Self, @@ -570,6 +572,8 @@ macro_rules! define_uint_in_range_type { HdlPartialEqImpl<$UIntInRangeType> for $UIntInRangeType { + const TRY_STRUCTURAL_EQ: bool = true; + fn cmp_value_eq( _lhs: Self, lhs_value: Cow<'_, Self::SimValue>, @@ -657,6 +661,8 @@ macro_rules! define_uint_in_range_type { impl HdlPartialEqImpl> for $UIntInRangeType { + const TRY_STRUCTURAL_EQ: bool = false; + fn cmp_value_eq( _lhs: Self, lhs_value: Cow<'_, Self::SimValue>, @@ -676,6 +682,8 @@ macro_rules! define_uint_in_range_type { impl HdlPartialEqImpl<$UIntInRangeType> for UIntType { + const TRY_STRUCTURAL_EQ: bool = false; + fn cmp_value_eq( _lhs: Self, lhs_value: Cow<'_, Self::SimValue>, diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 2535694..0968be6 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -2206,6 +2206,7 @@ impl transform::visit::Visitor for AssertExprValidity<'_> { | ExprEnum::CastBitsTo(_) | ExprEnum::ToTraceAsString(_) | ExprEnum::TraceAsStringAsInner(_) + | ExprEnum::StructuralEq(_) | ExprEnum::FormalInput(_) => v.default_visit(self), ExprEnum::VariantAccess(_) | ExprEnum::ModuleIO(_) diff --git a/crates/fayalite/src/module/transform/deduce_resets.rs b/crates/fayalite/src/module/transform/deduce_resets.rs index 611401b..50a58c5 100644 --- a/crates/fayalite/src/module/transform/deduce_resets.rs +++ b/crates/fayalite/src/module/transform/deduce_resets.rs @@ -1208,6 +1208,7 @@ impl RunPass

for ExprEnum { Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)) } ExprEnum::ToTraceAsString(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), + ExprEnum::StructuralEq(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::ModuleIO(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::Instance(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), ExprEnum::Wire(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)), @@ -1653,6 +1654,35 @@ impl RunPassExpr for ops::ToTraceAsString { } } +impl RunPassExpr for ops::StructuralEq { + type Args<'a> = [Expr; 2]; + + fn args<'a>(&'a self) -> Self::Args<'a> { + [self.lhs(), self.rhs()] + } + + fn source_location(&self) -> Option { + None + } + + fn union_parts( + &self, + _resets: Resets, + args_resets: Vec, + mut pass_args: PassArgs<'_, BuildResetGraph>, + ) -> Result<(), DeduceResetsError> { + pass_args.union(args_resets[0], args_resets[1], None) + } + + fn new( + &self, + _ty: CanonicalType, + new_args: Vec>, + ) -> Result { + Ok(Self::with_flags(new_args[0], new_args[1], self.flags())) + } +} + impl RunPassExpr for ModuleIO { type Args<'a> = [Expr; 0]; diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 280701d..67b02b0 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -1,28 +1,23 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - array::{Array, ArrayType}, - bundle::{Bundle, BundleField, BundleType}, - enum_::{Enum, EnumType, EnumVariant}, + bundle::{BundleField, BundleType}, + enum_::{EnumType, EnumVariant}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, ValueType, - ops::{self, EnumLiteral}, + ExprEnum, + ops::{self, EnumLiteral, StructuralEq, StructuralEqFlags}, }, - hdl, - int::UInt, intern::{Intern, InternSlice, Interned, Memoize}, - memory::{DynPortType, Mem, MemPort}, + memory::{DynPortType, MemPort}, module::{ - Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, + Block, Id, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, transform::visit::{Fold, Folder}, }, - source_location::SourceLocation, - ty::{CanonicalType, TraceAsString, Type}, + prelude::*, util::HashMap, - wire::Wire, }; -use core::fmt; use serde::{Deserialize, Serialize}; +use std::fmt; #[derive(Debug)] pub enum SimplifyEnumsError { @@ -97,6 +92,7 @@ enum EnumTypeState { struct ModuleState { module_name: NameId, expr_cache: HashMap, + source_location: SourceLocation, } impl ModuleState { @@ -110,6 +106,45 @@ struct State { replacement_mem_ports: HashMap, Wire>, kind: SimplifyEnumsKind, module_state_stack: Vec, + new_prefix_stmts_for_block: Vec, + new_suffix_stmts_for_block: Vec, +} + +struct BlockScope<'a> { + state: &'a mut State, + parent_new_prefix_stmts_for_block: Vec, + parent_new_suffix_stmts_for_block: Vec, +} + +impl<'a> BlockScope<'a> { + fn new( + state: &'a mut State, + new_prefix_stmts_for_block: Vec, + new_suffix_stmts_for_block: Vec, + ) -> Self { + let parent_new_prefix_stmts_for_block = std::mem::replace( + &mut state.new_prefix_stmts_for_block, + new_prefix_stmts_for_block, + ); + let parent_new_suffix_stmts_for_block = std::mem::replace( + &mut state.new_suffix_stmts_for_block, + new_suffix_stmts_for_block, + ); + Self { + state, + parent_new_prefix_stmts_for_block, + parent_new_suffix_stmts_for_block, + } + } +} + +impl Drop for BlockScope<'_> { + fn drop(&mut self) { + self.state.new_prefix_stmts_for_block = + std::mem::take(&mut self.parent_new_prefix_stmts_for_block); + self.state.new_suffix_stmts_for_block = + std::mem::take(&mut self.parent_new_suffix_stmts_for_block); + } } impl State { @@ -549,6 +584,185 @@ impl State { | CanonicalType::DynSimOnly(_) => unreachable!(), } } + fn handle_enum_structural_eq( + &mut self, + unfolded_ty: Enum, + folded_lhs: Expr, + folded_rhs: Expr, + flags: StructuralEqFlags, + ) -> Result, SimplifyEnumsError> { + if flags.assume_padding_is_zeroed { + return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()); + } + let enum_type_state = self.get_or_make_enum_type_state(unfolded_ty)?; + if let EnumTypeState::Unchanged = enum_type_state { + return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()); + } + let module_state = self.module_state_stack.last_mut().unwrap(); + let source_location = module_state.source_location; + let output_wire = Wire::new_unchecked( + module_state.gen_name("__enum_structural_eq"), + source_location, + Bool, + ); + self.new_prefix_stmts_for_block.push( + StmtWire { + annotations: Interned::default(), + wire: output_wire.canonical(), + } + .into(), + ); + let output_wire = output_wire.to_expr(); + self.new_suffix_stmts_for_block.push( + StmtConnect { + lhs: Expr::canonical(output_wire), + rhs: Expr::canonical(false.to_expr()), + source_location, + } + .into(), + ); + let tags_eq = match enum_type_state { + EnumTypeState::TagEnumAndBody(_) => StructuralEq::with_flags( + Expr::canonical(Expr::>::from_canonical(folded_lhs).tag), + Expr::canonical(Expr::>::from_canonical(folded_rhs).tag), + StructuralEqFlags { + assume_padding_is_zeroed: true, + }, + ) + .to_expr(), + EnumTypeState::TagUIntAndBody(_) => { + let lhs = Expr::>::from_canonical(folded_lhs).tag; + let rhs = Expr::>::from_canonical(folded_rhs).tag; + lhs.cmp_eq(rhs) + } + EnumTypeState::UInt(_) => { + let lhs_int_tag_expr = Expr::::from_canonical(folded_lhs) + [..unfolded_ty.discriminant_bit_width()]; + let rhs_int_tag_expr = Expr::::from_canonical(folded_rhs) + [..unfolded_ty.discriminant_bit_width()]; + lhs_int_tag_expr.cmp_eq(rhs_int_tag_expr) + } + EnumTypeState::Unchanged => unreachable!(), + }; + let mut match_arms = Vec::with_capacity(unfolded_ty.variants().len()); + for (variant_index, variant) in unfolded_ty.variants().iter().enumerate() { + let block_scope = BlockScope::new(self, vec![], vec![]); + let this = &mut *block_scope.state; + let eq = if let Some(variant_ty) = variant.ty { + let folded_lhs = + this.handle_variant_access(unfolded_ty, folded_lhs, variant_index)?; + let folded_rhs = + this.handle_variant_access(unfolded_ty, folded_rhs, variant_index)?; + this.handle_structural_eq(variant_ty, folded_lhs, folded_rhs, flags)? + } else { + true.to_expr() + }; + match_arms.push(Block { + memories: [].intern_slice(), + stmts: this + .new_prefix_stmts_for_block + .drain(..) + .chain([StmtConnect { + lhs: Expr::canonical(output_wire), + rhs: Expr::canonical(eq), + source_location, + } + .into()]) + .chain(this.new_suffix_stmts_for_block.drain(..)) + .collect(), + }); + } + let match_stmt = + self.handle_match(unfolded_ty, folded_lhs, source_location, &match_arms)?; + self.new_suffix_stmts_for_block.push( + StmtIf { + cond: tags_eq, + source_location, + blocks: [ + Block { + memories: [].intern_slice(), + stmts: [match_stmt].intern_slice(), + }, + Block { + memories: [].intern_slice(), + stmts: [].intern_slice(), + }, + ], + } + .into(), + ); + Ok(output_wire) + } + fn handle_structural_eq( + &mut self, + unfolded_ty: CanonicalType, + folded_lhs: Expr, + folded_rhs: Expr, + flags: StructuralEqFlags, + ) -> Result, SimplifyEnumsError> { + if !contains_any_enum_types(unfolded_ty) { + return Ok(StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr()); + } + match unfolded_ty { + CanonicalType::Array(unfolded_ty) => { + let unfolded_element_ty = unfolded_ty.element(); + let mut retval = None; + for i in 0..unfolded_ty.len() { + let element_eq = self.handle_structural_eq( + unfolded_element_ty, + ops::ArrayIndex::new(Expr::from_canonical(folded_lhs), i).to_expr(), + ops::ArrayIndex::new(Expr::from_canonical(folded_rhs), i).to_expr(), + flags, + )?; + retval = Some(match retval { + Some(old_eq) => old_eq & element_eq, + None => element_eq, + }); + } + Ok(retval.unwrap_or_else(|| { + StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr() + })) + } + CanonicalType::Enum(unfolded_ty) => { + self.handle_enum_structural_eq(unfolded_ty, folded_lhs, folded_rhs, flags) + } + CanonicalType::Bundle(unfolded_ty) => { + let mut retval = None; + for (i, field) in unfolded_ty.fields().iter().enumerate() { + let field_eq = self.handle_structural_eq( + field.ty, + ops::FieldAccess::new_by_index(Expr::from_canonical(folded_lhs), i) + .to_expr(), + ops::FieldAccess::new_by_index(Expr::from_canonical(folded_rhs), i) + .to_expr(), + flags, + )?; + retval = Some(match retval { + Some(old_eq) => old_eq & field_eq, + None => field_eq, + }); + } + Ok(retval.unwrap_or_else(|| { + StructuralEq::with_flags(folded_lhs, folded_rhs, flags).to_expr() + })) + } + CanonicalType::TraceAsString(unfolded_ty) => self.handle_structural_eq( + unfolded_ty.inner_ty(), + *Expr::::from_canonical(folded_lhs), + *Expr::::from_canonical(folded_rhs), + flags, + ), + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) + | CanonicalType::DynSimOnly(_) => unreachable!("doesn't contain any enum types"), + } + } } fn connect_port( @@ -677,6 +891,7 @@ impl Folder for State { self.module_state_stack.push(ModuleState { module_name: v.name_id(), expr_cache: HashMap::default(), + source_location: v.source_location(), }); let retval = Fold::default_fold(v, self); self.module_state_stack.pop(); @@ -710,6 +925,18 @@ impl Folder for State { op.variant_index(), )?) } + ExprEnum::StructuralEq(op) => { + let ty = op.lhs().ty(); + assert_eq!(ty, op.rhs().ty()); + let folded_lhs = Expr::canonical(op.lhs()).fold(self)?; + let folded_rhs = Expr::canonical(op.rhs()).fold(self)?; + *Expr::expr_enum(self.handle_structural_eq( + ty, + folded_lhs, + folded_rhs, + op.flags(), + )?) + } ExprEnum::MemPort(mem_port) => { if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { ExprEnum::Wire(wire) @@ -837,11 +1064,13 @@ impl Folder for State { } fn fold_block(&mut self, block: Block) -> Result { + let block_scope = BlockScope::new(self, vec![], vec![]); + let this = &mut *block_scope.state; let mut memories = vec![]; let mut stmts = vec![]; for memory in block.memories { let old_element_ty = memory.array_type().element(); - let new_element_ty = memory.array_type().element().fold(self)?; + let new_element_ty = memory.array_type().element().fold(this)?; if new_element_ty != old_element_ty { let mut new_ports = vec![]; for port in memory.ports() { @@ -867,7 +1096,7 @@ impl Folder for State { continue; } let wire = Wire::new_unchecked( - self.module_state_stack + this.module_state_stack .last_mut() .unwrap() .gen_name(&format!( @@ -891,7 +1120,7 @@ impl Folder for State { Expr::canonical(wire.to_expr()), port.source_location(), ); - self.replacement_mem_ports.insert(port, wire.canonical()); + this.replacement_mem_ports.insert(port, wire.canonical()); } memories.push(Mem::new_unchecked( memory.scoped_name(), @@ -906,10 +1135,12 @@ impl Folder for State { memory.mem_annotations(), )); } else { - memories.push(memory.fold(self)?); + memories.push(memory.fold(this)?); } } - stmts.extend_from_slice(&block.stmts.fold(self)?); + stmts.extend_from_slice(&block.stmts.fold(this)?); + stmts.splice(0..0, this.new_prefix_stmts_for_block.drain(..)); + stmts.extend_from_slice(&this.new_suffix_stmts_for_block); Ok(Block { memories: Intern::intern_owned(memories), stmts: Intern::intern_owned(stmts), @@ -1031,10 +1262,13 @@ pub fn simplify_enums( module: Interned>, kind: SimplifyEnumsKind, ) -> Result>, SimplifyEnumsError> { + // TODO: deduce StructuralEq's assume_padding_is_zeroed module.fold(&mut State { enum_types: HashMap::default(), replacement_mem_ports: HashMap::default(), kind, module_state_stack: vec![], + new_prefix_stmts_for_block: vec![], + new_suffix_stmts_for_block: vec![], }) } diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index ba85817..1552eab 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -384,6 +384,8 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst { } impl HdlPartialEqImpl for PhantomConst { + const TRY_STRUCTURAL_EQ: bool = true; + #[track_caller] fn cmp_value_eq( lhs: Self, diff --git a/crates/fayalite/src/sim/compiler.rs b/crates/fayalite/src/sim/compiler.rs index c3c47fd..282507f 100644 --- a/crates/fayalite/src/sim/compiler.rs +++ b/crates/fayalite/src/sim/compiler.rs @@ -3974,6 +3974,10 @@ impl Compiler { .compile_expr(instantiated_module_or_global, Expr::canonical(expr.arg())) .map_ty(TraceAsString::from_canonical) .inner(), + ExprEnum::StructuralEq(expr) => self.compile_expr( + instantiated_module_or_global, + Expr::canonical(expr.lhs().cast_to_bits().cmp_eq(expr.rhs().cast_to_bits())), + ), ExprEnum::ModuleIO(expr) => self .compile_value(TargetInInstantiatedModuleOrGlobal::from_target( instantiated_module_or_global, diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index 24bc1ef..dd47633 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -1508,6 +1508,8 @@ impl ToSimValue for SimOnlyValue { } impl HdlPartialEqImpl for DynSimOnly { + const TRY_STRUCTURAL_EQ: bool = false; + #[track_caller] fn cmp_value_eq( _lhs: Self, @@ -1527,6 +1529,8 @@ impl HdlPartialEqImpl for DynSimOnly { impl, R: SimOnlyValueTrait> HdlPartialEqImpl> for SimOnly { + const TRY_STRUCTURAL_EQ: bool = false; + #[track_caller] fn cmp_value_eq( _lhs: Self, diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 843aedb..6505742 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -354,6 +354,39 @@ impl CanonicalType { } MyMemoize.get_owned((self, other)) } + pub fn contains_sim_only(self) -> bool { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = bool; + + fn inner(self, input: &Self::Input) -> Self::Output { + match input { + CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_) => { + false + } + CanonicalType::Array(ty) => ty.element().contains_sim_only(), + CanonicalType::Enum(ty) => ty + .variants() + .iter() + .any(|v| v.ty.is_some_and(CanonicalType::contains_sim_only)), + CanonicalType::Bundle(ty) => { + ty.fields().iter().any(|v| v.ty.contains_sim_only()) + } + CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::PhantomConst(_) => false, + CanonicalType::DynSimOnly(_) => true, + CanonicalType::TraceAsString(ty) => ty.inner_ty().contains_sim_only(), + } + } + } + MyMemoize.get_owned(self) + } } pub trait MatchVariantAndInactiveScope: Sized { @@ -1882,6 +1915,8 @@ fn trace_as_string_cow_into_inner_value( } impl, U: Type> HdlPartialEqImpl> for TraceAsString { + const TRY_STRUCTURAL_EQ: bool = >::TRY_STRUCTURAL_EQ; + #[track_caller] fn cmp_value_eq( lhs: Self, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index aa028f4..a6699ed 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -709,44 +709,35 @@ circuit check_enum_cmp_eq: input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] - wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1] - match lhs: @[module-XXXXXXXXXX.rs 5:1] + wire _cast_enum_to_bits_expr: UInt<10> + match lhs: A: - match rhs: @[module-XXXXXXXXXX.rs 5:1] - A: - connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1] - B(_match_arm_value): - skip - C(_match_arm_value_1): - skip - B(_match_arm_value_2): - match rhs: @[module-XXXXXXXXXX.rs 5:1] - A: - skip - B(_match_arm_value_3): - connect TestEnum_cmp_eq, eq(_match_arm_value_2, _match_arm_value_3) @[module-XXXXXXXXXX.rs 5:1] - C(_match_arm_value_4): - skip - C(_match_arm_value_5): - match rhs: @[module-XXXXXXXXXX.rs 5:1] - A: - skip - B(_match_arm_value_6): - skip - C(_match_arm_value_7): - wire _array_literal_expr: UInt<1>[3] - connect _array_literal_expr[0], eq(_match_arm_value_5[0], _match_arm_value_7[0]) - connect _array_literal_expr[1], eq(_match_arm_value_5[1], _match_arm_value_7[1]) - connect _array_literal_expr[2], eq(_match_arm_value_5[2], _match_arm_value_7[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 TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] - connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1] + connect _cast_enum_to_bits_expr, UInt<10>(0) + B(_cast_enum_to_bits_expr_B): + connect _cast_enum_to_bits_expr, pad(cat(_cast_enum_to_bits_expr_B, UInt<2>(1)), 10) + C(_cast_enum_to_bits_expr_C): + wire _cast_array_to_bits_expr: UInt<1>[3] + connect _cast_array_to_bits_expr[0], _cast_enum_to_bits_expr_C[0] + connect _cast_array_to_bits_expr[1], _cast_enum_to_bits_expr_C[1] + connect _cast_array_to_bits_expr[2], _cast_enum_to_bits_expr_C[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 _cast_enum_to_bits_expr, pad(cat(_cast_to_bits_expr, UInt<2>(2)), 10) + wire _cast_enum_to_bits_expr_1: UInt<10> + match rhs: + A: + connect _cast_enum_to_bits_expr_1, UInt<10>(0) + B(_cast_enum_to_bits_expr_B_1): + connect _cast_enum_to_bits_expr_1, pad(cat(_cast_enum_to_bits_expr_B_1, UInt<2>(1)), 10) + C(_cast_enum_to_bits_expr_C_1): + wire _cast_array_to_bits_expr_1: UInt<1>[3] + connect _cast_array_to_bits_expr_1[0], _cast_enum_to_bits_expr_C_1[0] + connect _cast_array_to_bits_expr_1[1], _cast_enum_to_bits_expr_C_1[1] + connect _cast_array_to_bits_expr_1[2], _cast_enum_to_bits_expr_C_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 _cast_enum_to_bits_expr_1, pad(cat(_cast_to_bits_expr_1, UInt<2>(2)), 10) + connect eq, eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1) @[module-XXXXXXXXXX.rs 5:1] ", }; #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 @@ -764,60 +755,53 @@ circuit check_enum_cmp_eq: input lhs: Ty1 @[module-XXXXXXXXXX.rs 2:1] input rhs: Ty1 @[module-XXXXXXXXXX.rs 3:1] output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] - wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1] - match lhs.tag: @[module-XXXXXXXXXX.rs 5:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + wire _cast_enum_to_bits_expr: UInt<2> + match lhs.tag: A: - match rhs.tag: @[module-XXXXXXXXXX.rs 5:1] - A: - connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1] - B: - skip - C: - skip + connect _cast_enum_to_bits_expr, UInt<2>(0) B: - match rhs.tag: @[module-XXXXXXXXXX.rs 5:1] - A: - skip - B: - connect TestEnum_cmp_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 5:1] - C: - skip + connect _cast_enum_to_bits_expr, UInt<2>(1) C: - match rhs.tag: @[module-XXXXXXXXXX.rs 5:1] - A: - skip - B: - skip - C: - wire _array_literal_expr: UInt<1>[3] - wire _cast_bits_to_array_expr: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0) - connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] - connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1) - connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] - connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2) - connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] - wire _cast_bits_to_array_expr_1: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0) - connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] - connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1) - connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] - connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2) - connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] - connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]) - connect _array_literal_expr[1], eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]) - connect _array_literal_expr[2], eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[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 TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] - connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1] + connect _cast_enum_to_bits_expr, UInt<2>(2) + wire _cast_enum_to_bits_expr_1: UInt<2> + match rhs.tag: + A: + connect _cast_enum_to_bits_expr_1, UInt<2>(0) + B: + connect _cast_enum_to_bits_expr_1, UInt<2>(1) + C: + connect _cast_enum_to_bits_expr_1, UInt<2>(2) + when eq(_cast_enum_to_bits_expr, _cast_enum_to_bits_expr_1): @[module-XXXXXXXXXX.rs 1:1] + match lhs.tag: @[module-XXXXXXXXXX.rs 1:1] + A: + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + B: + connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1] + C: + wire _cast_bits_to_array_expr: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0) + connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] + connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1) + connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] + connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2) + connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + wire _cast_bits_to_array_expr_1: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0) + connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] + connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1) + connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] + connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2) + connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + wire _array_structural_eq: UInt<1> + connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1])) + wire _array_structural_eq_1: UInt<1> + connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2])) + connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1] ", }; #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 @@ -834,51 +818,36 @@ circuit check_enum_cmp_eq: input lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] input rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] - wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1] - when eq(lhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1] - else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(lhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 5:1] - else when eq(rhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(rhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - skip - else: - wire _array_literal_expr: UInt<1>[3] - wire _cast_bits_to_array_expr: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0) - connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] - connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1) - connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] - connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2) - connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] - wire _cast_bits_to_array_expr_1: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0) - connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] - connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1) - connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] - connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2) - connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] - connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]) - connect _array_literal_expr[1], eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]) - connect _array_literal_expr[2], eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[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 TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] - connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(lhs.tag, rhs.tag): @[module-XXXXXXXXXX.rs 1:1] + when eq(lhs.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else when eq(lhs.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, eq(bits(lhs.body, 7, 0), bits(rhs.body, 7, 0)) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_array_expr: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened[0], bits(bits(lhs.body, 2, 0), 0, 0) + connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] + connect _cast_bits_to_array_expr_flattened[1], bits(bits(lhs.body, 2, 0), 1, 1) + connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] + connect _cast_bits_to_array_expr_flattened[2], bits(bits(lhs.body, 2, 0), 2, 2) + connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + wire _cast_bits_to_array_expr_1: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(rhs.body, 2, 0), 0, 0) + connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] + connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(rhs.body, 2, 0), 1, 1) + connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] + connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(rhs.body, 2, 0), 2, 2) + connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + wire _array_structural_eq: UInt<1> + connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1])) + wire _array_structural_eq_1: UInt<1> + connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2])) + connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1] ", }; #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 @@ -894,51 +863,36 @@ circuit check_enum_cmp_eq: input lhs: UInt<10> @[module-XXXXXXXXXX.rs 2:1] input rhs: UInt<10> @[module-XXXXXXXXXX.rs 3:1] output eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] - wire TestEnum_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 5:1] - when eq(bits(lhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 5:1] - else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(bits(lhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - connect TestEnum_cmp_eq, eq(bits(bits(lhs, 9, 2), 7, 0), bits(bits(rhs, 9, 2), 7, 0)) @[module-XXXXXXXXXX.rs 5:1] - else when eq(bits(rhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 5:1] - skip - else when eq(bits(rhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 5:1] - skip - else: - wire _array_literal_expr: UInt<1>[3] - wire _cast_bits_to_array_expr: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0) - connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] - connect _cast_bits_to_array_expr_flattened[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1) - connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] - connect _cast_bits_to_array_expr_flattened[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2) - connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] - wire _cast_bits_to_array_expr_1: UInt<1>[3] - wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] - connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0) - connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] - connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1) - connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] - connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2) - connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] - connect _array_literal_expr[0], eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]) - connect _array_literal_expr[1], eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1]) - connect _array_literal_expr[2], eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[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 TestEnum_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] - connect eq, TestEnum_cmp_eq @[module-XXXXXXXXXX.rs 6:1] + wire __enum_structural_eq: UInt<1> @[module-XXXXXXXXXX.rs 1:1] + connect eq, __enum_structural_eq @[module-XXXXXXXXXX.rs 5:1] + connect __enum_structural_eq, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(lhs, 1, 0), bits(rhs, 1, 0)): @[module-XXXXXXXXXX.rs 1:1] + when eq(bits(lhs, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 1:1] + else when eq(bits(lhs, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 1:1] + connect __enum_structural_eq, eq(bits(bits(lhs, 9, 2), 7, 0), bits(bits(rhs, 9, 2), 7, 0)) @[module-XXXXXXXXXX.rs 1:1] + else: + wire _cast_bits_to_array_expr: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened[0], bits(bits(bits(lhs, 9, 2), 2, 0), 0, 0) + connect _cast_bits_to_array_expr[0], _cast_bits_to_array_expr_flattened[0] + connect _cast_bits_to_array_expr_flattened[1], bits(bits(bits(lhs, 9, 2), 2, 0), 1, 1) + connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] + connect _cast_bits_to_array_expr_flattened[2], bits(bits(bits(lhs, 9, 2), 2, 0), 2, 2) + connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + wire _cast_bits_to_array_expr_1: UInt<1>[3] + wire _cast_bits_to_array_expr_flattened_1: UInt<1>[3] + connect _cast_bits_to_array_expr_flattened_1[0], bits(bits(bits(rhs, 9, 2), 2, 0), 0, 0) + connect _cast_bits_to_array_expr_1[0], _cast_bits_to_array_expr_flattened_1[0] + connect _cast_bits_to_array_expr_flattened_1[1], bits(bits(bits(rhs, 9, 2), 2, 0), 1, 1) + connect _cast_bits_to_array_expr_1[1], _cast_bits_to_array_expr_flattened_1[1] + connect _cast_bits_to_array_expr_flattened_1[2], bits(bits(bits(rhs, 9, 2), 2, 0), 2, 2) + connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + wire _array_structural_eq: UInt<1> + connect _array_structural_eq, and(eq(_cast_bits_to_array_expr[0], _cast_bits_to_array_expr_1[0]), eq(_cast_bits_to_array_expr[1], _cast_bits_to_array_expr_1[1])) + wire _array_structural_eq_1: UInt<1> + connect _array_structural_eq_1, and(_array_structural_eq, eq(_cast_bits_to_array_expr[2], _cast_bits_to_array_expr_1[2])) + connect __enum_structural_eq, _array_structural_eq_1 @[module-XXXXXXXXXX.rs 1:1] ", }; } @@ -4931,12 +4885,14 @@ circuit check_struct_cmp_eq: 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] - connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1] - connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13: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 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, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19: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] - connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1] + connect test_struct_3_cmp_ne, not(UInt<1>(0h1)) @[module-XXXXXXXXXX.rs 25:1] ", }; } diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr index 8eff725..75bb262 100644 --- a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -75,7 +75,7 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 896 | pub struct OpaqueSimValue { + 929 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs @@ -214,7 +214,7 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 896 | pub struct OpaqueSimValue { + 929 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs @@ -326,7 +326,7 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 896 | pub struct OpaqueSimValue { + 929 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 1267aa7..5c36374 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1055,6 +1055,15 @@ "global()": "Visible" } }, + "ops::StructuralEq": { + "data": { + "$kind": "Struct", + "$constructor": "ops::StructuralEq::with_flags", + "lhs()": "Visible", + "rhs()": "Visible", + "flags()": "Opaque" + } + }, "BlockId": { "data": { "$kind": "Opaque"