fayalite::expr::ops: add and automatically generate ops::StructuralEq

This commit is contained in:
Jacob Lifshay 2026-06-08 23:36:26 -07:00
parent ffca1a279d
commit 98e7e91fc9
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
18 changed files with 1030 additions and 220 deletions

View file

@ -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<Self> 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<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::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<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::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
}

View file

@ -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<TokenStream> = 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<Self> 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<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__lhs_value: ::fayalite::__std::borrow::Cow<'_,
<Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs_value: ::fayalite::__std::borrow::Cow<'_,
<Self as ::fayalite::ty::Type>::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<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
let __retval = ::fayalite::module::wire(::fayalite::module::ImplicitName(#cmp_expr_eq_wire_name), ::fayalite::int::Bool);
if <Self as ::fayalite::expr::HdlPartialEqImpl<Self>>::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<Self>;
type MatchVariantAndInactiveScope =
::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
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: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
) -> (
<Self as ::fayalite::ty::Type>::MatchVariant,
<Self as ::fayalite::ty::Type>::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,

View file

@ -367,6 +367,8 @@ impl<Lhs: Type, Rhs: Type, Len: Size> HdlPartialEqImpl<ArrayType<Rhs, Len>> for
where
Lhs: HdlPartialEqImpl<Rhs>,
{
const TRY_STRUCTURAL_EQ: bool = <Lhs as HdlPartialEqImpl<Rhs>>::TRY_STRUCTURAL_EQ;
fn cmp_value_eq(
lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,

View file

@ -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,

View file

@ -221,6 +221,7 @@ expr_enum! {
CastBitsTo(ops::CastBitsTo),
ToTraceAsString(ops::ToTraceAsString),
TraceAsStringAsInner(ops::TraceAsStringAsInner),
StructuralEq(ops::StructuralEq),
ModuleIO(ModuleIO<CanonicalType>),
Instance(Instance<Bundle>),
Wire(Wire<CanonicalType>),
@ -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<Type: $HdlCmpImpl:ident<Rhs::Type> $(+ $HdlCmpImplBase:ident<Rhs::Type>)?> $(+ $HdlCmpBase:ident<Rhs>)?
@ -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<Rhs: ValueType>:
ValueType<Type: HdlPartialEqImpl<Rhs::Type> >

View file

@ -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<'_, <Self as Type>::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<LhsWidth>, UIntType<RhsWidth>)]
#[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<LhsWidth>, SIntType<RhsWidth>)]
#[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<Self> 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<CanonicalType>,
rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
literal_bits: Result<Interned<BitSlice>, NotALiteralExpr>,
}
impl StructuralEq {
fn literal_eq(
ty: CanonicalType,
l: Interned<BitSlice>,
r: Interned<BitSlice>,
) -> Result<bool, NotALiteralExpr> {
enum PairRefOrInternedBitSlice<'a> {
Ref(&'a BitSlice, &'a BitSlice),
Interned(Interned<BitSlice>, Interned<BitSlice>),
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MyMemoize(CanonicalType);
impl MemoizeGeneric for MyMemoize {
type InputRef<'a> = (&'a BitSlice, &'a BitSlice);
type InputOwned = (Interned<BitSlice>, Interned<BitSlice>);
type InputCow<'a> = PairRefOrInternedBitSlice<'a>;
type Output = Result<bool, NotALiteralExpr>;
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::<Lsb0>()[..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<CanonicalType>, rhs: Expr<CanonicalType>) -> Self {
Self::with_flags(lhs, rhs, StructuralEqFlags::DEFAULT)
}
#[track_caller]
pub fn with_flags(
lhs: Expr<CanonicalType>,
rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Self {
match Self::try_with_flags(lhs, rhs, flags) {
Ok(retval) => retval,
Err(e) => panic!("{e}"),
}
}
pub fn try_new(
lhs: Expr<CanonicalType>,
rhs: Expr<CanonicalType>,
) -> Result<Self, StructuralEqError> {
Self::try_with_flags(lhs, rhs, StructuralEqFlags::DEFAULT)
}
pub fn try_with_flags(
lhs: Expr<CanonicalType>,
rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Result<Self, StructuralEqError> {
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<CanonicalType> {
self.lhs
}
pub fn rhs(self) -> Expr<CanonicalType> {
self.rhs
}
pub fn flags(self) -> StructuralEqFlags {
self.flags
}
}
impl ToLiteralBits for StructuralEq {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, 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<Self::Type> {
Expr {
__enum: ExprEnum::StructuralEq(*self).intern(),
__ty: Bool,
__flow: Flow::Source,
}
}
}

View file

@ -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<HashMap<(String, Array), String>>,
cast_bits_to_phantom_const_exprs: RefCell<HashMap<(String, PhantomConst), String>>,
per_module_formal_inputs: RefCell<HashMap<(FormalInput, bool), String>>,
structural_eq_exprs: RefCell<HashMap<(String, String), String>>,
}
struct BlockDefinitions<'a> {
@ -1774,6 +1775,143 @@ endmodule
},
)
}
fn expr_structural_eq_bit<T: CastToImpl<Bool>>(
&mut self,
lhs: Expr<CanonicalType>,
rhs: Expr<CanonicalType>,
definitions: &BlockDefinitions<'_>,
const_ty: bool,
) -> Result<String> {
self.expr_binary(
"eq",
Expr::<T>::from_canonical(lhs).cast_to(Bool),
Expr::<T>::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<String> {
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<CanonicalType>,
@ -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");

View file

@ -177,6 +177,8 @@ impl CastToImpl<UIntInRangeMaskType> for Bool {
}
impl HdlPartialEqImpl<Self> 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<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
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<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
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<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
const TRY_STRUCTURAL_EQ: bool = false;
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,

View file

@ -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(_)

View file

@ -1208,6 +1208,7 @@ impl<P: Pass> RunPass<P> 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<CanonicalType>; 2];
fn args<'a>(&'a self) -> Self::Args<'a> {
[self.lhs(), self.rhs()]
}
fn source_location(&self) -> Option<SourceLocation> {
None
}
fn union_parts(
&self,
_resets: Resets,
args_resets: Vec<Resets>,
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<Expr<CanonicalType>>,
) -> Result<Self, DeduceResetsError> {
Ok(Self::with_flags(new_args[0], new_args[1], self.flags()))
}
}
impl RunPassExpr for ModuleIO<CanonicalType> {
type Args<'a> = [Expr<CanonicalType>; 0];

View file

@ -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<ExprEnum, ExprEnum>,
source_location: SourceLocation,
}
impl ModuleState {
@ -110,6 +106,45 @@ struct State {
replacement_mem_ports: HashMap<MemPort<DynPortType>, Wire<CanonicalType>>,
kind: SimplifyEnumsKind,
module_state_stack: Vec<ModuleState>,
new_prefix_stmts_for_block: Vec<Stmt>,
new_suffix_stmts_for_block: Vec<Stmt>,
}
struct BlockScope<'a> {
state: &'a mut State,
parent_new_prefix_stmts_for_block: Vec<Stmt>,
parent_new_suffix_stmts_for_block: Vec<Stmt>,
}
impl<'a> BlockScope<'a> {
fn new(
state: &'a mut State,
new_prefix_stmts_for_block: Vec<Stmt>,
new_suffix_stmts_for_block: Vec<Stmt>,
) -> 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<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Result<Expr<Bool>, 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::<TagAndBody<Enum, UInt>>::from_canonical(folded_lhs).tag),
Expr::canonical(Expr::<TagAndBody<Enum, UInt>>::from_canonical(folded_rhs).tag),
StructuralEqFlags {
assume_padding_is_zeroed: true,
},
)
.to_expr(),
EnumTypeState::TagUIntAndBody(_) => {
let lhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_lhs).tag;
let rhs = Expr::<TagAndBody<UInt, UInt>>::from_canonical(folded_rhs).tag;
lhs.cmp_eq(rhs)
}
EnumTypeState::UInt(_) => {
let lhs_int_tag_expr = Expr::<UInt>::from_canonical(folded_lhs)
[..unfolded_ty.discriminant_bit_width()];
let rhs_int_tag_expr = Expr::<UInt>::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<CanonicalType>,
folded_rhs: Expr<CanonicalType>,
flags: StructuralEqFlags,
) -> Result<Expr<Bool>, 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::<TraceAsString>::from_canonical(folded_lhs),
*Expr::<TraceAsString>::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<Block, Self::Error> {
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<Module<Bundle>>,
kind: SimplifyEnumsKind,
) -> Result<Interned<Module<Bundle>>, 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![],
})
}

View file

@ -384,6 +384,8 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
}
impl<T: ?Sized + PhantomConstValue> HdlPartialEqImpl<Self> for PhantomConst<T> {
const TRY_STRUCTURAL_EQ: bool = true;
#[track_caller]
fn cmp_value_eq(
lhs: Self,

View file

@ -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,

View file

@ -1508,6 +1508,8 @@ impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
}
impl HdlPartialEqImpl<Self> for DynSimOnly {
const TRY_STRUCTURAL_EQ: bool = false;
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
@ -1527,6 +1529,8 @@ impl HdlPartialEqImpl<Self> for DynSimOnly {
impl<L: SimOnlyValueTrait + PartialEq<R>, R: SimOnlyValueTrait> HdlPartialEqImpl<SimOnly<R>>
for SimOnly<L>
{
const TRY_STRUCTURAL_EQ: bool = false;
#[track_caller]
fn cmp_value_eq(
_lhs: Self,

View file

@ -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<T: Type>(
}
impl<T: HdlPartialEqImpl<U>, U: Type> HdlPartialEqImpl<TraceAsString<U>> for TraceAsString<T> {
const TRY_STRUCTURAL_EQ: bool = <T as HdlPartialEqImpl<U>>::TRY_STRUCTURAL_EQ;
#[track_caller]
fn cmp_value_eq(
lhs: Self,

View file

@ -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]
",
};
}

View file

@ -75,7 +75,7 @@ note: required because it appears within the type `Vec<DynSimOnlyValue>`
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<DynSimOnlyValue>`
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<DynSimOnlyValue>`
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

View file

@ -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"