From 3458c21f442652713f2f531f02d747d43550562c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 16 Feb 2025 20:48:16 -0800 Subject: [PATCH] add #[hdl(cmp_eq)] to implement HdlPartialEq automatically --- .../src/hdl_bundle.rs | 65 +++++++++ .../fayalite-proc-macros-impl/src/hdl_enum.rs | 5 + .../src/hdl_type_alias.rs | 5 + .../src/hdl_type_common.rs | 1 + crates/fayalite-proc-macros-impl/src/lib.rs | 1 + crates/fayalite/src/array.rs | 40 +++++- crates/fayalite/src/bundle.rs | 67 +++++++-- crates/fayalite/src/enum_.rs | 59 +++++++- crates/fayalite/tests/module.rs | 128 +++++++++++++++++- 9 files changed, 351 insertions(+), 20 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 79326e2..b0fe498 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -83,6 +83,7 @@ impl ParsedBundle { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq: _, } = options.body; let mut fields = match fields { syn::Fields::Named(fields) => fields, @@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq, } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); @@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle { } } .to_tokens(tokens); + if let Some((cmp_eq,)) = cmp_eq { + let mut where_clause = + Generics::from(generics) + .where_clause + .unwrap_or_else(|| syn::WhereClause { + where_token: Token![where](span), + predicates: Punctuated::new(), + }); + let mut fields_cmp_eq = vec![]; + let mut fields_cmp_ne = vec![]; + for field in fields.named() { + let field_ident = field.ident(); + let field_ty = field.ty(); + where_clause + .predicates + .push(parse_quote_spanned! {cmp_eq.span=> + #field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty> + }); + fields_cmp_eq.push(quote_spanned! {span=> + ::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident) + }); + fields_cmp_ne.push(quote_spanned! {span=> + ::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident) + }); + } + let cmp_eq_body; + let cmp_ne_body; + if fields_len == 0 { + cmp_eq_body = quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&true) + }; + cmp_ne_body = quote_spanned! {span=> + ::fayalite::expr::ToExpr::to_expr(&false) + }; + } else { + cmp_eq_body = quote_spanned! {span=> + #(#fields_cmp_eq)&* + }; + cmp_ne_body = quote_spanned! {span=> + #(#fields_cmp_ne)|* + }; + }; + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::fayalite::expr::ops::ExprPartialEq for #target #type_generics + #where_clause + { + fn cmp_eq( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_eq_body + } + fn cmp_ne( + __lhs: ::fayalite::expr::Expr, + __rhs: ::fayalite::expr::Expr, + ) -> ::fayalite::expr::Expr<::fayalite::int::Bool> { + #cmp_ne_body + } + } + } + .to_tokens(tokens); + } if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) { let static_generics = generics.clone().for_static_type(); let (static_impl_generics, static_type_generics, static_where_clause) = diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 1d16177..9174566 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -155,7 +155,11 @@ impl ParsedEnum { custom_bounds, no_static: _, no_runtime_generics: _, + cmp_eq, } = options.body; + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); + } attrs.retain(|attr| { if attr.path().is_ident("repr") { errors.error(attr, "#[repr] is not supported on #[hdl] enums"); @@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum { custom_bounds: _, no_static, no_runtime_generics, + cmp_eq: _, // TODO: implement cmp_eq for enums } = &options.body; let target = get_target(target, ident); let mut struct_attrs = attrs.clone(); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs index e5d5f7b..97501e7 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -49,10 +49,14 @@ impl ParsedTypeAlias { custom_bounds, no_static, no_runtime_generics: _, + cmp_eq, } = options.body; if let Some((no_static,)) = no_static { errors.error(no_static, "no_static is not valid on type aliases"); } + if let Some((cmp_eq,)) = cmp_eq { + errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); + } let generics = if custom_bounds.is_some() { MaybeParsed::Unrecognized(generics) } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { @@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias { custom_bounds: _, no_static: _, no_runtime_generics, + cmp_eq: _, } = &options.body; let target = get_target(target, ident); let mut type_attrs = attrs.clone(); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 3b2e1ec..2da0915 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -26,6 +26,7 @@ crate::options! { CustomBounds(custom_bounds), NoStatic(no_static), NoRuntimeGenerics(no_runtime_generics), + CmpEq(cmp_eq), } } diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index cbd5f4a..5fe3ae8 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -72,6 +72,7 @@ mod kw { custom_keyword!(cfg); custom_keyword!(cfg_attr); custom_keyword!(clock_domain); + custom_keyword!(cmp_eq); custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); custom_keyword!(flip); diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index f617f91..647b2e2 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -2,8 +2,11 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::ArrayIndex, Expr, ToExpr}, - int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE}, + expr::{ + ops::{ArrayIndex, ArrayLiteral, ExprPartialEq}, + CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr, + }, + int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE}, intern::{Intern, Interned, LazyInterned}, module::transform::visit::{Fold, Folder, Visit, Visitor}, source_location::SourceLocation, @@ -218,3 +221,36 @@ impl Index for ArrayWithoutLen { Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) } } + +impl ExprPartialEq> for ArrayType +where + Lhs: ExprPartialEq, +{ + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + ArrayLiteral::::new( + Bool, + (0..lhs_ty.len()) + .map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i]))) + .collect(), + ) + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + let lhs_ty = Expr::ty(lhs); + let rhs_ty = Expr::ty(rhs); + assert_eq!(lhs_ty.len(), rhs_ty.len()); + ArrayLiteral::::new( + Bool, + (0..lhs_ty.len()) + .map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i]))) + .collect(), + ) + .cast_to_bits() + .any_one_bits() + } +} diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 995510e..9807b92 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -2,7 +2,11 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::BundleLiteral, Expr, ToExpr}, + expr::{ + ops::{ArrayLiteral, BundleLiteral, ExprPartialEq}, + CastToBits, Expr, ReduceBits, ToExpr, + }, + int::{Bool, DynSize}, intern::{Intern, Interned}, sim::{SimValue, ToSimValue}, source_location::SourceLocation, @@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields { } macro_rules! impl_tuples { - ([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => { + ( + [$({ + #[ + num = $num:tt, + field = $field:ident, + ty = $ty_var:ident: $Ty:ident, + lhs = $lhs_var:ident: $Lhs:ident, + rhs = $rhs_var:ident: $Rhs:ident + ] + $var:ident: $T:ident + })*] + [] + ) => { impl_tuple_builder_fields! { {} [$({ @@ -498,6 +514,29 @@ macro_rules! impl_tuples { Self::into_sim_value(*self, ty) } } + impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) { + fn cmp_eq(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .all_one_bits() + } + + fn cmp_ne(lhs: Expr, rhs: Expr<($($Rhs,)*)>) -> Expr { + let ($($lhs_var,)*) = *lhs; + let ($($rhs_var,)*) = *rhs; + ArrayLiteral::::new( + Bool, + FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]), + ) + .cast_to_bits() + .any_one_bits() + } + } }; ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => { impl_tuples!([$($lhs)*] []); @@ -507,18 +546,18 @@ macro_rules! impl_tuples { impl_tuples! { [] [ - {#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0} - {#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1} - {#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2} - {#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3} - {#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4} - {#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5} - {#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6} - {#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7} - {#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8} - {#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9} - {#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10} - {#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11} + {#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0} + {#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1} + {#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2} + {#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3} + {#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4} + {#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5} + {#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6} + {#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7} + {#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8} + {#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9} + {#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10} + {#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11} ] } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 2ed0b8e..70c58c0 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -2,7 +2,10 @@ // See Notices.txt for copyright information use crate::{ - expr::{ops::VariantAccess, Expr, ToExpr}, + expr::{ + ops::{ExprPartialEq, VariantAccess}, + Expr, ToExpr, + }, hdl, int::Bool, intern::{Intern, Interned}, @@ -360,6 +363,60 @@ pub enum HdlOption { HdlSome(T), } +impl, Rhs: Type> ExprPartialEq> for HdlOption { + #[hdl] + fn cmp_eq(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_eq = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)), + HdlNone => connect(cmp_eq, false), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_eq, false), + HdlNone => connect(cmp_eq, true), + } + } + } + cmp_eq + } + + #[hdl] + fn cmp_ne(lhs: Expr, rhs: Expr>) -> Expr { + #[hdl] + let cmp_ne = wire(); + #[hdl] + match lhs { + HdlSome(lhs) => + { + #[hdl] + match rhs { + HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)), + HdlNone => connect(cmp_ne, true), + } + } + HdlNone => + { + #[hdl] + match rhs { + HdlSome(_) => connect(cmp_ne, true), + HdlNone => connect(cmp_ne, false), + } + } + } + cmp_ne + } +} + #[allow(non_snake_case)] pub fn HdlNone() -> Expr> { HdlOption[T::TYPE].HdlNone() diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 2f93fa5..49b226a 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -380,18 +380,18 @@ circuit check_written_inside_both_if_else: }; } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct { pub a: T, pub b: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct2 { pub v: UInt<8>, } -#[hdl(outline_generated)] +#[hdl(outline_generated, cmp_eq)] pub struct TestStruct3 {} #[hdl_module(outline_generated)] @@ -4425,3 +4425,125 @@ circuit check_let_patterns: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_struct_cmp_eq() { + #[hdl] + let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input(); + #[hdl] + let tuple_cmp_eq: Bool = m.output(); + connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs)); + #[hdl] + let tuple_cmp_ne: Bool = m.output(); + connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs)); + + #[hdl] + let test_struct_lhs: TestStruct> = m.input(); + #[hdl] + let test_struct_rhs: TestStruct> = m.input(); + #[hdl] + let test_struct_cmp_eq: Bool = m.output(); + connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs)); + #[hdl] + let test_struct_cmp_ne: Bool = m.output(); + connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs)); + + #[hdl] + let test_struct_2_lhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_rhs: TestStruct2 = m.input(); + #[hdl] + let test_struct_2_cmp_eq: Bool = m.output(); + connect( + test_struct_2_cmp_eq, + test_struct_2_lhs.cmp_eq(test_struct_2_rhs), + ); + #[hdl] + let test_struct_2_cmp_ne: Bool = m.output(); + connect( + test_struct_2_cmp_ne, + test_struct_2_lhs.cmp_ne(test_struct_2_rhs), + ); + + #[hdl] + let test_struct_3_lhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_rhs: TestStruct3 = m.input(); + #[hdl] + let test_struct_3_cmp_eq: Bool = m.output(); + connect( + test_struct_3_cmp_eq, + test_struct_3_lhs.cmp_eq(test_struct_3_rhs), + ); + #[hdl] + let test_struct_3_cmp_ne: Bool = m.output(); + connect( + test_struct_3_cmp_ne, + test_struct_3_lhs.cmp_ne(test_struct_3_rhs), + ); +} + +#[test] +fn test_struct_cmp_eq() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_struct_cmp_eq(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0 +circuit check_struct_cmp_eq: + type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>} + type Ty1 = {a: SInt<8>, b: UInt<8>} + type Ty2 = {v: UInt<8>} + type Ty3 = {} + module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1] + input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1] + input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1] + output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1] + input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1] + input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1] + output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1] + output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1] + input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1] + input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1] + output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1] + output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1] + input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1] + input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1] + output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1] + output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1] + wire _array_literal_expr: UInt<1>[3] + connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr: UInt<1>[3] + connect _cast_array_to_bits_expr[0], _array_literal_expr[0] + connect _cast_array_to_bits_expr[1], _array_literal_expr[1] + connect _cast_array_to_bits_expr[2], _array_literal_expr[2] + wire _cast_to_bits_expr: UInt<3> + connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) + connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1] + wire _array_literal_expr_1: UInt<1>[3] + connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`) + connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`) + connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`) + wire _cast_array_to_bits_expr_1: UInt<1>[3] + connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0] + connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1] + connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2] + wire _cast_to_bits_expr_1: UInt<3> + connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0])) + connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1] + 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] + 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_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] +", + }; +}