From ff94dda922ebd2a57b371ce9dd257001103edd9a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 20 Sep 2024 18:42:24 -0700 Subject: [PATCH 001/190] support #[hdl] on functions -- enables #[hdl] usage in function body --- .../src/hdl_bundle.rs | 10 +- .../fayalite-proc-macros-impl/src/hdl_enum.rs | 12 +- .../src/hdl_type_common.rs | 28 ++- crates/fayalite-proc-macros-impl/src/lib.rs | 137 ++++++---- .../fayalite-proc-macros-impl/src/module.rs | 236 +++++++++++++----- .../src/module/transform_body.rs | 49 ++-- .../expand_aggregate_literals.rs | 18 +- .../src/module/transform_body/expand_match.rs | 5 +- crates/fayalite/tests/ui/hdl_types.stderr | 2 +- 9 files changed, 341 insertions(+), 156 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f29f43a..7f7d626 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -19,13 +19,13 @@ use syn::{ #[derive(Clone, Debug)] pub(crate) struct ParsedBundle { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) struct_token: Token![struct], pub(crate) ident: Ident, pub(crate) generics: MaybeParsed, pub(crate) fields: MaybeParsed, - pub(crate) field_flips: Vec>>, + pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident, pub(crate) match_variant_ident: Ident, @@ -38,7 +38,7 @@ impl ParsedBundle { errors: &mut Errors, field: &mut Field, index: usize, - ) -> Option> { + ) -> Option> { let Field { attrs, vis: _, @@ -71,7 +71,9 @@ impl ParsedBundle { } = item; let mut errors = Errors::new(); let mut options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(&mut attrs)) + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) .unwrap_or_default(); errors.ok(options.body.validate()); let ItemOptions { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 1e0b66b..d7e5b61 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -3,7 +3,7 @@ use crate::{ common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl, TypesParser, WrappedInConst, }, - Errors, HdlAttr, PairsIterExt, + kw, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::TokenStream; use quote::{format_ident, quote_spanned, ToTokens}; @@ -29,7 +29,7 @@ crate::options! { pub(crate) struct ParsedVariantField { pub(crate) paren_token: Paren, pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ty: MaybeParsed, pub(crate) comma_token: Option, } @@ -37,7 +37,7 @@ pub(crate) struct ParsedVariantField { #[derive(Clone, Debug)] pub(crate) struct ParsedVariant { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) field: Option, } @@ -119,7 +119,7 @@ impl ParsedVariant { #[derive(Clone, Debug)] pub(crate) struct ParsedEnum { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) enum_token: Token![enum], pub(crate) ident: Ident, @@ -142,7 +142,9 @@ impl ParsedEnum { } = item; let mut errors = Errors::new(); let mut options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(&mut attrs)) + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) .unwrap_or_default(); errors.ok(options.body.validate()); let ItemOptions { 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 88da9a7..e33d0e0 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1745,7 +1745,7 @@ impl, I, P: Clone> ParseTypes> for Punctuated< pub(crate) enum UnparsedGenericParam { Type { attrs: Vec, - options: HdlAttr, + options: HdlAttr, ident: Ident, colon_token: Token![:], bounds: ParsedBounds, @@ -1753,7 +1753,7 @@ pub(crate) enum UnparsedGenericParam { }, Const { attrs: Vec, - options: HdlAttr, + options: HdlAttr, const_token: Token![const], ident: Ident, colon_token: Token![:], @@ -2278,7 +2278,7 @@ impl ParsedBound { #[derive(Debug, Clone)] pub(crate) struct ParsedTypeParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) colon_token: Token![:], pub(crate) bounds: ParsedTypeBounds, @@ -2312,7 +2312,7 @@ impl ToTokens for ParsedTypeParam { #[derive(Debug, Clone)] pub(crate) struct ParsedSizeTypeParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) colon_token: Token![:], pub(crate) bounds: ParsedSizeTypeBounds, @@ -2356,7 +2356,7 @@ pub(crate) struct ParsedConstParamWhereBounds { #[derive(Debug, Clone)] pub(crate) struct ParsedConstParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) const_token: Token![const], pub(crate) ident: Ident, pub(crate) colon_token: Token![:], @@ -2413,7 +2413,7 @@ impl ParsedGenericParam { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub(crate) struct ParsedGenerics { pub(crate) lt_token: Option, pub(crate) params: Punctuated, @@ -2863,9 +2863,11 @@ impl ParsedGenerics { let (input_param, punct) = input_param.into_tuple(); let (unparsed_param, late_parsed_param) = match input_param { GenericParam::Lifetime(param) => { - errors.unwrap_or_default(HdlAttr::::parse_and_take_attr( - &mut param.attrs, - )); + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr( + &mut param.attrs, + ), + ); errors.error(param, "lifetime generics are not supported by #[hdl]"); continue; } @@ -2879,7 +2881,9 @@ impl ParsedGenerics { }) => { let span = ident.span(); let options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .unwrap_or_default(); let colon_token = colon_token.unwrap_or_else(|| Token![:](span)); if !bounds.is_empty() { @@ -2917,7 +2921,9 @@ impl ParsedGenerics { default, }) => { let options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .unwrap_or_default(); if let Some(default) = default { let _ = eq_token; diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 94fb040..3ec00bf 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -9,7 +9,8 @@ use syn::{ parse::{Parse, ParseStream, Parser}, parse_quote, punctuated::Pair, - AttrStyle, Attribute, Error, Item, Token, + spanned::Spanned, + AttrStyle, Attribute, Error, Item, ItemFn, Token, }; mod fold; @@ -17,8 +18,20 @@ mod hdl_bundle; mod hdl_enum; mod hdl_type_common; mod module; -//mod value_derive_common; -//mod value_derive_struct; + +pub(crate) trait CustomToken: + Copy + + Spanned + + ToTokens + + std::fmt::Debug + + Eq + + std::hash::Hash + + Default + + quote::IdentFragment + + Parse +{ + const IDENT_STR: &'static str; +} mod kw { pub(crate) use syn::token::Extern as extern_; @@ -38,6 +51,10 @@ mod kw { } crate::fold::no_op_fold!($kw); + + impl crate::CustomToken for $kw { + const IDENT_STR: &'static str = stringify!($kw); + } }; } @@ -46,6 +63,7 @@ mod kw { custom_keyword!(custom_bounds); custom_keyword!(flip); custom_keyword!(hdl); + custom_keyword!(hdl_module); custom_keyword!(input); custom_keyword!(instance); custom_keyword!(m); @@ -68,34 +86,34 @@ mod kw { type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676 #[derive(Clone, Debug)] -pub(crate) struct HdlAttr { +pub(crate) struct HdlAttr { pub(crate) pound_token: Pound, pub(crate) style: AttrStyle, pub(crate) bracket_token: syn::token::Bracket, - pub(crate) hdl: kw::hdl, + pub(crate) kw: KW, pub(crate) paren_token: Option, pub(crate) body: T, } crate::fold::impl_fold! { - struct HdlAttr { + struct HdlAttr { pound_token: Pound, style: AttrStyle, bracket_token: syn::token::Bracket, - hdl: kw::hdl, + kw: KW, paren_token: Option, body: T, } } #[allow(dead_code)] -impl HdlAttr { - pub(crate) fn split_body(self) -> (HdlAttr<()>, T) { +impl HdlAttr { + pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -104,19 +122,19 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: (), }, body, ) } - pub(crate) fn replace_body(self, body: T2) -> HdlAttr { + pub(crate) fn replace_body(self, body: T2) -> HdlAttr { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: _, } = self; @@ -124,17 +142,20 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } } - pub(crate) fn as_ref(&self) -> HdlAttr<&T> { + pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW> + where + KW: Clone, + { let Self { pound_token, style, bracket_token, - hdl, + ref kw, paren_token, ref body, } = *self; @@ -142,17 +163,20 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw: kw.clone(), paren_token, body, } } - pub(crate) fn try_map Result>(self, f: F) -> Result, E> { + pub(crate) fn try_map Result>( + self, + f: F, + ) -> Result, E> { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -160,17 +184,17 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: f(body)?, }) } - pub(crate) fn map R>(self, f: F) -> HdlAttr { + pub(crate) fn map R>(self, f: F) -> HdlAttr { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -178,7 +202,7 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: f(body), } @@ -186,31 +210,32 @@ impl HdlAttr { fn to_attr(&self) -> Attribute where T: ToTokens, + KW: ToTokens, { parse_quote! { #self } } } -impl Default for HdlAttr { +impl Default for HdlAttr { fn default() -> Self { T::default().into() } } -impl From for HdlAttr { +impl From for HdlAttr { fn from(body: T) -> Self { HdlAttr { pound_token: Default::default(), style: AttrStyle::Outer, bracket_token: Default::default(), - hdl: Default::default(), + kw: Default::default(), paren_token: Default::default(), body, } } } -impl ToTokens for HdlAttr { +impl ToTokens for HdlAttr { fn to_tokens(&self, tokens: &mut TokenStream) { self.pound_token.to_tokens(tokens); match self.style { @@ -218,7 +243,7 @@ impl ToTokens for HdlAttr { AttrStyle::Outer => {} }; self.bracket_token.surround(tokens, |tokens| { - self.hdl.to_tokens(tokens); + self.kw.to_tokens(tokens); match self.paren_token { Some(paren_token) => { paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens)) @@ -226,7 +251,7 @@ impl ToTokens for HdlAttr { None => { let body = self.body.to_token_stream(); if !body.is_empty() { - syn::token::Paren(self.hdl.span) + syn::token::Paren(self.kw.span()) .surround(tokens, |tokens| tokens.extend([body])); } } @@ -235,18 +260,24 @@ impl ToTokens for HdlAttr { } } -fn is_hdl_attr(attr: &Attribute) -> bool { - attr.path().is_ident("hdl") +fn is_hdl_attr(attr: &Attribute) -> bool { + attr.path().is_ident(KW::IDENT_STR) } -impl HdlAttr { - fn parse_and_take_attr(attrs: &mut Vec) -> syn::Result> { +impl HdlAttr { + fn parse_and_take_attr(attrs: &mut Vec) -> syn::Result> + where + KW: ToTokens, + { let mut retval = None; let mut errors = Errors::new(); attrs.retain(|attr| { - if is_hdl_attr(attr) { + if let Ok(kw) = syn::parse2::(attr.path().to_token_stream()) { if retval.is_some() { - errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); + errors.push(Error::new_spanned( + attr, + format_args!("more than one #[{}] attribute", kw.to_token_stream()), + )); } errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); false @@ -257,13 +288,19 @@ impl HdlAttr { errors.finish()?; Ok(retval) } - fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result> { + fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result> + where + KW: ToTokens, + { let mut retval = None; let mut errors = Errors::new(); for attr in attrs { - if is_hdl_attr(attr) { + if let Ok(kw) = syn::parse2::(attr.path().to_token_stream()) { if retval.is_some() { - errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); + errors.push(Error::new_spanned( + attr, + format_args!("more than one #[{}] attribute", kw.to_token_stream()), + )); } errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); } @@ -284,7 +321,7 @@ impl HdlAttr { ) -> syn::Result { let bracket_content; let bracket_token = bracketed!(bracket_content in input); - let hdl = bracket_content.parse()?; + let kw = bracket_content.parse()?; let paren_content; let body; let paren_token; @@ -305,7 +342,7 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, }) @@ -852,25 +889,31 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr } } -pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { - let options = syn::parse2::(attr)?; - let options = HdlAttr::from(options); - let func = syn::parse2::(quote! { #options #item })?; +fn hdl_module_impl(item: ItemFn) -> syn::Result { + let func = module::ModuleFn::parse_from_fn(item)?; + let options = func.config_options(); let mut contents = func.generate(); - if options.body.outline_generated.is_some() { + if options.outline_generated.is_some() { contents = outline_generated(contents, "module-"); } Ok(contents) } +pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { + let kw = kw::hdl_module::default(); + hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?) +} + pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result { - let item = syn::parse2::(quote! { #[hdl(#attr)] #item })?; + let kw = kw::hdl::default(); + let item = syn::parse2::(quote! { #[#kw(#attr)] #item })?; match item { Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item), + Item::Fn(item) => hdl_module_impl(item), _ => Err(syn::Error::new( Span::call_site(), - "top-level #[hdl] can only be used on structs or enums", + "top-level #[hdl] can only be used on structs, enums, or functions", )), } } diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 0945abb..6363bb3 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ hdl_type_common::{ParsedGenerics, SplitForImpl}, + kw, module::transform_body::{HdlLet, HdlLetKindIO}, options, Errors, HdlAttr, PairsIterExt, }; @@ -9,7 +10,6 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::collections::HashSet; use syn::{ - parse::{Parse, ParseStream}, parse_quote, visit::{visit_pat, Visit}, Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, @@ -59,9 +59,9 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> { pub(crate) type ModuleIO = HdlLet; -pub(crate) struct ModuleFn { +struct ModuleFnModule { attrs: Vec, - config_options: HdlAttr, + config_options: HdlAttr, module_kind: ModuleKind, vis: Visibility, sig: Signature, @@ -70,6 +70,26 @@ pub(crate) struct ModuleFn { the_struct: TokenStream, } +enum ModuleFnImpl { + Module(ModuleFnModule), + Fn { + attrs: Vec, + config_options: HdlAttr, + vis: Visibility, + sig: Signature, + block: Box, + }, +} + +options! { + pub(crate) enum HdlOrHdlModule { + Hdl(hdl), + HdlModule(hdl_module), + } +} + +pub(crate) struct ModuleFn(ModuleFnImpl); + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) enum ModuleKind { Extern, @@ -89,14 +109,25 @@ impl Visit<'_> for ContainsSkippedIdent<'_> { } } -impl Parse for ModuleFn { - fn parse(input: ParseStream) -> syn::Result { +impl ModuleFn { + pub(crate) fn config_options(&self) -> ConfigOptions { + let (ModuleFnImpl::Module(ModuleFnModule { + config_options: HdlAttr { body, .. }, + .. + }) + | ModuleFnImpl::Fn { + config_options: HdlAttr { body, .. }, + .. + }) = &self.0; + body.clone() + } + pub(crate) fn parse_from_fn(item: ItemFn) -> syn::Result { let ItemFn { mut attrs, vis, mut sig, block, - } = input.parse()?; + } = item; let Signature { ref constness, ref asyncness, @@ -111,43 +142,60 @@ impl Parse for ModuleFn { ref output, } = sig; let mut errors = Errors::new(); - let config_options = errors - .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) - .unwrap_or_default(); + let Some(mut config_options) = + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr(&mut attrs), + ) + else { + errors.error(sig.ident, "missing #[hdl] or #[hdl_module] attribute"); + errors.finish()?; + unreachable!(); + }; let ConfigOptions { outline_generated: _, extern_, } = config_options.body; - let module_kind = match extern_ { - Some(_) => ModuleKind::Extern, - None => ModuleKind::Normal, + let module_kind = match (config_options.kw, extern_) { + (HdlOrHdlModule::Hdl(_), None) => None, + (HdlOrHdlModule::Hdl(_), Some(extern2)) => { + config_options.body.extern_ = None; + errors.error( + extern2.0, + "extern can only be used as #[hdl_module(extern)]", + ); + None + } + (HdlOrHdlModule::HdlModule(_), None) => Some(ModuleKind::Normal), + (HdlOrHdlModule::HdlModule(_), Some(_)) => Some(ModuleKind::Extern), }; - for fn_arg in inputs { - match fn_arg { - FnArg::Receiver(_) => { - errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here")); - } - FnArg::Typed(fn_arg) => { - visit_pat( - &mut CheckNameConflictsWithModuleBuilderVisitor { - errors: &mut errors, - }, - &fn_arg.pat, - ); + if let HdlOrHdlModule::HdlModule(_) = config_options.kw { + for fn_arg in inputs { + match fn_arg { + FnArg::Receiver(_) => { + errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here")); + } + FnArg::Typed(fn_arg) => { + visit_pat( + &mut CheckNameConflictsWithModuleBuilderVisitor { + errors: &mut errors, + }, + &fn_arg.pat, + ); + } } } - } - if let Some(constness) = constness { - errors.push(syn::Error::new_spanned(constness, "const not allowed here")); - } - if let Some(asyncness) = asyncness { - errors.push(syn::Error::new_spanned(asyncness, "async not allowed here")); - } - if let Some(unsafety) = unsafety { - errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here")); - } - if let Some(abi) = abi { - errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); + if let Some(constness) = constness { + errors.push(syn::Error::new_spanned(constness, "const not allowed here")); + } + if let Some(asyncness) = asyncness { + errors.push(syn::Error::new_spanned(asyncness, "async not allowed here")); + } + if let Some(unsafety) = unsafety { + errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here")); + } + if let Some(abi) = abi { + errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); + } } let mut skipped_idents = HashSet::new(); let struct_generic_params = generics @@ -155,14 +203,17 @@ impl Parse for ModuleFn { .pairs_mut() .filter_map_pair_value_mut(|v| match v { GenericParam::Lifetime(LifetimeParam { attrs, .. }) => { - errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)); + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ); None } GenericParam::Type(TypeParam { attrs, ident, .. }) | GenericParam::Const(ConstParam { attrs, ident, .. }) => { if errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .is_some() { skipped_idents.insert(ident.clone()); @@ -176,6 +227,7 @@ impl Parse for ModuleFn { let struct_where_clause = generics .where_clause .as_mut() + .filter(|_| matches!(config_options.kw, HdlOrHdlModule::HdlModule(_))) .map(|where_clause| WhereClause { where_token: where_clause.where_token, predicates: where_clause @@ -198,22 +250,26 @@ impl Parse for ModuleFn { }) .collect(), }); - let struct_generics = Generics { - lt_token: generics.lt_token, - params: struct_generic_params, - gt_token: generics.gt_token, - where_clause: struct_where_clause, + let struct_generics = if let HdlOrHdlModule::HdlModule(_) = config_options.kw { + let mut struct_generics = Generics { + lt_token: generics.lt_token, + params: struct_generic_params, + gt_token: generics.gt_token, + where_clause: struct_where_clause, + }; + if let Some(variadic) = variadic { + errors.push(syn::Error::new_spanned(variadic, "... not allowed here")); + } + if !matches!(output, ReturnType::Default) { + errors.push(syn::Error::new_spanned( + output, + "return type not allowed here", + )); + } + errors.ok(ParsedGenerics::parse(&mut struct_generics)) + } else { + Some(ParsedGenerics::default()) }; - if let Some(variadic) = variadic { - errors.push(syn::Error::new_spanned(variadic, "... not allowed here")); - } - if !matches!(output, ReturnType::Default) { - errors.push(syn::Error::new_spanned( - output, - "return type not allowed here", - )); - } - let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics })); let body_results = struct_generics.as_ref().and_then(|struct_generics| { errors.ok(transform_body::transform_body( module_kind, @@ -224,6 +280,47 @@ impl Parse for ModuleFn { errors.finish()?; let struct_generics = struct_generics.unwrap(); let (block, io) = body_results.unwrap(); + let config_options = match config_options { + HdlAttr { + pound_token, + style, + bracket_token, + kw: HdlOrHdlModule::Hdl((kw,)), + paren_token, + body, + } => { + debug_assert!(io.is_empty()); + return Ok(Self(ModuleFnImpl::Fn { + attrs, + config_options: HdlAttr { + pound_token, + style, + bracket_token, + kw, + paren_token, + body, + }, + vis, + sig, + block, + })); + } + HdlAttr { + pound_token, + style, + bracket_token, + kw: HdlOrHdlModule::HdlModule((kw,)), + paren_token, + body, + } => HdlAttr { + pound_token, + style, + bracket_token, + kw, + paren_token, + body, + }, + }; let (_struct_impl_generics, _struct_type_generics, struct_where_clause) = struct_generics.split_for_impl(); let struct_where_clause: Option = parse_quote! { #struct_where_clause }; @@ -259,22 +356,22 @@ impl Parse for ModuleFn { } }; let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?; - Ok(Self { + Ok(Self(ModuleFnImpl::Module(ModuleFnModule { attrs, config_options, - module_kind, + module_kind: module_kind.unwrap(), vis, sig, block, struct_generics, the_struct, - }) + }))) } } impl ModuleFn { pub(crate) fn generate(self) -> TokenStream { - let Self { + let ModuleFnModule { attrs, config_options, module_kind, @@ -283,7 +380,28 @@ impl ModuleFn { block, struct_generics, the_struct, - } = self; + } = match self.0 { + ModuleFnImpl::Module(v) => v, + ModuleFnImpl::Fn { + attrs, + config_options, + vis, + sig, + block, + } => { + let ConfigOptions { + outline_generated: _, + extern_: _, + } = config_options.body; + return ItemFn { + attrs, + vis, + sig, + block, + } + .into_token_stream(); + } + }; let ConfigOptions { outline_generated: _, extern_: _, diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 76e1d69..d4d92b2 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -925,7 +925,7 @@ with_debug_clone_and_fold! { #[allow(dead_code)] pub(crate) struct HdlLet { pub(crate) attrs: Vec, - pub(crate) hdl_attr: HdlAttr, + pub(crate) hdl_attr: HdlAttr, pub(crate) let_token: Token![let], pub(crate) mut_token: Option, pub(crate) name: Ident, @@ -1112,7 +1112,7 @@ impl ToTokens for ImplicitName { } struct Visitor<'a> { - module_kind: ModuleKind, + module_kind: Option, errors: Errors, io: Vec, block_depth: usize, @@ -1120,22 +1120,33 @@ struct Visitor<'a> { } impl Visitor<'_> { - fn take_hdl_attr(&mut self, attrs: &mut Vec) -> Option> { + fn take_hdl_attr( + &mut self, + attrs: &mut Vec, + ) -> Option> { self.errors.unwrap_or( HdlAttr::parse_and_take_attr(attrs), Some(syn::parse2::(quote! {}).unwrap().into()), ) } - fn require_normal_module(&mut self, spanned: impl ToTokens) { + fn require_normal_module_or_fn(&mut self, spanned: impl ToTokens) { match self.module_kind { - ModuleKind::Extern => { + Some(ModuleKind::Extern) => { self.errors .error(spanned, "not allowed in #[hdl_module(extern)]"); } - ModuleKind::Normal => {} + Some(ModuleKind::Normal) | None => {} } } - fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { + fn require_module(&mut self, spanned: impl ToTokens) { + match self.module_kind { + None => { + self.errors.error(spanned, "not allowed in #[hdl] fn"); + } + Some(_) => {} + } + } + fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { let ExprIf { attrs, if_token, @@ -1143,7 +1154,7 @@ impl Visitor<'_> { then_branch, else_branch, } = expr_if; - self.require_normal_module(if_token); + self.require_normal_module_or_fn(if_token); let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), expr => expr, @@ -1208,11 +1219,12 @@ impl Visitor<'_> { .to_tokens(expr); }); let mut attrs = hdl_let.attrs.clone(); + self.require_module(kind); match self.module_kind { - ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> + Some(ModuleKind::Extern) => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> #[allow(unused_variables)] }), - ModuleKind::Normal => {} + Some(ModuleKind::Normal) | None => {} } let let_stmt = Local { attrs, @@ -1249,7 +1261,7 @@ impl Visitor<'_> { }, semi_token, } = hdl_let; - self.require_normal_module(instance); + self.require_normal_module_or_fn(instance); let mut expr = instance.to_token_stream(); paren.surround(&mut expr, |expr| { let name_str = ImplicitName { @@ -1276,7 +1288,7 @@ impl Visitor<'_> { fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet) -> Local { let name = &hdl_let.name; let reg_builder = hdl_let.kind.reg_builder; - self.require_normal_module(reg_builder); + self.require_normal_module_or_fn(reg_builder); let mut expr = reg_builder.to_token_stream(); hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { let name_str = ImplicitName { @@ -1327,7 +1339,7 @@ impl Visitor<'_> { fn process_hdl_let_wire(&mut self, hdl_let: HdlLet) -> Local { let name = &hdl_let.name; let wire = hdl_let.kind.wire; - self.require_normal_module(wire); + self.require_normal_module_or_fn(wire); let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); let mut expr = wire.to_token_stream(); hdl_let.kind.paren.surround(&mut expr, |expr| { @@ -1361,7 +1373,7 @@ impl Visitor<'_> { let name = &hdl_let.name; let memory_fn = hdl_let.kind.memory_fn; let memory_fn_name = memory_fn.name(); - self.require_normal_module(memory_fn_name); + self.require_normal_module_or_fn(memory_fn_name); let mut expr = memory_fn_name.to_token_stream(); let (paren, arg) = match memory_fn { MemoryFn::Memory { @@ -1543,7 +1555,7 @@ impl Fold for Visitor<'_> { } fn fold_attribute(&mut self, attr: Attribute) -> Attribute { - if is_hdl_attr(&attr) { + if is_hdl_attr::(&attr) { self.errors .error(&attr, "#[hdl] attribute not supported here"); } @@ -1610,8 +1622,9 @@ impl Fold for Visitor<'_> { fn fold_local(&mut self, let_stmt: Local) -> Local { match self .errors - .ok(HdlAttr::::parse_and_leave_attr(&let_stmt.attrs)) - { + .ok(HdlAttr::::parse_and_leave_attr( + &let_stmt.attrs, + )) { None => return empty_let(), Some(None) => return fold_local(self, let_stmt), Some(Some(HdlAttr { .. })) => {} @@ -1646,7 +1659,7 @@ impl Fold for Visitor<'_> { } pub(crate) fn transform_body( - module_kind: ModuleKind, + module_kind: Option, mut body: Box, parsed_generics: &ParsedGenerics, ) -> syn::Result<(Box, Vec)> { diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index 00ee706..b5a0ad3 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{module::transform_body::Visitor, HdlAttr}; +use crate::{kw, module::transform_body::Visitor, HdlAttr}; use quote::{format_ident, quote_spanned}; use syn::{ parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, @@ -10,10 +10,10 @@ use syn::{ impl Visitor<'_> { pub(crate) fn process_hdl_array( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_array: ExprArray, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); for elem in &mut expr_array.elems { *elem = parse_quote_spanned! {elem.span()=> ::fayalite::expr::ToExpr::to_expr(&(#elem)) @@ -23,10 +23,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_repeat( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_repeat: ExprRepeat, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); let repeated_value = &expr_repeat.expr; *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) @@ -35,10 +35,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_struct( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_struct: ExprStruct, ) -> Expr { - self.require_normal_module(&hdl_attr); + self.require_normal_module_or_fn(&hdl_attr); let name_span = expr_struct.path.segments.last().unwrap().ident.span(); let builder_ident = format_ident!("__builder", span = name_span); let empty_builder = if expr_struct.qself.is_some() @@ -91,10 +91,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_tuple( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_tuple: ExprTuple, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); parse_quote_spanned! {expr_tuple.span()=> ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index ae21a73..1d53104 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ fold::{impl_fold, DoFold}, + kw, module::transform_body::{with_debug_clone_and_fold, Visitor}, Errors, HdlAttr, PairsIterExt, }; @@ -749,7 +750,7 @@ struct HdlMatchParseState<'a> { impl Visitor<'_> { pub(crate) fn process_hdl_match( &mut self, - _hdl_attr: HdlAttr, + _hdl_attr: HdlAttr, expr_match: ExprMatch, ) -> Expr { let span = expr_match.match_token.span(); @@ -761,7 +762,7 @@ impl Visitor<'_> { brace_token: _, arms, } = expr_match; - self.require_normal_module(match_token); + self.require_normal_module_or_fn(match_token); let mut state = HdlMatchParseState { match_span: span, errors: &mut self.errors, diff --git a/crates/fayalite/tests/ui/hdl_types.stderr b/crates/fayalite/tests/ui/hdl_types.stderr index e01b04f..a65d796 100644 --- a/crates/fayalite/tests/ui/hdl_types.stderr +++ b/crates/fayalite/tests/ui/hdl_types.stderr @@ -1,4 +1,4 @@ -error: top-level #[hdl] can only be used on structs or enums +error: top-level #[hdl] can only be used on structs, enums, or functions --> tests/ui/hdl_types.rs:5:1 | 5 | #[hdl] From df55a514e442d07e1ef76275bcf481146381989d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 20 Sep 2024 18:46:56 -0700 Subject: [PATCH 002/190] add support for incomplete_wire -- a wire that you can supply the type of later --- crates/fayalite-proc-macros-impl/src/lib.rs | 1 + .../src/module/transform_body.rs | 83 +++++++++++++++++ crates/fayalite/src/module.rs | 91 ++++++++++++++++++- crates/fayalite/src/prelude.rs | 4 +- crates/fayalite/src/wire.rs | 60 +++++++++++- 5 files changed, 231 insertions(+), 8 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 3ec00bf..903983f 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -65,6 +65,7 @@ mod kw { custom_keyword!(hdl); custom_keyword!(hdl_module); custom_keyword!(input); + custom_keyword!(incomplete_wire); custom_keyword!(instance); custom_keyword!(m); custom_keyword!(memory); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index d4d92b2..1f9565a 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -34,6 +34,7 @@ options! { Instance(instance), RegBuilder(reg_builder), Wire(wire), + IncompleteWire(incomplete_wire), Memory(memory), MemoryArray(memory_array), MemoryWithInit(memory_with_init), @@ -533,6 +534,41 @@ impl HdlLetKindToTokens for HdlLetKindWire { } } +options! { + pub(crate) enum LetFnKindIncomplete { + IncompleteWire(incomplete_wire), + } +} + +#[derive(Clone, Debug)] +pub(crate) struct HdlLetKindIncomplete { + pub(crate) kind: LetFnKindIncomplete, + pub(crate) paren: Paren, +} + +impl ParseTypes for HdlLetKindIncomplete { + fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result { + Ok(input.clone()) + } +} + +impl_fold! { + struct HdlLetKindIncomplete<> { + kind: LetFnKindIncomplete, + paren: Paren, + } +} + +impl HdlLetKindToTokens for HdlLetKindIncomplete { + fn ty_to_tokens(&self, _tokens: &mut TokenStream) {} + + fn expr_to_tokens(&self, tokens: &mut TokenStream) { + let Self { kind, paren } = self; + kind.to_tokens(tokens); + paren.surround(tokens, |_| {}); + } +} + options! { pub(crate) enum MemoryFnName { Memory(memory), @@ -697,6 +733,7 @@ impl HdlLetKindMemory { #[derive(Clone, Debug)] pub(crate) enum HdlLetKind { IO(HdlLetKindIO), + Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), Wire(HdlLetKindWire), @@ -706,6 +743,7 @@ pub(crate) enum HdlLetKind { impl_fold! { enum HdlLetKind { IO(HdlLetKindIO), + Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), Wire(HdlLetKindWire), @@ -720,6 +758,9 @@ impl, I> ParseTypes> for HdlLetKind { ) -> Result { match input { HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO), + HdlLetKind::Incomplete(input) => { + ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete) + } HdlLetKind::Instance(input) => { ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance) } @@ -871,6 +912,20 @@ impl HdlLetKindParse for HdlLetKind { ty_expr: paren_contents.call(parse_optional_fn_arg)?, })) } + LetFnKind::IncompleteWire(incomplete_wire) => { + if let Some(parsed_ty) = parsed_ty { + return Err(Error::new_spanned( + parsed_ty.1, + "type annotation not allowed for incomplete_wire", + )); + } + check_empty_m_dot(m_dot, kind)?; + let _paren_contents; + Ok(Self::Incomplete(HdlLetKindIncomplete { + kind: LetFnKindIncomplete::IncompleteWire(incomplete_wire), + paren: parenthesized!(_paren_contents in input), + })) + } LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse( input, parsed_ty, @@ -903,6 +958,7 @@ impl HdlLetKindToTokens for HdlLetKind { fn ty_to_tokens(&self, tokens: &mut TokenStream) { match self { HdlLetKind::IO(v) => v.ty_to_tokens(tokens), + HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens), HdlLetKind::Instance(v) => v.ty_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens), HdlLetKind::Wire(v) => v.ty_to_tokens(tokens), @@ -913,6 +969,7 @@ impl HdlLetKindToTokens for HdlLetKind { fn expr_to_tokens(&self, tokens: &mut TokenStream) { match self { HdlLetKind::IO(v) => v.expr_to_tokens(tokens), + HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens), HdlLetKind::Instance(v) => v.expr_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens), HdlLetKind::Wire(v) => v.expr_to_tokens(tokens), @@ -1369,6 +1426,31 @@ impl Visitor<'_> { semi_token: hdl_let.semi_token, } } + fn process_hdl_let_incomplete(&mut self, hdl_let: HdlLet) -> Local { + let name = &hdl_let.name; + let kind = hdl_let.kind.kind; + self.require_normal_module_or_fn(kind); + let mut expr = kind.to_token_stream(); + hdl_let.kind.paren.surround(&mut expr, |expr| { + ImplicitName { + name, + span: name.span(), + } + .to_tokens(expr); + }); + let mut_token = &hdl_let.mut_token; + Local { + attrs: hdl_let.attrs.clone(), + let_token: hdl_let.let_token, + pat: parse_quote! { #mut_token #name }, + init: Some(LocalInit { + eq_token: hdl_let.eq_token, + expr: parse_quote! { #expr }, + diverge: None, + }), + semi_token: hdl_let.semi_token, + } + } fn process_hdl_let_memory(&mut self, hdl_let: HdlLet) -> Local { let name = &hdl_let.name; let memory_fn = hdl_let.kind.memory_fn; @@ -1438,6 +1520,7 @@ impl Visitor<'_> { } the_match! { IO => process_hdl_let_io, + Incomplete => process_hdl_let_incomplete, Instance => process_hdl_let_instance, RegBuilder => process_hdl_let_reg_builder, Wire => process_hdl_let_wire, diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index cb57758..3a17343 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -22,7 +22,7 @@ use crate::{ source_location::SourceLocation, ty::{CanonicalType, Type}, util::ScopedRef, - wire::Wire, + wire::{IncompleteWire, Wire}, }; use hashbrown::{hash_map::Entry, HashMap, HashSet}; use num_bigint::BigInt; @@ -118,9 +118,35 @@ pub trait BlockRef: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug {} impl BlockRef for BlockId {} +pub(crate) enum IncompleteDeclaration { + Incomplete { + name: ScopedNameId, + source_location: SourceLocation, + }, + Complete(StmtDeclaration), + Taken, +} + +impl fmt::Debug for IncompleteDeclaration { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Incomplete { + name, + source_location: _, + } => f + .debug_struct("Incomplete") + .field("name", name) + .finish_non_exhaustive(), + Self::Complete(v) => v.fmt(f), + Self::Taken => f.write_str("Taken"), + } + } +} + #[derive(Debug)] pub struct BuilderBlock { memories: Vec>>, + incomplete_declarations: Vec>>, stmts: Vec>, } @@ -831,13 +857,34 @@ impl From> for NormalModuleBody { annotations_map: &mut HashMap, Vec>, block_id: BlockId, ) -> Block { - let BuilderBlock { memories, stmts } = &mut blocks[block_id.as_usize()]; + let BuilderBlock { + memories, + incomplete_declarations, + stmts, + } = &mut blocks[block_id.as_usize()]; let memories = Interned::from_iter( memories .drain(..) .filter_map(|memory| memory.borrow().make_memory()), ); - let stmts = std::mem::take(stmts); + let stmts = Vec::from_iter( + incomplete_declarations + .drain(..) + .map(|decl| { + match std::mem::replace( + &mut *decl.borrow_mut(), + IncompleteDeclaration::Taken, + ) { + IncompleteDeclaration::Incomplete { + name, + source_location, + } => panic!("incomplete declaration: {name:?}\nat: {source_location}"), + IncompleteDeclaration::Complete(v) => Stmt::Declaration(v), + IncompleteDeclaration::Taken => unreachable!(), + } + }) + .chain(stmts.drain(..)), + ); let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| { match stmt { Stmt::Connect(stmt) => stmt.into(), @@ -908,6 +955,7 @@ impl NormalModuleBody { let index = self.body.blocks.len(); self.body.blocks.push(BuilderBlock { memories: vec![], + incomplete_declarations: vec![], stmts: vec![], }); BlockId(index) @@ -1943,6 +1991,7 @@ impl ModuleBuilder { body: BuilderModuleBody { blocks: vec![BuilderBlock { memories: vec![], + incomplete_declarations: vec![], stmts: vec![], }], annotations_map: HashMap::new(), @@ -2156,6 +2205,42 @@ pub fn wire(implicit_name: ImplicitName<'_>, ty: T) -> Expr { wire_with_loc(implicit_name.0, SourceLocation::caller(), ty) } +#[track_caller] +fn incomplete_declaration( + name: &str, + source_location: SourceLocation, +) -> Rc> { + ModuleBuilder::with(|m| { + let mut impl_ = m.impl_.borrow_mut(); + let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern())); + drop(impl_); + let retval = Rc::new(RefCell::new(IncompleteDeclaration::Incomplete { + name: scoped_name, + source_location, + })); + let mut impl_ = m.impl_.borrow_mut(); + impl_ + .body + .builder_normal_body() + .block(m.block_stack.top()) + .incomplete_declarations + .push(retval.clone()); + retval + }) +} + +#[track_caller] +pub fn incomplete_wire_with_loc(name: &str, source_location: SourceLocation) -> IncompleteWire { + IncompleteWire { + declaration: incomplete_declaration(name, source_location), + } +} + +#[track_caller] +pub fn incomplete_wire(implicit_name: ImplicitName<'_>) -> IncompleteWire { + incomplete_wire_with_loc(implicit_name.0, SourceLocation::caller()) +} + #[track_caller] pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> { ModuleBuilder::with(|m| { diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 14c3aa7..bedece2 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -9,8 +9,8 @@ pub use crate::{ int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ - annotate, connect, connect_any, instance, memory, memory_array, memory_with_init, - reg_builder, wire, Instance, Module, ModuleBuilder, + annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, + memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index b84d6ae..85ab342 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -1,13 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::Flow, + expr::{Expr, Flow, ToExpr}, intern::Interned, - module::{NameId, ScopedNameId}, + module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, source_location::SourceLocation, ty::{CanonicalType, Type}, }; -use std::fmt; +use std::{cell::RefCell, fmt, rc::Rc}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Wire { @@ -76,3 +76,57 @@ impl Wire { true } } + +#[derive(Clone)] +pub struct IncompleteWire { + pub(crate) declaration: Rc>, +} + +impl IncompleteWire { + #[track_caller] + pub fn complete(&mut self, ty: T) -> Expr { + let canonical_type = ty.canonical(); + let mut declaration = self.declaration.borrow_mut(); + if let IncompleteDeclaration::Incomplete { + name, + source_location, + } = *declaration + { + *declaration = IncompleteDeclaration::Complete( + StmtWire { + annotations: (), + wire: Wire { + name, + source_location, + ty: canonical_type, + }, + } + .into(), + ); + } + match *declaration { + IncompleteDeclaration::Complete(StmtDeclaration::Wire(StmtWire { + wire: + Wire { + name, + source_location, + ty: wire_ty, + }, + .. + })) => { + drop(declaration); + assert_eq!(wire_ty, canonical_type, "type mismatch"); + Wire { + name, + source_location, + ty, + } + .to_expr() + } + IncompleteDeclaration::Taken => panic!("can't use wire outside of containing module"), + IncompleteDeclaration::Complete(_) | IncompleteDeclaration::Incomplete { .. } => { + unreachable!() + } + } + } +} From ff269e5def6395f4533180ceea51083d76801c07 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 20 Sep 2024 18:49:12 -0700 Subject: [PATCH 003/190] add utility functions on HdlOption, inspired by Option's API --- crates/fayalite/src/enum_.rs | 311 ++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 3 deletions(-) diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 384414c..13724ef 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -7,14 +7,14 @@ use crate::{ int::Bool, intern::{Intern, Interned}, module::{ - enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl, - EnumMatchVariantsIterImpl, Scope, + connect, enum_match_variants_helper, incomplete_wire, wire, + EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, }, source_location::SourceLocation, ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, }; use hashbrown::HashMap; -use std::{fmt, iter::FusedIterator}; +use std::{convert::Infallible, fmt, iter::FusedIterator}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct EnumVariant { @@ -364,3 +364,308 @@ pub fn HdlSome(value: impl ToExpr) -> Expr> { let value = value.to_expr(); HdlOption[Expr::ty(value)].HdlSome(value) } + +impl HdlOption { + #[track_caller] + pub fn try_map( + expr: Expr, + f: impl FnOnce(Expr) -> Result, E>, + ) -> Result>, E> { + Self::try_and_then(expr, |v| Ok(HdlSome(f(v)?))) + } + #[track_caller] + pub fn map( + expr: Expr, + f: impl FnOnce(Expr) -> Expr, + ) -> Expr> { + Self::and_then(expr, |v| HdlSome(f(v))) + } + #[hdl] + #[track_caller] + pub fn try_and_then( + expr: Expr, + f: impl FnOnce(Expr) -> Result>, E>, + ) -> Result>, E> { + // manually run match steps so we can extract the return type to construct HdlNone + type Wrap = T; + #[hdl] + let mut and_then_out = incomplete_wire(); + let mut iter = Self::match_variants(expr, SourceLocation::caller()); + let none = iter.next().unwrap(); + let some = iter.next().unwrap(); + assert!(iter.next().is_none()); + let (Wrap::<::MatchVariant>::HdlSome(value), some_scope) = + Self::match_activate_scope(some) + else { + unreachable!(); + }; + let value = f(value).map_err(|e| { + and_then_out.complete(()); // avoid error + e + })?; + let and_then_out = and_then_out.complete(Expr::ty(value)); + connect(and_then_out, value); + drop(some_scope); + let (Wrap::<::MatchVariant>::HdlNone, none_scope) = + Self::match_activate_scope(none) + else { + unreachable!(); + }; + connect(and_then_out, Expr::ty(and_then_out).HdlNone()); + drop(none_scope); + Ok(and_then_out) + } + #[track_caller] + pub fn and_then( + expr: Expr, + f: impl FnOnce(Expr) -> Expr>, + ) -> Expr> { + match Self::try_and_then(expr, |v| Ok::<_, Infallible>(f(v))) { + Ok(v) => v, + Err(e) => match e {}, + } + } + #[hdl] + #[track_caller] + pub fn and(expr: Expr, opt_b: Expr>) -> Expr> { + #[hdl] + let and_out = wire(Expr::ty(opt_b)); + connect(and_out, Expr::ty(opt_b).HdlNone()); + #[hdl] + if let HdlSome(_) = expr { + connect(and_out, opt_b); + } + and_out + } + #[hdl] + #[track_caller] + pub fn try_filter( + expr: Expr, + f: impl FnOnce(Expr) -> Result, E>, + ) -> Result, E> { + #[hdl] + let filtered = wire(Expr::ty(expr)); + connect(filtered, Expr::ty(expr).HdlNone()); + let mut f = Some(f); + #[hdl] + if let HdlSome(v) = expr { + #[hdl] + if f.take().unwrap()(v)? { + connect(filtered, HdlSome(v)); + } + } + Ok(filtered) + } + #[hdl] + #[track_caller] + pub fn filter(expr: Expr, f: impl FnOnce(Expr) -> Expr) -> Expr { + match Self::try_filter(expr, |v| Ok::<_, Infallible>(f(v))) { + Ok(v) => v, + Err(e) => match e {}, + } + } + #[hdl] + #[track_caller] + pub fn try_inspect( + expr: Expr, + f: impl FnOnce(Expr) -> Result<(), E>, + ) -> Result, E> { + let mut f = Some(f); + #[hdl] + if let HdlSome(v) = expr { + f.take().unwrap()(v)?; + } + Ok(expr) + } + #[hdl] + #[track_caller] + pub fn inspect(expr: Expr, f: impl FnOnce(Expr)) -> Expr { + let mut f = Some(f); + #[hdl] + if let HdlSome(v) = expr { + f.take().unwrap()(v); + } + expr + } + #[hdl] + #[track_caller] + pub fn is_none(expr: Expr) -> Expr { + #[hdl] + let is_none_out: Bool = wire(); + connect(is_none_out, false); + #[hdl] + if let HdlNone = expr { + connect(is_none_out, true); + } + is_none_out + } + #[hdl] + #[track_caller] + pub fn is_some(expr: Expr) -> Expr { + #[hdl] + let is_some_out: Bool = wire(); + connect(is_some_out, false); + #[hdl] + if let HdlSome(_) = expr { + connect(is_some_out, true); + } + is_some_out + } + #[hdl] + #[track_caller] + pub fn map_or( + expr: Expr, + default: Expr, + f: impl FnOnce(Expr) -> Expr, + ) -> Expr { + #[hdl] + let mapped = wire(Expr::ty(default)); + let mut f = Some(f); + #[hdl] + match expr { + HdlSome(v) => connect(mapped, f.take().unwrap()(v)), + HdlNone => connect(mapped, default), + } + mapped + } + #[hdl] + #[track_caller] + pub fn map_or_else( + expr: Expr, + default: impl FnOnce() -> Expr, + f: impl FnOnce(Expr) -> Expr, + ) -> Expr { + #[hdl] + let mut mapped = incomplete_wire(); + let mut default = Some(default); + let mut f = Some(f); + let mut retval = None; + #[hdl] + match expr { + HdlSome(v) => { + let v = f.take().unwrap()(v); + let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + connect(mapped, v); + } + HdlNone => { + let v = default.take().unwrap()(); + let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v))); + connect(mapped, v); + } + } + retval.unwrap() + } + #[hdl] + #[track_caller] + pub fn or(expr: Expr, opt_b: Expr) -> Expr { + #[hdl] + let or_out = wire(Expr::ty(expr)); + connect(or_out, opt_b); + #[hdl] + if let HdlSome(_) = expr { + connect(or_out, expr); + } + or_out + } + #[hdl] + #[track_caller] + pub fn or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { + #[hdl] + let or_else_out = wire(Expr::ty(expr)); + connect(or_else_out, f()); + #[hdl] + if let HdlSome(_) = expr { + connect(or_else_out, expr); + } + or_else_out + } + #[hdl] + #[track_caller] + pub fn unwrap_or(expr: Expr, default: Expr) -> Expr { + #[hdl] + let unwrap_or_else_out = wire(Expr::ty(default)); + connect(unwrap_or_else_out, default); + #[hdl] + if let HdlSome(v) = expr { + connect(unwrap_or_else_out, v); + } + unwrap_or_else_out + } + #[hdl] + #[track_caller] + pub fn unwrap_or_else(expr: Expr, f: impl FnOnce() -> Expr) -> Expr { + #[hdl] + let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome); + connect(unwrap_or_else_out, f()); + #[hdl] + if let HdlSome(v) = expr { + connect(unwrap_or_else_out, v); + } + unwrap_or_else_out + } + #[hdl] + #[track_caller] + pub fn xor(expr: Expr, opt_b: Expr) -> Expr { + #[hdl] + let xor_out = wire(Expr::ty(expr)); + #[hdl] + if let HdlSome(_) = expr { + #[hdl] + if let HdlNone = opt_b { + connect(xor_out, expr); + } else { + connect(xor_out, Expr::ty(expr).HdlNone()); + } + } else { + connect(xor_out, opt_b); + } + xor_out + } + #[hdl] + #[track_caller] + pub fn zip(expr: Expr, other: Expr>) -> Expr> { + #[hdl] + let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]); + connect(zip_out, Expr::ty(zip_out).HdlNone()); + #[hdl] + if let HdlSome(l) = expr { + #[hdl] + if let HdlSome(r) = other { + connect(zip_out, HdlSome((l, r))); + } + } + zip_out + } +} + +impl HdlOption> { + #[hdl] + #[track_caller] + pub fn flatten(expr: Expr) -> Expr> { + #[hdl] + let flattened = wire(Expr::ty(expr).HdlSome); + #[hdl] + match expr { + HdlSome(v) => connect(flattened, v), + HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()), + } + flattened + } +} + +impl HdlOption<(T, U)> { + #[hdl] + #[track_caller] + pub fn unzip(expr: Expr) -> Expr<(HdlOption, HdlOption)> { + let (t, u) = Expr::ty(expr).HdlSome; + #[hdl] + let unzipped = wire((HdlOption[t], HdlOption[u])); + connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone())); + #[hdl] + if let HdlSome(v) = expr { + connect(unzipped.0, HdlSome(v.0)); + connect(unzipped.1, HdlSome(v.1)); + } + unzipped + } +} From 51ce7b079ec1b0ea53c31348be86f5c2fb52c124 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 20 Sep 2024 19:11:30 -0700 Subject: [PATCH 004/190] add ReadyValid --- crates/fayalite/src/util.rs | 2 ++ crates/fayalite/src/util/ready_valid.rs | 34 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 crates/fayalite/src/util/ready_valid.rs diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index fc5daf4..5b97e3b 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -25,3 +25,5 @@ pub use scoped_ref::ScopedRef; pub use misc::{ interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, }; + +pub mod ready_valid; diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs new file mode 100644 index 0000000..a5893cf --- /dev/null +++ b/crates/fayalite/src/util/ready_valid.rs @@ -0,0 +1,34 @@ +use crate::prelude::*; + +#[hdl] +pub struct ReadyValid { + pub data: HdlOption, + #[hdl(flip)] + pub ready: Bool, +} + +impl ReadyValid { + #[hdl] + pub fn fire(expr: Expr) -> Expr { + #[hdl] + let fire: Bool = wire(); + #[hdl] + match expr.data { + HdlNone => connect(fire, false), + HdlSome(_) => connect(fire, expr.ready), + } + fire + } + #[hdl] + pub fn map( + expr: Expr, + f: impl FnOnce(Expr) -> Expr, + ) -> Expr> { + let data = HdlOption::map(expr.data, f); + #[hdl] + let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]); + connect(mapped.data, data); + connect(expr.ready, mapped.ready); + mapped + } +} From 053391b01035ac0d4a62deea9a86a63865ebd739 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 15:29:28 -0700 Subject: [PATCH 005/190] add script for checking copyright headers --- scripts/check-copyright.sh | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 scripts/check-copyright.sh diff --git a/scripts/check-copyright.sh b/scripts/check-copyright.sh new file mode 100755 index 0000000..8104f9c --- /dev/null +++ b/scripts/check-copyright.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# SPDX-License-Identifier: LGPL-3.0-or-later +# See Notices.txt for copyright information +set -e + +function fail() +{ + local error="$1" + echo "error: $error" >&2 + exit 1 +} + +function fail_file() +{ + local file="$1" line="$2" error="$3" + fail "$file:$((line + 1)): $error" +} + +function check_file() +{ + local file="$1" regexes=("${@:2}") + local lines + mapfile -t lines < "$file" + local line + for line in "${!regexes[@]}"; do + eval '[[ "${lines[i]}" =~ '"${regexes[i]}"' ]]' || + fail_file "$file" "$line" "doesn't match regex: ${regexes[i]}" + done +} + +POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notices.txt for copyright information"$') +SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$') +MD_HEADER=('^" # Fayalite Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/). diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 7f7d626..30cf90f 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::{ hdl_type_common::{ common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index d7e5b61..50fb138 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::{ hdl_type_common::{ common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, 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 e33d0e0..efbe7f3 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::{fold::impl_fold, kw, Errors, HdlAttr, PairsIterExt}; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote_spanned, ToTokens}; diff --git a/crates/fayalite/examples/blinky.rs b/crates/fayalite/examples/blinky.rs index 588ca9a..94c7910 100644 --- a/crates/fayalite/examples/blinky.rs +++ b/crates/fayalite/examples/blinky.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use clap::Parser; use fayalite::{cli, prelude::*}; diff --git a/crates/fayalite/src/_docs.rs b/crates/fayalite/src/_docs.rs index 4d254a7..5b1888b 100644 --- a/crates/fayalite/src/_docs.rs +++ b/crates/fayalite/src/_docs.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information #![doc = include_str!("../README.md")] //! diff --git a/crates/fayalite/src/_docs/modules.rs b/crates/fayalite/src/_docs/modules.rs index c392f2e..99b98e8 100644 --- a/crates/fayalite/src/_docs/modules.rs +++ b/crates/fayalite/src/_docs/modules.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Fayalite Modules //! //! The [`#[hdl_module]`][`crate::hdl_module`] attribute is applied to a Rust diff --git a/crates/fayalite/src/_docs/modules/extern_module.rs b/crates/fayalite/src/_docs/modules/extern_module.rs index bf2034b..c1367d9 100644 --- a/crates/fayalite/src/_docs/modules/extern_module.rs +++ b/crates/fayalite/src/_docs/modules/extern_module.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! These are for when you want to use modules written in //! some other language, such as Verilog. //! diff --git a/crates/fayalite/src/_docs/modules/module_bodies.rs b/crates/fayalite/src/_docs/modules/module_bodies.rs index bd85c61..c12ae21 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Module Function Bodies //! //! The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_array_expressions.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_array_expressions.rs index c4bbfa4..c0b15ad 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_array_expressions.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_array_expressions.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # `#[hdl]` Array Expressions //! //! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][type@Array] expression: diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs index 46bb568..7d09943 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_if_statements.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # `#[hdl] if` Statements //! //! `#[hdl] if` statements behave similarly to Rust `if` statements, except they end up as muxes diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs index c4e3e70..61d29b5 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! ## `#[hdl] let` statements pub mod inputs_outputs; diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs index bfd7521..14169d9 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/inputs_outputs.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! ### Inputs/Outputs //! //! Inputs/Outputs create a Rust variable with type [`Expr`] where `T` is the type of the input/output. diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs index 2776754..75def03 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/instances.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! ### Module Instances //! //! module instances are kinda like the hardware equivalent of calling a function, diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs index e491eef..ddd60b9 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/memories.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Memories //! //! Memories are optimized for storing large amounts of data. diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs index 28db27f..5a81c5b 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! ### Registers //! //! Registers are memory devices that will change their state only on a clock diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs index b22e0fd..7d92b41 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/wires.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! ### Wires //! //! Wires are kinda like variables, but unlike registers, diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs index a6c9b58..91710e7 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_literals.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # `_hdl`-suffixed literals //! //! You can have integer literals with an arbitrary number of bits like so: diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs index c0d4ea6..9e6c511 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_match_statements.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # `#[hdl] match` Statements //! //! `#[hdl] match` statements behave similarly to Rust `match` statements, except they end up as muxes diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_struct_variant_expressions.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_struct_variant_expressions.rs index 9d63895..68cd685 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_struct_variant_expressions.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_struct_variant_expressions.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # `#[hdl]` Struct/Variant Expressions //! //! Note: Structs are also known as [Bundles] when used in Fayalite, the Bundle name comes from [FIRRTL]. diff --git a/crates/fayalite/src/_docs/modules/normal_module.rs b/crates/fayalite/src/_docs/modules/normal_module.rs index f84678e..1267551 100644 --- a/crates/fayalite/src/_docs/modules/normal_module.rs +++ b/crates/fayalite/src/_docs/modules/normal_module.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Normal Modules //! //! See also: [Extern Modules][`super::extern_module`] diff --git a/crates/fayalite/src/_docs/semantics.rs b/crates/fayalite/src/_docs/semantics.rs index a499e8e..2282f25 100644 --- a/crates/fayalite/src/_docs/semantics.rs +++ b/crates/fayalite/src/_docs/semantics.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Fayalite Semantics //! //! Fayalite's semantics are based on [FIRRTL]. Due to their significance, some of the semantics are also documented here. diff --git a/crates/fayalite/src/_docs/semantics/connection_semantics.rs b/crates/fayalite/src/_docs/semantics/connection_semantics.rs index 41155bf..ba2a679 100644 --- a/crates/fayalite/src/_docs/semantics/connection_semantics.rs +++ b/crates/fayalite/src/_docs/semantics/connection_semantics.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information //! # Connection Semantics //! //! Fayalite's connection semantics are unlike assignments in software, so be careful! diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index f848d36..5071279 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::{ bundle::{Bundle, BundleType}, firrtl, diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index f33b286..0f85f62 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::{ array::Array, bundle::{Bundle, BundleField}, diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index bedece2..6a74e67 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information pub use crate::{ annotations::Annotation, array::{Array, ArrayType}, diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index a5893cf..ec761c2 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information use crate::prelude::*; #[hdl] diff --git a/crates/fayalite/src/util/scoped_ref.rs b/crates/fayalite/src/util/scoped_ref.rs index f168853..7705377 100644 --- a/crates/fayalite/src/util/scoped_ref.rs +++ b/crates/fayalite/src/util/scoped_ref.rs @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information mod safety_boundary { use std::{cell::Cell, ptr::NonNull}; diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 1748e47..ad9ed25 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1,4 +1,8 @@ { + "license_header": [ + "SPDX-License-Identifier: LGPL-3.0-or-later", + "See Notices.txt for copyright information" + ], "types": { "Module": { "data": { From bdbc6d89bd8046483e8b3ad7ff3e9cde7afb0f7b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 15:30:53 -0700 Subject: [PATCH 007/190] add check-copyright to CI --- .forgejo/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index df5f59c..6e82abf 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -9,6 +9,8 @@ jobs: - uses: https://code.forgejo.org/actions/checkout@v3 with: fetch-depth: 0 + - run: | + scripts/check-copyright.sh - run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 source "$HOME/.cargo/env" From 790bb15408536d38856e8ffcc76069baf3269b91 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 16:01:23 -0700 Subject: [PATCH 008/190] remove reset_default from proc-macro, forgot to remove when removing from RegBuilder --- crates/fayalite-proc-macros-impl/src/lib.rs | 1 - .../src/module/transform_body.rs | 36 ++----------------- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 903983f..194dbf3 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -78,7 +78,6 @@ mod kw { custom_keyword!(output); custom_keyword!(reg_builder); custom_keyword!(reset); - custom_keyword!(reset_default); custom_keyword!(skip); custom_keyword!(target); custom_keyword!(wire); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 1f9565a..6e99e87 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -265,11 +265,6 @@ pub(crate) enum RegBuilderReset { paren: Paren, init_expr: Box, }, - ResetDefault { - dot_token: Token![.], - reset_default: kw::reset_default, - paren: Paren, - }, } impl_fold! { @@ -286,11 +281,6 @@ impl_fold! { paren: Paren, init_expr: Box, }, - ResetDefault { - dot_token: Token![.], - reset_default: kw::reset_default, - paren: Paren, - }, } } @@ -312,11 +302,6 @@ impl Parse for RegBuilderReset { paren: parenthesized!(paren_contents in input), init_expr: paren_contents.call(parse_single_fn_arg)?, }), - RegBuilderMethod::ResetDefault(reset_default) => Ok(Self::ResetDefault { - dot_token, - reset_default, - paren: parenthesized!(paren_contents in input), - }), } } } @@ -344,15 +329,6 @@ impl ToTokens for RegBuilderReset { reset.to_tokens(tokens); paren.surround(tokens, |tokens| init_expr.to_tokens(tokens)); } - RegBuilderReset::ResetDefault { - dot_token, - reset_default, - paren, - } => { - dot_token.to_tokens(tokens); - reset_default.to_tokens(tokens); - paren.surround(tokens, |_| {}); - } } } } @@ -401,8 +377,6 @@ make_builder_method_enum! { NoReset(no_reset), #[cond = need_reset] Reset(reset), - #[cond = need_reset] - ResetDefault(reset_default), } } @@ -445,17 +419,13 @@ impl HdlLetKindRegBuilder { let mut clock_domain = None; match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, true)?.1 { RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?), - RegBuilderMethod::NoReset(_) - | RegBuilderMethod::Reset(_) - | RegBuilderMethod::ResetDefault(_) => {} + RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => {} } let reset = input.parse()?; if clock_domain.is_none() { match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, false)?.1 { RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?), - RegBuilderMethod::NoReset(_) - | RegBuilderMethod::Reset(_) - | RegBuilderMethod::ResetDefault(_) => unreachable!(), + RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => unreachable!(), } } Ok(Self { @@ -1370,7 +1340,7 @@ impl Visitor<'_> { no_reset.to_tokens(&mut expr); paren.surround(&mut expr, |expr| ty_expr.to_tokens(expr)); } - RegBuilderReset::Reset { .. } | RegBuilderReset::ResetDefault { .. } => { + RegBuilderReset::Reset { .. } => { hdl_let.kind.reset.to_tokens(&mut expr); } } From 8449854cac07c28ad421211da3217a52e6de2588 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 17:19:58 -0700 Subject: [PATCH 009/190] add ToExpr for usize/isize/NonZero --- crates/fayalite/src/expr/ops.rs | 18 ++++++++++-------- crates/fayalite/src/int.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 4814d2d..e5d3d9b 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -1236,10 +1236,11 @@ macro_rules! impl_dyn_shl { } } - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Shl::shl(lhs: $ty, rhs: UIntType) -> $ty { - $name::new(Expr::as_dyn_int(lhs), Expr::as_dyn_int(rhs)).to_expr() + impl Shl>> for Expr<$ty> { + type Output = Expr<$ty>; + + fn shl(self, rhs: Expr>) -> Self::Output { + $name::new(Expr::as_dyn_int(self), Expr::as_dyn_int(rhs)).to_expr() } } }; @@ -1308,10 +1309,11 @@ macro_rules! impl_dyn_shr { } } - impl_binary_op_trait! { - #[generics(LhsWidth: Size, RhsWidth: Size)] - fn Shr::shr(lhs: $ty, rhs: UIntType) -> $ty { - $name::new(lhs, Expr::as_dyn_int(rhs)).to_expr() + impl Shr>> for Expr<$ty> { + type Output = Expr<$ty>; + + fn shr(self, rhs: Expr>) -> Self::Output { + $name::new(self, Expr::as_dyn_int(rhs)).to_expr() } } }; diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index 2950086..b48f617 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -18,6 +18,7 @@ use std::{ borrow::{BorrowMut, Cow}, fmt, marker::PhantomData, + num::NonZero, ops::{Bound, Index, Not, Range, RangeBounds, RangeInclusive}, sync::Arc, }; @@ -468,7 +469,11 @@ impl SInt { } macro_rules! impl_prim_int { - ($prim_int:ident, $ty:ty) => { + ( + $(#[$meta:meta])* + $prim_int:ident, $ty:ty + ) => { + $(#[$meta])* impl ToExpr for $prim_int { type Type = $ty; @@ -479,6 +484,17 @@ macro_rules! impl_prim_int { ) } } + $(#[$meta])* + impl ToExpr for NonZero<$prim_int> { + type Type = $ty; + + fn to_expr(&self) -> Expr { + <$ty>::le_bytes_to_expr_wrapping( + &self.get().to_le_bytes(), + <$ty as BoolOrIntType>::Width::VALUE, + ) + } + } }; } @@ -493,6 +509,16 @@ impl_prim_int!(i32, SInt<32>); impl_prim_int!(i64, SInt<64>); impl_prim_int!(i128, SInt<128>); +impl_prim_int!( + /// for portability reasons, [`usize`] always translates to [`UInt<64>`] + usize, UInt<64> +); + +impl_prim_int!( + /// for portability reasons, [`isize`] always translates to [`SInt<64>`] + isize, SInt<64> +); + pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { type Width: Size; type Signed: GenericConstBool; From 9ad4ec0f39d2d2d26c71ca9116555340d08876a4 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 17:26:23 -0700 Subject: [PATCH 010/190] add ty.uninit() --- crates/fayalite/src/expr.rs | 11 +++++ crates/fayalite/src/expr/ops.rs | 38 ++++++++++++++++ crates/fayalite/src/firrtl.rs | 15 +++++++ .../src/module/transform/simplify_enums.rs | 1 + crates/fayalite/src/prelude.rs | 2 +- crates/fayalite/tests/module.rs | 43 +++++++++++++++++++ crates/fayalite/visit_types.json | 10 +++++ 7 files changed, 119 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 607ff1e..5ba6dbf 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -111,6 +111,7 @@ expr_enum! { BundleLiteral(ops::BundleLiteral), ArrayLiteral(ops::ArrayLiteral), EnumLiteral(ops::EnumLiteral), + Uninit(ops::Uninit), NotU(ops::NotU), NotS(ops::NotS), NotB(ops::NotB), @@ -697,3 +698,13 @@ pub fn check_match_expr( _check_fn: impl FnOnce(T::MatchVariant, Infallible), ) { } + +pub trait MakeUninitExpr: Type { + fn uninit(self) -> Expr; +} + +impl MakeUninitExpr for T { + fn uninit(self) -> Expr { + ops::Uninit::new(self).to_expr() + } +} diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index e5d3d9b..144a56f 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -2529,3 +2529,41 @@ impl ToExpr for CastBitsTo { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Uninit { + ty: T, +} + +impl Uninit { + #[track_caller] + pub fn new(ty: T) -> Self { + Self { ty } + } + pub fn ty(self) -> T { + self.ty + } +} + +impl ToLiteralBits for Uninit { + fn to_literal_bits(&self) -> Result, NotALiteralExpr> { + Err(NotALiteralExpr) + } +} + +impl_get_target_none!([T: Type] Uninit); + +impl ToExpr for Uninit { + type Type = T; + + fn to_expr(&self) -> Expr { + Expr { + __enum: ExprEnum::Uninit(Uninit { + ty: self.ty.canonical(), + }) + .intern(), + __ty: self.ty, + __flow: Flow::Source, + } + } +} diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 5a562c0..dbbdd7d 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -912,6 +912,20 @@ impl<'a> Exporter<'a> { } ident.to_string() } + fn uninit_expr( + &mut self, + expr: ops::Uninit, + definitions: &RcDefinitions, + const_ty: bool, + ) -> String { + let ident = self.module.ns.make_new("_uninit_expr"); + let ty = expr.ty(); + let ty_ident = self.type_state.ty(ty); + let const_ = if const_ty { "const " } else { "" }; + definitions.add_definition_line(format_args!("wire {ident}: {const_}{ty_ident}")); + definitions.add_definition_line(format_args!("invalidate {ident}")); + ident.to_string() + } fn enum_literal_expr( &mut self, expr: ops::EnumLiteral, @@ -1367,6 +1381,7 @@ impl<'a> Exporter<'a> { ExprEnum::EnumLiteral(enum_literal) => { self.enum_literal_expr(enum_literal, definitions, const_ty) } + ExprEnum::Uninit(uninit) => self.uninit_expr(uninit, definitions, const_ty), ExprEnum::NotU(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotS(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), ExprEnum::NotB(expr) => self.expr_unary("not", expr.arg(), definitions, const_ty), diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 91bbba5..d6a9c02 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -287,6 +287,7 @@ impl Folder for State { | ExprEnum::BoolLiteral(_) | ExprEnum::BundleLiteral(_) | ExprEnum::ArrayLiteral(_) + | ExprEnum::Uninit(_) | ExprEnum::NotU(_) | ExprEnum::NotS(_) | ExprEnum::NotB(_) diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 6a74e67..b376093 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -6,7 +6,7 @@ pub use crate::{ cli::Cli, clock::{Clock, ClockDomain, ToClock}, enum_::{HdlNone, HdlOption, HdlSome}, - expr::{CastBitsTo, CastTo, CastToBits, Expr, ReduceBits, ToExpr}, + expr::{CastBitsTo, CastTo, CastToBits, Expr, MakeUninitExpr, ReduceBits, ToExpr}, hdl, hdl_module, int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 23cd61f..c135f51 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3139,3 +3139,46 @@ circuit check_annotations: %[[ "#, }; } + +#[hdl_module(outline_generated)] +pub fn check_uninit(ty: T) { + #[hdl] + let o: T = m.output(ty); + connect(o, ty.uninit()); +} + +#[test] +fn test_uninit() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_uninit((UInt[3], SInt[5], Clock)); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uninit.fir": r"FIRRTL version 3.2.0 +circuit check_uninit: + type Ty0 = {`0`: UInt<3>, `1`: SInt<5>, `2`: Clock} + module check_uninit: @[module-XXXXXXXXXX.rs 1:1] + output o: Ty0 @[module-XXXXXXXXXX.rs 2:1] + wire _uninit_expr: Ty0 + invalidate _uninit_expr + connect o, _uninit_expr @[module-XXXXXXXXXX.rs 3:1] +", + }; + let m = check_uninit(Array[HdlOption[()]][3]); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_uninit_1.fir": r"FIRRTL version 3.2.0 +circuit check_uninit_1: + type Ty0 = {} + type Ty1 = {|HdlNone, HdlSome: Ty0|} + module check_uninit_1: @[module-XXXXXXXXXX.rs 1:1] + output o: Ty1[3] @[module-XXXXXXXXXX.rs 2:1] + wire _uninit_expr: Ty1[3] + invalidate _uninit_expr + connect o, _uninit_expr @[module-XXXXXXXXXX.rs 3:1] +", + }; +} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index ad9ed25..cbaae05 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -283,6 +283,16 @@ "fold_where": "T: Fold", "visit_where": "T: Visit" }, + "ops::Uninit": { + "data": { + "$kind": "Struct", + "$constructor": "ops::Uninit::new", + "ty()": "Visible" + }, + "generics": "", + "fold_where": "T: Fold", + "visit_where": "T: Visit" + }, "ops::NotU": { "data": { "$kind": "Struct", From 78edfc97b2f3aec69c869999cb57acd0687ab2eb Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 17:28:46 -0700 Subject: [PATCH 011/190] split int::IntCmp into expr::HdlPartialEq and expr::HdlPartialOrd --- crates/fayalite/src/expr.rs | 12 ++++ crates/fayalite/src/expr/ops.rs | 63 +++++++++++++++---- crates/fayalite/src/int.rs | 9 --- .../src/module/transform/simplify_enums.rs | 4 +- crates/fayalite/src/prelude.rs | 7 ++- 5 files changed, 69 insertions(+), 26 deletions(-) diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 5ba6dbf..644d058 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -641,6 +641,18 @@ impl GetTarget for MemPort { } } +pub trait HdlPartialEq { + fn cmp_eq(self, rhs: Rhs) -> Expr; + fn cmp_ne(self, rhs: Rhs) -> Expr; +} + +pub trait HdlPartialOrd: HdlPartialEq { + fn cmp_lt(self, rhs: Rhs) -> Expr; + fn cmp_le(self, rhs: Rhs) -> Expr; + fn cmp_gt(self, rhs: Rhs) -> Expr; + fn cmp_ge(self, rhs: Rhs) -> Expr; +} + pub trait ReduceBits { type UIntOutput; type BoolOutput; diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 144a56f..6069f20 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -11,11 +11,12 @@ use crate::{ GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, }, - CastTo, Expr, ExprEnum, Flow, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits, + CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits, + ToExpr, ToLiteralBits, }, int::{ - Bool, BoolOrIntType, DynSize, IntCmp, IntType, KnownSize, SInt, SIntType, SIntValue, Size, - UInt, UIntType, UIntValue, + Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, + UIntType, UIntValue, }, intern::{Intern, Interned}, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, @@ -1422,36 +1423,45 @@ forward_value_to_expr_binary_op_trait! { Shr::shr } -pub trait IntCmpExpr: Type { +pub trait ExprPartialEq: Type { fn cmp_eq(lhs: Expr, rhs: Expr) -> Expr; fn cmp_ne(lhs: Expr, rhs: Expr) -> Expr; +} + +pub trait ExprPartialOrd: ExprPartialEq { fn cmp_lt(lhs: Expr, rhs: Expr) -> Expr; fn cmp_le(lhs: Expr, rhs: Expr) -> Expr; fn cmp_gt(lhs: Expr, rhs: Expr) -> Expr; fn cmp_ge(lhs: Expr, rhs: Expr) -> Expr; } -impl IntCmp for Lhs +impl HdlPartialEq for Lhs where - Lhs::Type: IntCmpExpr, + Lhs::Type: ExprPartialEq, { fn cmp_eq(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_eq(self.to_expr(), rhs.to_expr()) + ExprPartialEq::cmp_eq(self.to_expr(), rhs.to_expr()) } fn cmp_ne(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_ne(self.to_expr(), rhs.to_expr()) + ExprPartialEq::cmp_ne(self.to_expr(), rhs.to_expr()) } +} + +impl HdlPartialOrd for Lhs +where + Lhs::Type: ExprPartialOrd, +{ fn cmp_lt(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_lt(self.to_expr(), rhs.to_expr()) + ExprPartialOrd::cmp_lt(self.to_expr(), rhs.to_expr()) } fn cmp_le(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_le(self.to_expr(), rhs.to_expr()) + ExprPartialOrd::cmp_le(self.to_expr(), rhs.to_expr()) } fn cmp_gt(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_gt(self.to_expr(), rhs.to_expr()) + ExprPartialOrd::cmp_gt(self.to_expr(), rhs.to_expr()) } fn cmp_ge(self, rhs: Rhs) -> Expr { - IntCmpExpr::cmp_ge(self.to_expr(), rhs.to_expr()) + ExprPartialOrd::cmp_ge(self.to_expr(), rhs.to_expr()) } } @@ -1461,6 +1471,7 @@ macro_rules! impl_compare_op { #[dyn_type($DynTy:ident)] #[to_dyn_type($lhs:ident => $dyn_lhs:expr, $rhs:ident => $dyn_rhs:expr)] #[type($Lhs:ty, $Rhs:ty)] + #[trait($Trait:ident)] $( struct $name:ident; fn $method:ident(); @@ -1512,7 +1523,7 @@ macro_rules! impl_compare_op { } })* - impl$(<$LhsWidth: Size, $RhsWidth: Size>)? IntCmpExpr<$Rhs> for $Lhs { + impl$(<$LhsWidth: Size, $RhsWidth: Size>)? $Trait<$Rhs> for $Lhs { $(fn $method($lhs: Expr, $rhs: Expr<$Rhs>) -> Expr { $name::new($dyn_lhs, $dyn_rhs).to_expr() })* @@ -1524,8 +1535,16 @@ impl_compare_op! { #[dyn_type(Bool)] #[to_dyn_type(lhs => lhs, rhs => rhs)] #[type(Bool, Bool)] + #[trait(ExprPartialEq)] struct CmpEqB; fn cmp_eq(); PartialEq::eq(); struct CmpNeB; fn cmp_ne(); PartialEq::ne(); +} + +impl_compare_op! { + #[dyn_type(Bool)] + #[to_dyn_type(lhs => lhs, rhs => rhs)] + #[type(Bool, Bool)] + #[trait(ExprPartialOrd)] struct CmpLtB; fn cmp_lt(); PartialOrd::lt(); struct CmpLeB; fn cmp_le(); PartialOrd::le(); struct CmpGtB; fn cmp_gt(); PartialOrd::gt(); @@ -1537,8 +1556,17 @@ impl_compare_op! { #[dyn_type(UInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[type(UIntType, UIntType)] + #[trait(ExprPartialEq)] struct CmpEqU; fn cmp_eq(); PartialEq::eq(); struct CmpNeU; fn cmp_ne(); PartialEq::ne(); +} + +impl_compare_op! { + #[width(LhsWidth, RhsWidth)] + #[dyn_type(UInt)] + #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[type(UIntType, UIntType)] + #[trait(ExprPartialOrd)] struct CmpLtU; fn cmp_lt(); PartialOrd::lt(); struct CmpLeU; fn cmp_le(); PartialOrd::le(); struct CmpGtU; fn cmp_gt(); PartialOrd::gt(); @@ -1550,8 +1578,17 @@ impl_compare_op! { #[dyn_type(SInt)] #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] #[type(SIntType, SIntType)] + #[trait(ExprPartialEq)] struct CmpEqS; fn cmp_eq(); PartialEq::eq(); struct CmpNeS; fn cmp_ne(); PartialEq::ne(); +} + +impl_compare_op! { + #[width(LhsWidth, RhsWidth)] + #[dyn_type(SInt)] + #[to_dyn_type(lhs => Expr::as_dyn_int(lhs), rhs => Expr::as_dyn_int(rhs))] + #[type(SIntType, SIntType)] + #[trait(ExprPartialOrd)] struct CmpLtS; fn cmp_lt(); PartialOrd::lt(); struct CmpLeS; fn cmp_le(); PartialOrd::le(); struct CmpGtS; fn cmp_gt(); PartialOrd::gt(); diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index b48f617..e0d258a 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -679,15 +679,6 @@ impl StaticType for Bool { const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES; } -pub trait IntCmp { - fn cmp_eq(self, rhs: Rhs) -> Expr; - fn cmp_ne(self, rhs: Rhs) -> Expr; - fn cmp_lt(self, rhs: Rhs) -> Expr; - fn cmp_le(self, rhs: Rhs) -> Expr; - fn cmp_gt(self, rhs: Rhs) -> Expr; - fn cmp_ge(self, rhs: Rhs) -> Expr; -} - impl ToLiteralBits for bool { fn to_literal_bits(&self) -> Result, NotALiteralExpr> { Ok(interned_bit(*self)) diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index d6a9c02..9e1e552 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -6,10 +6,10 @@ use crate::{ enum_::{Enum, EnumType, EnumVariant}, expr::{ ops::{self, EnumLiteral}, - CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, + CastBitsTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, }, hdl, - int::{DynSize, IntCmp, Size, UInt, UIntType}, + int::{DynSize, Size, UInt, UIntType}, intern::{Intern, Interned}, memory::{DynPortType, Mem, MemPort}, module::{ diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index b376093..5dc503e 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -6,9 +6,12 @@ pub use crate::{ cli::Cli, clock::{Clock, ClockDomain, ToClock}, enum_::{HdlNone, HdlOption, HdlSome}, - expr::{CastBitsTo, CastTo, CastToBits, Expr, MakeUninitExpr, ReduceBits, ToExpr}, + expr::{ + CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, + ReduceBits, ToExpr, + }, hdl, hdl_module, - int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, + int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, From a701f99fd6fc5eb21c138312dc93b581329bf50b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 18:56:26 -0700 Subject: [PATCH 012/190] add repeat() --- crates/fayalite/src/expr.rs | 17 ++++++++++++++++- crates/fayalite/src/prelude.rs | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 644d058..fa50852 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -9,7 +9,7 @@ use crate::{ ops::ExprCastTo, target::{GetTarget, Target}, }, - int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, UInt, UIntType, UIntValue}, + int::{Bool, DynSize, IntType, SIntType, SIntValue, Size, SizeType, UInt, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{DynPortType, MemPort, PortType}, module::{ @@ -720,3 +720,18 @@ impl MakeUninitExpr for T { ops::Uninit::new(self).to_expr() } } + +pub fn repeat( + element: impl ToExpr, + len: L, +) -> Expr> { + let element = element.to_expr(); + let canonical_element = Expr::canonical(element); + ops::ArrayLiteral::new( + Expr::ty(element), + std::iter::repeat(canonical_element) + .take(L::Size::as_usize(len)) + .collect(), + ) + .to_expr() +} diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 5dc503e..7b7d718 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -7,7 +7,7 @@ pub use crate::{ clock::{Clock, ClockDomain, ToClock}, enum_::{HdlNone, HdlOption, HdlSome}, expr::{ - CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, + repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, ReduceBits, ToExpr, }, hdl, hdl_module, From f6146048d1a6fa4c06db8c0289794481c36f9a01 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 18:57:30 -0700 Subject: [PATCH 013/190] add memory::splat_mask to generate mask types from a Bool --- crates/fayalite/src/memory.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index e6bbea5..1762f57 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -7,7 +7,7 @@ use crate::{ array::{Array, ArrayType}, bundle::{Bundle, BundleType}, clock::Clock, - expr::{Expr, Flow, ToExpr, ToLiteralBits}, + expr::{ops::BundleLiteral, repeat, Expr, Flow, ToExpr, ToLiteralBits}, hdl, int::{Bool, DynSize, Size, UInt, UIntType}, intern::{Intern, Interned}, @@ -1050,3 +1050,32 @@ impl MemBuilder { .extend(annotations.into_annotations()); } } + +pub fn splat_mask(ty: T, value: Expr) -> Expr> { + let canonical_ty = ty.canonical(); + match canonical_ty { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) + | CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)), + CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat( + splat_mask(array.element(), value), + array.len(), + ))), + CanonicalType::Bundle(bundle) => Expr::from_canonical(Expr::canonical( + BundleLiteral::new( + bundle.mask_type(), + bundle + .fields() + .iter() + .map(|field| splat_mask(field.ty, value)) + .collect(), + ) + .to_expr(), + )), + } +} From 716c65edcd421fa3a28d271ec647188331c2d52a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 22 Sep 2024 18:59:12 -0700 Subject: [PATCH 014/190] add WIP version of queue() --- crates/fayalite/src/util/ready_valid.rs | 130 +++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index ec761c2..94154c9 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::prelude::*; +use crate::{memory::splat_mask, prelude::*}; +use std::num::NonZeroUsize; #[hdl] pub struct ReadyValid { @@ -34,3 +35,130 @@ impl ReadyValid { mapped } } + +// TODO: needs testing +#[hdl_module] +pub fn queue( + ty: T, + capacity: NonZeroUsize, + inp_ready_is_comb: bool, + out_valid_is_comb: bool, +) { + let count_ty = UInt::range_inclusive(0..=capacity.get()); + let index_ty = UInt::range(0..capacity.get()); + + #[hdl] + let cd: ClockDomain = m.input(); + #[hdl] + let inp: ReadyValid = m.input(ReadyValid[ty]); + #[hdl] + let out: ReadyValid = m.output(ReadyValid[ty]); + #[hdl] + let count: UInt = m.output(count_ty); + + #[hdl] + let inp_index = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + #[hdl] + let out_index = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + #[hdl] + let maybe_full = reg_builder().clock_domain(cd).reset(false); + + #[hdl] + let mut mem = memory(ty); + mem.depth(capacity.get()); + let read_port = mem.new_read_port(); + let write_port = mem.new_write_port(); + + #[hdl] + let inp_fire: Bool = wire(); + connect(inp_fire, ReadyValid::fire(inp)); + #[hdl] + let out_fire: Bool = wire(); + connect(out_fire, ReadyValid::fire(out)); + #[hdl] + let indexes_equal: Bool = wire(); + connect(indexes_equal, inp_index.cmp_eq(out_index)); + #[hdl] + let empty: Bool = wire(); + connect(empty, indexes_equal & !maybe_full); + #[hdl] + let full: Bool = wire(); + connect(full, indexes_equal & maybe_full); + + connect(read_port.addr, out_index); + connect(read_port.en, true); + connect(read_port.clk, cd.clk); + connect(write_port.addr, inp_index); + connect(write_port.en, inp_fire); + connect(write_port.clk, cd.clk); + connect(write_port.data, HdlOption::unwrap_or(inp.data, ty.uninit())); + connect(write_port.mask, splat_mask(ty, true.to_expr())); + + connect(inp.ready, !full); + if inp_ready_is_comb { + #[hdl] + if out.ready { + connect(inp.ready, true); + } + } + + #[hdl] + if !empty { + connect(out.data, HdlSome(read_port.data)); + } else { + if out_valid_is_comb { + connect(out.data, inp.data); + } else { + connect(out.data, HdlOption[ty].HdlNone()); + } + } + + #[hdl] + if inp_fire.cmp_ne(out_fire) { + connect(maybe_full, inp_fire); + } + + #[hdl] + if inp_fire { + #[hdl] + if inp_index.cmp_eq(capacity) { + connect_any(inp_index, 0_hdl_u0); + } else { + connect_any(inp_index, inp_index + 1_hdl_u1); + } + } + + #[hdl] + if out_fire { + #[hdl] + if out_index.cmp_eq(capacity) { + connect_any(out_index, 0_hdl_u0); + } else { + connect_any(out_index, out_index + 1_hdl_u1); + } + } + + #[hdl] + if indexes_equal { + connect( + count, + maybe_full.cast_to_static::>() << (count_ty.width() - 1), + ); + } else { + if capacity.is_power_of_two() { + debug_assert_eq!(count_ty.width(), index_ty.width() + 1); + #[hdl] + let count_lower = wire(index_ty); + connect(count_lower, (inp_index - out_index).cast_to(index_ty)); // wrap + connect(count, count_lower.cast_to(count_ty)); + } else { + debug_assert_eq!(count_ty.width(), index_ty.width()); + #[hdl] + if inp_index.cmp_lt(out_index) { + connect(count, inp_index + capacity - out_index); + } else { + connect(count, inp_index - out_index); + } + } + } +} From 28aad19bf5c96f071a6f123ed934e3e2019f34cd Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 23 Sep 2024 19:10:51 -0700 Subject: [PATCH 015/190] add assert/assume/cover --- crates/fayalite/src/firrtl.rs | 31 +++- crates/fayalite/src/module.rs | 175 +++++++++++++++++- .../src/module/transform/simplify_enums.rs | 4 +- crates/fayalite/src/module/transform/visit.rs | 4 +- crates/fayalite/src/prelude.rs | 5 +- crates/fayalite/tests/module.rs | 52 ++++++ crates/fayalite/visit_types.json | 20 ++ 7 files changed, 282 insertions(+), 9 deletions(-) diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index dbbdd7d..b8ba89a 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -20,8 +20,8 @@ use crate::{ module::{ transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody, - NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance, - StmtMatch, StmtReg, StmtWire, + NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, + StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -1954,6 +1954,33 @@ impl<'a> Exporter<'a> { ) .unwrap(); } + Stmt::Formal(StmtFormal { + kind, + clk, + pred, + en, + text, + source_location, + }) => { + let clk = self.expr(Expr::canonical(clk), &definitions, false); + let pred = self.expr(Expr::canonical(pred), &definitions, false); + let en = self.expr(Expr::canonical(en), &definitions, false); + let kind = match kind { + StmtFormalKind::Assert => "assert", + StmtFormalKind::Assume => "assume", + StmtFormalKind::Cover => "cover", + }; + let text = EscapedString { + value: &text, + raw: false, + }; + writeln!( + body, + "{indent}{kind}({clk}, {pred}, {en}, {text}){}", + FileInfo::new(source_location), + ) + .unwrap(); + } Stmt::If(StmtIf { mut cond, mut source_location, diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 3a17343..2b7534c 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -5,7 +5,7 @@ use crate::{ annotations::{Annotation, IntoAnnotations, TargetedAnnotation}, array::ArrayType, bundle::{Bundle, BundleField, BundleType}, - clock::ClockDomain, + clock::{Clock, ClockDomain}, enum_::{Enum, EnumMatchVariantsIter, EnumType}, expr::{ ops::VariantAccess, @@ -199,6 +199,52 @@ impl fmt::Debug for StmtConnect { } } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum StmtFormalKind { + Assert, + Assume, + Cover, +} + +impl StmtFormalKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Assert => "assert", + Self::Assume => "assume", + Self::Cover => "cover", + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct StmtFormal { + pub kind: StmtFormalKind, + pub clk: Expr, + pub pred: Expr, + pub en: Expr, + pub text: Interned, + pub source_location: SourceLocation, +} + +impl fmt::Debug for StmtFormal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + kind, + clk, + pred, + en, + text, + source_location: _, + } = self; + f.debug_struct(kind.as_str()) + .field("clk", clk) + .field("pred", pred) + .field("en", en) + .field("text", text) + .finish_non_exhaustive() + } +} + #[derive(Clone, PartialEq, Eq, Hash)] pub struct StmtIf { pub cond: Expr, @@ -450,6 +496,8 @@ wrapper_enum! { pub enum Stmt { #[is = is_connect, as_ref = connect] Connect(StmtConnect), + #[is = is_formal, as_ref = formal] + Formal(StmtFormal), #[is = is_if, as_ref = if_] If(StmtIf), #[is = is_match, as_ref = match_] @@ -462,7 +510,7 @@ wrapper_enum! { impl Stmt { pub fn sub_stmt_blocks(&self) -> &[S::Block] { match self { - Stmt::Connect(_) => &[], + Stmt::Connect(_) | Stmt::Formal(_) => &[], Stmt::If(v) => &v.blocks, Stmt::Match(v) => &v.blocks, Stmt::Declaration(v) => v.sub_stmt_blocks(), @@ -609,6 +657,14 @@ impl NameIdGen { rhs: _, source_location: _, }) + | Stmt::Formal(StmtFormal { + kind: _, + clk: _, + pred: _, + en: _, + text: _, + source_location: _, + }) | Stmt::If(StmtIf { cond: _, source_location: _, @@ -888,6 +944,7 @@ impl From> for NormalModuleBody { let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| { match stmt { Stmt::Connect(stmt) => stmt.into(), + Stmt::Formal(stmt) => stmt.into(), Stmt::If(StmtIf { cond, source_location, @@ -1608,6 +1665,7 @@ impl AssertValidityState { self.set_connect_side_written(lhs, source_location, true, block); self.set_connect_side_written(rhs, source_location, false, block); } + Stmt::Formal(_) => {} Stmt::If(if_stmt) => { let sub_blocks = if_stmt.blocks.map(|block| self.make_block_index(block)); self.process_conditional_sub_blocks(block, sub_blocks) @@ -2336,6 +2394,119 @@ pub fn match_with_loc( T::match_variants(expr.to_expr(), source_location) } +#[track_caller] +pub fn formal_with_enable_and_loc( + kind: StmtFormalKind, + clk: Expr, + pred: Expr, + en: Expr, + text: &str, + source_location: SourceLocation, +) { + ModuleBuilder::with(|m| { + m.impl_ + .borrow_mut() + .body + .builder_normal_body() + .block(m.block_stack.top()) + .stmts + .push( + StmtFormal { + kind, + clk, + pred, + en, + text: text.intern(), + source_location, + } + .into(), + ); + }); +} + +#[track_caller] +pub fn formal_with_enable( + kind: StmtFormalKind, + clk: Expr, + pred: Expr, + en: Expr, + text: &str, +) { + formal_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller()); +} + +#[track_caller] +pub fn formal_with_loc( + kind: StmtFormalKind, + clk: Expr, + pred: Expr, + text: &str, + source_location: SourceLocation, +) { + formal_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location); +} + +#[track_caller] +pub fn formal(kind: StmtFormalKind, clk: Expr, pred: Expr, text: &str) { + formal_with_loc(kind, clk, pred, text, SourceLocation::caller()); +} + +macro_rules! make_formal { + ($kind:ident, $formal_with_enable_and_loc:ident, $formal_with_enable:ident, $formal_with_loc:ident, $formal:ident) => { + #[track_caller] + pub fn $formal_with_enable_and_loc( + clk: Expr, + pred: Expr, + en: Expr, + text: &str, + source_location: SourceLocation, + ) { + formal_with_enable_and_loc(StmtFormalKind::$kind, clk, pred, en, text, source_location); + } + #[track_caller] + pub fn $formal_with_enable(clk: Expr, pred: Expr, en: Expr, text: &str) { + formal_with_enable(StmtFormalKind::$kind, clk, pred, en, text); + } + #[track_caller] + pub fn $formal_with_loc( + clk: Expr, + pred: Expr, + text: &str, + source_location: SourceLocation, + ) { + formal_with_loc(StmtFormalKind::$kind, clk, pred, text, source_location); + } + #[track_caller] + pub fn $formal(clk: Expr, pred: Expr, text: &str) { + formal(StmtFormalKind::$kind, clk, pred, text); + } + }; +} + +make_formal!( + Assert, + hdl_assert_with_enable_and_loc, + hdl_assert_with_enable, + hdl_assert_with_loc, + hdl_assert +); + +make_formal!( + Assume, + hdl_assume_with_enable_and_loc, + hdl_assume_with_enable, + hdl_assume_with_loc, + hdl_assume +); + +make_formal!( + Cover, + hdl_cover_with_enable_and_loc, + hdl_cover_with_enable, + hdl_cover_with_loc, + hdl_cover +); + #[track_caller] pub fn connect_any_with_loc( lhs: Lhs, diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 9e1e552..c817e45 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -538,7 +538,9 @@ impl Folder for State { } .into()), }, - Stmt::Connect(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self), + Stmt::Connect(_) | Stmt::Formal(_) | Stmt::If(_) | Stmt::Declaration(_) => { + stmt.default_fold(self) + } } } diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 7edfe04..440eecb 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -21,8 +21,8 @@ use crate::{ module::{ AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId, - NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance, - StmtMatch, StmtReg, StmtWire, + NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, + StmtFormalKind, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset}, diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 7b7d718..16dccb9 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -14,8 +14,9 @@ pub use crate::{ int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ - annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, - memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, + annotate, connect, connect_any, hdl_assert, hdl_assert_with_enable, hdl_assume, + hdl_assume_with_enable, hdl_cover, hdl_cover_with_enable, incomplete_wire, instance, + memory, memory_array, memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index c135f51..4139ee1 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3182,3 +3182,55 @@ circuit check_uninit_1: ", }; } + +#[hdl_module(outline_generated)] +pub fn check_formal() { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let en1: Bool = m.input(); + #[hdl] + let en2: Bool = m.input(); + #[hdl] + let en3: Bool = m.input(); + #[hdl] + let pred1: Bool = m.input(); + #[hdl] + let pred2: Bool = m.input(); + #[hdl] + let pred3: Bool = m.input(); + hdl_assert_with_enable(clk, pred1, en1, "en check 1"); + hdl_assume_with_enable(clk, pred2, en2, "en check 2"); + hdl_cover_with_enable(clk, pred3, en3, "en check 3"); + hdl_assert(clk, pred1, "check 1"); + hdl_assume(clk, pred2, "check 2"); + hdl_cover(clk, pred3, "check 3"); +} + +#[test] +fn test_formal() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_formal(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_formal.fir": r#"FIRRTL version 3.2.0 +circuit check_formal: + module check_formal: @[module-XXXXXXXXXX.rs 1:1] + input clk: Clock @[module-XXXXXXXXXX.rs 2:1] + input en1: UInt<1> @[module-XXXXXXXXXX.rs 3:1] + input en2: UInt<1> @[module-XXXXXXXXXX.rs 4:1] + input en3: UInt<1> @[module-XXXXXXXXXX.rs 5:1] + input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1] + input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1] + input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + assert(clk, pred1, en1, "en check 1") @[module-XXXXXXXXXX.rs 9:1] + assume(clk, pred2, en2, "en check 2") @[module-XXXXXXXXXX.rs 10:1] + cover(clk, pred3, en3, "en check 3") @[module-XXXXXXXXXX.rs 11:1] + assert(clk, pred1, UInt<1>(0h1), "check 1") @[module-XXXXXXXXXX.rs 12:1] + assume(clk, pred2, UInt<1>(0h1), "check 2") @[module-XXXXXXXXXX.rs 13:1] + cover(clk, pred3, UInt<1>(0h1), "check 3") @[module-XXXXXXXXXX.rs 14:1] +"#, + }; +} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index cbaae05..f1969eb 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1067,6 +1067,7 @@ "data": { "$kind": "Enum", "Connect": "Visible", + "Formal": "Visible", "If": "Visible", "Match": "Visible", "Declaration": "Visible" @@ -1088,6 +1089,25 @@ "source_location": "Visible" } }, + "StmtFormalKind": { + "data": { + "$kind": "Enum", + "Assert": null, + "Assume": null, + "Cover": null + } + }, + "StmtFormal": { + "data": { + "$kind": "Struct", + "kind": "Visible", + "clk": "Visible", + "pred": "Visible", + "en": "Visible", + "text": "Visible", + "source_location": "Visible" + } + }, "StmtIf": { "data": { "$kind": "Struct", From 4ff01690a7971d390a9d5a6dff3732b9a3a6a86c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:22:35 -0700 Subject: [PATCH 016/190] clean up deps and move missed deps to workspace --- Cargo.toml | 3 +++ crates/fayalite-proc-macros-impl/Cargo.toml | 16 +++++++------- crates/fayalite-proc-macros/Cargo.toml | 2 +- crates/fayalite-visit-gen/Cargo.toml | 16 +++++++------- crates/fayalite/Cargo.toml | 24 ++++++++++----------- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1608a79..9ee7445 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ fayalite-proc-macros-impl = { version = "=0.2.0", path = "crates/fayalite-proc-m fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" } base16ct = "0.2.0" bitvec = { version = "1.0.1", features = ["serde"] } +clap = { version = "4.5.9", features = ["derive", "env", "string"] } +eyre = "0.6.12" hashbrown = "0.14.3" indexmap = { version = "2.2.6", features = ["serde"] } num-bigint = "0.4.4" @@ -33,3 +35,4 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] tempfile = "3.10.1" thiserror = "1.0.61" trybuild = "1.0" +which = "6.0.1" diff --git a/crates/fayalite-proc-macros-impl/Cargo.toml b/crates/fayalite-proc-macros-impl/Cargo.toml index 31c4465..d56f03d 100644 --- a/crates/fayalite-proc-macros-impl/Cargo.toml +++ b/crates/fayalite-proc-macros-impl/Cargo.toml @@ -13,11 +13,11 @@ rust-version.workspace = true version.workspace = true [dependencies] -base16ct = { workspace = true } -num-bigint = { workspace = true } -prettyplease = { workspace = true } -proc-macro2 = { workspace = true } -quote = { workspace = true } -sha2 = { workspace = true } -syn = { workspace = true } -tempfile = { workspace = true } +base16ct.workspace = true +num-bigint.workspace = true +prettyplease.workspace = true +proc-macro2.workspace = true +quote.workspace = true +sha2.workspace = true +syn.workspace = true +tempfile.workspace = true diff --git a/crates/fayalite-proc-macros/Cargo.toml b/crates/fayalite-proc-macros/Cargo.toml index 08c630a..6941d12 100644 --- a/crates/fayalite-proc-macros/Cargo.toml +++ b/crates/fayalite-proc-macros/Cargo.toml @@ -16,4 +16,4 @@ version.workspace = true proc-macro = true [dependencies] -fayalite-proc-macros-impl = { workspace = true } +fayalite-proc-macros-impl.workspace = true diff --git a/crates/fayalite-visit-gen/Cargo.toml b/crates/fayalite-visit-gen/Cargo.toml index 6da95d2..5a98947 100644 --- a/crates/fayalite-visit-gen/Cargo.toml +++ b/crates/fayalite-visit-gen/Cargo.toml @@ -13,11 +13,11 @@ rust-version.workspace = true version.workspace = true [dependencies] -indexmap = { workspace = true } -prettyplease = { workspace = true } -proc-macro2 = { workspace = true } -quote = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -syn = { workspace = true } -thiserror = { workspace = true } +indexmap.workspace = true +prettyplease.workspace = true +proc-macro2.workspace = true +quote.workspace = true +serde.workspace = true +serde_json.workspace = true +syn.workspace = true +thiserror.workspace = true diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 555f7f5..55a29ad 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -14,22 +14,22 @@ rust-version.workspace = true version.workspace = true [dependencies] -bitvec = { workspace = true } -hashbrown = { workspace = true } -num-bigint = { workspace = true } -num-traits = { workspace = true } -fayalite-proc-macros = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -clap = { version = "4.5.9", features = ["derive", "env"] } -eyre = "0.6.12" -which = "6.0.1" +bitvec.workspace = true +clap.workspace = true +eyre.workspace = true +fayalite-proc-macros.workspace = true +hashbrown.workspace = true +num-bigint.workspace = true +num-traits.workspace = true +serde_json.workspace = true +serde.workspace = true +which.workspace = true [dev-dependencies] -trybuild = { workspace = true } +trybuild.workspace = true [build-dependencies] -fayalite-visit-gen = { workspace = true } +fayalite-visit-gen.workspace = true [features] unstable-doc = [] From f32c0a78636fb4c6527662ad3da97d61f765b0fb Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:28:11 -0700 Subject: [PATCH 017/190] switch to #[derive(Parser)] instead of #[derive(Args)] --- crates/fayalite/src/cli.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 5071279..d1bbfb1 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -8,7 +8,7 @@ use crate::{ }; use clap::{ builder::{OsStringValueParser, TypedValueParser}, - Args, Parser, Subcommand, ValueEnum, ValueHint, + Parser, Subcommand, ValueEnum, ValueHint, }; use eyre::{eyre, Report}; use std::{error, ffi::OsString, fmt, io, path::PathBuf, process}; @@ -42,7 +42,7 @@ pub trait RunPhase { fn run(&self, arg: Arg) -> Result; } -#[derive(Args, Debug)] +#[derive(Parser, Debug, Clone)] #[non_exhaustive] pub struct BaseArgs { /// the directory to put the generated main output file and associated files in @@ -62,7 +62,7 @@ impl BaseArgs { } } -#[derive(Args, Debug)] +#[derive(Parser, Debug, Clone)] #[non_exhaustive] pub struct FirrtlArgs { #[command(flatten)] @@ -140,7 +140,7 @@ impl VerilogDialect { } } -#[derive(Args, Debug)] +#[derive(Parser, Debug, Clone)] #[non_exhaustive] pub struct VerilogArgs { #[command(flatten)] From efc3a539ed771fcd9ec53a8dc231bc7268224000 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:36:15 -0700 Subject: [PATCH 018/190] support redirecting subprocesses' stdout/stderr to print!() so it gets captured for rust tests --- Cargo.lock | 73 +++++++++++++------ Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/cli.rs | 33 ++++++++- crates/fayalite/src/util.rs | 1 + .../fayalite/src/util/streaming_read_utf8.rs | 31 ++++++++ 6 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 crates/fayalite/src/util/streaming_read_utf8.rs diff --git a/Cargo.lock b/Cargo.lock index 1c17ac7..1c237af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,7 +56,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -66,7 +66,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -218,7 +218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -249,6 +249,7 @@ dependencies = [ "hashbrown", "num-bigint", "num-traits", + "os_pipe", "serde", "serde_json", "trybuild", @@ -334,7 +335,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -413,6 +414,16 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "prettyplease" version = "0.2.20" @@ -457,7 +468,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -541,7 +552,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -665,14 +676,24 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.52.4" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -681,45 +702,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winsafe" diff --git a/Cargo.toml b/Cargo.toml index 9ee7445..15d13b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ hashbrown = "0.14.3" indexmap = { version = "2.2.6", features = ["serde"] } num-bigint = "0.4.4" num-traits = "0.2.16" +os_pipe = "1.2.1" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 55a29ad..2306e7a 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -21,6 +21,7 @@ fayalite-proc-macros.workspace = true hashbrown.workspace = true num-bigint.workspace = true num-traits.workspace = true +os_pipe.workspace = true serde_json.workspace = true serde.workspace = true which.workspace = true diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index d1bbfb1..1dbf85f 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -5,6 +5,7 @@ use crate::{ firrtl, intern::Interned, module::Module, + util::streaming_read_utf8::streaming_read_utf8, }; use clap::{ builder::{OsStringValueParser, TypedValueParser}, @@ -51,6 +52,8 @@ pub struct BaseArgs { /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo #[arg(long)] pub file_stem: Option, + #[arg(skip = false)] + pub redirect_output_for_rust_test: bool, } impl BaseArgs { @@ -60,6 +63,34 @@ impl BaseArgs { top_fir_file_stem: self.file_stem.clone(), } } + /// handles possibly redirecting the command's output for Rust tests + pub fn run_external_command( + &self, + mut command: process::Command, + ) -> io::Result { + if self.redirect_output_for_rust_test { + let (reader, writer) = os_pipe::pipe()?; + let mut reader = io::BufReader::new(reader); + command.stderr(writer.try_clone()?); + command.stdout(writer); // must not leave writer around after spawning child + command.stdin(process::Stdio::null()); + let mut child = command.spawn()?; + drop(command); // close writers + Ok(loop { + let status = child.try_wait()?; + streaming_read_utf8(&mut reader, |s| { + // use print! so output goes to Rust test output capture + print!("{s}"); + io::Result::Ok(()) + })?; + if let Some(status) = status { + break status; + } + }) + } else { + command.status() + } + } } #[derive(Parser, Debug, Clone)] @@ -188,7 +219,7 @@ impl VerilogArgs { } cmd.args(&self.firtool_extra_args); cmd.current_dir(&self.firrtl.base.output); - let status = cmd.status()?; + let status = self.firrtl.base.run_external_command(cmd)?; if status.success() { Ok(output) } else { diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 5b97e3b..95f5793 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -6,6 +6,7 @@ mod const_cmp; mod const_usize; mod misc; mod scoped_ref; +pub(crate) mod streaming_read_utf8; #[doc(inline)] pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; diff --git a/crates/fayalite/src/util/streaming_read_utf8.rs b/crates/fayalite/src/util/streaming_read_utf8.rs new file mode 100644 index 0000000..bd8bdc8 --- /dev/null +++ b/crates/fayalite/src/util/streaming_read_utf8.rs @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use std::{ + io::{self, BufRead}, + str, +}; + +pub(crate) fn streaming_read_utf8>( + reader: R, + mut callback: impl FnMut(&str) -> Result<(), E>, +) -> Result<(), E> { + let mut buf = [0; 4]; + let mut buf_len = 0; + for byte in reader.bytes() { + buf[buf_len] = byte?; + buf_len += 1; + match str::from_utf8(&buf[..buf_len]) { + Ok(buf) => { + callback(buf)?; + buf_len = 0; + } + Err(e) => { + if e.error_len().is_some() { + callback("\u{FFFD}")?; // replacement character + buf_len = 0; + } + } + } + } + Ok(()) +} From bb860d54ccba824a26427b02cc5888dafd9745de Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:47:48 -0700 Subject: [PATCH 019/190] add command line options for selecting which transforms to apply when generating firrtl --- crates/fayalite/src/cli.rs | 6 +- crates/fayalite/src/firrtl.rs | 133 ++++++++++++++++-- .../src/module/transform/simplify_enums.rs | 10 +- 3 files changed, 137 insertions(+), 12 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 1dbf85f..c171c22 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -2,7 +2,7 @@ // See Notices.txt for copyright information use crate::{ bundle::{Bundle, BundleType}, - firrtl, + firrtl::{self, ExportOptions}, intern::Interned, module::Module, util::streaming_read_utf8::streaming_read_utf8, @@ -98,6 +98,8 @@ impl BaseArgs { pub struct FirrtlArgs { #[command(flatten)] pub base: BaseArgs, + #[command(flatten)] + pub export_options: ExportOptions, } #[derive(Debug)] @@ -118,7 +120,7 @@ impl FirrtlArgs { fn run_impl(&self, top_module: Module) -> Result { let firrtl::FileBackend { top_fir_file_stem, .. - } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?; + } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module, self.export_options)?; Ok(FirrtlOutput { file_stem: top_fir_file_stem.expect( "export is known to set the file stem from the circuit name if not provided", diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index b8ba89a..ba37bcc 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -18,10 +18,14 @@ use crate::{ intern::{Intern, Interned}, memory::{Mem, PortKind, PortName, ReadUnderWrite}, module::{ - transform::simplify_memories::simplify_memories, AnnotatedModuleIO, Block, - ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody, - NameId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, - StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + transform::{ + simplify_enums::{simplify_enums, SimplifyEnumsError, SimplifyEnumsKind}, + simplify_memories::simplify_memories, + }, + AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, + ExternModuleParameterValue, Module, ModuleBody, NameId, NormalModuleBody, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, StmtIf, StmtInstance, StmtMatch, + StmtReg, StmtWire, }, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -32,6 +36,7 @@ use crate::{ }, }; use bitvec::slice::BitSlice; +use clap::value_parser; use hashbrown::{HashMap, HashSet}; use num_traits::Signed; use serde::Serialize; @@ -476,6 +481,7 @@ trait WrappedFileBackendTrait { circuit_name: String, contents: String, ) -> Result<(), WrappedError>; + fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError; } struct WrappedFileBackend { @@ -533,6 +539,11 @@ impl WrappedFileBackendTrait for WrappedFileBackend { WrappedError }) } + + fn simplify_enums_error(&mut self, error: SimplifyEnumsError) -> WrappedError { + self.error = Err(error.into()); + WrappedError + } } #[derive(Clone)] @@ -2225,7 +2236,7 @@ impl<'a> Exporter<'a> { } pub trait FileBackendTrait { - type Error; + type Error: From; type Path: AsRef + fmt::Debug + ?Sized; type PathBuf: AsRef + fmt::Debug; fn path_to_string(&mut self, path: &Self::Path) -> Result; @@ -2370,6 +2381,7 @@ impl Default for TestBackendPrivate { pub struct TestBackend { pub files: BTreeMap, pub error_after: Option, + pub options: ExportOptions, #[doc(hidden)] /// `#[non_exhaustive]` except allowing struct update syntax pub __private: TestBackendPrivate, @@ -2380,6 +2392,7 @@ impl fmt::Debug for TestBackend { let Self { files, error_after, + options, __private: TestBackendPrivate { module_var_name }, } = self; writeln!( @@ -2394,6 +2407,9 @@ impl fmt::Debug for TestBackend { if *error_after != Option::default() { writeln!(f, " error_after: {error_after:?},")?; } + if *options != ExportOptions::default() { + writeln!(f, " options: {options:?},")?; + } write!(f, " }};") } } @@ -2409,6 +2425,12 @@ impl fmt::Display for TestBackendError { impl Error for TestBackendError {} +impl From for TestBackendError { + fn from(value: SimplifyEnumsError) -> Self { + TestBackendError(value.to_string()) + } +} + impl TestBackend { #[track_caller] pub fn step_error_after(&mut self, args: &dyn fmt::Debug) -> Result<(), TestBackendError> { @@ -2465,9 +2487,20 @@ impl FileBackendTrait for TestBackend { fn export_impl( file_backend: &mut dyn WrappedFileBackendTrait, - top_module: Interned>, + mut top_module: Interned>, + options: ExportOptions, ) -> Result<(), WrappedError> { - let top_module = simplify_memories(top_module); + let ExportOptions { + simplify_memories: do_simplify_memories, + simplify_enums: do_simplify_enums, + } = options; + if let Some(kind) = do_simplify_enums { + top_module = + simplify_enums(top_module, kind).map_err(|e| file_backend.simplify_enums_error(e))?; + } + if do_simplify_memories { + top_module = simplify_memories(top_module); + } let indent_depth = Cell::new(0); let mut global_ns = Namespace::default(); let circuit_name = global_ns.get(top_module.name_id()); @@ -2488,20 +2521,102 @@ fn export_impl( .run(top_module) } +#[derive(Clone)] +struct OptionSimplifyEnumsKindValueParser; + +impl OptionSimplifyEnumsKindValueParser { + const NONE_NAME: &'static str = "off"; +} + +impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser { + type Value = Option; + + fn parse_ref( + &self, + cmd: &clap::Command, + arg: Option<&clap::Arg>, + value: &std::ffi::OsStr, + ) -> Result { + if value == Self::NONE_NAME { + Ok(None) + } else { + Ok(Some( + value_parser!(SimplifyEnumsKind).parse_ref(cmd, arg, value)?, + )) + } + } + + fn possible_values( + &self, + ) -> Option + '_>> { + Some(Box::new( + [Self::NONE_NAME.into()] + .into_iter() + .chain(value_parser!(SimplifyEnumsKind).possible_values()?) + .collect::>() + .into_iter(), + )) + } +} + +#[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub struct ExportOptions { + #[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)] + pub simplify_memories: bool, + #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = OptionSimplifyEnumsKindValueParser::NONE_NAME)] + pub simplify_enums: std::option::Option, +} + +impl fmt::Debug for ExportOptions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("ExportOptions {")?; + if f.alternate() { + f.write_str("\n")?; + } + let mut sep = if f.alternate() { "\n " } else { " " }; + let comma_sep = if f.alternate() { ",\n " } else { ", " }; + let default = ExportOptions::default(); + if self.simplify_memories != default.simplify_memories { + write!(f, "{sep}simplify_memories: {:?}", self.simplify_memories)?; + sep = comma_sep; + } + if self.simplify_enums != default.simplify_enums { + write!(f, "{sep}simplify_enums: {:?}", self.simplify_enums)?; + sep = comma_sep; + } + write!( + f, + "{sep}..ExportOptions::default(){}", + if f.alternate() { "\n}" } else { " }" } + ) + } +} + +impl Default for ExportOptions { + fn default() -> Self { + Self { + simplify_memories: true, + simplify_enums: None, + } + } +} + pub fn export( file_backend: B, top_module: &Module, + options: ExportOptions, ) -> Result { let top_module = Intern::intern_sized(top_module.canonical()); WrappedFileBackend::with(file_backend, |file_backend| { - export_impl(file_backend, top_module) + export_impl(file_backend, top_module, options) }) } #[doc(hidden)] #[track_caller] pub fn assert_export_firrtl_impl(top_module: &Module, expected: TestBackend) { - let result = export(TestBackend::default(), top_module).unwrap(); + let result = export(TestBackend::default(), top_module, expected.options).unwrap(); if result != expected { panic!( "assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------" diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index c817e45..cf85875 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -41,6 +41,12 @@ impl fmt::Display for SimplifyEnumsError { impl std::error::Error for SimplifyEnumsError {} +impl From for std::io::Error { + fn from(value: SimplifyEnumsError) -> Self { + std::io::Error::new(std::io::ErrorKind::Other, value) + } +} + #[hdl] struct TagAndBody { tag: T, @@ -595,10 +601,12 @@ impl Folder for State { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, clap::ValueEnum)] pub enum SimplifyEnumsKind { SimplifyToEnumsWithNoBody, + #[clap(name = "replace-with-bundle-of-uints")] ReplaceWithBundleOfUInts, + #[clap(name = "replace-with-uint")] ReplaceWithUInt, } From 45dbb554d07e8a0ea05b0e88ae79bc88f616f968 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:52:41 -0700 Subject: [PATCH 020/190] add formal subcommand --- Cargo.lock | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/cli.rs | 312 +++++++++++++++++++++++++++++++--- crates/fayalite/src/firrtl.rs | 7 +- 4 files changed, 297 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c237af..fa6fb6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,6 +252,7 @@ dependencies = [ "os_pipe", "serde", "serde_json", + "tempfile", "trybuild", "which", ] diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 2306e7a..21089c0 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -24,6 +24,7 @@ num-traits.workspace = true os_pipe.workspace = true serde_json.workspace = true serde.workspace = true +tempfile.workspace = true which.workspace = true [dev-dependencies] diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index c171c22..a771de6 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -13,6 +13,7 @@ use clap::{ }; use eyre::{eyre, Report}; use std::{error, ffi::OsString, fmt, io, path::PathBuf, process}; +use tempfile::TempDir; pub type Result = std::result::Result; @@ -47,21 +48,40 @@ pub trait RunPhase { #[non_exhaustive] pub struct BaseArgs { /// the directory to put the generated main output file and associated files in - #[arg(short, long, value_hint = ValueHint::DirPath)] - pub output: PathBuf, + #[arg(short, long, value_hint = ValueHint::DirPath, required = true)] + pub output: Option, /// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo #[arg(long)] pub file_stem: Option, + #[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")] + pub keep_temp_dir: bool, #[arg(skip = false)] pub redirect_output_for_rust_test: bool, } impl BaseArgs { - pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend { - firrtl::FileBackend { - dir_path: self.output.clone(), - top_fir_file_stem: self.file_stem.clone(), - } + fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option)> { + let (dir_path, temp_dir) = match &self.output { + Some(output) => (output.clone(), None), + None => { + let temp_dir = TempDir::new()?; + if self.keep_temp_dir { + let temp_dir = temp_dir.into_path(); + println!("created temporary directory: {}", temp_dir.display()); + (temp_dir, None) + } else { + (temp_dir.path().to_path_buf(), Some(temp_dir)) + } + } + }; + Ok(( + firrtl::FileBackend { + dir_path, + top_fir_file_stem: self.file_stem.clone(), + circuit_name: None, + }, + temp_dir, + )) } /// handles possibly redirecting the command's output for Rust tests pub fn run_external_command( @@ -106,25 +126,37 @@ pub struct FirrtlArgs { #[non_exhaustive] pub struct FirrtlOutput { pub file_stem: String, + pub top_module: String, + pub output_dir: PathBuf, + pub temp_dir: Option, } impl FirrtlOutput { - pub fn firrtl_file(&self, args: &FirrtlArgs) -> PathBuf { - let mut retval = args.base.output.join(&self.file_stem); - retval.set_extension("fir"); + pub fn file_with_ext(&self, ext: &str) -> PathBuf { + let mut retval = self.output_dir.join(&self.file_stem); + retval.set_extension(ext); retval } + pub fn firrtl_file(&self) -> PathBuf { + self.file_with_ext("fir") + } } impl FirrtlArgs { fn run_impl(&self, top_module: Module) -> Result { + let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?; let firrtl::FileBackend { - top_fir_file_stem, .. - } = firrtl::export(self.base.to_firrtl_file_backend(), &top_module, self.export_options)?; + top_fir_file_stem, + circuit_name, + dir_path, + } = firrtl::export(file_backend, &top_module, self.export_options)?; Ok(FirrtlOutput { file_stem: top_fir_file_stem.expect( "export is known to set the file stem from the circuit name if not provided", ), + top_module: circuit_name.expect("export is known to set the circuit name"), + output_dir: dir_path, + temp_dir, }) } } @@ -155,7 +187,22 @@ pub enum VerilogDialect { Yosys, } +impl fmt::Display for VerilogDialect { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + impl VerilogDialect { + pub fn as_str(self) -> &'static str { + match self { + VerilogDialect::Questa => "questa", + VerilogDialect::Spyglass => "spyglass", + VerilogDialect::Verilator => "verilator", + VerilogDialect::Vivado => "vivado", + VerilogDialect::Yosys => "yosys", + } + } pub fn firtool_extra_args(self) -> &'static [&'static str] { match self { VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"], @@ -191,6 +238,8 @@ pub struct VerilogArgs { /// adapt the generated Verilog for a particular toolchain #[arg(long)] pub verilog_dialect: Option, + #[arg(long, short = 'g')] + pub debug: bool, } #[derive(Debug)] @@ -200,28 +249,37 @@ pub struct VerilogOutput { } impl VerilogOutput { - pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf { - let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem); - retval.set_extension("v"); - retval + pub fn verilog_file(&self) -> PathBuf { + self.firrtl.file_with_ext("v") } } impl VerilogArgs { fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result { + let Self { + firrtl, + firtool, + firtool_extra_args, + verilog_dialect, + debug, + } = self; let output = VerilogOutput { firrtl: firrtl_output, }; - let mut cmd = process::Command::new(&self.firtool); - cmd.arg(output.firrtl.firrtl_file(&self.firrtl)); + let mut cmd = process::Command::new(firtool); + cmd.arg(output.firrtl.firrtl_file()); cmd.arg("-o"); - cmd.arg(output.verilog_file(self)); - if let Some(dialect) = self.verilog_dialect { + cmd.arg(output.verilog_file()); + if *debug { + cmd.arg("-g"); + cmd.arg("--preserve-values=named"); + } + if let Some(dialect) = verilog_dialect { cmd.args(dialect.firtool_extra_args()); } - cmd.args(&self.firtool_extra_args); - cmd.current_dir(&self.firrtl.base.output); - let status = self.firrtl.base.run_external_command(cmd)?; + cmd.args(firtool_extra_args); + cmd.current_dir(&output.firrtl.output_dir); + let status = firrtl.base.run_external_command(cmd)?; if status.success() { Ok(output) } else { @@ -244,12 +302,217 @@ where } } +#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default)] +#[non_exhaustive] +pub enum FormalMode { + #[default] + BMC, + Prove, + Live, + Cover, +} + +impl FormalMode { + pub fn as_str(self) -> &'static str { + match self { + FormalMode::BMC => "bmc", + FormalMode::Prove => "prove", + FormalMode::Live => "live", + FormalMode::Cover => "cover", + } + } +} + +impl fmt::Display for FormalMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +#[derive(Clone)] +struct FormalAdjustArgs; + +impl clap::FromArgMatches for FormalAdjustArgs { + fn from_arg_matches(_matches: &clap::ArgMatches) -> Result { + Ok(Self) + } + + fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> { + Ok(()) + } +} + +impl clap::Args for FormalAdjustArgs { + fn augment_args(cmd: clap::Command) -> clap::Command { + cmd.mut_arg("output", |arg| arg.required(false)) + .mut_arg("verilog_dialect", |arg| { + arg.default_value(VerilogDialect::Yosys.to_string()) + .hide(true) + }) + } + + fn augment_args_for_update(cmd: clap::Command) -> clap::Command { + Self::augment_args(cmd) + } +} + +#[derive(Parser, Clone)] +#[non_exhaustive] +pub struct FormalArgs { + #[command(flatten)] + pub verilog: VerilogArgs, + #[arg( + long, + default_value = "sby", + env = "SBY", + value_hint = ValueHint::CommandName, + value_parser = OsStringValueParser::new().try_map(which::which) + )] + pub sby: PathBuf, + #[arg(long)] + pub sby_extra_args: Vec, + #[arg(long, default_value_t)] + pub mode: FormalMode, + #[arg(long, default_value_t = Self::DEFAULT_DEPTH)] + pub depth: u64, + #[arg(long)] + pub solver: Option, + #[arg(long)] + pub smtbmc_extra_args: Vec, + #[command(flatten)] + _formal_adjust_args: FormalAdjustArgs, +} + +impl fmt::Debug for FormalArgs { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + verilog, + sby, + sby_extra_args, + mode, + depth, + solver, + smtbmc_extra_args, + _formal_adjust_args: _, + } = self; + f.debug_struct("FormalArgs") + .field("verilog", &verilog) + .field("sby", &sby) + .field("sby_extra_args", &sby_extra_args) + .field("mode", &mode) + .field("depth", &depth) + .field("solver", &solver) + .field("smtbmc_extra_args", &smtbmc_extra_args) + .finish_non_exhaustive() + } +} + +impl FormalArgs { + pub const DEFAULT_DEPTH: u64 = 20; +} + +#[derive(Debug)] +#[non_exhaustive] +pub struct FormalOutput { + pub verilog: VerilogOutput, +} + +impl FormalOutput { + pub fn sby_file(&self) -> PathBuf { + self.verilog.firrtl.file_with_ext("sby") + } +} + +impl FormalArgs { + fn sby_contents(&self, output: &FormalOutput) -> String { + let Self { + verilog: _, + sby: _, + sby_extra_args: _, + mode, + depth, + smtbmc_extra_args, + solver, + _formal_adjust_args: _, + } = self; + struct OptArg(Option); + impl fmt::Display for OptArg { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(v) = &self.0 { + f.write_str(" ")?; + v.fmt(f) + } else { + Ok(()) + } + } + } + let space_solver = OptArg(solver.as_ref()); + let smtbmc_options = smtbmc_extra_args.join(" "); + let verilog_file = output + .verilog + .verilog_file() + .into_os_string() + .into_string() + .ok() + .expect("verilog file path is not UTF-8"); + let top_module = &output.verilog.firrtl.top_module; + format!( + "[options]\n\ + mode {mode}\n\ + depth {depth}\n\ + wait on\n\ + \n\ + [engines]\n\ + smtbmc{space_solver} -- -- {smtbmc_options}\n\ + \n\ + [script]\n\ + read_verilog -sv -formal {verilog_file}\n\ + prep -top {top_module}\n + " + ) + } + fn run_impl(&self, verilog_output: VerilogOutput) -> Result { + let output = FormalOutput { + verilog: verilog_output, + }; + let sby_file = output.sby_file(); + std::fs::write(&sby_file, self.sby_contents(&output))?; + let mut cmd = process::Command::new(&self.sby); + cmd.arg("-f"); + cmd.arg(sby_file); + cmd.args(&self.sby_extra_args); + cmd.current_dir(&output.verilog.firrtl.output_dir); + let status = self.verilog.firrtl.base.run_external_command(cmd)?; + if status.success() { + Ok(output) + } else { + Err(CliError(eyre!( + "running {} failed: {status}", + self.sby.display() + ))) + } + } +} + +impl RunPhase for FormalArgs +where + VerilogArgs: RunPhase, +{ + type Output = FormalOutput; + fn run(&self, arg: Arg) -> Result { + let verilog_output = self.verilog.run(arg)?; + self.run_impl(verilog_output) + } +} + #[derive(Subcommand, Debug)] enum CliCommand { /// Generate FIRRTL Firrtl(FirrtlArgs), /// Generate Verilog Verilog(VerilogArgs), + /// Run a formal proof + Formal(FormalArgs), } /// a simple CLI @@ -335,6 +598,9 @@ where CliCommand::Verilog(c) => { c.run(arg)?; } + CliCommand::Formal(c) => { + c.run(arg)?; + } } Ok(()) } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index ba37bcc..4a1547d 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2311,6 +2311,7 @@ impl FileBackendTrait for &'_ mut T { #[non_exhaustive] pub struct FileBackend { pub dir_path: PathBuf, + pub circuit_name: Option, pub top_fir_file_stem: Option, } @@ -2318,6 +2319,7 @@ impl FileBackend { pub fn new(dir_path: impl AsRef) -> Self { Self { dir_path: dir_path.as_ref().to_owned(), + circuit_name: None, top_fir_file_stem: None, } } @@ -2353,7 +2355,10 @@ impl FileBackendTrait for FileBackend { circuit_name: String, contents: String, ) -> Result<(), Self::Error> { - let top_fir_file_stem = self.top_fir_file_stem.get_or_insert(circuit_name); + let top_fir_file_stem = self + .top_fir_file_stem + .get_or_insert_with(|| circuit_name.clone()); + self.circuit_name = Some(circuit_name); let mut path = self.dir_path.join(top_fir_file_stem); if let Some(parent) = path.parent().filter(|v| !v.as_os_str().is_empty()) { fs::create_dir_all(parent)?; From 5fc7dbd6e9a6c698d817643cb8ff670310614d40 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:54:07 -0700 Subject: [PATCH 021/190] add assert_formal helper for running formal proofs in rust tests --- crates/fayalite/src/lib.rs | 1 + crates/fayalite/src/testing.rs | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 crates/fayalite/src/testing.rs diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 83b61b2..70ce724 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -50,4 +50,5 @@ pub mod ty; pub mod util; //pub mod valueless; pub mod prelude; +pub mod testing; pub mod wire; diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs new file mode 100644 index 0000000..8046f05 --- /dev/null +++ b/crates/fayalite/src/testing.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use crate::{ + cli::{FormalArgs, FormalMode, FormalOutput, RunPhase}, + firrtl::ExportOptions, +}; +use clap::Parser; +use std::sync::OnceLock; + +fn assert_formal_helper() -> FormalArgs { + static FORMAL_ARGS: OnceLock = OnceLock::new(); + // ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads + FORMAL_ARGS + .get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"])) + .clone() +} + +#[track_caller] +pub fn assert_formal( + module: M, + mode: FormalMode, + depth: u64, + solver: Option<&str>, + export_options: ExportOptions, +) where + FormalArgs: RunPhase, +{ + let mut args = assert_formal_helper(); + args.verilog.firrtl.base.redirect_output_for_rust_test = true; + args.verilog.firrtl.export_options = export_options; + args.verilog.debug = true; + args.mode = mode; + args.depth = depth; + args.solver = solver.map(String::from); + args.run(module).expect("testing::assert_formal() failed"); +} From e661aeab114312c6ec74b27c18ba1c89394254e4 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 01:55:48 -0700 Subject: [PATCH 022/190] add WIP formal proof for queue() --- crates/fayalite/src/util/ready_valid.rs | 103 ++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 94154c9..ab19cd1 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -162,3 +162,106 @@ pub fn queue( } } } + +#[cfg(todo)] +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cli::FormalMode, firrtl::ExportOptions, + module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal, + }; + + #[test] + fn test_queue() { + #[hdl_module] + fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let rst: SyncReset = m.input(); + #[hdl] + let inp_data: HdlOption> = m.input(); + #[hdl] + let out_ready: Bool = m.input(); + #[hdl] + let cd = wire(); + connect( + cd, + #[hdl] + ClockDomain { + clk, + rst: rst.to_reset(), + }, + ); + #[hdl] + let dut = instance(queue( + UInt[ConstUsize::<8>], + capacity, + inp_ready_is_comb, + out_valid_is_comb, + )); + connect(dut.cd, cd); + connect(dut.inp.data, inp_data); + connect(dut.out.ready, out_ready); + #[hdl] + let count = reg_builder().clock_domain(cd).reset(0u32); + #[hdl] + let next_count = wire(); + connect(next_count, count); + connect(count, next_count); + #[hdl] + if ReadyValid::fire(dut.inp) & !ReadyValid::fire(dut.out) { + connect_any(next_count, count + 1u8); + } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) { + connect_any(next_count, count - 1u8); + } + hdl_assert(clk, count.cmp_eq(dut.count), ""); + #[hdl] + let index = reg_builder().clock_domain(cd).reset(HdlNone::>()); + #[hdl] + let data = reg_builder().clock_domain(cd).reset(HdlNone()); + #[hdl] + match index { + HdlNone => + { + #[hdl] + if ReadyValid::fire(dut.inp) { + connect(index, HdlSome(0u32)); + connect(data, dut.inp.data); + } + } + HdlSome(cur_index) => + { + #[hdl] + if cur_index.cmp_ge(next_count) { + connect(index, HdlNone()); + #[hdl] + if let HdlSome(data) = data { + #[hdl] + if let HdlSome(out_data) = dut.out.data { + hdl_assert(clk, data.cmp_eq(out_data), ""); + } else { + hdl_assert(clk, false.to_expr(), ""); + } + } else { + hdl_assert(clk, false.to_expr(), ""); + } + } else { + connect(index, HdlSome((cur_index + 1u8).cast_to_static())); + } + } + } + } + assert_formal( + queue_test(NonZeroUsize::new(2).unwrap(), false, false), + FormalMode::BMC, + 20, + None, + ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + ..ExportOptions::default() + }, + ); + } +} From 04752c5037761626e58cc91e7c2e78aa231b7904 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 25 Sep 2024 21:50:49 -0700 Subject: [PATCH 023/190] add test for connect_any with nested enums with different-sized variant bodies simplify_enums is currently broken in that case --- crates/fayalite/tests/module.rs | 130 ++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 4139ee1..cddf808 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3234,3 +3234,133 @@ circuit check_formal: "#, }; } + +#[hdl] +pub enum OneOfThree { + A(A), + B(B), + C(C), +} + +#[hdl_module(outline_generated)] +pub fn check_enum_connect_any() { + #[hdl] + let index: UInt<2> = m.input(); + #[hdl] + let i0: OneOfThree, HdlOption>, Bool> = m.input(); + #[hdl] + let i1: OneOfThree, HdlOption>, Bool> = m.input(); + #[hdl] + let i2: OneOfThree, HdlOption>, Bool> = m.input(); + #[hdl] + let i3: OneOfThree, HdlOption>, Bool> = m.input(); + #[hdl] + let o0: OneOfThree, HdlOption>, Bool> = m.output(); + #[hdl] + let o1: OneOfThree, HdlOption>, Bool> = m.output(); + #[hdl] + let o2: OneOfThree, HdlOption>, Bool> = m.output(); + #[hdl] + let o3: OneOfThree, HdlOption>, Bool> = m.output(); + #[hdl] + if index.cmp_eq(0u8) { + connect_any(o0, i0); + connect_any(o1, i1); + connect_any(o2, i2); + connect_any(o3, i3); + } else if index.cmp_eq(1u8) { + connect_any(o0, i1); + connect_any(o1, i2); + connect_any(o2, i3); + connect_any(o3, i0); + } else if index.cmp_eq(2u8) { + connect_any(o0, i2); + connect_any(o1, i3); + connect_any(o2, i0); + connect_any(o3, i1); + } else { + connect_any(o0, i3); + connect_any(o1, i0); + connect_any(o2, i1); + connect_any(o3, i2); + } +} + +#[cfg(todo)] +#[test] +fn test_enum_connect_any() { + let _n = SourceLocation::normalize_files_for_tests(); + let m = check_enum_connect_any(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 +circuit check_enum_connect_any: + type Ty0 = {|HdlNone, HdlSome: SInt<0>|} + type Ty1 = {|A: UInt<0>, B: Ty0, C: UInt<1>|} + type Ty2 = {|HdlNone, HdlSome: SInt<1>|} + type Ty3 = {|A: UInt<1>, B: Ty2, C: UInt<1>|} + type Ty4 = {|HdlNone, HdlSome: SInt<2>|} + type Ty5 = {|A: UInt<2>, B: Ty4, C: UInt<1>|} + type Ty6 = {|HdlNone, HdlSome: SInt<3>|} + type Ty7 = {|A: UInt<3>, B: Ty6, C: UInt<1>|} + module check_enum_connect_any: @[module-XXXXXXXXXX.rs 1:1] + input index: UInt<2> @[module-XXXXXXXXXX.rs 2:1] + input i0: Ty1 @[module-XXXXXXXXXX.rs 3:1] + input i1: Ty3 @[module-XXXXXXXXXX.rs 4:1] + input i2: Ty5 @[module-XXXXXXXXXX.rs 5:1] + input i3: Ty7 @[module-XXXXXXXXXX.rs 6:1] + output o0: Ty1 @[module-XXXXXXXXXX.rs 7:1] + output o1: Ty3 @[module-XXXXXXXXXX.rs 8:1] + output o2: Ty5 @[module-XXXXXXXXXX.rs 9:1] + output o3: Ty7 @[module-XXXXXXXXXX.rs 10:1] + when eq(index, UInt<8>(0h0)): @[module-XXXXXXXXXX.rs 11:1] + connect o0, i0 @[module-XXXXXXXXXX.rs 12:1] + connect o1, i1 @[module-XXXXXXXXXX.rs 13:1] + connect o2, i2 @[module-XXXXXXXXXX.rs 14:1] + connect o3, i3 @[module-XXXXXXXXXX.rs 15:1] + else when eq(index, UInt<8>(0h1)): @[module-XXXXXXXXXX.rs 16:1] + connect o0, i1 @[module-XXXXXXXXXX.rs 17:1] + connect o1, i2 @[module-XXXXXXXXXX.rs 18:1] + connect o2, i3 @[module-XXXXXXXXXX.rs 19:1] + connect o3, i0 @[module-XXXXXXXXXX.rs 20:1] + else when eq(index, UInt<8>(0h2)): @[module-XXXXXXXXXX.rs 21:1] + connect o0, i2 @[module-XXXXXXXXXX.rs 22:1] + connect o1, i3 @[module-XXXXXXXXXX.rs 23:1] + connect o2, i0 @[module-XXXXXXXXXX.rs 24:1] + connect o3, i1 @[module-XXXXXXXXXX.rs 25:1] + else: + connect o0, i3 @[module-XXXXXXXXXX.rs 26:1] + connect o1, i0 @[module-XXXXXXXXXX.rs 27:1] + connect o2, i1 @[module-XXXXXXXXXX.rs 28:1] + connect o3, i2 @[module-XXXXXXXXXX.rs 29:1] +", + }; + // FIXME: simplify_enums is broken when connecting enums that contain + // UInt/SInt where their widths don't match. it should recurse into the + // enum bodies so it can properly sign/zero-extend or truncate the + // contained UInt/SInt fields. + let orig_m = m.canonical().intern(); + let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_enum_connect_any.fir": r"TODO", + }; + let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_enum_connect_any.fir": r"TODO", + }; + let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); + dbg!(m); + #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 + assert_export_firrtl! { + m => + "/test/check_enum_connect_any.fir": r"TODO", + }; +} From d2ba313f0f1abf3d1f27f56e3047fa3231052471 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 30 Sep 2024 21:19:20 -0700 Subject: [PATCH 024/190] fix simplify_memories trying to connect Bool with UInt --- crates/fayalite/src/module/transform/simplify_memories.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 0df1e81..19fb0ec 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -569,7 +569,7 @@ impl ModuleState { port_wmask.map(Expr::from_canonical), connect_read_enum, connect_write_enum, - connect_write_enum, + connect_write, ), CanonicalType::Array(array_type) => { input_array_types.push(array_type); From 1e2831da47cca6df7e97f3d936fedbb5269f5f02 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 30 Sep 2024 21:20:35 -0700 Subject: [PATCH 025/190] add validation of connects and matches when validating module this is useful for catching errors in transformation passes --- crates/fayalite/src/module.rs | 87 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 2b7534c..e88b37a 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -185,6 +185,40 @@ pub struct StmtConnect { pub source_location: SourceLocation, } +impl StmtConnect { + #[track_caller] + fn assert_validity_with_original_types(&self, lhs_orig_ty: impl Type, rhs_orig_ty: impl Type) { + let Self { + lhs, + rhs, + source_location, + } = *self; + assert!( + Expr::ty(lhs).can_connect(Expr::ty(rhs)), + "can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}", + ); + assert!( + matches!(Expr::flow(lhs), Flow::Sink | Flow::Duplex), + "can't connect to source, connect lhs must have sink or duplex flow\nat: {source_location}" + ); + assert!( + lhs.target().is_some(), + "can't connect to non-target\nat: {source_location}" + ); + match Expr::flow(rhs) { + Flow::Source | Flow::Duplex => {} + Flow::Sink => assert!( + Expr::ty(rhs).is_passive(), + "can't connect from sink with non-passive type\nat: {source_location}" + ), + } + } + #[track_caller] + fn assert_validity(&self) { + self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs)); + } +} + impl fmt::Debug for StmtConnect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { @@ -283,6 +317,13 @@ pub struct StmtMatch { pub blocks: Interned<[S::Block]>, } +impl StmtMatch { + #[track_caller] + fn assert_validity(&self) { + assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len()); + } +} + impl fmt::Debug for StmtMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { @@ -1657,11 +1698,13 @@ impl AssertValidityState { } for stmt in stmts { match stmt { - Stmt::Connect(StmtConnect { - lhs, - rhs, - source_location, - }) => { + Stmt::Connect(connect) => { + connect.assert_validity(); + let StmtConnect { + lhs, + rhs, + source_location, + } = connect; self.set_connect_side_written(lhs, source_location, true, block); self.set_connect_side_written(rhs, source_location, false, block); } @@ -1671,6 +1714,7 @@ impl AssertValidityState { self.process_conditional_sub_blocks(block, sub_blocks) } Stmt::Match(match_stmt) => { + match_stmt.assert_validity(); let sub_blocks = Vec::from_iter( match_stmt .blocks @@ -2517,24 +2561,12 @@ pub fn connect_any_with_loc( let rhs_orig = rhs.to_expr(); let lhs = Expr::canonical(lhs_orig); let rhs = Expr::canonical(rhs_orig); - assert!( - Expr::ty(lhs).can_connect(Expr::ty(rhs)), - "can't connect types that are not equivalent:\nlhs type:\n{:?}\nrhs type:\n{:?}", - Expr::ty(lhs_orig), - Expr::ty(rhs_orig) - ); - assert!( - matches!(Expr::flow(lhs), Flow::Sink | Flow::Duplex), - "can't connect to source, connect lhs must have sink or duplex flow" - ); - assert!(lhs.target().is_some(), "can't connect to non-target"); - match Expr::flow(rhs) { - Flow::Source | Flow::Duplex => {} - Flow::Sink => assert!( - Expr::ty(rhs).is_passive(), - "can't connect from sink with non-passive type" - ), - } + let connect = StmtConnect { + lhs, + rhs, + source_location, + }; + connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig)); ModuleBuilder::with(|m| { m.impl_ .borrow_mut() @@ -2542,14 +2574,7 @@ pub fn connect_any_with_loc( .builder_normal_body() .block(m.block_stack.top()) .stmts - .push( - StmtConnect { - lhs, - rhs, - source_location, - } - .into(), - ); + .push(connect.into()); }); } From 30a38bc8da427c08c2ba9db159fb1424df288ebe Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 30 Sep 2024 22:31:16 -0700 Subject: [PATCH 026/190] fix simplify_enums to properly handle nested enums and connects with different types --- .../src/module/transform/simplify_enums.rs | 693 +++++++++++++----- crates/fayalite/tests/module.rs | 585 ++++++++++++--- 2 files changed, 1028 insertions(+), 250 deletions(-) diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index cf85875..3b3677b 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -2,19 +2,20 @@ // See Notices.txt for copyright information use crate::{ array::{Array, ArrayType}, - bundle::{Bundle, BundleType}, + bundle::{Bundle, BundleField, BundleType}, enum_::{Enum, EnumType, EnumVariant}, expr::{ ops::{self, EnumLiteral}, - CastBitsTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, + CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, }, hdl, - int::{DynSize, Size, UInt, UIntType}, - intern::{Intern, Interned}, + int::UInt, + intern::{Intern, Interned, Memoize}, memory::{DynPortType, Mem, MemPort}, module::{ transform::visit::{Fold, Folder}, - Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, + Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, + StmtWire, }, source_location::SourceLocation, ty::{CanonicalType, Type}, @@ -47,25 +48,65 @@ impl From for std::io::Error { } } +fn contains_any_enum_types(ty: CanonicalType) -> bool { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct TheMemoize; + impl Memoize for TheMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = bool; + + fn inner(self, ty: &Self::Input) -> Self::Output { + match *ty { + CanonicalType::Array(array_type) => contains_any_enum_types(array_type.element()), + CanonicalType::Enum(_) => true, + CanonicalType::Bundle(bundle) => bundle + .fields() + .iter() + .any(|field| contains_any_enum_types(field.ty)), + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => false, + } + } + } + TheMemoize.get_owned(ty) +} + #[hdl] -struct TagAndBody { - tag: T, - body: UIntType, +struct TagAndBody { + tag: Tag, + body: Body, } #[derive(Clone, Debug)] enum EnumTypeState { - TagEnumAndBody(TagAndBody), - TagUIntAndBody(TagAndBody), + TagEnumAndBody(TagAndBody), + TagUIntAndBody(TagAndBody), UInt(UInt), Unchanged, } +struct ModuleState { + name_id_gen: NameIdGen, + module_name: NameId, +} + +impl ModuleState { + fn gen_name(&mut self, name: &str) -> ScopedNameId { + ScopedNameId(self.module_name, self.name_id_gen.gen(name.intern())) + } +} + struct State { enum_types: HashMap, replacement_mem_ports: HashMap, Wire>, kind: SimplifyEnumsKind, - name_id_gen: NameIdGen, + module_state_stack: Vec, } impl State { @@ -113,6 +154,377 @@ impl State { self.enum_types.insert(enum_type, retval.clone()); Ok(retval) } + #[hdl] + fn handle_enum_literal( + &mut self, + unfolded_enum_type: Enum, + variant_index: usize, + folded_variant_value: Option>, + ) -> Result, SimplifyEnumsError> { + Ok( + match self.get_or_make_enum_type_state(unfolded_enum_type)? { + EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => Expr::canonical( + #[hdl] + TagAndBody { + tag: EnumLiteral::new_by_index(tag, variant_index, None), + body: match folded_variant_value { + Some(variant_value) => variant_value.cast_to_bits().cast_to(body), + None => body.zero().to_expr(), + }, + }, + ), + EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => Expr::canonical( + #[hdl] + TagAndBody { + tag: tag.from_int_wrapping(variant_index), + body: match folded_variant_value { + Some(folded_variant_value) => { + folded_variant_value.cast_to_bits().cast_to(body) + } + None => body.zero().to_expr(), + }, + }, + ), + EnumTypeState::UInt(_) => { + let tag = UInt[unfolded_enum_type.discriminant_bit_width()]; + let body = UInt[unfolded_enum_type.type_properties().bit_width - tag.width()]; + Expr::canonical( + (#[hdl] + TagAndBody { + tag: tag.from_int_wrapping(variant_index), + body: match folded_variant_value { + Some(folded_variant_value) => { + folded_variant_value.cast_to_bits().cast_to(body) + } + None => body.zero().to_expr(), + }, + }) + .cast_to_bits(), + ) + } + EnumTypeState::Unchanged => Expr::canonical( + ops::EnumLiteral::new_by_index( + unfolded_enum_type, + variant_index, + folded_variant_value, + ) + .to_expr(), + ), + }, + ) + } + fn handle_variant_access( + &mut self, + unfolded_enum_type: Enum, + folded_base_expr: Expr, + variant_index: usize, + ) -> Result, SimplifyEnumsError> { + let unfolded_variant_type = unfolded_enum_type.variants()[variant_index].ty; + Ok( + match self.get_or_make_enum_type_state(unfolded_enum_type)? { + EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => { + match unfolded_variant_type { + Some(variant_type) => Expr::canonical( + Expr::>::from_canonical( + folded_base_expr, + ) + .body[..variant_type.bit_width()] + .cast_bits_to(variant_type.fold(self)?), + ), + None => Expr::canonical(().to_expr()), + } + } + EnumTypeState::UInt(_) => match unfolded_variant_type { + Some(variant_type) => { + let base_int = Expr::::from_canonical(folded_base_expr); + let variant_type_bit_width = variant_type.bit_width(); + Expr::canonical( + base_int[unfolded_enum_type.discriminant_bit_width()..] + [..variant_type_bit_width] + .cast_bits_to(variant_type.fold(self)?), + ) + } + None => Expr::canonical(().to_expr()), + }, + EnumTypeState::Unchanged => match unfolded_variant_type { + Some(_) => ops::VariantAccess::new_by_index( + Expr::from_canonical(folded_base_expr), + variant_index, + ) + .to_expr(), + None => Expr::canonical(().to_expr()), + }, + }, + ) + } + fn handle_match( + &mut self, + unfolded_enum_type: Enum, + folded_expr: Expr, + source_location: SourceLocation, + folded_blocks: &[Block], + ) -> Result { + match self.get_or_make_enum_type_state(unfolded_enum_type)? { + EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch { + expr: Expr::>::from_canonical(folded_expr).tag, + source_location, + blocks: folded_blocks.intern(), + } + .into()), + EnumTypeState::TagUIntAndBody(_) => { + let int_tag_expr = Expr::>::from_canonical(folded_expr).tag; + Ok(match_int_tag(int_tag_expr, source_location, &folded_blocks).into()) + } + EnumTypeState::UInt(_) => { + let int_tag_expr = Expr::::from_canonical(folded_expr) + [..unfolded_enum_type.discriminant_bit_width()]; + Ok(match_int_tag(int_tag_expr, source_location, &folded_blocks).into()) + } + EnumTypeState::Unchanged => Ok(StmtMatch { + expr: Expr::from_canonical(folded_expr), + source_location, + blocks: folded_blocks.intern(), + } + .into()), + } + } + fn handle_stmt_connect_array( + &mut self, + unfolded_lhs_ty: Array, + unfolded_rhs_ty: Array, + folded_lhs: Expr, + folded_rhs: Expr, + source_location: SourceLocation, + output_stmts: &mut Vec, + ) -> Result<(), SimplifyEnumsError> { + assert_eq!(unfolded_lhs_ty.len(), unfolded_rhs_ty.len()); + let unfolded_lhs_element_ty = unfolded_lhs_ty.element(); + let unfolded_rhs_element_ty = unfolded_rhs_ty.element(); + for array_index in 0..unfolded_lhs_ty.len() { + self.handle_stmt_connect( + unfolded_lhs_element_ty, + unfolded_rhs_element_ty, + folded_lhs[array_index], + folded_rhs[array_index], + source_location, + output_stmts, + )?; + } + Ok(()) + } + fn handle_stmt_connect_bundle( + &mut self, + unfolded_lhs_ty: Bundle, + unfolded_rhs_ty: Bundle, + folded_lhs: Expr, + folded_rhs: Expr, + source_location: SourceLocation, + output_stmts: &mut Vec, + ) -> Result<(), SimplifyEnumsError> { + let unfolded_lhs_fields = unfolded_lhs_ty.fields(); + let unfolded_rhs_fields = unfolded_rhs_ty.fields(); + assert_eq!(unfolded_lhs_fields.len(), unfolded_rhs_fields.len()); + for ( + field_index, + ( + &BundleField { + name, + flipped, + ty: unfolded_lhs_field_ty, + }, + unfolded_rhs_field, + ), + ) in unfolded_lhs_fields + .iter() + .zip(&unfolded_rhs_fields) + .enumerate() + { + assert_eq!(name, unfolded_rhs_field.name); + assert_eq!(flipped, unfolded_rhs_field.flipped); + let folded_lhs_field = + ops::FieldAccess::new_by_index(folded_lhs, field_index).to_expr(); + let folded_rhs_field = + ops::FieldAccess::new_by_index(folded_rhs, field_index).to_expr(); + if flipped { + // swap lhs/rhs + self.handle_stmt_connect( + unfolded_rhs_field.ty, + unfolded_lhs_field_ty, + folded_rhs_field, + folded_lhs_field, + source_location, + output_stmts, + )?; + } else { + self.handle_stmt_connect( + unfolded_lhs_field_ty, + unfolded_rhs_field.ty, + folded_lhs_field, + folded_rhs_field, + source_location, + output_stmts, + )?; + } + } + Ok(()) + } + fn handle_stmt_connect_enum( + &mut self, + unfolded_lhs_ty: Enum, + unfolded_rhs_ty: Enum, + folded_lhs: Expr, + folded_rhs: Expr, + source_location: SourceLocation, + output_stmts: &mut Vec, + ) -> Result<(), SimplifyEnumsError> { + let unfolded_lhs_variants = unfolded_lhs_ty.variants(); + let unfolded_rhs_variants = unfolded_rhs_ty.variants(); + assert_eq!(unfolded_lhs_variants.len(), unfolded_rhs_variants.len()); + let mut folded_blocks = vec![]; + for ( + variant_index, + ( + &EnumVariant { + name, + ty: unfolded_lhs_variant_ty, + }, + unfolded_rhs_variant, + ), + ) in unfolded_lhs_variants + .iter() + .zip(&unfolded_rhs_variants) + .enumerate() + { + let mut output_stmts = vec![]; + assert_eq!(name, unfolded_rhs_variant.name); + assert_eq!( + unfolded_lhs_variant_ty.is_some(), + unfolded_rhs_variant.ty.is_some() + ); + let folded_variant_value = + if let (Some(unfolded_lhs_variant_ty), Some(unfolded_rhs_variant_ty)) = + (unfolded_lhs_variant_ty, unfolded_rhs_variant.ty) + { + let lhs_wire = Wire::new_unchecked( + self.module_state_stack + .last_mut() + .unwrap() + .gen_name("__connect_variant_body"), + source_location, + unfolded_lhs_variant_ty.fold(self)?, + ); + output_stmts.push( + StmtWire { + annotations: Interned::default(), + wire: lhs_wire, + } + .into(), + ); + let lhs_wire = lhs_wire.to_expr(); + let folded_rhs_variant = + self.handle_variant_access(unfolded_rhs_ty, folded_rhs, variant_index)?; + self.handle_stmt_connect( + unfolded_lhs_variant_ty, + unfolded_rhs_variant_ty, + lhs_wire, + folded_rhs_variant, + source_location, + &mut output_stmts, + )?; + Some(lhs_wire) + } else { + None + }; + output_stmts.push( + StmtConnect { + lhs: folded_lhs, + rhs: self.handle_enum_literal( + unfolded_lhs_ty, + variant_index, + folded_variant_value, + )?, + source_location, + } + .into(), + ); + folded_blocks.push(Block { + memories: Interned::default(), + stmts: Intern::intern_owned(output_stmts), + }); + } + output_stmts.push(self.handle_match( + unfolded_rhs_ty, + folded_rhs, + source_location, + &folded_blocks, + )?); + Ok(()) + } + fn handle_stmt_connect( + &mut self, + unfolded_lhs_ty: CanonicalType, + unfolded_rhs_ty: CanonicalType, + folded_lhs: Expr, + folded_rhs: Expr, + source_location: SourceLocation, + output_stmts: &mut Vec, + ) -> Result<(), SimplifyEnumsError> { + let needs_expansion = unfolded_lhs_ty != unfolded_rhs_ty + && (contains_any_enum_types(unfolded_lhs_ty) + || contains_any_enum_types(unfolded_rhs_ty)); + if !needs_expansion { + output_stmts.push( + StmtConnect { + lhs: folded_lhs, + rhs: folded_rhs, + source_location, + } + .into(), + ); + return Ok(()); + } + println!( + r"handle_stmt_connect( + unfolded_lhs_ty: {unfolded_lhs_ty:?}, + unfolded_rhs_ty: {unfolded_rhs_ty:?}, + folded_lhs: {folded_lhs:?}, + folded_rhs: {folded_rhs:?}, +)" + ); + match unfolded_lhs_ty { + CanonicalType::Array(unfolded_lhs_ty) => self.handle_stmt_connect_array( + unfolded_lhs_ty, + Array::from_canonical(unfolded_rhs_ty), + Expr::from_canonical(folded_lhs), + Expr::from_canonical(folded_rhs), + source_location, + output_stmts, + ), + CanonicalType::Enum(unfolded_lhs_ty) => self.handle_stmt_connect_enum( + unfolded_lhs_ty, + Enum::from_canonical(unfolded_rhs_ty), + folded_lhs, + folded_rhs, + source_location, + output_stmts, + ), + CanonicalType::Bundle(unfolded_lhs_ty) => self.handle_stmt_connect_bundle( + unfolded_lhs_ty, + Bundle::from_canonical(unfolded_rhs_ty), + Expr::from_canonical(folded_lhs), + Expr::from_canonical(folded_rhs), + source_location, + output_stmts, + ), + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => unreachable!(), + } + } } fn connect_port( @@ -183,6 +595,42 @@ fn connect_port( } } +fn match_int_tag( + int_tag_expr: Expr, + source_location: SourceLocation, + folded_blocks: &[Block], +) -> StmtIf { + let mut blocks_iter = folded_blocks.iter().copied().enumerate(); + let (_, last_block) = blocks_iter.next_back().unwrap_or_default(); + let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back() else { + return StmtIf { + cond: true.to_expr(), + source_location, + blocks: [last_block, Block::default()], + }; + }; + let mut retval = StmtIf { + cond: int_tag_expr + .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)), + source_location, + blocks: [next_to_last_block, last_block], + }; + for (variant_index, block) in blocks_iter.rev() { + retval = StmtIf { + cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)), + source_location, + blocks: [ + block, + Block { + memories: Default::default(), + stmts: [Stmt::from(retval)][..].intern(), + }, + ], + }; + } + retval +} + impl Folder for State { type Error = SimplifyEnumsError; @@ -191,96 +639,33 @@ impl Folder for State { } fn fold_module(&mut self, v: Module) -> Result, Self::Error> { - let old_name_id_gen = - std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical())); + self.module_state_stack.push(ModuleState { + name_id_gen: NameIdGen::for_module(v.canonical()), + module_name: v.name_id(), + }); let retval = Fold::default_fold(v, self); - self.name_id_gen = old_name_id_gen; + self.module_state_stack.pop(); retval } fn fold_expr_enum(&mut self, op: ExprEnum) -> Result { match op { - ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? { - EnumTypeState::TagEnumAndBody(TagAndBody { tag, body }) => *Expr::expr_enum( - as BundleType>::Builder::default() - .field_tag(EnumLiteral::new_by_index(tag, op.variant_index(), None)) - .field_body(match op.variant_value() { - Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), - None => body.zero().to_expr(), - }) - .to_expr(), - ), - EnumTypeState::TagUIntAndBody(TagAndBody { tag, body }) => *Expr::expr_enum( - as BundleType>::Builder::default() - .field_tag(tag.from_int_wrapping(op.variant_index())) - .field_body(match op.variant_value() { - Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), - None => body.zero().to_expr(), - }) - .to_expr(), - ), - EnumTypeState::UInt(_) => *Expr::expr_enum( - as BundleType>::Builder::default() - .field_tag( - UIntType::new(op.ty().discriminant_bit_width()) - .from_int_wrapping(op.variant_index()), - ) - .field_body(match op.variant_value() { - Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), - None => UIntType::new( - op.ty().type_properties().bit_width - - op.ty().discriminant_bit_width(), - ) - .zero() - .to_expr(), - }) - .cast_to_bits(), - ), - EnumTypeState::Unchanged => ExprEnum::EnumLiteral(ops::EnumLiteral::new_by_index( + ExprEnum::EnumLiteral(op) => { + let folded_variant_value = op.variant_value().map(|v| v.fold(self)).transpose()?; + Ok(*Expr::expr_enum(self.handle_enum_literal( op.ty(), op.variant_index(), - op.variant_value().map(|v| v.fold(self)).transpose()?, - )), - }), - ExprEnum::VariantAccess(op) => Ok( - match self.get_or_make_enum_type_state(Expr::ty(op.base()))? { - EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => { - match op.variant_type() { - Some(variant_type) => *Expr::expr_enum( - Expr::>::from_canonical( - (*Expr::expr_enum(op.base())).fold(self)?.to_expr(), - ) - .body[..variant_type.bit_width()] - .cast_bits_to(variant_type), - ), - None => *Expr::expr_enum(().to_expr()), - } - } - EnumTypeState::UInt(_) => match op.variant_type() { - Some(variant_type) => { - let base_int = Expr::::from_canonical( - (*Expr::expr_enum(op.base())).fold(self)?.to_expr(), - ); - dbg!(base_int); - let base_ty = Expr::ty(op.base()); - let variant_type_bit_width = variant_type.bit_width(); - *Expr::expr_enum( - base_int[base_ty.discriminant_bit_width()..] - [..variant_type_bit_width] - .cast_bits_to(variant_type), - ) - } - None => *Expr::expr_enum(().to_expr()), - }, - EnumTypeState::Unchanged => match op.variant_type() { - Some(_) => ExprEnum::VariantAccess(ops::VariantAccess::new_by_index( - op.base().fold(self)?, - op.variant_index(), - )), - None => *Expr::expr_enum(().to_expr()), - }, - }, - ), + folded_variant_value, + )?)) + } + ExprEnum::VariantAccess(op) => { + let folded_base_expr = Expr::canonical(op.base()).fold(self)?; + Ok(*Expr::expr_enum(self.handle_variant_access( + Expr::ty(op.base()), + folded_base_expr, + op.variant_index(), + )?)) + } ExprEnum::MemPort(mem_port) => Ok( if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) { ExprEnum::Wire(wire) @@ -424,11 +809,15 @@ impl Folder for State { if wire_ty == new_port_ty { continue; } - let wire_name = self.name_id_gen.gen( - (*format!("{}_{}", memory.scoped_name().1 .0, port.port_name())).intern(), - ); let wire = Wire::new_unchecked( - ScopedNameId(memory.scoped_name().0, wire_name), + self.module_state_stack + .last_mut() + .unwrap() + .gen_name(&format!( + "{}_{}", + memory.scoped_name().1 .0, + port.port_name() + )), port.source_location(), wire_ty, ); @@ -471,82 +860,50 @@ impl Folder for State { } fn fold_stmt(&mut self, stmt: Stmt) -> Result { - fn match_int_tag( - state: &mut State, - int_tag_expr: Expr, - source_location: SourceLocation, - blocks: Interned<[Block]>, - ) -> Result { - let mut blocks_iter = blocks.iter().copied().enumerate(); - let (_, last_block) = blocks_iter.next_back().unwrap_or_default(); - let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back() - else { - return Ok(StmtIf { - cond: true.to_expr(), - source_location, - blocks: [last_block.fold(state)?, Block::default()], - }); - }; - let mut retval = StmtIf { - cond: int_tag_expr - .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)), - source_location, - blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?], - }; - for (variant_index, block) in blocks_iter.rev() { - retval = StmtIf { - cond: int_tag_expr - .cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)), - source_location, - blocks: [ - block.fold(state)?, - Block { - memories: Default::default(), - stmts: [Stmt::from(retval)][..].intern(), - }, - ], - }; - } - Ok(retval) - } match stmt { Stmt::Match(StmtMatch { expr, source_location, blocks, - }) => match self.get_or_make_enum_type_state(Expr::ty(expr))? { - EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch { - expr: Expr::>::from_canonical( - Expr::canonical(expr).fold(self)?, - ) - .tag, - source_location, - blocks: blocks.fold(self)?, - } - .into()), - EnumTypeState::TagUIntAndBody(_) => { - let int_tag_expr = Expr::>::from_canonical( - Expr::canonical(expr).fold(self)?, - ) - .tag; - Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) - } - EnumTypeState::UInt(_) => { - let int_tag_expr = - Expr::::from_canonical(Expr::canonical(expr).fold(self)?) - [..Expr::ty(expr).discriminant_bit_width()]; - Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into()) - } - EnumTypeState::Unchanged => Ok(StmtMatch { - expr: expr.fold(self)?, - source_location, - blocks: blocks.fold(self)?, - } - .into()), - }, - Stmt::Connect(_) | Stmt::Formal(_) | Stmt::If(_) | Stmt::Declaration(_) => { - stmt.default_fold(self) + }) => { + let folded_expr = Expr::canonical(expr).fold(self)?; + let folded_blocks = blocks.fold(self)?; + self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks) } + Stmt::Connect(StmtConnect { + lhs, + rhs, + source_location, + }) => { + let folded_lhs = lhs.fold(self)?; + let folded_rhs = rhs.fold(self)?; + let mut output_stmts = vec![]; + self.handle_stmt_connect( + Expr::ty(lhs), + Expr::ty(rhs), + folded_lhs, + folded_rhs, + source_location, + &mut output_stmts, + )?; + if output_stmts.len() == 1 { + Ok(output_stmts.pop().unwrap()) + } else { + Ok(StmtIf { + cond: true.to_expr(), + source_location, + blocks: [ + Block { + memories: Interned::default(), + stmts: Intern::intern_owned(output_stmts), + }, + Block::default(), + ], + } + .into()) + } + } + Stmt::Formal(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self), } } @@ -618,6 +975,6 @@ pub fn simplify_enums( enum_types: HashMap::new(), replacement_mem_ports: HashMap::new(), kind, - name_id_gen: NameIdGen::default(), + module_state_stack: vec![], }) } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index cddf808..9d5a898 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -514,7 +514,6 @@ circuit check_enum_literals: type Ty1 = {tag: Ty0, body: UInt<8>} type Ty2 = {|A, B, C|} type Ty3 = {tag: Ty2, body: UInt<8>} - type Ty4 = {tag: Ty2, body: UInt<3>} module check_enum_literals: @[module-XXXXXXXXXX.rs 1:1] input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] output o: Ty1 @[module-XXXXXXXXXX.rs 3:1] @@ -534,7 +533,7 @@ circuit check_enum_literals: connect _bundle_literal_expr_2.body, i connect o2, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 9:1] else: - wire _bundle_literal_expr_3: Ty4 + wire _bundle_literal_expr_3: Ty3 connect _bundle_literal_expr_3.tag, {|A, B, C|}(C) wire _array_literal_expr: UInt<1>[3] connect _array_literal_expr[0], bits(i, 0, 0) @@ -546,7 +545,7 @@ circuit check_enum_literals: 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 _bundle_literal_expr_3.body, _cast_to_bits_expr + connect _bundle_literal_expr_3.body, pad(_cast_to_bits_expr, 8) connect o2, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 10:1] ", }; @@ -559,7 +558,6 @@ circuit check_enum_literals: circuit check_enum_literals: type Ty0 = {tag: UInt<1>, body: UInt<8>} type Ty1 = {tag: UInt<2>, body: UInt<8>} - type Ty2 = {tag: UInt<2>, body: UInt<3>} module check_enum_literals: @[module-XXXXXXXXXX.rs 1:1] input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] output o: Ty0 @[module-XXXXXXXXXX.rs 3:1] @@ -579,7 +577,7 @@ circuit check_enum_literals: connect _bundle_literal_expr_2.body, i connect o2, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 9:1] else: - wire _bundle_literal_expr_3: Ty2 + wire _bundle_literal_expr_3: Ty1 connect _bundle_literal_expr_3.tag, UInt<2>(0h2) wire _array_literal_expr: UInt<1>[3] connect _array_literal_expr[0], bits(i, 0, 0) @@ -591,7 +589,7 @@ circuit check_enum_literals: 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 _bundle_literal_expr_3.body, _cast_to_bits_expr + connect _bundle_literal_expr_3.body, pad(_cast_to_bits_expr, 8) connect o2, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 10:1] ", }; @@ -604,7 +602,6 @@ circuit check_enum_literals: circuit check_enum_literals: type Ty0 = {tag: UInt<1>, body: UInt<8>} type Ty1 = {tag: UInt<2>, body: UInt<8>} - type Ty2 = {tag: UInt<2>, body: UInt<3>} module check_enum_literals: @[module-XXXXXXXXXX.rs 1:1] input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] output o: UInt<9> @[module-XXXXXXXXXX.rs 3:1] @@ -639,7 +636,7 @@ circuit check_enum_literals: connect _cast_to_bits_expr_2, cat(_cast_bundle_to_bits_expr_2.body, _cast_bundle_to_bits_expr_2.tag) connect o2, _cast_to_bits_expr_2 @[module-XXXXXXXXXX.rs 9:1] else: - wire _bundle_literal_expr_3: Ty2 + wire _bundle_literal_expr_3: Ty1 connect _bundle_literal_expr_3.tag, UInt<2>(0h2) wire _array_literal_expr: UInt<1>[3] connect _array_literal_expr[0], bits(i, 0, 0) @@ -651,11 +648,11 @@ circuit check_enum_literals: connect _cast_array_to_bits_expr[2], _array_literal_expr[2] wire _cast_to_bits_expr_3: UInt<3> connect _cast_to_bits_expr_3, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0])) - connect _bundle_literal_expr_3.body, _cast_to_bits_expr_3 - wire _cast_bundle_to_bits_expr_3: Ty2 + connect _bundle_literal_expr_3.body, pad(_cast_to_bits_expr_3, 8) + wire _cast_bundle_to_bits_expr_3: Ty1 connect _cast_bundle_to_bits_expr_3.tag, _bundle_literal_expr_3.tag connect _cast_bundle_to_bits_expr_3.body, _bundle_literal_expr_3.body - wire _cast_to_bits_expr_4: UInt<5> + wire _cast_to_bits_expr_4: UInt<10> connect _cast_to_bits_expr_4, cat(_cast_bundle_to_bits_expr_3.body, _cast_bundle_to_bits_expr_3.tag) connect o2, _cast_to_bits_expr_4 @[module-XXXXXXXXXX.rs 10:1] ", @@ -3245,48 +3242,25 @@ pub enum OneOfThree { #[hdl_module(outline_generated)] pub fn check_enum_connect_any() { #[hdl] - let index: UInt<2> = m.input(); + let swap: Bool = m.input(); #[hdl] - let i0: OneOfThree, HdlOption>, Bool> = m.input(); + let i1: OneOfThree, HdlOption>, HdlOption<()>> = m.input(); #[hdl] - let i1: OneOfThree, HdlOption>, Bool> = m.input(); + let i2: OneOfThree, HdlOption>, HdlOption<()>> = m.input(); #[hdl] - let i2: OneOfThree, HdlOption>, Bool> = m.input(); + let o1: OneOfThree, HdlOption>, HdlOption<()>> = m.output(); #[hdl] - let i3: OneOfThree, HdlOption>, Bool> = m.input(); + let o2: OneOfThree, HdlOption>, HdlOption<()>> = m.output(); #[hdl] - let o0: OneOfThree, HdlOption>, Bool> = m.output(); - #[hdl] - let o1: OneOfThree, HdlOption>, Bool> = m.output(); - #[hdl] - let o2: OneOfThree, HdlOption>, Bool> = m.output(); - #[hdl] - let o3: OneOfThree, HdlOption>, Bool> = m.output(); - #[hdl] - if index.cmp_eq(0u8) { - connect_any(o0, i0); + if swap { + connect_any(o1, i2); + connect_any(o2, i1); + } else { connect_any(o1, i1); connect_any(o2, i2); - connect_any(o3, i3); - } else if index.cmp_eq(1u8) { - connect_any(o0, i1); - connect_any(o1, i2); - connect_any(o2, i3); - connect_any(o3, i0); - } else if index.cmp_eq(2u8) { - connect_any(o0, i2); - connect_any(o1, i3); - connect_any(o2, i0); - connect_any(o3, i1); - } else { - connect_any(o0, i3); - connect_any(o1, i0); - connect_any(o2, i1); - connect_any(o3, i2); } } -#[cfg(todo)] #[test] fn test_enum_connect_any() { let _n = SourceLocation::normalize_files_for_tests(); @@ -3297,44 +3271,24 @@ fn test_enum_connect_any() { m => "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 circuit check_enum_connect_any: - type Ty0 = {|HdlNone, HdlSome: SInt<0>|} - type Ty1 = {|A: UInt<0>, B: Ty0, C: UInt<1>|} - type Ty2 = {|HdlNone, HdlSome: SInt<1>|} - type Ty3 = {|A: UInt<1>, B: Ty2, C: UInt<1>|} + type Ty0 = {|HdlNone, HdlSome: SInt<1>|} + type Ty1 = {} + type Ty2 = {|HdlNone, HdlSome: Ty1|} + type Ty3 = {|A: UInt<1>, B: Ty0, C: Ty2|} type Ty4 = {|HdlNone, HdlSome: SInt<2>|} - type Ty5 = {|A: UInt<2>, B: Ty4, C: UInt<1>|} - type Ty6 = {|HdlNone, HdlSome: SInt<3>|} - type Ty7 = {|A: UInt<3>, B: Ty6, C: UInt<1>|} + type Ty5 = {|A: UInt<2>, B: Ty4, C: Ty2|} module check_enum_connect_any: @[module-XXXXXXXXXX.rs 1:1] - input index: UInt<2> @[module-XXXXXXXXXX.rs 2:1] - input i0: Ty1 @[module-XXXXXXXXXX.rs 3:1] - input i1: Ty3 @[module-XXXXXXXXXX.rs 4:1] - input i2: Ty5 @[module-XXXXXXXXXX.rs 5:1] - input i3: Ty7 @[module-XXXXXXXXXX.rs 6:1] - output o0: Ty1 @[module-XXXXXXXXXX.rs 7:1] - output o1: Ty3 @[module-XXXXXXXXXX.rs 8:1] - output o2: Ty5 @[module-XXXXXXXXXX.rs 9:1] - output o3: Ty7 @[module-XXXXXXXXXX.rs 10:1] - when eq(index, UInt<8>(0h0)): @[module-XXXXXXXXXX.rs 11:1] - connect o0, i0 @[module-XXXXXXXXXX.rs 12:1] - connect o1, i1 @[module-XXXXXXXXXX.rs 13:1] - connect o2, i2 @[module-XXXXXXXXXX.rs 14:1] - connect o3, i3 @[module-XXXXXXXXXX.rs 15:1] - else when eq(index, UInt<8>(0h1)): @[module-XXXXXXXXXX.rs 16:1] - connect o0, i1 @[module-XXXXXXXXXX.rs 17:1] - connect o1, i2 @[module-XXXXXXXXXX.rs 18:1] - connect o2, i3 @[module-XXXXXXXXXX.rs 19:1] - connect o3, i0 @[module-XXXXXXXXXX.rs 20:1] - else when eq(index, UInt<8>(0h2)): @[module-XXXXXXXXXX.rs 21:1] - connect o0, i2 @[module-XXXXXXXXXX.rs 22:1] - connect o1, i3 @[module-XXXXXXXXXX.rs 23:1] - connect o2, i0 @[module-XXXXXXXXXX.rs 24:1] - connect o3, i1 @[module-XXXXXXXXXX.rs 25:1] + input swap: UInt<1> @[module-XXXXXXXXXX.rs 2:1] + input i1: Ty3 @[module-XXXXXXXXXX.rs 3:1] + input i2: Ty5 @[module-XXXXXXXXXX.rs 4:1] + output o1: Ty3 @[module-XXXXXXXXXX.rs 5:1] + output o2: Ty5 @[module-XXXXXXXXXX.rs 6:1] + when swap: @[module-XXXXXXXXXX.rs 7:1] + connect o1, i2 @[module-XXXXXXXXXX.rs 8:1] + connect o2, i1 @[module-XXXXXXXXXX.rs 9:1] else: - connect o0, i3 @[module-XXXXXXXXXX.rs 26:1] - connect o1, i0 @[module-XXXXXXXXXX.rs 27:1] - connect o2, i1 @[module-XXXXXXXXXX.rs 28:1] - connect o3, i2 @[module-XXXXXXXXXX.rs 29:1] + connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] + connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] ", }; // FIXME: simplify_enums is broken when connecting enums that contain @@ -3347,20 +3301,487 @@ circuit check_enum_connect_any: #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => - "/test/check_enum_connect_any.fir": r"TODO", + "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 +circuit check_enum_connect_any: + type Ty0 = {|A, B, C|} + type Ty1 = {tag: Ty0, body: UInt<2>} + type Ty2 = {tag: Ty0, body: UInt<3>} + type Ty3 = {|HdlNone, HdlSome|} + type Ty4 = {tag: Ty3, body: UInt<1>} + type Ty5 = {tag: Ty3, body: UInt<2>} + type Ty6 = {tag: UInt<1>, body: UInt<2>} + type Ty7 = {tag: UInt<1>, body: UInt<1>} + type Ty8 = {tag: Ty3, body: UInt<0>} + type Ty9 = {tag: UInt<1>, body: UInt<0>} + module check_enum_connect_any: @[module-XXXXXXXXXX.rs 1:1] + input swap: UInt<1> @[module-XXXXXXXXXX.rs 2:1] + input i1: Ty1 @[module-XXXXXXXXXX.rs 3:1] + input i2: Ty2 @[module-XXXXXXXXXX.rs 4:1] + output o1: Ty1 @[module-XXXXXXXXXX.rs 5:1] + output o2: Ty2 @[module-XXXXXXXXXX.rs 6:1] + when swap: @[module-XXXXXXXXXX.rs 7:1] + match i2.tag: @[module-XXXXXXXXXX.rs 8:1] + A: + wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + connect __connect_variant_body, bits(i2.body, 1, 0) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr: Ty1 + connect _bundle_literal_expr.tag, {|A, B, C|}(A) + connect _bundle_literal_expr.body, pad(__connect_variant_body, 2) + connect o1, _bundle_literal_expr @[module-XXXXXXXXXX.rs 8:1] + B: + wire __connect_variant_body_1: Ty4 @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr: Ty5 + wire _cast_bits_to_bundle_expr_flattened: Ty6 + connect _cast_bits_to_bundle_expr_flattened.tag, bits(bits(i2.body, 2, 0), 0, 0) + wire _cast_bits_to_enum_expr: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened.tag, 0)): + connect _cast_bits_to_enum_expr, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr.tag, _cast_bits_to_enum_expr + connect _cast_bits_to_bundle_expr_flattened.body, bits(bits(i2.body, 2, 0), 2, 1) + connect _cast_bits_to_bundle_expr.body, _cast_bits_to_bundle_expr_flattened.body + match _cast_bits_to_bundle_expr.tag: @[module-XXXXXXXXXX.rs 8:1] + HdlNone: + wire _bundle_literal_expr_1: Ty4 + connect _bundle_literal_expr_1.tag, {|HdlNone, HdlSome|}(HdlNone) + connect _bundle_literal_expr_1.body, UInt<1>(0h0) + connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1] + HdlSome: + wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr_1: Ty5 + wire _cast_bits_to_bundle_expr_flattened_1: Ty6 + connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0) + wire _cast_bits_to_enum_expr_1: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_1.tag, 0)): + connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_1, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1 + connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1) + connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_2: Ty4 + connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome) + connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2) + connect __connect_variant_body_1, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_3: Ty1 + connect _bundle_literal_expr_3.tag, {|A, B, C|}(B) + wire _cast_bundle_to_bits_expr: Ty7 + wire _cast_enum_to_bits_expr: UInt<1> + match __connect_variant_body_1.tag: + HdlNone: + connect _cast_enum_to_bits_expr, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr, UInt<1>(1) + connect _cast_bundle_to_bits_expr.tag, _cast_enum_to_bits_expr + connect _cast_bundle_to_bits_expr.body, __connect_variant_body_1.body + wire _cast_to_bits_expr: UInt<2> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect _bundle_literal_expr_3.body, _cast_to_bits_expr + connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1] + C: + wire __connect_variant_body_3: Ty8 @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr_2: Ty8 + wire _cast_bits_to_bundle_expr_flattened_2: Ty9 + connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr_2: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_2.tag, 0)): + connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_2, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_enum_expr_2 + connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body + connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_4: Ty1 + connect _bundle_literal_expr_4.tag, {|A, B, C|}(C) + wire _cast_bundle_to_bits_expr_1: Ty9 + wire _cast_enum_to_bits_expr_1: UInt<1> + match __connect_variant_body_3.tag: + HdlNone: + connect _cast_enum_to_bits_expr_1, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_1, UInt<1>(1) + connect _cast_bundle_to_bits_expr_1.tag, _cast_enum_to_bits_expr_1 + connect _cast_bundle_to_bits_expr_1.body, __connect_variant_body_3.body + wire _cast_to_bits_expr_1: UInt<1> + connect _cast_to_bits_expr_1, cat(_cast_bundle_to_bits_expr_1.body, _cast_bundle_to_bits_expr_1.tag) + connect _bundle_literal_expr_4.body, pad(_cast_to_bits_expr_1, 2) + connect o1, _bundle_literal_expr_4 @[module-XXXXXXXXXX.rs 8:1] + match i1.tag: @[module-XXXXXXXXXX.rs 9:1] + A: + wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + connect __connect_variant_body_4, bits(i1.body, 0, 0) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_5: Ty2 + connect _bundle_literal_expr_5.tag, {|A, B, C|}(A) + connect _bundle_literal_expr_5.body, pad(__connect_variant_body_4, 3) + connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1] + B: + wire __connect_variant_body_5: Ty5 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_3: Ty4 + wire _cast_bits_to_bundle_expr_flattened_3: Ty7 + connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0) + wire _cast_bits_to_enum_expr_3: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_3.tag, 0)): + connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_3, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_enum_expr_3 + connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1) + connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body + match _cast_bits_to_bundle_expr_3.tag: @[module-XXXXXXXXXX.rs 9:1] + HdlNone: + wire _bundle_literal_expr_6: Ty5 + connect _bundle_literal_expr_6.tag, {|HdlNone, HdlSome|}(HdlNone) + connect _bundle_literal_expr_6.body, UInt<2>(0h0) + connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1] + HdlSome: + wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_4: Ty4 + wire _cast_bits_to_bundle_expr_flattened_4: Ty7 + connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0) + wire _cast_bits_to_enum_expr_4: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_4.tag, 0)): + connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_4, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_enum_expr_4 + connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1) + connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body + connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_7: Ty5 + connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome) + connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6) + connect __connect_variant_body_5, _bundle_literal_expr_7 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_8: Ty2 + connect _bundle_literal_expr_8.tag, {|A, B, C|}(B) + wire _cast_bundle_to_bits_expr_2: Ty6 + wire _cast_enum_to_bits_expr_2: UInt<1> + match __connect_variant_body_5.tag: + HdlNone: + connect _cast_enum_to_bits_expr_2, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_2, UInt<1>(1) + connect _cast_bundle_to_bits_expr_2.tag, _cast_enum_to_bits_expr_2 + connect _cast_bundle_to_bits_expr_2.body, __connect_variant_body_5.body + wire _cast_to_bits_expr_2: UInt<3> + connect _cast_to_bits_expr_2, cat(_cast_bundle_to_bits_expr_2.body, _cast_bundle_to_bits_expr_2.tag) + connect _bundle_literal_expr_8.body, _cast_to_bits_expr_2 + connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1] + C: + wire __connect_variant_body_7: Ty8 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_5: Ty8 + wire _cast_bits_to_bundle_expr_flattened_5: Ty9 + connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0) + wire _cast_bits_to_enum_expr_5: Ty3 + when eq(UInt<1>(0), tail(_cast_bits_to_bundle_expr_flattened_5.tag, 0)): + connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlNone) + else: + connect _cast_bits_to_enum_expr_5, {|HdlNone, HdlSome|}(HdlSome) + connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_enum_expr_5 + connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body + connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_9: Ty2 + connect _bundle_literal_expr_9.tag, {|A, B, C|}(C) + wire _cast_bundle_to_bits_expr_3: Ty9 + wire _cast_enum_to_bits_expr_3: UInt<1> + match __connect_variant_body_7.tag: + HdlNone: + connect _cast_enum_to_bits_expr_3, UInt<1>(0) + HdlSome: + connect _cast_enum_to_bits_expr_3, UInt<1>(1) + connect _cast_bundle_to_bits_expr_3.tag, _cast_enum_to_bits_expr_3 + connect _cast_bundle_to_bits_expr_3.body, __connect_variant_body_7.body + wire _cast_to_bits_expr_3: UInt<1> + connect _cast_to_bits_expr_3, cat(_cast_bundle_to_bits_expr_3.body, _cast_bundle_to_bits_expr_3.tag) + connect _bundle_literal_expr_9.body, pad(_cast_to_bits_expr_3, 3) + connect o2, _bundle_literal_expr_9 @[module-XXXXXXXXXX.rs 9:1] + else: + connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] + connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] +", }; let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => - "/test/check_enum_connect_any.fir": r"TODO", - }; + "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 +circuit check_enum_connect_any: + type Ty0 = {tag: UInt<2>, body: UInt<2>} + type Ty1 = {tag: UInt<2>, body: UInt<3>} + type Ty2 = {tag: UInt<1>, body: UInt<1>} + type Ty3 = {tag: UInt<1>, body: UInt<2>} + type Ty4 = {tag: UInt<1>, body: UInt<0>} + module check_enum_connect_any: @[module-XXXXXXXXXX.rs 1:1] + input swap: UInt<1> @[module-XXXXXXXXXX.rs 2:1] + input i1: Ty0 @[module-XXXXXXXXXX.rs 3:1] + input i2: Ty1 @[module-XXXXXXXXXX.rs 4:1] + output o1: Ty0 @[module-XXXXXXXXXX.rs 5:1] + output o2: Ty1 @[module-XXXXXXXXXX.rs 6:1] + when swap: @[module-XXXXXXXXXX.rs 7:1] + when eq(i2.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 8:1] + wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + connect __connect_variant_body, bits(i2.body, 1, 0) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr: Ty0 + connect _bundle_literal_expr.tag, UInt<2>(0h0) + connect _bundle_literal_expr.body, pad(__connect_variant_body, 2) + connect o1, _bundle_literal_expr @[module-XXXXXXXXXX.rs 8:1] + else when eq(i2.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 8:1] + wire __connect_variant_body_1: Ty2 @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr: Ty3 + wire _cast_bits_to_bundle_expr_flattened: Ty3 + connect _cast_bits_to_bundle_expr_flattened.tag, bits(bits(i2.body, 2, 0), 0, 0) + connect _cast_bits_to_bundle_expr.tag, _cast_bits_to_bundle_expr_flattened.tag + connect _cast_bits_to_bundle_expr_flattened.body, bits(bits(i2.body, 2, 0), 2, 1) + connect _cast_bits_to_bundle_expr.body, _cast_bits_to_bundle_expr_flattened.body + when eq(_cast_bits_to_bundle_expr.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_1: Ty2 + connect _bundle_literal_expr_1.tag, UInt<1>(0h0) + connect _bundle_literal_expr_1.body, UInt<1>(0h0) + connect __connect_variant_body_1, _bundle_literal_expr_1 @[module-XXXXXXXXXX.rs 8:1] + else: + wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr_1: Ty3 + wire _cast_bits_to_bundle_expr_flattened_1: Ty3 + connect _cast_bits_to_bundle_expr_flattened_1.tag, bits(bits(i2.body, 2, 0), 0, 0) + connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag + connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1) + connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_2: Ty2 + connect _bundle_literal_expr_2.tag, UInt<1>(0h1) + connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2) + connect __connect_variant_body_1, _bundle_literal_expr_2 @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_3: Ty0 + connect _bundle_literal_expr_3.tag, UInt<2>(0h1) + wire _cast_bundle_to_bits_expr: Ty2 + connect _cast_bundle_to_bits_expr.tag, __connect_variant_body_1.tag + connect _cast_bundle_to_bits_expr.body, __connect_variant_body_1.body + wire _cast_to_bits_expr: UInt<2> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect _bundle_literal_expr_3.body, _cast_to_bits_expr + connect o1, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 8:1] + else: + wire __connect_variant_body_3: Ty4 @[module-XXXXXXXXXX.rs 8:1] + wire _cast_bits_to_bundle_expr_2: Ty4 + wire _cast_bits_to_bundle_expr_flattened_2: Ty4 + connect _cast_bits_to_bundle_expr_flattened_2.tag, bits(bits(i2.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr_2.tag, _cast_bits_to_bundle_expr_flattened_2.tag + connect _cast_bits_to_bundle_expr_flattened_2.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_2.body, _cast_bits_to_bundle_expr_flattened_2.body + connect __connect_variant_body_3, _cast_bits_to_bundle_expr_2 @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_4: Ty0 + connect _bundle_literal_expr_4.tag, UInt<2>(0h2) + wire _cast_bundle_to_bits_expr_1: Ty4 + connect _cast_bundle_to_bits_expr_1.tag, __connect_variant_body_3.tag + connect _cast_bundle_to_bits_expr_1.body, __connect_variant_body_3.body + wire _cast_to_bits_expr_1: UInt<1> + connect _cast_to_bits_expr_1, cat(_cast_bundle_to_bits_expr_1.body, _cast_bundle_to_bits_expr_1.tag) + connect _bundle_literal_expr_4.body, pad(_cast_to_bits_expr_1, 2) + connect o1, _bundle_literal_expr_4 @[module-XXXXXXXXXX.rs 8:1] + when eq(i1.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 9:1] + wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + connect __connect_variant_body_4, bits(i1.body, 0, 0) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_5: Ty1 + connect _bundle_literal_expr_5.tag, UInt<2>(0h0) + connect _bundle_literal_expr_5.body, pad(__connect_variant_body_4, 3) + connect o2, _bundle_literal_expr_5 @[module-XXXXXXXXXX.rs 9:1] + else when eq(i1.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1] + wire __connect_variant_body_5: Ty3 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_3: Ty2 + wire _cast_bits_to_bundle_expr_flattened_3: Ty2 + connect _cast_bits_to_bundle_expr_flattened_3.tag, bits(bits(i1.body, 1, 0), 0, 0) + connect _cast_bits_to_bundle_expr_3.tag, _cast_bits_to_bundle_expr_flattened_3.tag + connect _cast_bits_to_bundle_expr_flattened_3.body, bits(bits(i1.body, 1, 0), 1, 1) + connect _cast_bits_to_bundle_expr_3.body, _cast_bits_to_bundle_expr_flattened_3.body + when eq(_cast_bits_to_bundle_expr_3.tag, UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_6: Ty3 + connect _bundle_literal_expr_6.tag, UInt<1>(0h0) + connect _bundle_literal_expr_6.body, UInt<2>(0h0) + connect __connect_variant_body_5, _bundle_literal_expr_6 @[module-XXXXXXXXXX.rs 9:1] + else: + wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_4: Ty2 + wire _cast_bits_to_bundle_expr_flattened_4: Ty2 + connect _cast_bits_to_bundle_expr_flattened_4.tag, bits(bits(i1.body, 1, 0), 0, 0) + connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_flattened_4.tag + connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1) + connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body + connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_7: Ty3 + connect _bundle_literal_expr_7.tag, UInt<1>(0h1) + connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6) + connect __connect_variant_body_5, _bundle_literal_expr_7 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_8: Ty1 + connect _bundle_literal_expr_8.tag, UInt<2>(0h1) + wire _cast_bundle_to_bits_expr_2: Ty3 + connect _cast_bundle_to_bits_expr_2.tag, __connect_variant_body_5.tag + connect _cast_bundle_to_bits_expr_2.body, __connect_variant_body_5.body + wire _cast_to_bits_expr_2: UInt<3> + connect _cast_to_bits_expr_2, cat(_cast_bundle_to_bits_expr_2.body, _cast_bundle_to_bits_expr_2.tag) + connect _bundle_literal_expr_8.body, _cast_to_bits_expr_2 + connect o2, _bundle_literal_expr_8 @[module-XXXXXXXXXX.rs 9:1] + else: + wire __connect_variant_body_7: Ty4 @[module-XXXXXXXXXX.rs 9:1] + wire _cast_bits_to_bundle_expr_5: Ty4 + wire _cast_bits_to_bundle_expr_flattened_5: Ty4 + connect _cast_bits_to_bundle_expr_flattened_5.tag, bits(bits(i1.body, 0, 0), 0, 0) + connect _cast_bits_to_bundle_expr_5.tag, _cast_bits_to_bundle_expr_flattened_5.tag + connect _cast_bits_to_bundle_expr_flattened_5.body, UInt<0>(0) + connect _cast_bits_to_bundle_expr_5.body, _cast_bits_to_bundle_expr_flattened_5.body + connect __connect_variant_body_7, _cast_bits_to_bundle_expr_5 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_9: Ty1 + connect _bundle_literal_expr_9.tag, UInt<2>(0h2) + wire _cast_bundle_to_bits_expr_3: Ty4 + connect _cast_bundle_to_bits_expr_3.tag, __connect_variant_body_7.tag + connect _cast_bundle_to_bits_expr_3.body, __connect_variant_body_7.body + wire _cast_to_bits_expr_3: UInt<1> + connect _cast_to_bits_expr_3, cat(_cast_bundle_to_bits_expr_3.body, _cast_bundle_to_bits_expr_3.tag) + connect _bundle_literal_expr_9.body, pad(_cast_to_bits_expr_3, 3) + connect o2, _bundle_literal_expr_9 @[module-XXXXXXXXXX.rs 9:1] + else: + connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] + connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] +", + } let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => - "/test/check_enum_connect_any.fir": r"TODO", + "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 +circuit check_enum_connect_any: + type Ty0 = {tag: UInt<2>, body: UInt<2>} + type Ty1 = {tag: UInt<1>, body: UInt<1>} + type Ty2 = {tag: UInt<2>, body: UInt<3>} + type Ty3 = {tag: UInt<1>, body: UInt<2>} + module check_enum_connect_any: @[module-XXXXXXXXXX.rs 1:1] + input swap: UInt<1> @[module-XXXXXXXXXX.rs 2:1] + input i1: UInt<4> @[module-XXXXXXXXXX.rs 3:1] + input i2: UInt<5> @[module-XXXXXXXXXX.rs 4:1] + output o1: UInt<4> @[module-XXXXXXXXXX.rs 5:1] + output o2: UInt<5> @[module-XXXXXXXXXX.rs 6:1] + when swap: @[module-XXXXXXXXXX.rs 7:1] + when eq(bits(i2, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 8:1] + wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + connect __connect_variant_body, bits(bits(i2, 4, 2), 1, 0) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr: Ty0 + connect _bundle_literal_expr.tag, UInt<2>(0h0) + connect _bundle_literal_expr.body, pad(__connect_variant_body, 2) + wire _cast_bundle_to_bits_expr: Ty0 + connect _cast_bundle_to_bits_expr.tag, _bundle_literal_expr.tag + connect _cast_bundle_to_bits_expr.body, _bundle_literal_expr.body + wire _cast_to_bits_expr: UInt<4> + connect _cast_to_bits_expr, cat(_cast_bundle_to_bits_expr.body, _cast_bundle_to_bits_expr.tag) + connect o1, _cast_to_bits_expr @[module-XXXXXXXXXX.rs 8:1] + else when eq(bits(i2, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 8:1] + wire __connect_variant_body_1: UInt<2> @[module-XXXXXXXXXX.rs 8:1] + when eq(bits(bits(bits(i2, 4, 2), 2, 0), 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_1: Ty1 + connect _bundle_literal_expr_1.tag, UInt<1>(0h0) + connect _bundle_literal_expr_1.body, UInt<1>(0h0) + wire _cast_bundle_to_bits_expr_1: Ty1 + connect _cast_bundle_to_bits_expr_1.tag, _bundle_literal_expr_1.tag + connect _cast_bundle_to_bits_expr_1.body, _bundle_literal_expr_1.body + wire _cast_to_bits_expr_1: UInt<2> + connect _cast_to_bits_expr_1, cat(_cast_bundle_to_bits_expr_1.body, _cast_bundle_to_bits_expr_1.tag) + connect __connect_variant_body_1, _cast_to_bits_expr_1 @[module-XXXXXXXXXX.rs 8:1] + else: + wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] + connect __connect_variant_body_2, asSInt(bits(bits(bits(bits(i2, 4, 2), 2, 0), 2, 1), 1, 0)) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_2: Ty1 + connect _bundle_literal_expr_2.tag, UInt<1>(0h1) + connect _bundle_literal_expr_2.body, asUInt(__connect_variant_body_2) + wire _cast_bundle_to_bits_expr_2: Ty1 + connect _cast_bundle_to_bits_expr_2.tag, _bundle_literal_expr_2.tag + connect _cast_bundle_to_bits_expr_2.body, _bundle_literal_expr_2.body + wire _cast_to_bits_expr_2: UInt<2> + connect _cast_to_bits_expr_2, cat(_cast_bundle_to_bits_expr_2.body, _cast_bundle_to_bits_expr_2.tag) + connect __connect_variant_body_1, _cast_to_bits_expr_2 @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_3: Ty0 + connect _bundle_literal_expr_3.tag, UInt<2>(0h1) + connect _bundle_literal_expr_3.body, __connect_variant_body_1 + wire _cast_bundle_to_bits_expr_3: Ty0 + connect _cast_bundle_to_bits_expr_3.tag, _bundle_literal_expr_3.tag + connect _cast_bundle_to_bits_expr_3.body, _bundle_literal_expr_3.body + wire _cast_to_bits_expr_3: UInt<4> + connect _cast_to_bits_expr_3, cat(_cast_bundle_to_bits_expr_3.body, _cast_bundle_to_bits_expr_3.tag) + connect o1, _cast_to_bits_expr_3 @[module-XXXXXXXXXX.rs 8:1] + else: + wire __connect_variant_body_3: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + connect __connect_variant_body_3, bits(bits(i2, 4, 2), 0, 0) @[module-XXXXXXXXXX.rs 8:1] + wire _bundle_literal_expr_4: Ty0 + connect _bundle_literal_expr_4.tag, UInt<2>(0h2) + connect _bundle_literal_expr_4.body, pad(__connect_variant_body_3, 2) + wire _cast_bundle_to_bits_expr_4: Ty0 + connect _cast_bundle_to_bits_expr_4.tag, _bundle_literal_expr_4.tag + connect _cast_bundle_to_bits_expr_4.body, _bundle_literal_expr_4.body + wire _cast_to_bits_expr_4: UInt<4> + connect _cast_to_bits_expr_4, cat(_cast_bundle_to_bits_expr_4.body, _cast_bundle_to_bits_expr_4.tag) + connect o1, _cast_to_bits_expr_4 @[module-XXXXXXXXXX.rs 8:1] + when eq(bits(i1, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 9:1] + wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + connect __connect_variant_body_4, bits(bits(i1, 3, 2), 0, 0) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_5: Ty2 + connect _bundle_literal_expr_5.tag, UInt<2>(0h0) + connect _bundle_literal_expr_5.body, pad(__connect_variant_body_4, 3) + wire _cast_bundle_to_bits_expr_5: Ty2 + connect _cast_bundle_to_bits_expr_5.tag, _bundle_literal_expr_5.tag + connect _cast_bundle_to_bits_expr_5.body, _bundle_literal_expr_5.body + wire _cast_to_bits_expr_5: UInt<5> + connect _cast_to_bits_expr_5, cat(_cast_bundle_to_bits_expr_5.body, _cast_bundle_to_bits_expr_5.tag) + connect o2, _cast_to_bits_expr_5 @[module-XXXXXXXXXX.rs 9:1] + else when eq(bits(i1, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 9:1] + wire __connect_variant_body_5: UInt<3> @[module-XXXXXXXXXX.rs 9:1] + when eq(bits(bits(bits(i1, 3, 2), 1, 0), 0, 0), UInt<1>(0h0)): @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_6: Ty3 + connect _bundle_literal_expr_6.tag, UInt<1>(0h0) + connect _bundle_literal_expr_6.body, UInt<2>(0h0) + wire _cast_bundle_to_bits_expr_6: Ty3 + connect _cast_bundle_to_bits_expr_6.tag, _bundle_literal_expr_6.tag + connect _cast_bundle_to_bits_expr_6.body, _bundle_literal_expr_6.body + wire _cast_to_bits_expr_6: UInt<3> + connect _cast_to_bits_expr_6, cat(_cast_bundle_to_bits_expr_6.body, _cast_bundle_to_bits_expr_6.tag) + connect __connect_variant_body_5, _cast_to_bits_expr_6 @[module-XXXXXXXXXX.rs 9:1] + else: + wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] + connect __connect_variant_body_6, asSInt(bits(bits(bits(bits(i1, 3, 2), 1, 0), 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_7: Ty3 + connect _bundle_literal_expr_7.tag, UInt<1>(0h1) + connect _bundle_literal_expr_7.body, asUInt(__connect_variant_body_6) + wire _cast_bundle_to_bits_expr_7: Ty3 + connect _cast_bundle_to_bits_expr_7.tag, _bundle_literal_expr_7.tag + connect _cast_bundle_to_bits_expr_7.body, _bundle_literal_expr_7.body + wire _cast_to_bits_expr_7: UInt<3> + connect _cast_to_bits_expr_7, cat(_cast_bundle_to_bits_expr_7.body, _cast_bundle_to_bits_expr_7.tag) + connect __connect_variant_body_5, _cast_to_bits_expr_7 @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_8: Ty2 + connect _bundle_literal_expr_8.tag, UInt<2>(0h1) + connect _bundle_literal_expr_8.body, __connect_variant_body_5 + wire _cast_bundle_to_bits_expr_8: Ty2 + connect _cast_bundle_to_bits_expr_8.tag, _bundle_literal_expr_8.tag + connect _cast_bundle_to_bits_expr_8.body, _bundle_literal_expr_8.body + wire _cast_to_bits_expr_8: UInt<5> + connect _cast_to_bits_expr_8, cat(_cast_bundle_to_bits_expr_8.body, _cast_bundle_to_bits_expr_8.tag) + connect o2, _cast_to_bits_expr_8 @[module-XXXXXXXXXX.rs 9:1] + else: + wire __connect_variant_body_7: UInt<1> @[module-XXXXXXXXXX.rs 9:1] + connect __connect_variant_body_7, bits(bits(i1, 3, 2), 0, 0) @[module-XXXXXXXXXX.rs 9:1] + wire _bundle_literal_expr_9: Ty2 + connect _bundle_literal_expr_9.tag, UInt<2>(0h2) + connect _bundle_literal_expr_9.body, pad(__connect_variant_body_7, 3) + wire _cast_bundle_to_bits_expr_9: Ty2 + connect _cast_bundle_to_bits_expr_9.tag, _bundle_literal_expr_9.tag + connect _cast_bundle_to_bits_expr_9.body, _bundle_literal_expr_9.body + wire _cast_to_bits_expr_9: UInt<5> + connect _cast_to_bits_expr_9, cat(_cast_bundle_to_bits_expr_9.body, _cast_bundle_to_bits_expr_9.tag) + connect o2, _cast_to_bits_expr_9 @[module-XXXXXXXXXX.rs 9:1] + else: + connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] + connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] +", }; } From edcea1adc3304d72766559790f070c8dd7eb099e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 30 Sep 2024 22:33:27 -0700 Subject: [PATCH 027/190] add firrtl comments when connecting expressions with different types --- crates/fayalite/src/firrtl.rs | 9 +++ crates/fayalite/tests/module.rs | 102 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 4a1547d..79db858 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -1956,6 +1956,15 @@ impl<'a> Exporter<'a> { rhs, source_location, }) => { + if Expr::ty(lhs) != Expr::ty(rhs) { + writeln!( + body, + "{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}", + Expr::ty(lhs), + Expr::ty(rhs), + ) + .unwrap(); + } let lhs = self.expr(lhs, &definitions, false); let rhs = self.expr(rhs, &definitions, false); writeln!( diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 9d5a898..4dfeca5 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -722,6 +722,9 @@ circuit check_struct_enum_match: A: connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] B(_match_arm_value): + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<9> connect o[0], add(_match_arm_value, UInt<8>(0h2)) @[module-XXXXXXXXXX.rs 8:1] C(_match_arm_value_1): connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] @@ -738,6 +741,9 @@ circuit check_struct_enum_match: B(_match_arm_value_4): connect o[2], _match_arm_value_4 @[module-XXXXXXXXXX.rs 15:1] C(_match_arm_value_5): + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[2], _match_arm_value_5[1] @[module-XXXXXXXXXX.rs 16:1] match i2: @[module-XXXXXXXXXX.rs 17:1] A: @@ -752,6 +758,9 @@ circuit check_struct_enum_match: B(_match_arm_value_8): connect o[4], UInt<8>(0h1) @[module-XXXXXXXXXX.rs 23:1] C(_match_arm_value_9): + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[4], _match_arm_value_9[2] @[module-XXXXXXXXXX.rs 24:1] ", }; @@ -777,6 +786,9 @@ circuit check_struct_enum_match: A: connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] B: + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<9> connect o[0], add(bits(i2.body, 7, 0), UInt<8>(0h2)) @[module-XXXXXXXXXX.rs 8:1] C: connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] @@ -801,6 +813,9 @@ circuit check_struct_enum_match: connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] connect _cast_bits_to_array_expr_flattened[2], bits(bits(i2.body, 2, 0), 2, 2) connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[2], _cast_bits_to_array_expr[1] @[module-XXXXXXXXXX.rs 16:1] match i2.tag: @[module-XXXXXXXXXX.rs 17:1] A: @@ -823,6 +838,9 @@ circuit check_struct_enum_match: 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(i2.body, 2, 0), 2, 2) connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[4], _cast_bits_to_array_expr_1[2] @[module-XXXXXXXXXX.rs 24:1] ", }; @@ -843,6 +861,9 @@ circuit check_struct_enum_match: when eq(i2.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 6:1] connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] else when eq(i2.tag, UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 6:1] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<9> connect o[0], add(bits(i2.body, 7, 0), UInt<8>(0h2)) @[module-XXXXXXXXXX.rs 8:1] else: connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] @@ -865,6 +886,9 @@ circuit check_struct_enum_match: connect _cast_bits_to_array_expr[1], _cast_bits_to_array_expr_flattened[1] connect _cast_bits_to_array_expr_flattened[2], bits(bits(i2.body, 2, 0), 2, 2) connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[2], _cast_bits_to_array_expr[1] @[module-XXXXXXXXXX.rs 16:1] when eq(i2.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 17:1] connect o[3], UInt<8>(0h0) @[module-XXXXXXXXXX.rs 18:1] @@ -885,6 +909,9 @@ circuit check_struct_enum_match: 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(i2.body, 2, 0), 2, 2) connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[4], _cast_bits_to_array_expr_1[2] @[module-XXXXXXXXXX.rs 24:1] ", }; @@ -903,6 +930,9 @@ circuit check_struct_enum_match: when eq(bits(i2, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 6:1] connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] else when eq(bits(i2, 1, 0), UInt<2>(0h1)): @[module-XXXXXXXXXX.rs 6:1] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<9> connect o[0], add(bits(bits(i2, 9, 2), 7, 0), UInt<8>(0h2)) @[module-XXXXXXXXXX.rs 8:1] else: connect o[0], UInt<8>(0h17) @[module-XXXXXXXXXX.rs 7:1] @@ -925,6 +955,9 @@ circuit check_struct_enum_match: 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(i2, 9, 2), 2, 0), 2, 2) connect _cast_bits_to_array_expr[2], _cast_bits_to_array_expr_flattened[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[2], _cast_bits_to_array_expr[1] @[module-XXXXXXXXXX.rs 16:1] when eq(bits(i2, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 17:1] connect o[3], UInt<8>(0h0) @[module-XXXXXXXXXX.rs 18:1] @@ -945,6 +978,9 @@ circuit check_struct_enum_match: 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(i2, 9, 2), 2, 0), 2, 2) connect _cast_bits_to_array_expr_1[2], _cast_bits_to_array_expr_flattened_1[2] + ; connect different types: + ; lhs: UInt<8> + ; rhs: UInt<1> connect o[4], _cast_bits_to_array_expr_1[2] @[module-XXXXXXXXXX.rs 24:1] ", }; @@ -2454,10 +2490,16 @@ circuit check_memory_of_enum: %[[ connect mem_1.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] connect mem_1.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] connect mem_1.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect mem_r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] connect rdata, mem_r0.data @[module-XXXXXXXXXX.rs 13:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] connect mem_w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] @@ -2578,10 +2620,16 @@ circuit check_memory_of_enum: %[[ connect mem_w1_1.data, mem_w1.data @[module-XXXXXXXXXX.rs 14:1] connect mem_w1_1.mask.tag, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] connect mem_w1_1.mask.body, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect mem_r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] connect rdata, mem_r0.data @[module-XXXXXXXXXX.rs 13:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] connect mem_w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] @@ -2702,10 +2750,16 @@ circuit check_memory_of_enum: %[[ connect mem_w1_1.data, mem_w1.data @[module-XXXXXXXXXX.rs 14:1] connect mem_w1_1.mask.tag, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] connect mem_w1_1.mask.body, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect mem_r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] connect rdata, mem_r0.data @[module-XXXXXXXXXX.rs 13:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect mem_w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] connect mem_w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] @@ -2778,10 +2832,16 @@ circuit check_memory_of_enum: %[[ read-under-write => old reader => r0 writer => w1 + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect `mem`.r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect `mem`.r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect `mem`.r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] connect rdata, `mem`.r0.data @[module-XXXXXXXXXX.rs 13:1] + ; connect different types: + ; lhs: UInt<4> + ; rhs: UInt<8> connect `mem`.w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] connect `mem`.w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] connect `mem`.w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] @@ -3284,7 +3344,13 @@ circuit check_enum_connect_any: output o1: Ty3 @[module-XXXXXXXXXX.rs 5:1] output o2: Ty5 @[module-XXXXXXXXXX.rs 6:1] when swap: @[module-XXXXXXXXXX.rs 7:1] + ; connect different types: + ; lhs: Enum {A(UInt<1>), B(Enum {HdlNone, HdlSome(SInt<1>)}), C(Enum {HdlNone, HdlSome(Bundle {})})} + ; rhs: Enum {A(UInt<2>), B(Enum {HdlNone, HdlSome(SInt<2>)}), C(Enum {HdlNone, HdlSome(Bundle {})})} connect o1, i2 @[module-XXXXXXXXXX.rs 8:1] + ; connect different types: + ; lhs: Enum {A(UInt<2>), B(Enum {HdlNone, HdlSome(SInt<2>)}), C(Enum {HdlNone, HdlSome(Bundle {})})} + ; rhs: Enum {A(UInt<1>), B(Enum {HdlNone, HdlSome(SInt<1>)}), C(Enum {HdlNone, HdlSome(Bundle {})})} connect o2, i1 @[module-XXXXXXXXXX.rs 9:1] else: connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] @@ -3323,6 +3389,9 @@ circuit check_enum_connect_any: match i2.tag: @[module-XXXXXXXXXX.rs 8:1] A: wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + ; connect different types: + ; lhs: UInt<1> + ; rhs: UInt<2> connect __connect_variant_body, bits(i2.body, 1, 0) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr: Ty1 connect _bundle_literal_expr.tag, {|A, B, C|}(A) @@ -3360,6 +3429,9 @@ circuit check_enum_connect_any: connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_enum_expr_1 connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1) connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + ; connect different types: + ; lhs: SInt<1> + ; rhs: SInt<2> connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr_2: Ty4 connect _bundle_literal_expr_2.tag, {|HdlNone, HdlSome|}(HdlSome) @@ -3412,6 +3484,9 @@ circuit check_enum_connect_any: match i1.tag: @[module-XXXXXXXXXX.rs 9:1] A: wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + ; connect different types: + ; lhs: UInt<2> + ; rhs: UInt<1> connect __connect_variant_body_4, bits(i1.body, 0, 0) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_5: Ty2 connect _bundle_literal_expr_5.tag, {|A, B, C|}(A) @@ -3449,6 +3524,9 @@ circuit check_enum_connect_any: connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_enum_expr_4 connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1) connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body + ; connect different types: + ; lhs: SInt<2> + ; rhs: SInt<1> connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_7: Ty5 connect _bundle_literal_expr_7.tag, {|HdlNone, HdlSome|}(HdlSome) @@ -3524,6 +3602,9 @@ circuit check_enum_connect_any: when swap: @[module-XXXXXXXXXX.rs 7:1] when eq(i2.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + ; connect different types: + ; lhs: UInt<1> + ; rhs: UInt<2> connect __connect_variant_body, bits(i2.body, 1, 0) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr: Ty0 connect _bundle_literal_expr.tag, UInt<2>(0h0) @@ -3550,6 +3631,9 @@ circuit check_enum_connect_any: connect _cast_bits_to_bundle_expr_1.tag, _cast_bits_to_bundle_expr_flattened_1.tag connect _cast_bits_to_bundle_expr_flattened_1.body, bits(bits(i2.body, 2, 0), 2, 1) connect _cast_bits_to_bundle_expr_1.body, _cast_bits_to_bundle_expr_flattened_1.body + ; connect different types: + ; lhs: SInt<1> + ; rhs: SInt<2> connect __connect_variant_body_2, asSInt(bits(_cast_bits_to_bundle_expr_1.body, 1, 0)) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr_2: Ty2 connect _bundle_literal_expr_2.tag, UInt<1>(0h1) @@ -3584,6 +3668,9 @@ circuit check_enum_connect_any: connect o1, _bundle_literal_expr_4 @[module-XXXXXXXXXX.rs 8:1] when eq(i1.tag, UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + ; connect different types: + ; lhs: UInt<2> + ; rhs: UInt<1> connect __connect_variant_body_4, bits(i1.body, 0, 0) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_5: Ty1 connect _bundle_literal_expr_5.tag, UInt<2>(0h0) @@ -3610,6 +3697,9 @@ circuit check_enum_connect_any: connect _cast_bits_to_bundle_expr_4.tag, _cast_bits_to_bundle_expr_flattened_4.tag connect _cast_bits_to_bundle_expr_flattened_4.body, bits(bits(i1.body, 1, 0), 1, 1) connect _cast_bits_to_bundle_expr_4.body, _cast_bits_to_bundle_expr_flattened_4.body + ; connect different types: + ; lhs: SInt<2> + ; rhs: SInt<1> connect __connect_variant_body_6, asSInt(bits(_cast_bits_to_bundle_expr_4.body, 0, 0)) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_7: Ty3 connect _bundle_literal_expr_7.tag, UInt<1>(0h1) @@ -3667,6 +3757,9 @@ circuit check_enum_connect_any: when swap: @[module-XXXXXXXXXX.rs 7:1] when eq(bits(i2, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 8:1] wire __connect_variant_body: UInt<1> @[module-XXXXXXXXXX.rs 8:1] + ; connect different types: + ; lhs: UInt<1> + ; rhs: UInt<2> connect __connect_variant_body, bits(bits(i2, 4, 2), 1, 0) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr: Ty0 connect _bundle_literal_expr.tag, UInt<2>(0h0) @@ -3691,6 +3784,9 @@ circuit check_enum_connect_any: connect __connect_variant_body_1, _cast_to_bits_expr_1 @[module-XXXXXXXXXX.rs 8:1] else: wire __connect_variant_body_2: SInt<1> @[module-XXXXXXXXXX.rs 8:1] + ; connect different types: + ; lhs: SInt<1> + ; rhs: SInt<2> connect __connect_variant_body_2, asSInt(bits(bits(bits(bits(i2, 4, 2), 2, 0), 2, 1), 1, 0)) @[module-XXXXXXXXXX.rs 8:1] wire _bundle_literal_expr_2: Ty1 connect _bundle_literal_expr_2.tag, UInt<1>(0h1) @@ -3724,6 +3820,9 @@ circuit check_enum_connect_any: connect o1, _cast_to_bits_expr_4 @[module-XXXXXXXXXX.rs 8:1] when eq(bits(i1, 1, 0), UInt<2>(0h0)): @[module-XXXXXXXXXX.rs 9:1] wire __connect_variant_body_4: UInt<2> @[module-XXXXXXXXXX.rs 9:1] + ; connect different types: + ; lhs: UInt<2> + ; rhs: UInt<1> connect __connect_variant_body_4, bits(bits(i1, 3, 2), 0, 0) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_5: Ty2 connect _bundle_literal_expr_5.tag, UInt<2>(0h0) @@ -3748,6 +3847,9 @@ circuit check_enum_connect_any: connect __connect_variant_body_5, _cast_to_bits_expr_6 @[module-XXXXXXXXXX.rs 9:1] else: wire __connect_variant_body_6: SInt<2> @[module-XXXXXXXXXX.rs 9:1] + ; connect different types: + ; lhs: SInt<2> + ; rhs: SInt<1> connect __connect_variant_body_6, asSInt(bits(bits(bits(bits(i1, 3, 2), 1, 0), 1, 1), 0, 0)) @[module-XXXXXXXXXX.rs 9:1] wire _bundle_literal_expr_7: Ty3 connect _bundle_literal_expr_7.tag, UInt<1>(0h1) From 186488a82e7ff45e638b87bcd8c1510043b5cbbf Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 30 Sep 2024 23:31:45 -0700 Subject: [PATCH 028/190] remove FIXME now that simplify_enums is fixed --- crates/fayalite/tests/module.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 4dfeca5..9927d69 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3357,10 +3357,6 @@ circuit check_enum_connect_any: connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] ", }; - // FIXME: simplify_enums is broken when connecting enums that contain - // UInt/SInt where their widths don't match. it should recurse into the - // enum bodies so it can properly sign/zero-extend or truncate the - // contained UInt/SInt fields. let orig_m = m.canonical().intern(); let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); dbg!(m); From 9d66fcc54893388cc21543717ad634432eb1db6b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 00:05:39 -0700 Subject: [PATCH 029/190] improve ExportOptions support in `assert_export_firrtl!` --- crates/fayalite/src/firrtl.rs | 174 ++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 19 deletions(-) diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 79db858..e1c9c5e 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2378,15 +2378,17 @@ impl FileBackendTrait for FileBackend { } #[doc(hidden)] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone, Copy)] pub struct TestBackendPrivate { pub module_var_name: &'static str, + pub included_fields: &'static [&'static str], } impl Default for TestBackendPrivate { fn default() -> Self { Self { module_var_name: "m", + included_fields: &[], } } } @@ -2407,7 +2409,11 @@ impl fmt::Debug for TestBackend { files, error_after, options, - __private: TestBackendPrivate { module_var_name }, + __private: + TestBackendPrivate { + module_var_name, + included_fields, + }, } = self; writeln!( f, @@ -2415,14 +2421,43 @@ impl fmt::Debug for TestBackend { )?; writeln!(f, " assert_export_firrtl! {{")?; writeln!(f, " {module_var_name} =>")?; - for (file, content) in files { - writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?; - } - if *error_after != Option::default() { + if *error_after != Option::default() || included_fields.contains(&"error_after") { writeln!(f, " error_after: {error_after:?},")?; } - if *options != ExportOptions::default() { - writeln!(f, " options: {options:?},")?; + if *options != ExportOptions::default() || included_fields.contains(&"options") { + struct DebugWithForceIncludeFields<'a> { + options: ExportOptions, + included_fields: &'a [&'a str], + } + impl fmt::Debug for DebugWithForceIncludeFields<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.options.debug_fmt(f, |field| { + self.included_fields.iter().any(|included_field| { + if let Some(("options", suffix)) = included_field.split_once(".") { + suffix == field + } else { + false + } + }) + }) + } + } + let options_str = format!( + "{:#?}", + DebugWithForceIncludeFields { + options: *options, + included_fields + } + ); + let mut sep = " options: "; + for line in options_str.lines() { + write!(f, "{sep}{line}")?; + sep = "\n "; + } + writeln!(f, ",")?; + } + for (file, content) in files { + writeln!(f, " {file:?}: {:?},", DebugAsRawString(content))?; } write!(f, " }};") } @@ -2507,6 +2542,7 @@ fn export_impl( let ExportOptions { simplify_memories: do_simplify_memories, simplify_enums: do_simplify_enums, + __private: _, } = options; if let Some(kind) = do_simplify_enums { top_module = @@ -2573,30 +2609,71 @@ impl clap::builder::TypedValueParser for OptionSimplifyEnumsKindValueParser { } } +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ExportOptionsPrivate(()); + #[derive(clap::Parser, Copy, Clone, PartialEq, Eq, Hash)] -#[non_exhaustive] pub struct ExportOptions { #[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)] pub simplify_memories: bool, #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = OptionSimplifyEnumsKindValueParser::NONE_NAME)] pub simplify_enums: std::option::Option, + #[doc(hidden)] + #[clap(skip = ExportOptionsPrivate(()))] + /// `#[non_exhaustive]` except allowing struct update syntax + pub __private: ExportOptionsPrivate, } impl fmt::Debug for ExportOptions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt(f, |_| false) + } +} + +impl ExportOptions { + fn debug_fmt( + &self, + f: &mut fmt::Formatter<'_>, + force_include_field: impl Fn(&str) -> bool, + ) -> fmt::Result { + let Self { + simplify_memories, + simplify_enums, + __private: _, + } = *self; f.write_str("ExportOptions {")?; - if f.alternate() { - f.write_str("\n")?; - } let mut sep = if f.alternate() { "\n " } else { " " }; let comma_sep = if f.alternate() { ",\n " } else { ", " }; let default = ExportOptions::default(); - if self.simplify_memories != default.simplify_memories { - write!(f, "{sep}simplify_memories: {:?}", self.simplify_memories)?; + if simplify_memories != default.simplify_memories + || force_include_field("simplify_memories") + { + write!(f, "{sep}simplify_memories: {:?}", simplify_memories)?; sep = comma_sep; } - if self.simplify_enums != default.simplify_enums { - write!(f, "{sep}simplify_enums: {:?}", self.simplify_enums)?; + if simplify_enums != default.simplify_enums || force_include_field("simplify_enums") { + write!(f, "{sep}simplify_enums: ")?; + macro_rules! debug_cases { + ($($ident:ident $(($($args:tt)*))?,)*) => { + match simplify_enums { + // use more complex stringify to avoid the compiler inserting spaces + $($ident $(($($args)*))? => { + f.write_str(concat!( + stringify!($ident), + $("(", + $(stringify!($args),)* + ")")? + ))?; + })* + } + }; + } + debug_cases! { + Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody), + Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + Some(SimplifyEnumsKind::ReplaceWithUInt), + None, + } sep = comma_sep; } write!( @@ -2612,6 +2689,7 @@ impl Default for ExportOptions { Self { simplify_memories: true, simplify_enums: None, + __private: ExportOptionsPrivate(()), } } } @@ -2630,7 +2708,17 @@ pub fn export( #[doc(hidden)] #[track_caller] pub fn assert_export_firrtl_impl(top_module: &Module, expected: TestBackend) { - let result = export(TestBackend::default(), top_module, expected.options).unwrap(); + let result = export( + TestBackend { + files: BTreeMap::default(), + error_after: expected.error_after, + options: expected.options, + __private: expected.__private, + }, + top_module, + expected.options, + ) + .unwrap(); if result != expected { panic!( "assert_export_firrtl failed:\nyou can update the expected output by using:\n-------START-------\n{result:?}\n-------END-------" @@ -2647,21 +2735,69 @@ pub fn make_test_expected_files(v: &[(&str, &str)]) -> BTreeMap macro_rules! assert_export_firrtl { { $m:ident => - $($file_name:literal: $file_contents:literal,)* $($field:ident: $value:expr,)* + @parsed_fields($($field_strings:expr,)*) + $($file_name:literal: $file_contents:literal,)* } => { $crate::firrtl::assert_export_firrtl_impl( &$m, $crate::firrtl::TestBackend { + $($field: $value,)* files: $crate::firrtl::make_test_expected_files(&[ $(($file_name, $file_contents),)* ]), - $($field: $value,)* __private: $crate::firrtl::TestBackendPrivate { module_var_name: stringify!($m), + included_fields: &[$($field_strings,)*], }, ..<$crate::firrtl::TestBackend as $crate::__std::default::Default>::default() }, ); }; + { + $m:ident => + $($parsed_fields:ident: $parsed_field_values:expr,)* + @parsed_fields($($field_strings:expr,)*) + options: ExportOptions { + $($export_option_fields:ident: $parsed_export_option_field_values:expr,)* + ..$export_option_default:expr + }, + $($rest:tt)* + } => { + $crate::assert_export_firrtl!( + $m => + $($parsed_fields: $parsed_field_values,)* + options: ExportOptions { + $($export_option_fields: $parsed_export_option_field_values,)* + ..$export_option_default + }, + @parsed_fields($($field_strings,)* "options", $(concat!("options.", stringify!($export_option_fields)),)*) + $($rest)* + ); + }; + { + $m:ident => + $($parsed_fields:ident: $parsed_field_values:expr,)* + @parsed_fields($($field_strings:expr,)*) + $field:ident: $field_value:expr, + $($rest:tt)* + } => { + $crate::assert_export_firrtl!( + $m => + $($parsed_fields: $parsed_field_values,)* + $field: $field_value, + @parsed_fields($($field_strings,)* stringify!($field),) + $($rest)* + ); + }; + { + $m:ident => + $($rest:tt)* + } => { + $crate::assert_export_firrtl!( + $m => + @parsed_fields() + $($rest)* + ); + }; } From d089095667d767258a17abcd5c0356f341d74696 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 00:07:48 -0700 Subject: [PATCH 030/190] change default to --simplify-enums=replace-with-bundle-of-uints --- crates/fayalite/src/firrtl.rs | 4 +- crates/fayalite/tests/module.rs | 112 ++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index e1c9c5e..13f211f 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2616,7 +2616,7 @@ pub struct ExportOptionsPrivate(()); pub struct ExportOptions { #[clap(long = "no-simplify-memories", action = clap::ArgAction::SetFalse)] pub simplify_memories: bool, - #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = OptionSimplifyEnumsKindValueParser::NONE_NAME)] + #[clap(long, value_parser = OptionSimplifyEnumsKindValueParser, default_value = "replace-with-bundle-of-uints")] pub simplify_enums: std::option::Option, #[doc(hidden)] #[clap(skip = ExportOptionsPrivate(()))] @@ -2688,7 +2688,7 @@ impl Default for ExportOptions { fn default() -> Self { Self { simplify_memories: true, - simplify_enums: None, + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), __private: ExportOptionsPrivate(()), } } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 9927d69..b4998da 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,11 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - annotations::CustomFirrtlAnnotation, - assert_export_firrtl, - intern::Intern, - module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind}, - prelude::*, + annotations::CustomFirrtlAnnotation, assert_export_firrtl, firrtl::ExportOptions, + intern::Intern, module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, ty::StaticType, }; use serde_json::json; @@ -86,6 +83,10 @@ fn test_mymodule() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/my_module.fir": r"FIRRTL version 3.2.0 circuit my_module: type Ty0 = {`0`: UInt<32>, `1`: SInt<5>} @@ -481,6 +482,10 @@ fn test_enum_literals() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_enum_literals.fir": r"FIRRTL version 3.2.0 circuit check_enum_literals: type Ty0 = {|HdlNone, HdlSome: UInt<8>|} @@ -502,12 +507,13 @@ circuit check_enum_literals: connect o2, {|A, B: UInt<8>, C: UInt<1>[3]|}(C, _array_literal_expr) @[module-XXXXXXXXXX.rs 10:1] ", }; - let orig_m = m.canonical().intern(); - let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody), + ..ExportOptions::default() + }, "/test/check_enum_literals.fir": r"FIRRTL version 3.2.0 circuit check_enum_literals: type Ty0 = {|HdlNone, HdlSome|} @@ -549,11 +555,13 @@ circuit check_enum_literals: connect o2, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 10:1] ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + ..ExportOptions::default() + }, "/test/check_enum_literals.fir": r"FIRRTL version 3.2.0 circuit check_enum_literals: type Ty0 = {tag: UInt<1>, body: UInt<8>} @@ -593,11 +601,13 @@ circuit check_enum_literals: connect o2, _bundle_literal_expr_3 @[module-XXXXXXXXXX.rs 10:1] ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt), + ..ExportOptions::default() + }, "/test/check_enum_literals.fir": r"FIRRTL version 3.2.0 circuit check_enum_literals: type Ty0 = {tag: UInt<1>, body: UInt<8>} @@ -708,6 +718,10 @@ fn test_struct_enum_match() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_struct_enum_match.fir": r"FIRRTL version 3.2.0 circuit check_struct_enum_match: type Ty0 = {|HdlNone, HdlSome: UInt<8>|} @@ -764,12 +778,13 @@ circuit check_struct_enum_match: connect o[4], _match_arm_value_9[2] @[module-XXXXXXXXXX.rs 24:1] ", }; - let orig_m = m.canonical().intern(); - let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody), + ..ExportOptions::default() + }, "/test/check_struct_enum_match.fir": r"FIRRTL version 3.2.0 circuit check_struct_enum_match: type Ty0 = {|HdlNone, HdlSome|} @@ -844,11 +859,13 @@ circuit check_struct_enum_match: connect o[4], _cast_bits_to_array_expr_1[2] @[module-XXXXXXXXXX.rs 24:1] ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + ..ExportOptions::default() + }, "/test/check_struct_enum_match.fir": r"FIRRTL version 3.2.0 circuit check_struct_enum_match: type Ty0 = {tag: UInt<1>, body: UInt<8>} @@ -915,11 +932,13 @@ circuit check_struct_enum_match: connect o[4], _cast_bits_to_array_expr_1[2] @[module-XXXXXXXXXX.rs 24:1] ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt), + ..ExportOptions::default() + }, "/test/check_struct_enum_match.fir": r"FIRRTL version 3.2.0 circuit check_struct_enum_match: module check_struct_enum_match: @[module-XXXXXXXXXX.rs 1:1] @@ -2419,6 +2438,10 @@ fn test_memory_of_enum() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_memory_of_enum.fir": r#"FIRRTL version 3.2.0 circuit check_memory_of_enum: %[[ { @@ -2524,12 +2547,13 @@ circuit check_memory_of_enum: %[[ 0000111101 ", }; - let orig_m = m.canonical().intern(); - let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody), + ..ExportOptions::default() + }, "/test/check_memory_of_enum.fir": r#"FIRRTL version 3.2.0 circuit check_memory_of_enum: %[[ { @@ -2671,11 +2695,12 @@ circuit check_memory_of_enum: %[[ 01 ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + ..ExportOptions::default() + }, "/test/check_memory_of_enum.fir": r#"FIRRTL version 3.2.0 circuit check_memory_of_enum: %[[ { @@ -2801,11 +2826,13 @@ circuit check_memory_of_enum: %[[ 01 ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt), + ..ExportOptions::default() + }, "/test/check_memory_of_enum.fir": r#"FIRRTL version 3.2.0 circuit check_memory_of_enum: %[[ { @@ -2906,6 +2933,10 @@ fn test_memory_of_array_of_enum() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_memory_of_array_of_enum.fir": r"FIRRTL version 3.2.0 circuit check_memory_of_array_of_enum: type Ty0 = {|A, B: UInt<8>, C: UInt<1>[3]|} @@ -3227,6 +3258,10 @@ circuit check_uninit: #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_uninit_1.fir": r"FIRRTL version 3.2.0 circuit check_uninit_1: type Ty0 = {} @@ -3329,6 +3364,10 @@ fn test_enum_connect_any() { #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: None, + ..ExportOptions::default() + }, "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 circuit check_enum_connect_any: type Ty0 = {|HdlNone, HdlSome: SInt<1>|} @@ -3357,12 +3396,13 @@ circuit check_enum_connect_any: connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] ", }; - let orig_m = m.canonical().intern(); - let m = simplify_enums(orig_m, SimplifyEnumsKind::SimplifyToEnumsWithNoBody).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::SimplifyToEnumsWithNoBody), + ..ExportOptions::default() + }, "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 circuit check_enum_connect_any: type Ty0 = {|A, B, C|} @@ -3577,11 +3617,13 @@ circuit check_enum_connect_any: connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] ", }; - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithBundleOfUInts).unwrap(); - dbg!(m); #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + ..ExportOptions::default() + }, "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 circuit check_enum_connect_any: type Ty0 = {tag: UInt<2>, body: UInt<2>} @@ -3732,12 +3774,14 @@ circuit check_enum_connect_any: connect o1, i1 @[module-XXXXXXXXXX.rs 10:1] connect o2, i2 @[module-XXXXXXXXXX.rs 11:1] ", - } - let m = simplify_enums(orig_m, SimplifyEnumsKind::ReplaceWithUInt).unwrap(); - dbg!(m); + }; #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => + options: ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithUInt), + ..ExportOptions::default() + }, "/test/check_enum_connect_any.fir": r"FIRRTL version 3.2.0 circuit check_enum_connect_any: type Ty0 = {tag: UInt<2>, body: UInt<2>} From 6e0b6c000d41a6b68d5b17462e016b6d6959c18c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 18:30:46 -0700 Subject: [PATCH 031/190] remove stray debugging prints --- .../fayalite/src/module/transform/simplify_enums.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 3b3677b..edb4b97 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -483,14 +483,6 @@ impl State { ); return Ok(()); } - println!( - r"handle_stmt_connect( - unfolded_lhs_ty: {unfolded_lhs_ty:?}, - unfolded_rhs_ty: {unfolded_rhs_ty:?}, - folded_lhs: {folded_lhs:?}, - folded_rhs: {folded_rhs:?}, -)" - ); match unfolded_lhs_ty { CanonicalType::Array(unfolded_lhs_ty) => self.handle_stmt_connect_array( unfolded_lhs_ty, @@ -535,11 +527,11 @@ fn connect_port( ) { if Expr::ty(lhs) == Expr::ty(rhs) { stmts.push( - dbg!(StmtConnect { + StmtConnect { lhs, rhs, source_location, - }) + } .into(), ); return; From 2a25dd9d7b74f5b6953e3833cec66cdc66954ee7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 18:31:44 -0700 Subject: [PATCH 032/190] fix annotations getting lost --- crates/fayalite/src/module.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index e88b37a..f93758a 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -2272,7 +2272,8 @@ pub fn annotate(target: Expr, annotations: impl IntoAnnotations) { .body .annotations_map .entry(decl) - .or_default(); + .or_default() + .extend(annotations); }); } From d0b406d288e0c8fef53ddc1703f1557ce81a250e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 18:33:32 -0700 Subject: [PATCH 033/190] add more annotation kinds --- crates/fayalite/src/annotations.rs | 25 +++++ crates/fayalite/src/firrtl.rs | 49 +++++++--- crates/fayalite/src/module/transform/visit.rs | 5 +- crates/fayalite/tests/module.rs | 94 ++++++++++++++++++- crates/fayalite/visit_types.json | 29 ++++++ 5 files changed, 187 insertions(+), 15 deletions(-) diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 8e645c3..6b96d01 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -118,10 +118,35 @@ pub struct CustomFirrtlAnnotation { pub additional_fields: CustomFirrtlAnnotationFields, } +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct SVAttributeAnnotation { + pub text: Interned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct BlackBoxInlineAnnotation { + pub path: Interned, + pub text: Interned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct BlackBoxPathAnnotation { + pub path: Interned, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct DocStringAnnotation { + pub text: Interned, +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] #[non_exhaustive] pub enum Annotation { DontTouch, + SVAttribute(SVAttributeAnnotation), + BlackBoxInline(BlackBoxInlineAnnotation), + BlackBoxPath(BlackBoxPathAnnotation), + DocString(DocStringAnnotation), CustomFirrtl(CustomFirrtlAnnotation), } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 13f211f..0be61b5 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -2,7 +2,10 @@ // See Notices.txt for copyright information #![allow(clippy::type_complexity)] use crate::{ - annotations::CustomFirrtlAnnotation, + annotations::{ + Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, + DocStringAnnotation, SVAttributeAnnotation, + }, array::Array, bundle::{Bundle, BundleField, BundleType}, clock::Clock, @@ -665,6 +668,17 @@ enum AnnotationData { }, #[serde(rename = "firrtl.transforms.DontTouchAnnotation")] DontTouch, + #[serde(rename = "firrtl.AttributeAnnotation")] + AttributeAnnotation { description: Interned }, + #[serde(rename = "firrtl.transforms.BlackBoxInlineAnno")] + BlackBoxInlineAnno { + name: Interned, + text: Interned, + }, + #[serde(rename = "firrtl.transforms.BlackBoxPathAnno")] + BlackBoxPathAnno { path: Interned }, + #[serde(rename = "firrtl.DocStringAnnotation")] + DocStringAnnotation { description: Interned }, #[allow(dead_code)] #[serde(untagged)] Other { @@ -675,7 +689,7 @@ enum AnnotationData { } #[derive(Serialize)] -struct Annotation { +struct FirrtlAnnotation { #[serde(flatten)] data: AnnotationData, target: AnnotationTarget, @@ -690,7 +704,7 @@ struct Exporter<'a> { module: ModuleState, type_state: TypeState, circuit_name: Ident, - annotations: Vec, + annotations: Vec, } struct PushIndent<'a> { @@ -1770,7 +1784,7 @@ impl<'a> Exporter<'a> { memory_name.0.to_string(), contents, )?; - self.annotations.push(Annotation { + self.annotations.push(FirrtlAnnotation { data: AnnotationData::MemoryFileInline { filename, hex_or_binary, @@ -1789,14 +1803,25 @@ impl<'a> Exporter<'a> { }); Ok(()) } - fn annotation( - &mut self, - path: AnnotationTargetPath, - annotation: &crate::annotations::Annotation, - ) { + fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { let data = match annotation { - crate::annotations::Annotation::DontTouch => AnnotationData::DontTouch, - crate::annotations::Annotation::CustomFirrtl(CustomFirrtlAnnotation { + Annotation::DontTouch => AnnotationData::DontTouch, + Annotation::SVAttribute(SVAttributeAnnotation { text }) => { + AnnotationData::AttributeAnnotation { description: *text } + } + Annotation::BlackBoxInline(BlackBoxInlineAnnotation { path, text }) => { + AnnotationData::BlackBoxInlineAnno { + name: *path, + text: *text, + } + } + Annotation::BlackBoxPath(BlackBoxPathAnnotation { path }) => { + AnnotationData::BlackBoxPathAnno { path: *path } + } + Annotation::DocString(DocStringAnnotation { text }) => { + AnnotationData::DocStringAnnotation { description: *text } + } + Annotation::CustomFirrtl(CustomFirrtlAnnotation { class, additional_fields, }) => AnnotationData::Other { @@ -1804,7 +1829,7 @@ impl<'a> Exporter<'a> { additional_fields: (*additional_fields).into(), }, }; - self.annotations.push(Annotation { + self.annotations.push(FirrtlAnnotation { data, target: AnnotationTarget { circuit: self.circuit_name, diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 440eecb..77079dd 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -2,7 +2,10 @@ // See Notices.txt for copyright information #![allow(clippy::multiple_bound_locations)] use crate::{ - annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation}, + annotations::{ + Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, + DocStringAnnotation, SVAttributeAnnotation, TargetedAnnotation, + }, array::ArrayType, bundle::{Bundle, BundleField, BundleType}, clock::Clock, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index b4998da..70b3f56 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,8 +1,15 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - annotations::CustomFirrtlAnnotation, assert_export_firrtl, firrtl::ExportOptions, - intern::Intern, module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, + annotations::{ + BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, + DocStringAnnotation, SVAttributeAnnotation, + }, + assert_export_firrtl, + firrtl::ExportOptions, + intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, + prelude::*, ty::StaticType, }; use serde_json::json; @@ -3058,6 +3065,14 @@ pub fn check_annotations() { .try_into() .unwrap(), })); + m.annotate_module(Annotation::DocString(DocStringAnnotation { + text: r"This module is used as a test that fayalite's firrtl +backend properly emits annotations. + +Testing... +" + .intern(), + })); #[hdl] let raddr: UInt<8> = m.input(); annotate(raddr, Annotation::DontTouch); @@ -3139,6 +3154,40 @@ pub fn check_annotations() { connect(write_port.clk, clk); connect(write_port.data, wdata); connect(write_port.mask, wmask); + #[hdl_module(extern)] + fn black_box1() { + m.verilog_name("BlackBox1"); + m.annotate_module(Annotation::BlackBoxInline(BlackBoxInlineAnnotation { + path: "black_box1.v".intern(), + text: r"(* blackbox *) +module BlackBox1(); +endmodule +" + .intern(), + })); + } + #[hdl] + let black_box1_instance = instance(black_box1()); + annotate(black_box1_instance, Annotation::DontTouch); + #[hdl_module(extern)] + fn black_box2() { + m.verilog_name("BlackBox2"); + m.annotate_module(Annotation::BlackBoxPath(BlackBoxPathAnnotation { + path: "black_box2.v".intern(), + })); + } + #[hdl] + let black_box2_instance = instance(black_box2()); + annotate(black_box2_instance, Annotation::DontTouch); + #[hdl] + let a_wire: (SInt<1>, Bool) = wire(); + annotate( + a_wire.1, + Annotation::SVAttribute(SVAttributeAnnotation { + text: "custom_sv_attr = \"abc\"".intern(), + }), + ); + connect(a_wire, (0_hdl_i1, false)); } #[test] @@ -3156,6 +3205,11 @@ circuit check_annotations: %[[ "bar": "a nice module!", "target": "~check_annotations|check_annotations" }, + { + "class": "firrtl.DocStringAnnotation", + "description": "This module is used as a test that fayalite's firrtl\nbackend properly emits annotations.\n\nTesting...\n", + "target": "~check_annotations|check_annotations" + }, { "class": "firrtl.transforms.DontTouchAnnotation", "target": "~check_annotations|check_annotations>raddr" @@ -3196,10 +3250,35 @@ circuit check_annotations: %[[ "class": "some.annotation.Class", "baz": "first mask bit", "target": "~check_annotations|check_annotations>mem.w1.data[0]" + }, + { + "class": "firrtl.transforms.DontTouchAnnotation", + "target": "~check_annotations|check_annotations>black_box1_instance" + }, + { + "class": "firrtl.transforms.DontTouchAnnotation", + "target": "~check_annotations|check_annotations>black_box2_instance" + }, + { + "class": "firrtl.AttributeAnnotation", + "description": "custom_sv_attr = \"abc\"", + "target": "~check_annotations|check_annotations>a_wire.1" + }, + { + "class": "firrtl.transforms.BlackBoxInlineAnno", + "name": "black_box1.v", + "text": "(* blackbox *)\nmodule BlackBox1();\nendmodule\n", + "target": "~check_annotations|black_box1" + }, + { + "class": "firrtl.transforms.BlackBoxPathAnno", + "path": "black_box2.v", + "target": "~check_annotations|black_box2" } ]] type Ty0 = {addr: UInt<8>, en: UInt<1>, clk: Clock, data: UInt<4>[2], mask: UInt<1>[2]} type Ty1 = {addr: UInt<8>, en: UInt<1>, clk: Clock, flip data: UInt<4>[2]} + type Ty2 = {`0`: SInt<1>, `1`: UInt<1>} module check_annotations: @[module-XXXXXXXXXX.rs 1:1] input raddr: UInt<8> @[module-XXXXXXXXXX.rs 2:1] output rdata: UInt<4>[2] @[module-XXXXXXXXXX.rs 3:1] @@ -3224,6 +3303,17 @@ circuit check_annotations: %[[ connect `mem`.w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] connect `mem`.w1.data, wdata @[module-XXXXXXXXXX.rs 18:1] connect `mem`.w1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] + inst black_box1_instance of black_box1 @[module-XXXXXXXXXX.rs 21:1] + inst black_box2_instance of black_box2 @[module-XXXXXXXXXX.rs 23:1] + wire a_wire: Ty2 @[module-XXXXXXXXXX.rs 24:1] + wire _bundle_literal_expr: Ty2 + connect _bundle_literal_expr.`0`, SInt<1>(0h0) + connect _bundle_literal_expr.`1`, UInt<1>(0h0) + connect a_wire, _bundle_literal_expr @[module-XXXXXXXXXX.rs 25:1] + extmodule black_box1: @[module-XXXXXXXXXX.rs 20:1] + defname = BlackBox1 + extmodule black_box2: @[module-XXXXXXXXXX.rs 22:1] + defname = BlackBox2 "#, }; } diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index f1969eb..7064df2 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1164,9 +1164,38 @@ "data": { "$kind": "Enum", "DontTouch": null, + "SVAttribute": "Visible", + "BlackBoxInline": "Visible", + "BlackBoxPath": "Visible", + "DocString": "Visible", "CustomFirrtl": "Visible" } }, + "SVAttributeAnnotation": { + "data": { + "$kind": "Struct", + "text": "Visible" + } + }, + "BlackBoxInlineAnnotation": { + "data": { + "$kind": "Struct", + "path": "Visible", + "text": "Visible" + } + }, + "BlackBoxPathAnnotation": { + "data": { + "$kind": "Struct", + "path": "Visible" + } + }, + "DocStringAnnotation": { + "data": { + "$kind": "Struct", + "text": "Visible" + } + }, "CustomFirrtlAnnotation": { "data": { "$kind": "Opaque" From e8c393f3bbbd7677864847f054adeefaaf55ea4f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 18:40:52 -0700 Subject: [PATCH 034/190] sort pub mod items --- crates/fayalite/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 70ce724..9192645 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -43,12 +43,12 @@ pub mod int; pub mod intern; pub mod memory; pub mod module; +pub mod prelude; pub mod reg; pub mod reset; pub mod source_location; +pub mod testing; pub mod ty; pub mod util; //pub mod valueless; -pub mod prelude; -pub mod testing; pub mod wire; From f35d88d2bbe5f95ec46773727e5d375b4f90ffff Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 18:41:41 -0700 Subject: [PATCH 035/190] remove unused valueless.rs --- crates/fayalite/src/lib.rs | 2 - crates/fayalite/src/valueless.rs | 88 -------------------------------- 2 files changed, 90 deletions(-) delete mode 100644 crates/fayalite/src/valueless.rs diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 9192645..6e1ccd3 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -30,7 +30,6 @@ pub struct __; #[cfg(feature = "unstable-doc")] pub mod _docs; -// FIXME: finish pub mod annotations; pub mod array; pub mod bundle; @@ -50,5 +49,4 @@ pub mod source_location; pub mod testing; pub mod ty; pub mod util; -//pub mod valueless; pub mod wire; diff --git a/crates/fayalite/src/valueless.rs b/crates/fayalite/src/valueless.rs deleted file mode 100644 index d34905e..0000000 --- a/crates/fayalite/src/valueless.rs +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information -use crate::{ - int::{DynIntType, DynSIntType, DynUIntType, IntTypeTrait, SIntType}, - ty::{Type, Value}, -}; -use std::ops::RangeBounds; - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)] -pub struct Valueless { - pub ty: T, -} - -impl Valueless { - pub fn to_canonical(&self) -> Valueless { - Valueless { - ty: self.ty.canonical(), - } - } - pub fn from_canonical(v: Valueless) -> Self { - Valueless { - ty: T::from_canonical_type(v.ty), - } - } -} - -mod sealed { - pub trait Sealed {} -} - -pub trait ValuelessTr: sealed::Sealed { - type Type: Type; - type Value: Value; -} - -impl sealed::Sealed for Valueless {} - -impl ValuelessTr for Valueless { - type Type = T; - type Value = T::Value; -} - -impl Valueless { - pub fn signum(&self) -> Valueless> { - Valueless::default() - } - pub fn as_same_width_uint(self) -> Valueless { - Valueless { - ty: self.ty.as_same_width_uint(), - } - } - pub fn as_same_width_sint(self) -> Valueless { - Valueless { - ty: self.ty.as_same_width_sint(), - } - } - pub fn as_same_value_uint(self) -> Valueless { - Valueless { - ty: self.ty.as_same_value_uint(), - } - } - pub fn as_same_value_sint(self) -> Valueless { - Valueless { - ty: self.ty.as_same_value_sint(), - } - } - pub fn concat( - &self, - high_part: Valueless, - ) -> Valueless> { - let ty = DynIntType::new( - self.ty - .width() - .checked_add(high_part.ty.width()) - .expect("result has too many bits"), - ); - Valueless { ty } - } - pub fn repeat(&self, count: usize) -> Valueless> { - let width = self.ty.width(); - let ty = DynIntType::new(width.checked_mul(count).expect("result has too many bits")); - Valueless { ty } - } - pub fn slice>(&self, index: I) -> Valueless { - let ty = self.ty.slice(index); - Valueless { ty } - } -} From f3d6528f5b1e23cb3e9a1ef338c477738c7d7b08 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 19:54:17 -0700 Subject: [PATCH 036/190] make annotations easier to use --- crates/fayalite/src/annotations.rs | 138 ++++++++++++++++-- crates/fayalite/src/firrtl.rs | 4 +- crates/fayalite/src/module/transform/visit.rs | 2 +- crates/fayalite/src/prelude.rs | 5 +- crates/fayalite/tests/module.rs | 58 ++++---- crates/fayalite/visit_types.json | 7 +- 6 files changed, 163 insertions(+), 51 deletions(-) diff --git a/crates/fayalite/src/annotations.rs b/crates/fayalite/src/annotations.rs index 6b96d01..8eff4a0 100644 --- a/crates/fayalite/src/annotations.rs +++ b/crates/fayalite/src/annotations.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use std::{ fmt, hash::{Hash, Hasher}, + iter::FusedIterator, ops::Deref, }; @@ -118,6 +119,9 @@ pub struct CustomFirrtlAnnotation { pub additional_fields: CustomFirrtlAnnotationFields, } +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] +pub struct DontTouchAnnotation; + #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] pub struct SVAttributeAnnotation { pub text: Interned, @@ -139,15 +143,63 @@ pub struct DocStringAnnotation { pub text: Interned, } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[non_exhaustive] -pub enum Annotation { - DontTouch, - SVAttribute(SVAttributeAnnotation), - BlackBoxInline(BlackBoxInlineAnnotation), - BlackBoxPath(BlackBoxPathAnnotation), - DocString(DocStringAnnotation), - CustomFirrtl(CustomFirrtlAnnotation), +macro_rules! make_annotation_enum { + ( + $(#[$meta:meta])* + $vis:vis enum $Annotation:ident { + $($Variant:ident($T:ident),)* + } + ) => { + $(#[$meta])* + $vis enum $Annotation { + $($Variant($T),)* + } + + $(impl IntoAnnotations for $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(self)] + } + } + + impl IntoAnnotations for &'_ $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + } + + impl IntoAnnotations for &'_ mut $T { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + } + + impl IntoAnnotations for Box<$T> { + type IntoAnnotations = [$Annotation; 1]; + + fn into_annotations(self) -> Self::IntoAnnotations { + [$Annotation::$Variant(*self)] + } + })* + }; +} + +make_annotation_enum! { + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + #[non_exhaustive] + pub enum Annotation { + DontTouch(DontTouchAnnotation), + SVAttribute(SVAttributeAnnotation), + BlackBoxInline(BlackBoxInlineAnnotation), + BlackBoxPath(BlackBoxPathAnnotation), + DocString(DocStringAnnotation), + CustomFirrtl(CustomFirrtlAnnotation), + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -212,10 +264,70 @@ impl IntoAnnotations for &'_ mut Annotation { } } -impl> IntoAnnotations for T { - type IntoAnnotations = Self; +pub struct IterIntoAnnotations> { + outer: T, + inner: Option<<::IntoAnnotations as IntoIterator>::IntoIter>, +} - fn into_annotations(self) -> Self::IntoAnnotations { - self +impl> Iterator for IterIntoAnnotations { + type Item = Annotation; + + fn next(&mut self) -> Option { + loop { + if let Some(inner) = &mut self.inner { + let Some(retval) = inner.next() else { + self.inner = None; + continue; + }; + return Some(retval); + } else { + self.inner = Some(self.outer.next()?.into_annotations().into_iter()); + } + } + } + + fn size_hint(&self) -> (usize, Option) { + if let (0, Some(0)) = self.outer.size_hint() { + self.inner + .as_ref() + .map(|v| v.size_hint()) + .unwrap_or((0, Some(0))) + } else { + ( + self.inner.as_ref().map(|v| v.size_hint().0).unwrap_or(0), + None, + ) + } + } + + fn fold(self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + self.inner + .into_iter() + .chain(self.outer.map(|v| v.into_annotations().into_iter())) + .flatten() + .fold(init, f) + } +} + +impl< + T: FusedIterator< + Item: IntoAnnotations>, + >, + > FusedIterator for IterIntoAnnotations +{ +} + +impl> IntoAnnotations for T { + type IntoAnnotations = IterIntoAnnotations; + + fn into_annotations(self) -> Self::IntoAnnotations { + IterIntoAnnotations { + outer: self.into_iter(), + inner: None, + } } } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 0be61b5..5a2cc00 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -4,7 +4,7 @@ use crate::{ annotations::{ Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, }, array::Array, bundle::{Bundle, BundleField, BundleType}, @@ -1805,7 +1805,7 @@ impl<'a> Exporter<'a> { } fn annotation(&mut self, path: AnnotationTargetPath, annotation: &Annotation) { let data = match annotation { - Annotation::DontTouch => AnnotationData::DontTouch, + Annotation::DontTouch(DontTouchAnnotation {}) => AnnotationData::DontTouch, Annotation::SVAttribute(SVAttributeAnnotation { text }) => { AnnotationData::AttributeAnnotation { description: *text } } diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 77079dd..1165a46 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -4,7 +4,7 @@ use crate::{ annotations::{ Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, TargetedAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation, }, array::ArrayType, bundle::{Bundle, BundleField, BundleType}, diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 16dccb9..c793775 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information pub use crate::{ - annotations::Annotation, + annotations::{ + BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, + DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, + }, array::{Array, ArrayType}, cli::Cli, clock::{Clock, ClockDomain, ToClock}, diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 70b3f56..856cbf0 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -1,16 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::{ - annotations::{ - BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation, - DocStringAnnotation, SVAttributeAnnotation, - }, - assert_export_firrtl, - firrtl::ExportOptions, - intern::Intern, - module::transform::simplify_enums::SimplifyEnumsKind, - prelude::*, - ty::StaticType, + assert_export_firrtl, firrtl::ExportOptions, intern::Intern, + module::transform::simplify_enums::SimplifyEnumsKind, prelude::*, ty::StaticType, }; use serde_json::json; @@ -3057,30 +3049,30 @@ circuit check_memory_of_array_of_enum: #[hdl_module(outline_generated)] pub fn check_annotations() { - m.annotate_module(Annotation::CustomFirrtl(CustomFirrtlAnnotation { + m.annotate_module(CustomFirrtlAnnotation { class: "the.annotation.Example".intern(), additional_fields: json!({ "bar": "a nice module!", }) .try_into() .unwrap(), - })); - m.annotate_module(Annotation::DocString(DocStringAnnotation { + }); + m.annotate_module(DocStringAnnotation { text: r"This module is used as a test that fayalite's firrtl backend properly emits annotations. Testing... " .intern(), - })); + }); #[hdl] let raddr: UInt<8> = m.input(); - annotate(raddr, Annotation::DontTouch); + annotate(raddr, DontTouchAnnotation); #[hdl] let rdata: Array, 2> = m.output(); annotate( rdata, - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "the.annotation.ExampleClass".intern(), additional_fields: json!({ "foo": "bar", @@ -3088,7 +3080,7 @@ Testing... }) .try_into() .unwrap(), - }), + }, ); #[hdl] let waddr: UInt<8> = m.input(); @@ -3098,21 +3090,21 @@ Testing... let wmask: Array = m.input(); annotate( wmask[1], - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "some.annotation.Class".intern(), additional_fields: json!({ "baz": "second mask bit", }) .try_into() .unwrap(), - }), + }, ); #[hdl] let clk: Clock = m.input(); #[hdl] let mut mem = memory(); mem.depth(0x100); - mem.annotate(Annotation::CustomFirrtl(CustomFirrtlAnnotation { + mem.annotate(CustomFirrtlAnnotation { class: "the.annotation.ExampleClass2".intern(), additional_fields: json!({ "bar": "foo", @@ -3120,18 +3112,18 @@ Testing... }) .try_into() .unwrap(), - })); + }); let read_port = mem.new_read_port(); annotate( read_port, - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "the.annotation.ExampleClass3".intern(), additional_fields: json!({ "foo": "my read port", }) .try_into() .unwrap(), - }), + }, ); connect_any(read_port.addr, raddr); connect(read_port.en, true); @@ -3140,14 +3132,14 @@ Testing... let write_port = mem.new_write_port(); annotate( write_port.data[0], - Annotation::CustomFirrtl(CustomFirrtlAnnotation { + CustomFirrtlAnnotation { class: "some.annotation.Class".intern(), additional_fields: json!({ "baz": "first mask bit", }) .try_into() .unwrap(), - }), + }, ); connect_any(write_port.addr, waddr); connect(write_port.en, true); @@ -3157,35 +3149,35 @@ Testing... #[hdl_module(extern)] fn black_box1() { m.verilog_name("BlackBox1"); - m.annotate_module(Annotation::BlackBoxInline(BlackBoxInlineAnnotation { + m.annotate_module(BlackBoxInlineAnnotation { path: "black_box1.v".intern(), text: r"(* blackbox *) module BlackBox1(); endmodule " .intern(), - })); + }); } #[hdl] let black_box1_instance = instance(black_box1()); - annotate(black_box1_instance, Annotation::DontTouch); + annotate(black_box1_instance, DontTouchAnnotation); #[hdl_module(extern)] fn black_box2() { m.verilog_name("BlackBox2"); - m.annotate_module(Annotation::BlackBoxPath(BlackBoxPathAnnotation { + m.annotate_module(BlackBoxPathAnnotation { path: "black_box2.v".intern(), - })); + }); } #[hdl] let black_box2_instance = instance(black_box2()); - annotate(black_box2_instance, Annotation::DontTouch); + annotate(black_box2_instance, DontTouchAnnotation); #[hdl] let a_wire: (SInt<1>, Bool) = wire(); annotate( a_wire.1, - Annotation::SVAttribute(SVAttributeAnnotation { + SVAttributeAnnotation { text: "custom_sv_attr = \"abc\"".intern(), - }), + }, ); connect(a_wire, (0_hdl_i1, false)); } diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 7064df2..09ae23f 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1163,7 +1163,7 @@ "Annotation": { "data": { "$kind": "Enum", - "DontTouch": null, + "DontTouch": "Visible", "SVAttribute": "Visible", "BlackBoxInline": "Visible", "BlackBoxPath": "Visible", @@ -1171,6 +1171,11 @@ "CustomFirrtl": "Visible" } }, + "DontTouchAnnotation": { + "data": { + "$kind": "Struct" + } + }, "SVAttributeAnnotation": { "data": { "$kind": "Struct", From 0cf01600b3b3883929ffb1edae914fa216c9c434 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 1 Oct 2024 19:56:17 -0700 Subject: [PATCH 037/190] add mod formal and move assert/assume/cover stuff to it --- crates/fayalite/src/firrtl.rs | 11 +- crates/fayalite/src/formal.rs | 186 ++++++++++++++++++ crates/fayalite/src/lib.rs | 1 + crates/fayalite/src/module.rs | 146 ++------------ crates/fayalite/src/module/transform/visit.rs | 5 +- crates/fayalite/src/prelude.rs | 9 +- crates/fayalite/visit_types.json | 2 +- 7 files changed, 218 insertions(+), 142 deletions(-) create mode 100644 crates/fayalite/src/formal.rs diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index 5a2cc00..ef955ea 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -17,6 +17,7 @@ use crate::{ }, Expr, ExprEnum, }, + formal::FormalKind, int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue}, intern::{Intern, Interned}, memory::{Mem, PortKind, PortName, ReadUnderWrite}, @@ -27,8 +28,8 @@ use crate::{ }, AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Module, ModuleBody, NameId, NormalModuleBody, Stmt, - StmtConnect, StmtDeclaration, StmtFormal, StmtFormalKind, StmtIf, StmtInstance, StmtMatch, - StmtReg, StmtWire, + StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, + StmtWire, }, reset::{AsyncReset, Reset, SyncReset}, source_location::SourceLocation, @@ -2011,9 +2012,9 @@ impl<'a> Exporter<'a> { let pred = self.expr(Expr::canonical(pred), &definitions, false); let en = self.expr(Expr::canonical(en), &definitions, false); let kind = match kind { - StmtFormalKind::Assert => "assert", - StmtFormalKind::Assume => "assume", - StmtFormalKind::Cover => "cover", + FormalKind::Assert => "assert", + FormalKind::Assume => "assume", + FormalKind::Cover => "cover", }; let text = EscapedString { value: &text, diff --git a/crates/fayalite/src/formal.rs b/crates/fayalite/src/formal.rs new file mode 100644 index 0000000..5e90b59 --- /dev/null +++ b/crates/fayalite/src/formal.rs @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use crate::{intern::Intern, prelude::*}; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum FormalKind { + Assert, + Assume, + Cover, +} + +impl FormalKind { + pub fn as_str(self) -> &'static str { + match self { + Self::Assert => "assert", + Self::Assume => "assume", + Self::Cover => "cover", + } + } +} + +#[track_caller] +pub fn formal_stmt_with_enable_and_loc( + kind: FormalKind, + clk: Expr, + pred: Expr, + en: Expr, + text: &str, + source_location: SourceLocation, +) { + crate::module::add_stmt_formal(crate::module::StmtFormal { + kind, + clk, + pred, + en, + text: text.intern(), + source_location, + }); +} + +#[track_caller] +pub fn formal_stmt_with_enable( + kind: FormalKind, + clk: Expr, + pred: Expr, + en: Expr, + text: &str, +) { + formal_stmt_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller()); +} + +#[track_caller] +pub fn formal_stmt_with_loc( + kind: FormalKind, + clk: Expr, + pred: Expr, + text: &str, + source_location: SourceLocation, +) { + formal_stmt_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location); +} + +#[track_caller] +pub fn formal_stmt(kind: FormalKind, clk: Expr, pred: Expr, text: &str) { + formal_stmt_with_loc(kind, clk, pred, text, SourceLocation::caller()); +} + +macro_rules! make_formal { + ($kind:ident, $formal_stmt_with_enable_and_loc:ident, $formal_stmt_with_enable:ident, $formal_stmt_with_loc:ident, $formal_stmt:ident) => { + #[track_caller] + pub fn $formal_stmt_with_enable_and_loc( + clk: Expr, + pred: Expr, + en: Expr, + text: &str, + source_location: SourceLocation, + ) { + formal_stmt_with_enable_and_loc( + FormalKind::$kind, + clk, + pred, + en, + text, + source_location, + ); + } + #[track_caller] + pub fn $formal_stmt_with_enable( + clk: Expr, + pred: Expr, + en: Expr, + text: &str, + ) { + formal_stmt_with_enable(FormalKind::$kind, clk, pred, en, text); + } + #[track_caller] + pub fn $formal_stmt_with_loc( + clk: Expr, + pred: Expr, + text: &str, + source_location: SourceLocation, + ) { + formal_stmt_with_loc(FormalKind::$kind, clk, pred, text, source_location); + } + #[track_caller] + pub fn $formal_stmt(clk: Expr, pred: Expr, text: &str) { + formal_stmt(FormalKind::$kind, clk, pred, text); + } + }; +} + +make_formal!( + Assert, + hdl_assert_with_enable_and_loc, + hdl_assert_with_enable, + hdl_assert_with_loc, + hdl_assert +); + +make_formal!( + Assume, + hdl_assume_with_enable_and_loc, + hdl_assume_with_enable, + hdl_assume_with_loc, + hdl_assume +); + +make_formal!( + Cover, + hdl_cover_with_enable_and_loc, + hdl_cover_with_enable, + hdl_cover_with_loc, + hdl_cover +); + +pub trait MakeFormalExpr: Type {} + +impl MakeFormalExpr for T {} + +#[hdl] +pub fn formal_global_clock() -> Expr { + #[hdl_module(extern)] + fn formal_global_clock() { + #[hdl] + let clk: Clock = m.output(); + m.annotate_module(BlackBoxInlineAnnotation { + path: "fayalite_formal_global_clock.v".intern(), + text: r"module __fayalite_formal_global_clock(output clk); + (* gclk *) + reg clk; +endmodule +" + .intern(), + }); + m.verilog_name("__fayalite_formal_global_clock"); + } + #[hdl] + let formal_global_clock = instance(formal_global_clock()); + formal_global_clock.clk +} + +#[hdl] +pub fn formal_reset() -> Expr { + #[hdl_module(extern)] + fn formal_reset() { + #[hdl] + let rst: AsyncReset = m.output(); + m.annotate_module(BlackBoxInlineAnnotation { + path: "fayalite_formal_reset.v".intern(), + text: r"module __fayalite_formal_reset(output rst); + reg rst; + (* gclk *) + reg gclk; + initial rst = 1; + always @(posedge gclk) + rst <= 0; +endmodule +" + .intern(), + }); + m.verilog_name("__fayalite_formal_reset"); + } + #[hdl] + let formal_reset = instance(formal_reset()); + formal_reset.rst +} diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 6e1ccd3..eedb1bb 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -38,6 +38,7 @@ pub mod clock; pub mod enum_; pub mod expr; pub mod firrtl; +pub mod formal; pub mod int; pub mod intern; pub mod memory; diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index f93758a..872c75f 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -15,6 +15,7 @@ use crate::{ }, Expr, Flow, ToExpr, }, + formal::FormalKind, int::{Bool, DynSize, Size}, intern::{Intern, Interned}, memory::{Mem, MemBuilder, MemBuilderTarget, PortName}, @@ -233,26 +234,9 @@ impl fmt::Debug for StmtConnect { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum StmtFormalKind { - Assert, - Assume, - Cover, -} - -impl StmtFormalKind { - pub fn as_str(self) -> &'static str { - match self { - Self::Assert => "assert", - Self::Assume => "assume", - Self::Cover => "cover", - } - } -} - #[derive(Clone, PartialEq, Eq, Hash)] pub struct StmtFormal { - pub kind: StmtFormalKind, + pub kind: FormalKind, pub clk: Expr, pub pred: Expr, pub en: Expr, @@ -279,6 +263,19 @@ impl fmt::Debug for StmtFormal { } } +#[track_caller] +pub(crate) fn add_stmt_formal(formal: StmtFormal) { + ModuleBuilder::with(|m| { + m.impl_ + .borrow_mut() + .body + .builder_normal_body() + .block(m.block_stack.top()) + .stmts + .push(formal.into()); + }); +} + #[derive(Clone, PartialEq, Eq, Hash)] pub struct StmtIf { pub cond: Expr, @@ -2439,119 +2436,6 @@ pub fn match_with_loc( T::match_variants(expr.to_expr(), source_location) } -#[track_caller] -pub fn formal_with_enable_and_loc( - kind: StmtFormalKind, - clk: Expr, - pred: Expr, - en: Expr, - text: &str, - source_location: SourceLocation, -) { - ModuleBuilder::with(|m| { - m.impl_ - .borrow_mut() - .body - .builder_normal_body() - .block(m.block_stack.top()) - .stmts - .push( - StmtFormal { - kind, - clk, - pred, - en, - text: text.intern(), - source_location, - } - .into(), - ); - }); -} - -#[track_caller] -pub fn formal_with_enable( - kind: StmtFormalKind, - clk: Expr, - pred: Expr, - en: Expr, - text: &str, -) { - formal_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller()); -} - -#[track_caller] -pub fn formal_with_loc( - kind: StmtFormalKind, - clk: Expr, - pred: Expr, - text: &str, - source_location: SourceLocation, -) { - formal_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location); -} - -#[track_caller] -pub fn formal(kind: StmtFormalKind, clk: Expr, pred: Expr, text: &str) { - formal_with_loc(kind, clk, pred, text, SourceLocation::caller()); -} - -macro_rules! make_formal { - ($kind:ident, $formal_with_enable_and_loc:ident, $formal_with_enable:ident, $formal_with_loc:ident, $formal:ident) => { - #[track_caller] - pub fn $formal_with_enable_and_loc( - clk: Expr, - pred: Expr, - en: Expr, - text: &str, - source_location: SourceLocation, - ) { - formal_with_enable_and_loc(StmtFormalKind::$kind, clk, pred, en, text, source_location); - } - #[track_caller] - pub fn $formal_with_enable(clk: Expr, pred: Expr, en: Expr, text: &str) { - formal_with_enable(StmtFormalKind::$kind, clk, pred, en, text); - } - #[track_caller] - pub fn $formal_with_loc( - clk: Expr, - pred: Expr, - text: &str, - source_location: SourceLocation, - ) { - formal_with_loc(StmtFormalKind::$kind, clk, pred, text, source_location); - } - #[track_caller] - pub fn $formal(clk: Expr, pred: Expr, text: &str) { - formal(StmtFormalKind::$kind, clk, pred, text); - } - }; -} - -make_formal!( - Assert, - hdl_assert_with_enable_and_loc, - hdl_assert_with_enable, - hdl_assert_with_loc, - hdl_assert -); - -make_formal!( - Assume, - hdl_assume_with_enable_and_loc, - hdl_assume_with_enable, - hdl_assume_with_loc, - hdl_assume -); - -make_formal!( - Cover, - hdl_cover_with_enable_and_loc, - hdl_cover_with_enable, - hdl_cover_with_loc, - hdl_cover -); - #[track_caller] pub fn connect_any_with_loc( lhs: Lhs, diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index 1165a46..2e1e48f 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -18,14 +18,15 @@ use crate::{ }, Expr, ExprEnum, }, + formal::FormalKind, int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue}, intern::{Intern, Interned}, memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, module::{ AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter, ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId, - NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, - StmtFormalKind, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, + NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, + StmtInstance, StmtMatch, StmtReg, StmtWire, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset}, diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index c793775..f1a9736 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -13,13 +13,16 @@ pub use crate::{ repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, ReduceBits, ToExpr, }, + formal::{ + formal_global_clock, formal_reset, hdl_assert, hdl_assert_with_enable, hdl_assume, + hdl_assume_with_enable, hdl_cover, hdl_cover_with_enable, MakeFormalExpr, + }, hdl, hdl_module, int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ - annotate, connect, connect_any, hdl_assert, hdl_assert_with_enable, hdl_assume, - hdl_assume_with_enable, hdl_cover, hdl_cover_with_enable, incomplete_wire, instance, - memory, memory_array, memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, + annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, + memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 09ae23f..366ee2f 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -1089,7 +1089,7 @@ "source_location": "Visible" } }, - "StmtFormalKind": { + "FormalKind": { "data": { "$kind": "Enum", "Assert": null, From 1c63a441a9ca35edfaec23fee0de21b6d48f4c10 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 00:27:28 -0700 Subject: [PATCH 038/190] add needed tools to CI --- .forgejo/workflows/test.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 6e82abf..762c1f8 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -11,10 +11,28 @@ jobs: fetch-depth: 0 - run: | scripts/check-copyright.sh + - run: | + apt-get update -qq + apt-get install -qq cvc5 z3 - run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 source "$HOME/.cargo/env" echo "$PATH" >> "$GITHUB_PATH" + - run: | + git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git + make -C sby install + - run: | + git clone --depth=1 --branch=0.45 https://github.com/YosysHQ/yosys.git + cd yosys + autoconf -f + ./configure + make install -j6 + - run: | + wget -O firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz + sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 firrtl.tar.gz' + tar -xvaf firrtl.tar.gz + export PATH="$(realpath firtool-1.86.0/bin):$PATH" + echo "$PATH" >> "$GITHUB_PATH" - uses: https://github.com/Swatinem/rust-cache@v2 with: save-if: ${{ github.ref == 'refs/heads/master' }} From 4497f09ea011eec88543c7d8d7616af24946bede Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 00:39:18 -0700 Subject: [PATCH 039/190] fix wrong build steps --- .forgejo/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 762c1f8..da9376e 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -24,8 +24,6 @@ jobs: - run: | git clone --depth=1 --branch=0.45 https://github.com/YosysHQ/yosys.git cd yosys - autoconf -f - ./configure make install -j6 - run: | wget -O firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz From eb65bec26ef2b8d511c0de9641bf1d323d9267e2 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 00:44:04 -0700 Subject: [PATCH 040/190] add yosys deps --- .forgejo/workflows/test.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index da9376e..b97b9de 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -13,7 +13,10 @@ jobs: scripts/check-copyright.sh - run: | apt-get update -qq - apt-get install -qq cvc5 z3 + apt-get install -qq cvc5 z3 build-essential clang lld bison flex \ + libreadline-dev gawk tcl-dev libffi-dev git \ + graphviz xdot pkg-config python3 libboost-system-dev \ + libboost-python-dev libboost-filesystem-dev zlib1g-dev - run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 source "$HOME/.cargo/env" @@ -22,7 +25,7 @@ jobs: git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git make -C sby install - run: | - git clone --depth=1 --branch=0.45 https://github.com/YosysHQ/yosys.git + git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git cd yosys make install -j6 - run: | From bc26fe32fd15e46c562bbea3dfed2ae5f33fdad1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 01:01:06 -0700 Subject: [PATCH 041/190] add ccache and clean up deps --- .forgejo/workflows/test.yml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index b97b9de..a6c9726 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -13,10 +13,31 @@ jobs: scripts/check-copyright.sh - run: | apt-get update -qq - apt-get install -qq cvc5 z3 build-essential clang lld bison flex \ - libreadline-dev gawk tcl-dev libffi-dev git \ - graphviz xdot pkg-config python3 libboost-system-dev \ - libboost-python-dev libboost-filesystem-dev zlib1g-dev + apt-get install -qq \ + bison \ + build-essential \ + ccache \ + clang \ + cvc5 \ + flex \ + gawk \ + git \ + libboost-filesystem-dev \ + libboost-python-dev \ + libboost-system-dev \ + libffi-dev \ + libreadline-dev \ + lld \ + pkg-config \ + python3 \ + tcl-dev \ + z3 \ + zlib1g-dev \ + - name: ccache + uses: https://github.com/hendrikmuhs/ccache-action@v1 + - run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + echo "$PATH" >> "$GITHUB_PATH" - run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 source "$HOME/.cargo/env" From 3e2fb9b94f9841fb930ff6ac5940865561d4e567 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 01:08:01 -0700 Subject: [PATCH 042/190] WIP getting queue formal to pass -- passes for capacity <= 2 --- crates/fayalite/src/cli.rs | 89 ++++++++--- crates/fayalite/src/formal.rs | 15 +- crates/fayalite/src/testing.rs | 86 +++++++++- crates/fayalite/src/util/ready_valid.rs | 199 ++++++++++++++++++------ crates/fayalite/tests/module.rs | 31 +++- 5 files changed, 343 insertions(+), 77 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index a771de6..cbef722 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -12,7 +12,14 @@ use clap::{ Parser, Subcommand, ValueEnum, ValueHint, }; use eyre::{eyre, Report}; -use std::{error, ffi::OsString, fmt, io, path::PathBuf, process}; +use std::{ + error, + ffi::OsString, + fmt::{self, Write}, + fs, io, mem, + path::{Path, PathBuf}, + process, +}; use tempfile::TempDir; pub type Result = std::result::Result; @@ -246,15 +253,51 @@ pub struct VerilogArgs { #[non_exhaustive] pub struct VerilogOutput { pub firrtl: FirrtlOutput, + pub verilog_files: Vec, } impl VerilogOutput { - pub fn verilog_file(&self) -> PathBuf { + pub fn main_verilog_file(&self) -> PathBuf { self.firrtl.file_with_ext("v") } + fn unadjusted_verilog_file(&self) -> PathBuf { + self.firrtl.file_with_ext("unadjusted.v") + } } impl VerilogArgs { + fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result { + let input = fs::read_to_string(output.unadjusted_verilog_file())?; + let file_separator_prefix = "\n// ----- 8< ----- FILE \""; + let file_separator_suffix = "\" ----- 8< -----\n\n"; + let mut input = &*input; + let main_verilog_file = output.main_verilog_file(); + let mut file_name: Option<&Path> = Some(&main_verilog_file); + loop { + let (chunk, next_file_name) = if let Some((chunk, rest)) = + input.split_once(file_separator_prefix) + { + let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else { + return Err(CliError(eyre!("parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}"))); + }; + input = rest; + (chunk, Some(next_file_name.as_ref())) + } else { + (mem::take(&mut input), None) + }; + let Some(file_name) = mem::replace(&mut file_name, next_file_name) else { + break; + }; + let file_name = output.firrtl.output_dir.join(file_name); + fs::write(&file_name, chunk)?; + if let Some(extension) = file_name.extension() { + if extension == "v" || extension == "sv" { + output.verilog_files.push(file_name); + } + } + } + Ok(output) + } fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result { let Self { firrtl, @@ -265,14 +308,15 @@ impl VerilogArgs { } = self; let output = VerilogOutput { firrtl: firrtl_output, + verilog_files: vec![], }; let mut cmd = process::Command::new(firtool); cmd.arg(output.firrtl.firrtl_file()); cmd.arg("-o"); - cmd.arg(output.verilog_file()); + cmd.arg(output.unadjusted_verilog_file()); if *debug { cmd.arg("-g"); - cmd.arg("--preserve-values=named"); + cmd.arg("--preserve-values=all"); } if let Some(dialect) = verilog_dialect { cmd.args(dialect.firtool_extra_args()); @@ -281,7 +325,7 @@ impl VerilogArgs { cmd.current_dir(&output.firrtl.output_dir); let status = firrtl.base.run_external_command(cmd)?; if status.success() { - Ok(output) + self.process_unadjusted_verilog_file(output) } else { Err(CliError(eyre!( "running {} failed: {status}", @@ -424,7 +468,7 @@ impl FormalOutput { } impl FormalArgs { - fn sby_contents(&self, output: &FormalOutput) -> String { + fn sby_contents(&self, output: &FormalOutput) -> Result { let Self { verilog: _, sby: _, @@ -448,15 +492,8 @@ impl FormalArgs { } let space_solver = OptArg(solver.as_ref()); let smtbmc_options = smtbmc_extra_args.join(" "); - let verilog_file = output - .verilog - .verilog_file() - .into_os_string() - .into_string() - .ok() - .expect("verilog file path is not UTF-8"); let top_module = &output.verilog.firrtl.top_module; - format!( + let mut retval = format!( "[options]\n\ mode {mode}\n\ depth {depth}\n\ @@ -465,18 +502,30 @@ impl FormalArgs { [engines]\n\ smtbmc{space_solver} -- -- {smtbmc_options}\n\ \n\ - [script]\n\ - read_verilog -sv -formal {verilog_file}\n\ - prep -top {top_module}\n - " - ) + [script]\n" + ); + for verilog_file in &output.verilog.verilog_files { + let verilog_file = verilog_file + .to_str() + .ok_or_else(|| CliError(eyre!("verilog file path is not UTF-8")))?; + if verilog_file.contains(|ch: char| { + (ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"' + }) { + return Err(CliError(eyre!( + "verilog file path contains characters that aren't permitted" + ))); + } + writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap(); + } + writeln!(retval, "prep -top {top_module}").unwrap(); + Ok(retval) } fn run_impl(&self, verilog_output: VerilogOutput) -> Result { let output = FormalOutput { verilog: verilog_output, }; let sby_file = output.sby_file(); - std::fs::write(&sby_file, self.sby_contents(&output))?; + std::fs::write(&sby_file, self.sby_contents(&output)?)?; let mut cmd = process::Command::new(&self.sby); cmd.arg("-f"); cmd.arg(sby_file); diff --git a/crates/fayalite/src/formal.rs b/crates/fayalite/src/formal.rs index 5e90b59..eab1969 100644 --- a/crates/fayalite/src/formal.rs +++ b/crates/fayalite/src/formal.rs @@ -1,6 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{intern::Intern, prelude::*}; +use crate::{ + intern::{Intern, Interned}, + prelude::*, +}; +use std::sync::OnceLock; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum FormalKind { @@ -32,7 +36,7 @@ pub fn formal_stmt_with_enable_and_loc( kind, clk, pred, - en, + en: en & !formal_reset().cast_to_static::(), text: text.intern(), source_location, }); @@ -160,11 +164,11 @@ endmodule } #[hdl] -pub fn formal_reset() -> Expr { +pub fn formal_reset() -> Expr { #[hdl_module(extern)] fn formal_reset() { #[hdl] - let rst: AsyncReset = m.output(); + let rst: SyncReset = m.output(); m.annotate_module(BlackBoxInlineAnnotation { path: "fayalite_formal_reset.v".intern(), text: r"module __fayalite_formal_reset(output rst); @@ -180,7 +184,8 @@ endmodule }); m.verilog_name("__fayalite_formal_reset"); } + static MOD: OnceLock>> = OnceLock::new(); #[hdl] - let formal_reset = instance(formal_reset()); + let formal_reset = instance(*MOD.get_or_init(formal_reset)); formal_reset.rst } diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index 8046f05..a647f47 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -5,7 +5,14 @@ use crate::{ firrtl::ExportOptions, }; use clap::Parser; -use std::sync::OnceLock; +use hashbrown::HashMap; +use serde::Deserialize; +use std::{ + fmt::Write, + path::{Path, PathBuf}, + process::Command, + sync::{Mutex, OnceLock}, +}; fn assert_formal_helper() -> FormalArgs { static FORMAL_ARGS: OnceLock = OnceLock::new(); @@ -15,8 +22,84 @@ fn assert_formal_helper() -> FormalArgs { .clone() } +#[derive(Deserialize)] +struct CargoMetadata { + target_directory: String, +} + +fn get_cargo_target_dir() -> &'static Path { + static RETVAL: OnceLock = OnceLock::new(); + RETVAL.get_or_init(|| { + let output = Command::new( + std::env::var_os("CARGO") + .as_deref() + .unwrap_or("cargo".as_ref()), + ) + .arg("metadata") + .output() + .expect("can't run `cargo metadata`"); + if !output.status.success() { + panic!( + "can't run `cargo metadata`:\n{}\nexited with status: {}", + String::from_utf8_lossy(&output.stderr), + output.status + ); + } + let CargoMetadata { target_directory } = + serde_json::from_slice(&output.stdout).expect("can't parse output of `cargo metadata`"); + PathBuf::from(target_directory) + }) +} + +#[track_caller] +fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { + static DIRS: Mutex>> = Mutex::new(None); + let test_name = test_name.to_string(); + // don't use line/column numbers since that constantly changes as you edit tests + let file = std::panic::Location::caller().file(); + // simple reproducible hash + let simple_hash = file.bytes().chain(test_name.bytes()).fold( + ((file.len() as u32) << 16).wrapping_add(test_name.len() as u32), + |mut h, b| { + h = h.wrapping_mul(0xaa0d184b); + h ^= h.rotate_right(5); + h ^= h.rotate_right(13); + h.wrapping_add(b as u32) + }, + ); + let mut dir = String::with_capacity(64); + write!(dir, "{simple_hash:08x}-").unwrap(); + for ch in Path::new(file) + .file_stem() + .unwrap_or_default() + .to_str() + .unwrap() + .chars() + .chain(['-']) + .chain(test_name.chars()) + { + dir.push(match ch { + ch if ch.is_alphanumeric() => ch, + '_' | '-' | '+' | '.' | ',' | ' ' => ch, + _ => '_', + }); + } + let index = *DIRS + .lock() + .unwrap() + .get_or_insert_with(HashMap::new) + .entry_ref(&dir) + .and_modify(|v| *v += 1) + .or_insert(0); + write!(dir, ".{index}").unwrap(); + get_cargo_target_dir() + .join("fayalite_assert_formal") + .join(dir) +} + #[track_caller] pub fn assert_formal( + test_name: impl std::fmt::Display, module: M, mode: FormalMode, depth: u64, @@ -27,6 +110,7 @@ pub fn assert_formal( { let mut args = assert_formal_helper(); args.verilog.firrtl.base.redirect_output_for_rust_test = true; + args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name)); args.verilog.firrtl.export_options = export_options; args.verilog.debug = true; args.mode = mode; diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index ab19cd1..41299a6 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -23,6 +23,19 @@ impl ReadyValid { fire } #[hdl] + pub fn fire_data(expr: impl ToExpr) -> Expr> { + let expr = expr.to_expr(); + let option_ty = Expr::ty(expr).data; + #[hdl] + let fire_data = wire(option_ty); + connect(fire_data, option_ty.HdlNone()); + #[hdl] + if expr.ready { + connect(fire_data, expr.data); + } + fire_data + } + #[hdl] pub fn map( expr: Expr, f: impl FnOnce(Expr) -> Expr, @@ -163,7 +176,6 @@ pub fn queue( } } -#[cfg(todo)] #[cfg(test)] mod tests { use super::*; @@ -171,27 +183,39 @@ mod tests { cli::FormalMode, firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal, }; + use std::num::NonZero; - #[test] - fn test_queue() { + #[track_caller] + fn test_queue(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { + assert_formal( + format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), + queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), + FormalMode::BMC, + 20, + None, + ExportOptions { + simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), + ..ExportOptions::default() + }, + ); #[hdl_module] fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { - #[hdl] - let clk: Clock = m.input(); - #[hdl] - let rst: SyncReset = m.input(); #[hdl] let inp_data: HdlOption> = m.input(); #[hdl] let out_ready: Bool = m.input(); #[hdl] + let start_check: Bool = m.input(); + #[hdl] + let clk: Clock = m.input(); + #[hdl] let cd = wire(); connect( cd, #[hdl] ClockDomain { clk, - rst: rst.to_reset(), + rst: formal_reset().to_reset(), }, ); #[hdl] @@ -216,52 +240,139 @@ mod tests { } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) { connect_any(next_count, count - 1u8); } - hdl_assert(clk, count.cmp_eq(dut.count), ""); + hdl_assert(cd.clk, count.cmp_eq(dut.count), ""); #[hdl] - let index = reg_builder().clock_domain(cd).reset(HdlNone::>()); + let started_check = reg_builder().clock_domain(cd).reset(false); #[hdl] - let data = reg_builder().clock_domain(cd).reset(HdlNone()); + let steps_till_output = reg_builder().clock_domain(cd).reset(0u32); #[hdl] - match index { - HdlNone => - { - #[hdl] - if ReadyValid::fire(dut.inp) { - connect(index, HdlSome(0u32)); - connect(data, dut.inp.data); - } + let expected_output = reg_builder().clock_domain(cd).reset(HdlNone()); + #[hdl] + if start_check & !started_check { + #[hdl] + if let HdlSome(inp) = ReadyValid::fire_data(dut.inp) { + connect(started_check, true); + connect_any( + steps_till_output, + count + (!ReadyValid::fire(dut.out)).cast_to(UInt[1]), + ); + connect(expected_output, HdlSome(inp)); } - HdlSome(cur_index) => + } else if started_check & steps_till_output.cmp_ne(0u32) & ReadyValid::fire(dut.out) { + connect_any(steps_till_output, steps_till_output - 1u32); + } + #[hdl] + let stored_output = reg_builder().clock_domain(cd).reset(HdlNone()); + #[hdl] + if let HdlSome(out) = ReadyValid::fire_data(dut.out) { + #[hdl] + if (start_check & !started_check) | (started_check & steps_till_output.cmp_ne(0u32)) { + connect(stored_output, HdlSome(out)); + } + } + #[hdl] + if started_check & steps_till_output.cmp_eq(0u32) { + #[hdl] + if let HdlSome(expected_output) = expected_output { #[hdl] - if cur_index.cmp_ge(next_count) { - connect(index, HdlNone()); - #[hdl] - if let HdlSome(data) = data { - #[hdl] - if let HdlSome(out_data) = dut.out.data { - hdl_assert(clk, data.cmp_eq(out_data), ""); - } else { - hdl_assert(clk, false.to_expr(), ""); - } - } else { - hdl_assert(clk, false.to_expr(), ""); - } + if let HdlSome(stored_output) = stored_output { + hdl_assert(cd.clk, stored_output.cmp_eq(expected_output), ""); } else { - connect(index, HdlSome((cur_index + 1u8).cast_to_static())); + hdl_assert(cd.clk, false.to_expr(), ""); } + } else { + hdl_assert(cd.clk, false.to_expr(), ""); } } } - assert_formal( - queue_test(NonZeroUsize::new(2).unwrap(), false, false), - FormalMode::BMC, - 20, - None, - ExportOptions { - simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), - ..ExportOptions::default() - }, - ); + } + + #[test] + fn test_1_false_false() { + test_queue(NonZero::new(1).unwrap(), false, false); + } + + #[test] + fn test_1_false_true() { + test_queue(NonZero::new(1).unwrap(), false, true); + } + + #[test] + fn test_1_true_false() { + test_queue(NonZero::new(1).unwrap(), true, false); + } + + #[test] + fn test_1_true_true() { + test_queue(NonZero::new(1).unwrap(), true, true); + } + + #[test] + fn test_2_false_false() { + test_queue(NonZero::new(2).unwrap(), false, false); + } + + #[test] + fn test_2_false_true() { + test_queue(NonZero::new(2).unwrap(), false, true); + } + + #[test] + fn test_2_true_false() { + test_queue(NonZero::new(2).unwrap(), true, false); + } + + #[test] + fn test_2_true_true() { + test_queue(NonZero::new(2).unwrap(), true, true); + } + + #[cfg(todo)] + #[test] + fn test_3_false_false() { + test_queue(NonZero::new(3).unwrap(), false, false); + } + + #[cfg(todo)] + #[test] + fn test_3_false_true() { + test_queue(NonZero::new(3).unwrap(), false, true); + } + + #[cfg(todo)] + #[test] + fn test_3_true_false() { + test_queue(NonZero::new(3).unwrap(), true, false); + } + + #[cfg(todo)] + #[test] + fn test_3_true_true() { + test_queue(NonZero::new(3).unwrap(), true, true); + } + + #[cfg(todo)] + #[test] + fn test_4_false_false() { + test_queue(NonZero::new(4).unwrap(), false, false); + } + + #[cfg(todo)] + #[test] + fn test_4_false_true() { + test_queue(NonZero::new(4).unwrap(), false, true); + } + + #[cfg(todo)] + #[test] + fn test_4_true_false() { + test_queue(NonZero::new(4).unwrap(), true, false); + } + + #[cfg(todo)] + #[test] + fn test_4_true_true() { + test_queue(NonZero::new(4).unwrap(), true, true); } } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 856cbf0..7d12739 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3390,7 +3390,15 @@ fn test_formal() { assert_export_firrtl! { m => "/test/check_formal.fir": r#"FIRRTL version 3.2.0 -circuit check_formal: +circuit check_formal: %[[ + { + "class": "firrtl.transforms.BlackBoxInlineAnno", + "name": "fayalite_formal_reset.v", + "text": "module __fayalite_formal_reset(output rst);\n reg rst;\n (* gclk *)\n reg gclk;\n initial rst = 1;\n always @(posedge gclk)\n rst <= 0;\nendmodule\n", + "target": "~check_formal|formal_reset" + } +]] + type Ty0 = {rst: UInt<1>} module check_formal: @[module-XXXXXXXXXX.rs 1:1] input clk: Clock @[module-XXXXXXXXXX.rs 2:1] input en1: UInt<1> @[module-XXXXXXXXXX.rs 3:1] @@ -3399,12 +3407,21 @@ circuit check_formal: input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1] input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1] input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1] - assert(clk, pred1, en1, "en check 1") @[module-XXXXXXXXXX.rs 9:1] - assume(clk, pred2, en2, "en check 2") @[module-XXXXXXXXXX.rs 10:1] - cover(clk, pred3, en3, "en check 3") @[module-XXXXXXXXXX.rs 11:1] - assert(clk, pred1, UInt<1>(0h1), "check 1") @[module-XXXXXXXXXX.rs 12:1] - assume(clk, pred2, UInt<1>(0h1), "check 2") @[module-XXXXXXXXXX.rs 13:1] - cover(clk, pred3, UInt<1>(0h1), "check 3") @[module-XXXXXXXXXX.rs 14:1] + inst formal_reset of formal_reset @[formal.rs 189:24] + assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1] + inst formal_reset_1 of formal_reset @[formal.rs 189:24] + assume(clk, pred2, and(en2, not(formal_reset_1.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1] + inst formal_reset_2 of formal_reset @[formal.rs 189:24] + cover(clk, pred3, and(en3, not(formal_reset_2.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1] + inst formal_reset_3 of formal_reset @[formal.rs 189:24] + assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset_3.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1] + inst formal_reset_4 of formal_reset @[formal.rs 189:24] + assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset_4.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1] + inst formal_reset_5 of formal_reset @[formal.rs 189:24] + cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset_5.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1] + extmodule formal_reset: @[formal.rs 168:5] + output rst: UInt<1> @[formal.rs 171:32] + defname = __fayalite_formal_reset "#, }; } From 4084a70485eeb0f78b7a13187053997d66a19405 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 01:43:46 -0700 Subject: [PATCH 043/190] switch default solver to z3 --- crates/fayalite/src/cli.rs | 18 +++--------------- crates/fayalite/src/testing.rs | 4 +++- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index cbef722..b0b818c 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -419,8 +419,8 @@ pub struct FormalArgs { pub mode: FormalMode, #[arg(long, default_value_t = Self::DEFAULT_DEPTH)] pub depth: u64, - #[arg(long)] - pub solver: Option, + #[arg(long, default_value = "z3")] + pub solver: String, #[arg(long)] pub smtbmc_extra_args: Vec, #[command(flatten)] @@ -479,18 +479,6 @@ impl FormalArgs { solver, _formal_adjust_args: _, } = self; - struct OptArg(Option); - impl fmt::Display for OptArg { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(v) = &self.0 { - f.write_str(" ")?; - v.fmt(f) - } else { - Ok(()) - } - } - } - let space_solver = OptArg(solver.as_ref()); let smtbmc_options = smtbmc_extra_args.join(" "); let top_module = &output.verilog.firrtl.top_module; let mut retval = format!( @@ -500,7 +488,7 @@ impl FormalArgs { wait on\n\ \n\ [engines]\n\ - smtbmc{space_solver} -- -- {smtbmc_options}\n\ + smtbmc {solver} -- -- {smtbmc_options}\n\ \n\ [script]\n" ); diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index a647f47..07252f1 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -115,6 +115,8 @@ pub fn assert_formal( args.verilog.debug = true; args.mode = mode; args.depth = depth; - args.solver = solver.map(String::from); + if let Some(solver) = solver { + args.solver = solver.into(); + } args.run(module).expect("testing::assert_formal() failed"); } From 15a28aa7a778dbba5422f6f9e40e8c530fc0e31a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 01:44:06 -0700 Subject: [PATCH 044/190] install python3-click -- needed by symbiyosys --- .forgejo/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index a6c9726..83d291b 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -30,6 +30,7 @@ jobs: lld \ pkg-config \ python3 \ + python3-click \ tcl-dev \ z3 \ zlib1g-dev \ From 343805f80b14982495fd4c208b906e7bc694a4f0 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 23:04:14 -0700 Subject: [PATCH 045/190] fix #[hdl] to work with unusual identifier hygiene from macros --- .../src/hdl_bundle.rs | 109 +++++++++--------- .../fayalite-proc-macros-impl/src/hdl_enum.rs | 96 +++++++-------- .../src/hdl_type_common.rs | 39 +++++-- crates/fayalite/src/ty.rs | 4 +- crates/fayalite/tests/hdl_types.rs | 66 +++++++++++ 5 files changed, 202 insertions(+), 112 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 30cf90f..f3a589a 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -431,6 +431,7 @@ impl ToTokens for ParsedBundle { builder_ident, mask_type_builder_ident, } = self; + let span = ident.span(); let ItemOptions { outline_generated: _, target, @@ -440,7 +441,7 @@ impl ToTokens for ParsedBundle { } = &options.body; let target = get_target(target, ident); let mut item_attrs = attrs.clone(); - item_attrs.push(common_derives(ident.span())); + item_attrs.push(common_derives(span)); ItemStruct { attrs: item_attrs, vis: vis.clone(), @@ -462,19 +463,19 @@ impl ToTokens for ParsedBundle { .map(|ParsedField { ident, ty, .. }| { let ident = ident.as_ref().unwrap(); let expr = ty.make_hdl_type_expr(context); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: #expr, } }) .collect(); - parse_quote_spanned! {ident.span()=> + parse_quote_spanned! {span=> #target { #(#fields)* } } }) } - let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let mut wrapped_in_const = WrappedInConst::new(tokens, span); let tokens = wrapped_in_const.inner(); let builder = Builder { vis: vis.clone(), @@ -488,9 +489,8 @@ impl ToTokens for ParsedBundle { let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled); let mut mask_type_fields = FieldsNamed::from(fields.clone()); - for Field { ident, ty, .. } in &mut mask_type_fields.named { - let ident = ident.as_ref().unwrap(); - *ty = parse_quote_spanned! {ident.span()=> + for Field { ty, .. } in &mut mask_type_fields.named { + *ty = parse_quote_spanned! {span=> <#ty as ::fayalite::ty::Type>::MaskType }; } @@ -509,8 +509,8 @@ impl ToTokens for ParsedBundle { mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled); ItemStruct { attrs: vec![ - common_derives(ident.span()), - parse_quote_spanned! {ident.span()=> + common_derives(span), + parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, ], @@ -523,16 +523,15 @@ impl ToTokens for ParsedBundle { } .to_tokens(tokens); let mut mask_type_match_variant_fields = mask_type_fields; - for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named { - let ident = ident.as_ref().unwrap(); - *ty = parse_quote_spanned! {ident.span()=> + for Field { ty, .. } in &mut mask_type_match_variant_fields.named { + *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> }; } ItemStruct { attrs: vec![ - common_derives(ident.span()), - parse_quote_spanned! {ident.span()=> + common_derives(span), + parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, ], @@ -545,16 +544,15 @@ impl ToTokens for ParsedBundle { } .to_tokens(tokens); let mut match_variant_fields = FieldsNamed::from(fields.clone()); - for Field { ident, ty, .. } in &mut match_variant_fields.named { - let ident = ident.as_ref().unwrap(); - *ty = parse_quote_spanned! {ident.span()=> + for Field { ty, .. } in &mut match_variant_fields.named { + *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> }; } ItemStruct { attrs: vec![ - common_derives(ident.span()), - parse_quote_spanned! {ident.span()=> + common_derives(span), + parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, ], @@ -566,17 +564,20 @@ impl ToTokens for ParsedBundle { semi_token: None, } .to_tokens(tokens); + let this_token = Ident::new("__this", span); + let fields_token = Ident::new("__fields", span); + let self_token = Token![self](span); let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); - quote_spanned! {ident.span()=> - #ident: ::fayalite::expr::Expr::field(__this, #ident_str), + quote_spanned! {span=> + #ident: ::fayalite::expr::Expr::field(#this_token, #ident_str), } })); let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); - quote_spanned! {ident.span()=> - #ident: ::fayalite::ty::Type::mask_type(&self.#ident), + quote_spanned! {span=> + #ident: ::fayalite::ty::Type::mask_type(&#self_token.#ident), } })); let from_canonical_body_fields = @@ -585,13 +586,13 @@ impl ToTokens for ParsedBundle { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); let flipped = flip.is_some(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: { let ::fayalite::bundle::BundleField { name: __name, flipped: __flipped, ty: __ty, - } = __fields[#index]; + } = #fields_token[#index]; ::fayalite::__std::assert_eq!(&*__name, #ident_str); ::fayalite::__std::assert_eq!(__flipped, #flipped); ::fayalite::ty::Type::from_canonical(__ty) @@ -604,17 +605,17 @@ impl ToTokens for ParsedBundle { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); let flipped = flip.is_some(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::bundle::BundleField { name: ::fayalite::intern::Intern::intern(#ident_str), flipped: #flipped, - ty: ::fayalite::ty::Type::canonical(&self.#ident), + ty: ::fayalite::ty::Type::canonical(&#self_token.#ident), }, } }, )); let fields_len = fields.named().into_iter().len(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics #where_clause @@ -630,7 +631,7 @@ impl ToTokens for ParsedBundle { ::MatchVariantAndInactiveScope, >; fn match_variants( - __this: ::fayalite::expr::Expr, + #this_token: ::fayalite::expr::Expr, __source_location: ::fayalite::source_location::SourceLocation, ) -> ::MatchVariantsIter { let __retval = #mask_type_match_variant_ident { @@ -638,19 +639,19 @@ impl ToTokens for ParsedBundle { }; ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) } - fn mask_type(&self) -> ::MaskType { - *self + fn mask_type(&#self_token) -> ::MaskType { + *#self_token } - fn canonical(&self) -> ::fayalite::ty::CanonicalType { - ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) + fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { + ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token))) } #[track_caller] fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { ::fayalite::__std::panic!("expected bundle"); }; - let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); - ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); + let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle); + ::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields"); Self { #(#from_canonical_body_fields)* } @@ -665,7 +666,7 @@ impl ToTokens for ParsedBundle { { type Builder = #unfilled_mask_type_builder_ty; type FilledBuilder = #filled_mask_type_builder_ty; - fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { + fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } } @@ -680,8 +681,8 @@ impl ToTokens for ParsedBundle { impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics #where_clause { - fn expr_deref(__this: &::fayalite::expr::Expr) -> &::MatchVariant { - let __this = *__this; + fn expr_deref(#this_token: &::fayalite::expr::Expr) -> &::MatchVariant { + let #this_token = *#this_token; let __retval = #mask_type_match_variant_ident { #(#match_variant_body_fields)* }; @@ -703,7 +704,7 @@ impl ToTokens for ParsedBundle { ::MatchVariantAndInactiveScope, >; fn match_variants( - __this: ::fayalite::expr::Expr, + #this_token: ::fayalite::expr::Expr, __source_location: ::fayalite::source_location::SourceLocation, ) -> ::MatchVariantsIter { let __retval = #match_variant_ident { @@ -711,21 +712,21 @@ impl ToTokens for ParsedBundle { }; ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval)) } - fn mask_type(&self) -> ::MaskType { + fn mask_type(&#self_token) -> ::MaskType { #mask_type_ident { #(#mask_type_body_fields)* } } - fn canonical(&self) -> ::fayalite::ty::CanonicalType { - ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self))) + fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { + ::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token))) } #[track_caller] fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self { let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else { ::fayalite::__std::panic!("expected bundle"); }; - let __fields = ::fayalite::bundle::BundleType::fields(&__bundle); - ::fayalite::__std::assert_eq!(__fields.len(), #fields_len, "bundle has wrong number of fields"); + let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle); + ::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields"); Self { #(#from_canonical_body_fields)* } @@ -740,7 +741,7 @@ impl ToTokens for ParsedBundle { { type Builder = #unfilled_builder_ty; type FilledBuilder = #filled_builder_ty; - fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { + fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> { ::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..]) } } @@ -755,8 +756,8 @@ impl ToTokens for ParsedBundle { impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics #where_clause { - fn expr_deref(__this: &::fayalite::expr::Expr) -> &::MatchVariant { - let __this = *__this; + fn expr_deref(#this_token: &::fayalite::expr::Expr) -> &::MatchVariant { + let #this_token = *#this_token; let __retval = #match_variant_ident { #(#match_variant_body_fields)* }; @@ -772,7 +773,7 @@ impl ToTokens for ParsedBundle { let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); let ty = field.ty(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: <#ty as ::fayalite::ty::StaticType>::TYPE, } })); @@ -780,28 +781,26 @@ impl ToTokens for ParsedBundle { Vec::from_iter(fields.named().into_iter().map(|field| { let ident: &Ident = field.ident().as_ref().unwrap(); let ty = field.ty(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE, } })); - let type_properties = format_ident!("__type_properties", span = ident.span()); + let type_properties = format_ident!("__type_properties", span = span); let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { - let ident: &Ident = field.ident().as_ref().unwrap(); let flipped = field_flip.is_some(); let ty = field.ty(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES); } })); let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| { - let ident: &Ident = field.ident().as_ref().unwrap(); let flipped = field_flip.is_some(); let ty = field.ty(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES); } })); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #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 50fb138..adddd74 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -204,6 +204,7 @@ impl ToTokens for ParsedEnum { variants, match_variant_ident, } = self; + let span = ident.span(); let ItemOptions { outline_generated: _, target, @@ -213,8 +214,8 @@ impl ToTokens for ParsedEnum { } = &options.body; let target = get_target(target, ident); let mut struct_attrs = attrs.clone(); - struct_attrs.push(common_derives(ident.span())); - struct_attrs.push(parse_quote_spanned! {ident.span()=> + struct_attrs.push(common_derives(span)); + struct_attrs.push(parse_quote_spanned! {span=> #[allow(non_snake_case)] }); let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref( @@ -238,8 +239,8 @@ impl ToTokens for ParsedEnum { colon_token = Token![:](paren_token.span.open()); ty.clone().into() } else { - colon_token = Token![:](ident.span()); - parse_quote_spanned! {ident.span()=> + colon_token = Token![:](span); + parse_quote_spanned! {span=> () } }; @@ -282,30 +283,30 @@ impl ToTokens for ParsedEnum { }) = field { let expr = ty.make_hdl_type_expr(context); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: #expr, } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: (), } } }) .collect(); - parse_quote_spanned! {ident.span()=> + parse_quote_spanned! {span=> #target { #(#fields)* } } }) } - let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let mut wrapped_in_const = WrappedInConst::new(tokens, span); let tokens = wrapped_in_const.inner(); { - let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span()); + let mut wrapped_in_const = WrappedInConst::new(tokens, span); let tokens = wrapped_in_const.inner(); let mut enum_attrs = attrs.clone(); - enum_attrs.push(parse_quote_spanned! {ident.span()=> + enum_attrs.push(parse_quote_spanned! {span=> #[allow(dead_code)] }); ItemEnum { @@ -354,7 +355,7 @@ impl ToTokens for ParsedEnum { .to_tokens(tokens); } let mut enum_attrs = attrs.clone(); - enum_attrs.push(parse_quote_spanned! {ident.span()=> + enum_attrs.push(parse_quote_spanned! {span=> #[allow(dead_code, non_camel_case_types)] }); ItemEnum { @@ -389,7 +390,7 @@ impl ToTokens for ParsedEnum { mutability: FieldMutability::None, ident: None, colon_token: None, - ty: parse_quote_spanned! {ident.span()=> + ty: parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> }, }, @@ -403,21 +404,22 @@ impl ToTokens for ParsedEnum { )), } .to_tokens(tokens); + let self_token = Token![self](span); for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() { if let Some(ParsedVariantField { ty, .. }) = field { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #impl_generics #target #type_generics #where_clause { #[allow(non_snake_case, dead_code)] #vis fn #ident<__V: ::fayalite::expr::ToExpr>( - self, + #self_token, v: __V, ) -> ::fayalite::expr::Expr { ::fayalite::expr::ToExpr::to_expr( &::fayalite::expr::ops::EnumLiteral::new_by_index( - self, + #self_token, #index, ::fayalite::__std::option::Option::Some( ::fayalite::expr::Expr::canonical( @@ -430,16 +432,16 @@ impl ToTokens for ParsedEnum { } } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #impl_generics #target #type_generics #where_clause { #[allow(non_snake_case, dead_code)] - #vis fn #ident(self) -> ::fayalite::expr::Expr { + #vis fn #ident(#self_token) -> ::fayalite::expr::Expr { ::fayalite::expr::ToExpr::to_expr( &::fayalite::expr::ops::EnumLiteral::new_by_index( - self, + #self_token, #index, ::fayalite::__std::option::Option::None, ), @@ -450,46 +452,48 @@ impl ToTokens for ParsedEnum { } .to_tokens(tokens); } + let variants_token = Ident::new("variants", span); let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map( |(index, ParsedVariant { ident, field, .. })| { let ident_str = ident.to_string(); let val = if field.is_some() { let missing_value_msg = format!("expected variant {ident} to have a field"); - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg)) } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::__std::assert!(ty.is_none()); } }; - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: { let ::fayalite::enum_::EnumVariant { name, ty, - } = variants[#index]; + } = #variants_token[#index]; ::fayalite::__std::assert_eq!(&*name, #ident_str); #val }, } }, )); + let variant_access_token = Ident::new("variant_access", span); let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map( |(index, ParsedVariant { ident, field, .. })| { if field.is_some() { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #index => #match_variant_ident::#ident( ::fayalite::expr::ToExpr::to_expr( &::fayalite::expr::ops::VariantAccess::new_by_index( - variant_access.base(), - variant_access.variant_index(), + #variant_access_token.base(), + #variant_access_token.variant_index(), ), ), ), } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #index => #match_variant_ident::#ident, } } @@ -507,16 +511,16 @@ impl ToTokens for ParsedEnum { match field { Some(ParsedVariantField { options, .. }) => { let FieldOptions {} = options.body; - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::enum_::EnumVariant { name: ::fayalite::intern::Intern::intern(#ident_str), ty: ::fayalite::__std::option::Option::Some( - ::fayalite::ty::Type::canonical(&self.#ident), + ::fayalite::ty::Type::canonical(&#self_token.#ident), ), }, } } - None => quote_spanned! {ident.span()=> + None => quote_spanned! {span=> ::fayalite::enum_::EnumVariant { name: ::fayalite::intern::Intern::intern(#ident_str), ty: ::fayalite::__std::option::Option::None, @@ -526,7 +530,7 @@ impl ToTokens for ParsedEnum { }, )); let variants_len = variants.len(); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #impl_generics ::fayalite::ty::Type for #target #type_generics #where_clause @@ -544,11 +548,11 @@ impl ToTokens for ParsedEnum { ) -> ::MatchVariantsIter { ::fayalite::module::enum_match_variants_helper(this, source_location) } - fn mask_type(&self) -> ::MaskType { + fn mask_type(&#self_token) -> ::MaskType { ::fayalite::int::Bool } - fn canonical(&self) -> ::fayalite::ty::CanonicalType { - ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(self))) + fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType { + ::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token))) } #[track_caller] #[allow(non_snake_case)] @@ -556,8 +560,8 @@ impl ToTokens for ParsedEnum { let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else { ::fayalite::__std::panic!("expected enum"); }; - let variants = ::fayalite::enum_::EnumType::variants(&enum_); - ::fayalite::__std::assert_eq!(variants.len(), #variants_len, "enum has wrong number of variants"); + let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_); + ::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants"); Self { #(#from_canonical_body_fields)* } @@ -573,16 +577,16 @@ impl ToTokens for ParsedEnum { fn match_activate_scope( v: ::MatchVariantAndInactiveScope, ) -> (::MatchVariant, ::MatchActiveScope) { - let (variant_access, scope) = v.activate(); + let (#variant_access_token, scope) = v.activate(); ( - match variant_access.variant_index() { + match #variant_access_token.variant_index() { #(#match_active_scope_match_arms)* #variants_len.. => ::fayalite::__std::panic!("invalid variant index"), }, scope, ) } - fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> { + fn variants(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> { ::fayalite::intern::Intern::intern(&[ #(#variants_body_variants)* ][..]) @@ -597,34 +601,34 @@ impl ToTokens for ParsedEnum { let static_type_body_variants = Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { if let Some(_) = field { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: ::fayalite::ty::StaticType::TYPE, } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> #ident: (), } } })); - let type_properties = format_ident!("__type_properties", span = ident.span()); + let type_properties = format_ident!("__type_properties", span = span); let type_properties_variants = - Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { + Vec::from_iter(variants.iter().map(|ParsedVariant { field, .. }| { let variant = if let Some(ParsedVariantField { ty, .. }) = field { - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::__std::option::Option::Some( <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES, ) } } else { - quote_spanned! {ident.span()=> + quote_spanned! {span=> ::fayalite::__std::option::Option::None } }; - quote_spanned! {ident.span()=> + quote_spanned! {span=> let #type_properties = #type_properties.variant(#variant); } })); - quote_spanned! {ident.span()=> + quote_spanned! {span=> #[automatically_derived] impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics 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 efbe7f3..e7561fe 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1896,6 +1896,7 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::array::ArrayType); impl_known_item!(::fayalite::bundle::BundleType); impl_known_item!(::fayalite::enum_::EnumType); + impl_known_item!(::fayalite::int::BoolOrIntType); impl_known_item!(::fayalite::int::DynSize); impl_known_item!(::fayalite::int::IntType); impl_known_item!(::fayalite::int::KnownSize); @@ -2085,6 +2086,7 @@ macro_rules! impl_bounds { impl_bounds! { #[struct = ParsedBounds] pub(crate) enum ParsedBound { + BoolOrIntType, BundleType, EnumType, IntType, @@ -2098,6 +2100,7 @@ impl_bounds! { impl_bounds! { #[struct = ParsedTypeBounds] pub(crate) enum ParsedTypeBound { + BoolOrIntType, BundleType, EnumType, IntType, @@ -2109,6 +2112,7 @@ impl_bounds! { impl From for ParsedBound { fn from(value: ParsedTypeBound) -> Self { match value { + ParsedTypeBound::BoolOrIntType(v) => ParsedBound::BoolOrIntType(v), ParsedTypeBound::BundleType(v) => ParsedBound::BundleType(v), ParsedTypeBound::EnumType(v) => ParsedBound::EnumType(v), ParsedTypeBound::IntType(v) => ParsedBound::IntType(v), @@ -2121,6 +2125,7 @@ impl From for ParsedBound { impl From for ParsedBounds { fn from(value: ParsedTypeBounds) -> Self { let ParsedTypeBounds { + BoolOrIntType, BundleType, EnumType, IntType, @@ -2128,6 +2133,7 @@ impl From for ParsedBounds { Type, } = value; Self { + BoolOrIntType, BundleType, EnumType, IntType, @@ -2143,6 +2149,10 @@ impl ParsedTypeBound { fn implied_bounds(self) -> ParsedTypeBounds { let span = self.span(); match self { + Self::BoolOrIntType(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), Self::BundleType(v) => ParsedTypeBounds::from_iter([ ParsedTypeBound::from(v), ParsedTypeBound::Type(known_items::Type(span)), @@ -2153,6 +2163,7 @@ impl ParsedTypeBound { ]), Self::IntType(v) => ParsedTypeBounds::from_iter([ ParsedTypeBound::from(v), + ParsedTypeBound::BoolOrIntType(known_items::BoolOrIntType(span)), ParsedTypeBound::Type(known_items::Type(span)), ]), Self::StaticType(v) => ParsedTypeBounds::from_iter([ @@ -2185,6 +2196,7 @@ impl From for ParsedBounds { fn from(value: ParsedSizeTypeBounds) -> Self { let ParsedSizeTypeBounds { KnownSize, Size } = value; Self { + BoolOrIntType: None, BundleType: None, EnumType: None, IntType: None, @@ -2260,6 +2272,7 @@ pub(crate) enum ParsedBoundCategory { impl ParsedBound { fn categorize(self) -> ParsedBoundCategory { match self { + Self::BoolOrIntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BoolOrIntType(v)), Self::BundleType(v) => ParsedBoundCategory::Type(ParsedTypeBound::BundleType(v)), Self::EnumType(v) => ParsedBoundCategory::Type(ParsedTypeBound::EnumType(v)), Self::IntType(v) => ParsedBoundCategory::Type(ParsedTypeBound::IntType(v)), @@ -2575,6 +2588,7 @@ impl ParsedGenerics { } }) .collect(); + let param_token = Ident::new("__param", ident.span()); for (param_count, (generics_accumulation_type, next_param)) in generics_accumulation_types .iter() .zip(&self.params) @@ -2623,7 +2637,7 @@ impl ParsedGenerics { next_generics.split_for_impl(); let next_turbofish = next_type_generics.as_turbofish(); let mut param: Expr = parse_quote_spanned! {ident.span()=> - __param + #param_token }; let mut generics = next_generics.clone(); let mut index_type = param_ident.clone(); @@ -2638,7 +2652,7 @@ impl ParsedGenerics { is_const: false, }); param = parse_quote_spanned! {ident.span()=> - ::fayalite::ty::TypeOrDefault::get(__param, || #default_expr) + ::fayalite::ty::TypeOrDefault::get(#param_token, || #default_expr) }; let context = MakeHdlTypeExprContext { named_param_values: self_members[..param_count] @@ -2703,7 +2717,7 @@ impl ParsedGenerics { { type Output = #next_target #next_type_generics; - fn index(&self, __param: #index_type) -> &Self::Output { + fn index(&self, #param_token: #index_type) -> &Self::Output { ::fayalite::intern::Interned::<_>::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) @@ -2724,7 +2738,7 @@ impl ParsedGenerics { .iter() .cloned() .chain([parse_quote_spanned! {ident.span()=> - __param + #param_token }]) .collect(), is_const: false, @@ -2763,7 +2777,7 @@ impl ParsedGenerics { { type Output = #next_target #next_target_args; - fn index(&self, __param: #param_ident) -> &Self::Output { + fn index(&self, #param_token: #param_ident) -> &Self::Output { ::fayalite::intern::Interned::<_>::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) @@ -2791,7 +2805,7 @@ impl ParsedGenerics { .iter() .cloned() .chain([parse_quote_spanned! {ident.span()=> - __param + #param_token }]) .collect(), is_const: false, @@ -2833,7 +2847,7 @@ impl ParsedGenerics { { type Output = #next_target #next_target_args; - fn index(&self, __param: __Param) -> &Self::Output { + fn index(&self, #param_token: __Param) -> &Self::Output { ::fayalite::intern::Interned::<_>::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) @@ -3145,16 +3159,21 @@ impl ParsedGenerics { .Type .get_or_insert_with(|| known_items::Type(bound.span())); match bound { - ParsedTypeBound::BundleType(_) + ParsedTypeBound::BoolOrIntType(_) + | ParsedTypeBound::BundleType(_) | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) => { errors.error(bound, "bound on mask type not implemented"); } ParsedTypeBound::StaticType(bound) => { if bounds.StaticType.is_none() { - errors.error(bound, "StaticType bound on mask type without corresponding StaticType bound on original type is not implemented"); + errors.error( + bound, + "StaticType bound on mask type without corresponding \ + StaticType bound on original type is not implemented", + ); } - }, + } ParsedTypeBound::Type(_) => {} } } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index c7081eb..380d2e6 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -210,7 +210,9 @@ impl sealed::BaseTypeSealed for CanonicalType {} impl BaseType for CanonicalType {} -pub trait TypeOrDefault: sealed::TypeOrDefaultSealed { +pub trait TypeOrDefault: + sealed::TypeOrDefaultSealed + Copy + Eq + Hash + fmt::Debug +{ type Type: Type; fn get D>(self, default: F) -> Self::Type; } diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index d043013..3f11de3 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -29,3 +29,69 @@ pub enum E { pub struct S2 { pub v: E, } + +// check that #[hdl] properly handles hygiene +macro_rules! types_in_macros { + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => { + #[hdl] + struct $F {} + #[hdl] + struct $A<$B, $C: Size, const $D: usize, $E = $F> { + $a: $B, + $b: UIntType<$C>, + $c: SInt<$D>, + $d: HdlOption<$E>, + $e: $E, + $f: $F, + } + #[allow(non_camel_case_types)] + #[hdl] + enum $B<$C: Size, const $D: usize, $E = $F> { + $a($A<(), $C, $D, $E>), + $b(UIntType<$C>), + $c(SInt<$D>), + $d, + $e($E), + $f($F), + } + }; + // ensure every identifier has different hygiene + () => { + types_in_macros!(a); + }; + ($a:ident) => { + types_in_macros!($a, b); + }; + ($a:ident, $b:ident) => { + types_in_macros!($a, $b, c); + }; + ($a:ident, $b:ident, $c:ident) => { + types_in_macros!($a, $b, $c, d); + }; + ($a:ident, $b:ident, $c:ident, $d:ident) => { + types_in_macros!($a, $b, $c, $d, e); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident) => { + types_in_macros!($a, $b, $c, $d, $e, f); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, A); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, $A, B); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, C); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, D); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, E); + }; + ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident) => { + types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, $E, F); + }; +} + +types_in_macros!(); From 0d54b9a2a9abd7c8b34566ae6e55ca0d508c6a21 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 23:07:14 -0700 Subject: [PATCH 046/190] queue formal proof passes! --- crates/fayalite/src/cli.rs | 2 +- crates/fayalite/src/formal.rs | 70 +++++++++- crates/fayalite/src/prelude.rs | 5 +- crates/fayalite/src/testing.rs | 4 +- crates/fayalite/src/util/ready_valid.rs | 170 +++++++++++++++--------- crates/fayalite/tests/module.rs | 18 +-- 6 files changed, 183 insertions(+), 86 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index b0b818c..f1d69d2 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -516,7 +516,7 @@ impl FormalArgs { std::fs::write(&sby_file, self.sby_contents(&output)?)?; let mut cmd = process::Command::new(&self.sby); cmd.arg("-f"); - cmd.arg(sby_file); + cmd.arg(sby_file.file_name().unwrap()); cmd.args(&self.sby_extra_args); cmd.current_dir(&output.verilog.firrtl.output_dir); let status = self.verilog.firrtl.base.run_external_command(cmd)?; diff --git a/crates/fayalite/src/formal.rs b/crates/fayalite/src/formal.rs index eab1969..17d3122 100644 --- a/crates/fayalite/src/formal.rs +++ b/crates/fayalite/src/formal.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - intern::{Intern, Interned}, + int::BoolOrIntType, + intern::{Intern, Interned, Memoize}, prelude::*, }; use std::sync::OnceLock; @@ -172,12 +173,7 @@ pub fn formal_reset() -> Expr { m.annotate_module(BlackBoxInlineAnnotation { path: "fayalite_formal_reset.v".intern(), text: r"module __fayalite_formal_reset(output rst); - reg rst; - (* gclk *) - reg gclk; - initial rst = 1; - always @(posedge gclk) - rst <= 0; + assign rst = $initstate; endmodule " .intern(), @@ -189,3 +185,63 @@ endmodule let formal_reset = instance(*MOD.get_or_init(formal_reset)); formal_reset.rst } + +macro_rules! make_any_const_fn { + ($ident:ident, $verilog_attribute:literal) => { + #[hdl] + pub fn $ident(ty: T) -> Expr { + #[hdl_module(extern)] + pub(super) fn $ident(ty: T) { + #[hdl] + let out: T = m.output(ty); + let width = ty.width(); + let verilog_bitslice = if width == 1 { + String::new() + } else { + format!(" [{}:0]", width - 1) + }; + m.annotate_module(BlackBoxInlineAnnotation { + path: Intern::intern_owned(format!( + "fayalite_{}_{width}.v", + stringify!($ident), + )), + text: Intern::intern_owned(format!( + r"module __fayalite_{}_{width}(output{verilog_bitslice} out); + (* {} *) + reg{verilog_bitslice} out; +endmodule +", + stringify!($ident), + $verilog_attribute, + )), + }); + m.verilog_name(format!("__fayalite_{}_{width}", stringify!($ident))); + } + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct TheMemoize(T); + impl Memoize for TheMemoize { + type Input = (); + type InputOwned = (); + type Output = Option>>>; + fn inner(self, _input: &Self::Input) -> Self::Output { + if self.0.width() == 0 { + None + } else { + Some($ident(self.0)) + } + } + } + let Some(module) = TheMemoize(ty).get_owned(()) else { + return 0_hdl_u0.cast_bits_to(ty); + }; + #[hdl] + let $ident = instance(module); + $ident.out + } + }; +} + +make_any_const_fn!(any_const, "anyconst"); +make_any_const_fn!(any_seq, "anyseq"); +make_any_const_fn!(all_const, "allconst"); +make_any_const_fn!(all_seq, "allseq"); diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index f1a9736..46d9e6e 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -14,8 +14,9 @@ pub use crate::{ ReduceBits, ToExpr, }, formal::{ - formal_global_clock, formal_reset, hdl_assert, hdl_assert_with_enable, hdl_assume, - hdl_assume_with_enable, hdl_cover, hdl_cover_with_enable, MakeFormalExpr, + all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert, + hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover, + hdl_cover_with_enable, MakeFormalExpr, }, hdl, hdl_module, int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType}, diff --git a/crates/fayalite/src/testing.rs b/crates/fayalite/src/testing.rs index 07252f1..4517e34 100644 --- a/crates/fayalite/src/testing.rs +++ b/crates/fayalite/src/testing.rs @@ -68,7 +68,6 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { }, ); let mut dir = String::with_capacity(64); - write!(dir, "{simple_hash:08x}-").unwrap(); for ch in Path::new(file) .file_stem() .unwrap_or_default() @@ -84,6 +83,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { _ => '_', }); } + write!(dir, "-{simple_hash:08x}").unwrap(); let index = *DIRS .lock() .unwrap() @@ -91,7 +91,7 @@ fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf { .entry_ref(&dir) .and_modify(|v| *v += 1) .or_insert(0); - write!(dir, ".{index}").unwrap(); + write!(dir, "-{index}").unwrap(); get_cargo_target_dir() .join("fayalite_assert_formal") .join(dir) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 41299a6..f3d5653 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -49,7 +49,6 @@ impl ReadyValid { } } -// TODO: needs testing #[hdl_module] pub fn queue( ty: T, @@ -134,7 +133,7 @@ pub fn queue( #[hdl] if inp_fire { #[hdl] - if inp_index.cmp_eq(capacity) { + if inp_index.cmp_eq(capacity.get() - 1) { connect_any(inp_index, 0_hdl_u0); } else { connect_any(inp_index, inp_index + 1_hdl_u1); @@ -144,7 +143,7 @@ pub fn queue( #[hdl] if out_fire { #[hdl] - if out_index.cmp_eq(capacity) { + if out_index.cmp_eq(capacity.get() - 1) { connect_any(out_index, 0_hdl_u0); } else { connect_any(out_index, out_index + 1_hdl_u1); @@ -153,10 +152,12 @@ pub fn queue( #[hdl] if indexes_equal { - connect( - count, - maybe_full.cast_to_static::>() << (count_ty.width() - 1), - ); + #[hdl] + if maybe_full { + connect_any(count, capacity); + } else { + connect_any(count, 0_hdl_u0); + } } else { if capacity.is_power_of_two() { debug_assert_eq!(count_ty.width(), index_ty.width() + 1); @@ -182,6 +183,7 @@ mod tests { use crate::{ cli::FormalMode, firrtl::ExportOptions, module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal, + ty::StaticType, }; use std::num::NonZero; @@ -190,8 +192,8 @@ mod tests { assert_formal( format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), - FormalMode::BMC, - 20, + FormalMode::Prove, + 14, None, ExportOptions { simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), @@ -200,12 +202,6 @@ mod tests { ); #[hdl_module] fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { - #[hdl] - let inp_data: HdlOption> = m.input(); - #[hdl] - let out_ready: Bool = m.input(); - #[hdl] - let start_check: Bool = m.input(); #[hdl] let clk: Clock = m.input(); #[hdl] @@ -219,6 +215,24 @@ mod tests { }, ); #[hdl] + let inp_data: HdlOption> = wire(); + #[hdl] + if any_seq(Bool) { + connect(inp_data, HdlSome(any_seq(UInt::<8>::TYPE))); + } else { + connect(inp_data, HdlNone()); + } + #[hdl] + let out_ready: Bool = wire(); + connect(out_ready, any_seq(Bool)); + let index_ty: UInt<32> = UInt::TYPE; + #[hdl] + let index_to_check = wire(); + connect(index_to_check, any_const(index_ty)); + let index_max = !index_ty.zero(); + // we saturate at index_max, so only check indexes where we properly maintain position + hdl_assume(clk, index_to_check.cmp_ne(index_max), ""); + #[hdl] let dut = instance(queue( UInt[ConstUsize::<8>], capacity, @@ -228,63 +242,97 @@ mod tests { connect(dut.cd, cd); connect(dut.inp.data, inp_data); connect(dut.out.ready, out_ready); + hdl_assume( + clk, + index_to_check.cmp_ne(!Expr::ty(index_to_check).zero()), + "", + ); + #[hdl] - let count = reg_builder().clock_domain(cd).reset(0u32); + let expected_count = reg_builder().clock_domain(cd).reset(0u32); #[hdl] - let next_count = wire(); - connect(next_count, count); - connect(count, next_count); + let next_expected_count = wire(); + connect(next_expected_count, expected_count); + connect(expected_count, next_expected_count); #[hdl] if ReadyValid::fire(dut.inp) & !ReadyValid::fire(dut.out) { - connect_any(next_count, count + 1u8); + connect_any(next_expected_count, expected_count + 1u8); } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) { - connect_any(next_count, count - 1u8); + connect_any(next_expected_count, expected_count - 1u8); } - hdl_assert(cd.clk, count.cmp_eq(dut.count), ""); + hdl_assert(cd.clk, expected_count.cmp_eq(dut.count), ""); + #[hdl] - let started_check = reg_builder().clock_domain(cd).reset(false); + let prev_out_ready = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + connect_any( + prev_out_ready, + (prev_out_ready << 1) | out_ready.cast_to(UInt[1]), + ); #[hdl] - let steps_till_output = reg_builder().clock_domain(cd).reset(0u32); + let prev_inp_valid = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + connect_any( + prev_inp_valid, + (prev_inp_valid << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), + ); + hdl_assume(clk, (prev_out_ready & prev_inp_valid).cmp_ne(0u8), ""); + #[hdl] - let expected_output = reg_builder().clock_domain(cd).reset(HdlNone()); + let inp_index = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - if start_check & !started_check { + let stored_inp_data = reg_builder().clock_domain(cd).reset(0u8); + + #[hdl] + if let HdlSome(data) = ReadyValid::fire_data(dut.inp) { #[hdl] - if let HdlSome(inp) = ReadyValid::fire_data(dut.inp) { - connect(started_check, true); - connect_any( - steps_till_output, - count + (!ReadyValid::fire(dut.out)).cast_to(UInt[1]), - ); - connect(expected_output, HdlSome(inp)); - } - } else if started_check & steps_till_output.cmp_ne(0u32) & ReadyValid::fire(dut.out) { - connect_any(steps_till_output, steps_till_output - 1u32); - } - #[hdl] - let stored_output = reg_builder().clock_domain(cd).reset(HdlNone()); - #[hdl] - if let HdlSome(out) = ReadyValid::fire_data(dut.out) { - #[hdl] - if (start_check & !started_check) | (started_check & steps_till_output.cmp_ne(0u32)) - { - connect(stored_output, HdlSome(out)); - } - } - #[hdl] - if started_check & steps_till_output.cmp_eq(0u32) { - #[hdl] - if let HdlSome(expected_output) = expected_output { + if inp_index.cmp_lt(index_max) { + connect_any(inp_index, inp_index + 1u8); #[hdl] - if let HdlSome(stored_output) = stored_output { - hdl_assert(cd.clk, stored_output.cmp_eq(expected_output), ""); - } else { - hdl_assert(cd.clk, false.to_expr(), ""); + if inp_index.cmp_eq(index_to_check) { + connect(stored_inp_data, data); } - } else { - hdl_assert(cd.clk, false.to_expr(), ""); } } + + #[hdl] + if inp_index.cmp_lt(index_to_check) { + hdl_assert(clk, stored_inp_data.cmp_eq(0u8), ""); + } + + #[hdl] + let out_index = reg_builder().clock_domain(cd).reset(index_ty.zero()); + #[hdl] + let stored_out_data = reg_builder().clock_domain(cd).reset(0u8); + + #[hdl] + if let HdlSome(data) = ReadyValid::fire_data(dut.out) { + #[hdl] + if out_index.cmp_lt(index_max) { + connect_any(out_index, out_index + 1u8); + #[hdl] + if out_index.cmp_eq(index_to_check) { + connect(stored_out_data, data); + } + } + } + + #[hdl] + if out_index.cmp_lt(index_to_check) { + hdl_assert(clk, stored_out_data.cmp_eq(0u8), ""); + } + + hdl_assert(clk, inp_index.cmp_ge(out_index), ""); + + #[hdl] + if inp_index.cmp_lt(index_max) & out_index.cmp_lt(index_max) { + hdl_assert(clk, expected_count.cmp_eq(inp_index - out_index), ""); + } else { + hdl_assert(clk, expected_count.cmp_ge(inp_index - out_index), ""); + } + + #[hdl] + if inp_index.cmp_gt(index_to_check) & out_index.cmp_gt(index_to_check) { + hdl_assert(clk, stored_inp_data.cmp_eq(stored_out_data), ""); + } } } @@ -328,49 +376,41 @@ mod tests { test_queue(NonZero::new(2).unwrap(), true, true); } - #[cfg(todo)] #[test] fn test_3_false_false() { test_queue(NonZero::new(3).unwrap(), false, false); } - #[cfg(todo)] #[test] fn test_3_false_true() { test_queue(NonZero::new(3).unwrap(), false, true); } - #[cfg(todo)] #[test] fn test_3_true_false() { test_queue(NonZero::new(3).unwrap(), true, false); } - #[cfg(todo)] #[test] fn test_3_true_true() { test_queue(NonZero::new(3).unwrap(), true, true); } - #[cfg(todo)] #[test] fn test_4_false_false() { test_queue(NonZero::new(4).unwrap(), false, false); } - #[cfg(todo)] #[test] fn test_4_false_true() { test_queue(NonZero::new(4).unwrap(), false, true); } - #[cfg(todo)] #[test] fn test_4_true_false() { test_queue(NonZero::new(4).unwrap(), true, false); } - #[cfg(todo)] #[test] fn test_4_true_true() { test_queue(NonZero::new(4).unwrap(), true, true); diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 7d12739..0d690f2 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -3394,7 +3394,7 @@ circuit check_formal: %[[ { "class": "firrtl.transforms.BlackBoxInlineAnno", "name": "fayalite_formal_reset.v", - "text": "module __fayalite_formal_reset(output rst);\n reg rst;\n (* gclk *)\n reg gclk;\n initial rst = 1;\n always @(posedge gclk)\n rst <= 0;\nendmodule\n", + "text": "module __fayalite_formal_reset(output rst);\n assign rst = $initstate;\nendmodule\n", "target": "~check_formal|formal_reset" } ]] @@ -3407,20 +3407,20 @@ circuit check_formal: %[[ input pred1: UInt<1> @[module-XXXXXXXXXX.rs 6:1] input pred2: UInt<1> @[module-XXXXXXXXXX.rs 7:1] input pred3: UInt<1> @[module-XXXXXXXXXX.rs 8:1] - inst formal_reset of formal_reset @[formal.rs 189:24] + inst formal_reset of formal_reset @[formal.rs 185:24] assert(clk, pred1, and(en1, not(formal_reset.rst)), "en check 1") @[module-XXXXXXXXXX.rs 9:1] - inst formal_reset_1 of formal_reset @[formal.rs 189:24] + inst formal_reset_1 of formal_reset @[formal.rs 185:24] assume(clk, pred2, and(en2, not(formal_reset_1.rst)), "en check 2") @[module-XXXXXXXXXX.rs 10:1] - inst formal_reset_2 of formal_reset @[formal.rs 189:24] + inst formal_reset_2 of formal_reset @[formal.rs 185:24] cover(clk, pred3, and(en3, not(formal_reset_2.rst)), "en check 3") @[module-XXXXXXXXXX.rs 11:1] - inst formal_reset_3 of formal_reset @[formal.rs 189:24] + inst formal_reset_3 of formal_reset @[formal.rs 185:24] assert(clk, pred1, and(UInt<1>(0h1), not(formal_reset_3.rst)), "check 1") @[module-XXXXXXXXXX.rs 12:1] - inst formal_reset_4 of formal_reset @[formal.rs 189:24] + inst formal_reset_4 of formal_reset @[formal.rs 185:24] assume(clk, pred2, and(UInt<1>(0h1), not(formal_reset_4.rst)), "check 2") @[module-XXXXXXXXXX.rs 13:1] - inst formal_reset_5 of formal_reset @[formal.rs 189:24] + inst formal_reset_5 of formal_reset @[formal.rs 185:24] cover(clk, pred3, and(UInt<1>(0h1), not(formal_reset_5.rst)), "check 3") @[module-XXXXXXXXXX.rs 14:1] - extmodule formal_reset: @[formal.rs 168:5] - output rst: UInt<1> @[formal.rs 171:32] + extmodule formal_reset: @[formal.rs 169:5] + output rst: UInt<1> @[formal.rs 172:32] defname = __fayalite_formal_reset "#, }; From 9f154e6b9619a081b94628cacecffbdc2504c25f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 23:36:39 -0700 Subject: [PATCH 047/190] try caching ccache manually --- .forgejo/workflows/test.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 83d291b..962449e 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -34,8 +34,13 @@ jobs: tcl-dev \ z3 \ zlib1g-dev \ - - name: ccache - uses: https://github.com/hendrikmuhs/ccache-action@v1 + - name: Cache ccache dir + uses: https://code.forgejo.org/actions/checkout@v3 + with: + path: ~/.ccache + key: ${{ runner.os }}-${{ github.sha }} + restore-keys: | + ${{ runner.os }}- - run: | export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" echo "$PATH" >> "$GITHUB_PATH" From c0d4de56a9756fecc6e9d4a66017258304363018 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 3 Oct 2024 23:40:44 -0700 Subject: [PATCH 048/190] try to make yosys build faster --- .forgejo/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 962449e..4cbd5f2 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -53,8 +53,8 @@ jobs: make -C sby install - run: | git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git - cd yosys - make install -j6 + make -C yosys -j$(nproc) + make -C yosys install - run: | wget -O firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 firrtl.tar.gz' From 487af0715413be3cbd99cfa343e46749599c4cbb Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Oct 2024 01:03:17 -0700 Subject: [PATCH 049/190] yosys build runs out of memory --- .forgejo/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 4cbd5f2..ef58281 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -53,7 +53,7 @@ jobs: make -C sby install - run: | git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git - make -C yosys -j$(nproc) + make -C yosys -j2 make -C yosys install - run: | wget -O firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz From b7f11011643f4e4ab5fc32c9ddab6aa6eb726060 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Oct 2024 17:03:51 -0700 Subject: [PATCH 050/190] reduce parallelism to fit within the number of available cpus even when running sby in prove mode (which likes to run 2 smt solvers in parallel) --- crates/fayalite/src/cli.rs | 182 +++++++++++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 20 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index f1d69d2..fa1b247 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -17,8 +17,10 @@ use std::{ ffi::OsString, fmt::{self, Write}, fs, io, mem, + num::NonZeroUsize, path::{Path, PathBuf}, process, + sync::{Condvar, Mutex, OnceLock}, }; use tempfile::TempDir; @@ -46,9 +48,109 @@ impl From for CliError { } } +#[derive(Debug)] +pub struct AcquiredJob { + job_count: NonZeroUsize, +} + +impl Drop for AcquiredJob { + fn drop(&mut self) { + Self::change_job_count(Some(self.job_count), None); + } +} + +impl AcquiredJob { + pub fn max_available_job_count() -> NonZeroUsize { + static RETVAL: OnceLock = OnceLock::new(); + *RETVAL.get_or_init(|| { + std::thread::available_parallelism().unwrap_or(NonZeroUsize::new(1).unwrap()) + }) + } + fn change_job_count(released: Option, acquired: Option) { + static AVAILABLE_JOB_COUNT: OnceLock> = OnceLock::new(); + static COND_VAR: Condvar = Condvar::new(); + let mut available_job_count_lock = AVAILABLE_JOB_COUNT + .get_or_init(|| Mutex::new(Self::max_available_job_count().get())) + .lock() + .unwrap(); + if let Some(released) = released { + *available_job_count_lock = available_job_count_lock + .checked_add(released.get()) + .expect("tried to release too many jobs"); + COND_VAR.notify_all(); + } + if let Some(acquired) = acquired { + loop { + match available_job_count_lock.checked_sub(acquired.get()) { + Some(jobs_left) => { + *available_job_count_lock = jobs_left; + break; + } + None => { + available_job_count_lock = COND_VAR.wait(available_job_count_lock).unwrap() + } + } + } + } + } + pub fn job_count(&self) -> NonZeroUsize { + self.job_count + } + pub fn increase_job_count( + &mut self, + new_minimum_count: NonZeroUsize, + f: impl FnOnce(&mut AcquiredJob) -> R, + ) -> R { + if new_minimum_count <= self.job_count { + return f(self); + } + struct ReleaseOnDrop<'a> { + acquired_job: &'a mut AcquiredJob, + old_job_count: NonZeroUsize, + } + impl Drop for ReleaseOnDrop<'_> { + fn drop(&mut self) { + AcquiredJob::change_job_count( + NonZeroUsize::new(self.acquired_job.job_count.get() - self.old_job_count.get()), + None, + ); + self.acquired_job.job_count = self.old_job_count; + } + } + let release_on_drop = ReleaseOnDrop { + old_job_count: self.job_count, + acquired_job: self, + }; + // release our current jobs when acquiring new jobs to avoid deadlock + Self::change_job_count(Some(release_on_drop.old_job_count), Some(new_minimum_count)); + release_on_drop.acquired_job.job_count = new_minimum_count; + let retval = f(release_on_drop.acquired_job); + drop(release_on_drop); + retval + } + pub fn acquire_jobs(job_count: NonZeroUsize) -> Self { + Self::change_job_count(None, Some(job_count)); + Self { job_count } + } + pub fn run_command( + &mut self, + mut cmd: std::process::Command, + f: impl FnOnce(&mut std::process::Command) -> std::io::Result, + ) -> std::io::Result { + // TODO: if we implement a make job server, add the proper env vars to cmd + f(&mut cmd) + } +} + pub trait RunPhase { type Output; - fn run(&self, arg: Arg) -> Result; + fn run(&self, arg: Arg) -> Result { + self.run_with_job( + arg, + &mut AcquiredJob::acquire_jobs(NonZeroUsize::new(1).unwrap()), + ) + } + fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result; } #[derive(Parser, Debug, Clone)] @@ -93,6 +195,7 @@ impl BaseArgs { /// handles possibly redirecting the command's output for Rust tests pub fn run_external_command( &self, + _acquired_job: &mut AcquiredJob, mut command: process::Command, ) -> io::Result { if self.redirect_output_for_rust_test { @@ -150,7 +253,11 @@ impl FirrtlOutput { } impl FirrtlArgs { - fn run_impl(&self, top_module: Module) -> Result { + fn run_impl( + &self, + top_module: Module, + _acquired_job: &mut AcquiredJob, + ) -> Result { let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?; let firrtl::FileBackend { top_fir_file_stem, @@ -170,15 +277,23 @@ impl FirrtlArgs { impl RunPhase> for FirrtlArgs { type Output = FirrtlOutput; - fn run(&self, top_module: Module) -> Result { - self.run_impl(top_module.canonical()) + fn run_with_job( + &self, + top_module: Module, + acquired_job: &mut AcquiredJob, + ) -> Result { + self.run_impl(top_module.canonical(), acquired_job) } } impl RunPhase>> for FirrtlArgs { type Output = FirrtlOutput; - fn run(&self, top_module: Interned>) -> Result { - self.run(*top_module) + fn run_with_job( + &self, + top_module: Interned>, + acquired_job: &mut AcquiredJob, + ) -> Result { + self.run_with_job(*top_module, acquired_job) } } @@ -298,7 +413,11 @@ impl VerilogArgs { } Ok(output) } - fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result { + fn run_impl( + &self, + firrtl_output: FirrtlOutput, + acquired_job: &mut AcquiredJob, + ) -> Result { let Self { firrtl, firtool, @@ -323,7 +442,7 @@ impl VerilogArgs { } cmd.args(firtool_extra_args); cmd.current_dir(&output.firrtl.output_dir); - let status = firrtl.base.run_external_command(cmd)?; + let status = firrtl.base.run_external_command(acquired_job, cmd)?; if status.success() { self.process_unadjusted_verilog_file(output) } else { @@ -340,9 +459,9 @@ where FirrtlArgs: RunPhase, { type Output = VerilogOutput; - fn run(&self, arg: Arg) -> Result { - let firrtl_output = self.firrtl.run(arg)?; - self.run_impl(firrtl_output) + fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result { + let firrtl_output = self.firrtl.run_with_job(arg, acquired_job)?; + self.run_impl(firrtl_output, acquired_job) } } @@ -365,6 +484,14 @@ impl FormalMode { FormalMode::Cover => "cover", } } + fn needs_extra_job(self) -> bool { + match self { + FormalMode::BMC => false, + FormalMode::Prove => true, + FormalMode::Live => false, + FormalMode::Cover => false, + } + } } impl fmt::Display for FormalMode { @@ -508,7 +635,11 @@ impl FormalArgs { writeln!(retval, "prep -top {top_module}").unwrap(); Ok(retval) } - fn run_impl(&self, verilog_output: VerilogOutput) -> Result { + fn run_impl( + &self, + verilog_output: VerilogOutput, + acquired_job: &mut AcquiredJob, + ) -> Result { let output = FormalOutput { verilog: verilog_output, }; @@ -519,7 +650,18 @@ impl FormalArgs { cmd.arg(sby_file.file_name().unwrap()); cmd.args(&self.sby_extra_args); cmd.current_dir(&output.verilog.firrtl.output_dir); - let status = self.verilog.firrtl.base.run_external_command(cmd)?; + let new_minimum_count = if self.mode.needs_extra_job() { + NonZeroUsize::new(2).unwrap() + } else { + NonZeroUsize::new(1).unwrap() + }; + let new_minimum_count = AcquiredJob::max_available_job_count().min(new_minimum_count); + let status = acquired_job.increase_job_count(new_minimum_count, |acquired_job| { + self.verilog + .firrtl + .base + .run_external_command(acquired_job, cmd) + })?; if status.success() { Ok(output) } else { @@ -536,9 +678,9 @@ where VerilogArgs: RunPhase, { type Output = FormalOutput; - fn run(&self, arg: Arg) -> Result { - let verilog_output = self.verilog.run(arg)?; - self.run_impl(verilog_output) + fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result { + let verilog_output = self.verilog.run_with_job(arg, acquired_job)?; + self.run_impl(verilog_output, acquired_job) } } @@ -627,16 +769,16 @@ where FirrtlArgs: RunPhase, { type Output = (); - fn run(&self, arg: T) -> Result { + fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result { match &self.subcommand { CliCommand::Firrtl(c) => { - c.run(arg)?; + c.run_with_job(arg, acquired_job)?; } CliCommand::Verilog(c) => { - c.run(arg)?; + c.run_with_job(arg, acquired_job)?; } CliCommand::Formal(c) => { - c.run(arg)?; + c.run_with_job(arg, acquired_job)?; } } Ok(()) From ec77559e2b9d2460674082ec5be3691381028991 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 4 Oct 2024 17:10:06 -0700 Subject: [PATCH 051/190] fix cache action name --- .forgejo/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index ef58281..68ded33 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -35,7 +35,7 @@ jobs: z3 \ zlib1g-dev \ - name: Cache ccache dir - uses: https://code.forgejo.org/actions/checkout@v3 + uses: https://code.forgejo.org/actions/cache@v3 with: path: ~/.ccache key: ${{ runner.os }}-${{ github.sha }} From e05c3686880e8d0d433356d866bd6e38a26dd8fc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 6 Oct 2024 18:50:09 -0700 Subject: [PATCH 052/190] change register names to end in `_reg` by convention --- crates/fayalite/examples/blinky.rs | 8 +- .../hdl_let_statements/registers.rs | 9 +- crates/fayalite/src/util/ready_valid.rs | 125 ++++++++++-------- crates/fayalite/tests/module.rs | 14 +- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/crates/fayalite/examples/blinky.rs b/crates/fayalite/examples/blinky.rs index 94c7910..87b77c1 100644 --- a/crates/fayalite/examples/blinky.rs +++ b/crates/fayalite/examples/blinky.rs @@ -17,15 +17,15 @@ fn blinky(clock_frequency: u64) { let max_value = clock_frequency / 2 - 1; let int_ty = UInt::range_inclusive(0..=max_value); #[hdl] - let counter: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); + let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty)); #[hdl] let output_reg: Bool = reg_builder().clock_domain(cd).reset(false); #[hdl] - if counter.cmp_eq(max_value) { - connect_any(counter, 0u8); + if counter_reg.cmp_eq(max_value) { + connect_any(counter_reg, 0u8); connect(output_reg, !output_reg); } else { - connect_any(counter, counter + 1_hdl_u1); + connect_any(counter_reg, counter_reg + 1_hdl_u1); } #[hdl] let led: Bool = m.output(); diff --git a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs index 5a81c5b..2876389 100644 --- a/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs +++ b/crates/fayalite/src/_docs/modules/module_bodies/hdl_let_statements/registers.rs @@ -9,6 +9,9 @@ //! //! Registers follow [connection semantics], which are unlike assignments in software, so you should read it. //! +//! By convention, register names end in `_reg` -- this helps you tell which values are written +//! immediately or on the next clock edge when connecting to them. +//! //! ``` //! # use fayalite::prelude::*; //! # #[hdl_module] @@ -18,11 +21,11 @@ //! #[hdl] //! let cd: ClockDomain = m.input(); //! #[hdl] -//! let my_register: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8); +//! let my_reg: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8); //! #[hdl] //! if v { -//! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs. -//! connect(my_register, 0x45_hdl_u8); +//! // my_reg is only changed when both `v` is set and `cd`'s clock edge occurs. +//! connect(my_reg, 0x45_hdl_u8); //! } //! # } //! ``` diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index f3d5653..4fe480d 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -69,11 +69,11 @@ pub fn queue( let count: UInt = m.output(count_ty); #[hdl] - let inp_index = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] - let out_index = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); + let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty)); #[hdl] - let maybe_full = reg_builder().clock_domain(cd).reset(false); + let maybe_full_reg = reg_builder().clock_domain(cd).reset(false); #[hdl] let mut mem = memory(ty); @@ -89,18 +89,18 @@ pub fn queue( connect(out_fire, ReadyValid::fire(out)); #[hdl] let indexes_equal: Bool = wire(); - connect(indexes_equal, inp_index.cmp_eq(out_index)); + connect(indexes_equal, inp_index_reg.cmp_eq(out_index_reg)); #[hdl] let empty: Bool = wire(); - connect(empty, indexes_equal & !maybe_full); + connect(empty, indexes_equal & !maybe_full_reg); #[hdl] let full: Bool = wire(); - connect(full, indexes_equal & maybe_full); + connect(full, indexes_equal & maybe_full_reg); - connect(read_port.addr, out_index); + connect(read_port.addr, out_index_reg); connect(read_port.en, true); connect(read_port.clk, cd.clk); - connect(write_port.addr, inp_index); + connect(write_port.addr, inp_index_reg); connect(write_port.en, inp_fire); connect(write_port.clk, cd.clk); connect(write_port.data, HdlOption::unwrap_or(inp.data, ty.uninit())); @@ -127,33 +127,33 @@ pub fn queue( #[hdl] if inp_fire.cmp_ne(out_fire) { - connect(maybe_full, inp_fire); + connect(maybe_full_reg, inp_fire); } #[hdl] if inp_fire { #[hdl] - if inp_index.cmp_eq(capacity.get() - 1) { - connect_any(inp_index, 0_hdl_u0); + if inp_index_reg.cmp_eq(capacity.get() - 1) { + connect_any(inp_index_reg, 0_hdl_u0); } else { - connect_any(inp_index, inp_index + 1_hdl_u1); + connect_any(inp_index_reg, inp_index_reg + 1_hdl_u1); } } #[hdl] if out_fire { #[hdl] - if out_index.cmp_eq(capacity.get() - 1) { - connect_any(out_index, 0_hdl_u0); + if out_index_reg.cmp_eq(capacity.get() - 1) { + connect_any(out_index_reg, 0_hdl_u0); } else { - connect_any(out_index, out_index + 1_hdl_u1); + connect_any(out_index_reg, out_index_reg + 1_hdl_u1); } } #[hdl] if indexes_equal { #[hdl] - if maybe_full { + if maybe_full_reg { connect_any(count, capacity); } else { connect_any(count, 0_hdl_u0); @@ -163,15 +163,18 @@ pub fn queue( debug_assert_eq!(count_ty.width(), index_ty.width() + 1); #[hdl] let count_lower = wire(index_ty); - connect(count_lower, (inp_index - out_index).cast_to(index_ty)); // wrap + connect( + count_lower, + (inp_index_reg - out_index_reg).cast_to(index_ty), + ); // wrap connect(count, count_lower.cast_to(count_ty)); } else { debug_assert_eq!(count_ty.width(), index_ty.width()); #[hdl] - if inp_index.cmp_lt(out_index) { - connect(count, inp_index + capacity - out_index); + if inp_index_reg.cmp_lt(out_index_reg) { + connect(count, inp_index_reg + capacity - out_index_reg); } else { - connect(count, inp_index - out_index); + connect(count, inp_index_reg - out_index_reg); } } } @@ -249,89 +252,101 @@ mod tests { ); #[hdl] - let expected_count = reg_builder().clock_domain(cd).reset(0u32); + let expected_count_reg = reg_builder().clock_domain(cd).reset(0u32); #[hdl] let next_expected_count = wire(); - connect(next_expected_count, expected_count); - connect(expected_count, next_expected_count); + connect(next_expected_count, expected_count_reg); + connect(expected_count_reg, next_expected_count); #[hdl] if ReadyValid::fire(dut.inp) & !ReadyValid::fire(dut.out) { - connect_any(next_expected_count, expected_count + 1u8); + connect_any(next_expected_count, expected_count_reg + 1u8); } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) { - connect_any(next_expected_count, expected_count - 1u8); + connect_any(next_expected_count, expected_count_reg - 1u8); } - hdl_assert(cd.clk, expected_count.cmp_eq(dut.count), ""); + hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); #[hdl] - let prev_out_ready = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + let prev_out_ready_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); connect_any( - prev_out_ready, - (prev_out_ready << 1) | out_ready.cast_to(UInt[1]), + prev_out_ready_reg, + (prev_out_ready_reg << 1) | out_ready.cast_to(UInt[1]), ); #[hdl] - let prev_inp_valid = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + let prev_inp_valid_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); connect_any( - prev_inp_valid, - (prev_inp_valid << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), + prev_inp_valid_reg, + (prev_inp_valid_reg << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), + ); + hdl_assume( + clk, + (prev_out_ready_reg & prev_inp_valid_reg).cmp_ne(0u8), + "", ); - hdl_assume(clk, (prev_out_ready & prev_inp_valid).cmp_ne(0u8), ""); #[hdl] - let inp_index = reg_builder().clock_domain(cd).reset(index_ty.zero()); + let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - let stored_inp_data = reg_builder().clock_domain(cd).reset(0u8); + let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8); #[hdl] if let HdlSome(data) = ReadyValid::fire_data(dut.inp) { #[hdl] - if inp_index.cmp_lt(index_max) { - connect_any(inp_index, inp_index + 1u8); + if inp_index_reg.cmp_lt(index_max) { + connect_any(inp_index_reg, inp_index_reg + 1u8); #[hdl] - if inp_index.cmp_eq(index_to_check) { - connect(stored_inp_data, data); + if inp_index_reg.cmp_eq(index_to_check) { + connect(stored_inp_data_reg, data); } } } #[hdl] - if inp_index.cmp_lt(index_to_check) { - hdl_assert(clk, stored_inp_data.cmp_eq(0u8), ""); + if inp_index_reg.cmp_lt(index_to_check) { + hdl_assert(clk, stored_inp_data_reg.cmp_eq(0u8), ""); } #[hdl] - let out_index = reg_builder().clock_domain(cd).reset(index_ty.zero()); + let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - let stored_out_data = reg_builder().clock_domain(cd).reset(0u8); + let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8); #[hdl] if let HdlSome(data) = ReadyValid::fire_data(dut.out) { #[hdl] - if out_index.cmp_lt(index_max) { - connect_any(out_index, out_index + 1u8); + if out_index_reg.cmp_lt(index_max) { + connect_any(out_index_reg, out_index_reg + 1u8); #[hdl] - if out_index.cmp_eq(index_to_check) { - connect(stored_out_data, data); + if out_index_reg.cmp_eq(index_to_check) { + connect(stored_out_data_reg, data); } } } #[hdl] - if out_index.cmp_lt(index_to_check) { - hdl_assert(clk, stored_out_data.cmp_eq(0u8), ""); + if out_index_reg.cmp_lt(index_to_check) { + hdl_assert(clk, stored_out_data_reg.cmp_eq(0u8), ""); } - hdl_assert(clk, inp_index.cmp_ge(out_index), ""); + hdl_assert(clk, inp_index_reg.cmp_ge(out_index_reg), ""); #[hdl] - if inp_index.cmp_lt(index_max) & out_index.cmp_lt(index_max) { - hdl_assert(clk, expected_count.cmp_eq(inp_index - out_index), ""); + if inp_index_reg.cmp_lt(index_max) & out_index_reg.cmp_lt(index_max) { + hdl_assert( + clk, + expected_count_reg.cmp_eq(inp_index_reg - out_index_reg), + "", + ); } else { - hdl_assert(clk, expected_count.cmp_ge(inp_index - out_index), ""); + hdl_assert( + clk, + expected_count_reg.cmp_ge(inp_index_reg - out_index_reg), + "", + ); } #[hdl] - if inp_index.cmp_gt(index_to_check) & out_index.cmp_gt(index_to_check) { - hdl_assert(clk, stored_inp_data.cmp_eq(stored_out_data), ""); + if inp_index_reg.cmp_gt(index_to_check) & out_index_reg.cmp_gt(index_to_check) { + hdl_assert(clk, stored_inp_data_reg.cmp_eq(stored_out_data_reg), ""); } } } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 0d690f2..69438f2 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -41,13 +41,13 @@ pub fn my_module(width: usize) { #[hdl] let m2 = instance(module2()); #[hdl] - let r: UInt<8> = reg_builder().clock_domain(clock_domain).reset(8_hdl_u8); + let r_reg: UInt<8> = reg_builder().clock_domain(clock_domain).reset(8_hdl_u8); connect(m2.i, i); - connect(r, m2.o); + connect(r_reg, m2.o); connect( o, #[hdl] - [r, r, b'\r'_hdl], + [r_reg, r_reg, b'\r'_hdl], ); connect(o[1], 30_hdl_u8); connect(o2, i2); @@ -109,12 +109,12 @@ circuit my_module: connect _bundle_literal_expr.`1`, SInt<5>(-0h3) connect o3, _bundle_literal_expr @[module-XXXXXXXXXX.rs 12:1] inst m2 of module2 @[module-XXXXXXXXXX.rs 13:1] - regreset r: UInt<8>, clock_domain.clk, clock_domain.rst, UInt<8>(0h8) @[module-XXXXXXXXXX.rs 14:1] + regreset r_reg: UInt<8>, clock_domain.clk, clock_domain.rst, UInt<8>(0h8) @[module-XXXXXXXXXX.rs 14:1] connect m2.i, i @[module-XXXXXXXXXX.rs 15:1] - connect r, m2.o @[module-XXXXXXXXXX.rs 16:1] + connect r_reg, m2.o @[module-XXXXXXXXXX.rs 16:1] wire _array_literal_expr: UInt<8>[3] - connect _array_literal_expr[0], r - connect _array_literal_expr[1], r + connect _array_literal_expr[0], r_reg + connect _array_literal_expr[1], r_reg connect _array_literal_expr[2], UInt<8>(0hD) connect o, _array_literal_expr @[module-XXXXXXXXXX.rs 17:1] connect o[1], UInt<8>(0h1E) @[module-XXXXXXXXXX.rs 18:1] From 2e8b73d2fc8c0161deb56da86ef1cd2679978607 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 6 Oct 2024 19:04:48 -0700 Subject: [PATCH 053/190] rename fire/fire_data to firing/firing_data --- crates/fayalite/src/util/ready_valid.rs | 46 ++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index 4fe480d..82d3f1e 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -12,28 +12,28 @@ pub struct ReadyValid { impl ReadyValid { #[hdl] - pub fn fire(expr: Expr) -> Expr { + pub fn firing(expr: Expr) -> Expr { #[hdl] - let fire: Bool = wire(); + let firing: Bool = wire(); #[hdl] match expr.data { - HdlNone => connect(fire, false), - HdlSome(_) => connect(fire, expr.ready), + HdlNone => connect(firing, false), + HdlSome(_) => connect(firing, expr.ready), } - fire + firing } #[hdl] - pub fn fire_data(expr: impl ToExpr) -> Expr> { + pub fn firing_data(expr: impl ToExpr) -> Expr> { let expr = expr.to_expr(); let option_ty = Expr::ty(expr).data; #[hdl] - let fire_data = wire(option_ty); - connect(fire_data, option_ty.HdlNone()); + let firing_data = wire(option_ty); + connect(firing_data, option_ty.HdlNone()); #[hdl] if expr.ready { - connect(fire_data, expr.data); + connect(firing_data, expr.data); } - fire_data + firing_data } #[hdl] pub fn map( @@ -82,11 +82,11 @@ pub fn queue( let write_port = mem.new_write_port(); #[hdl] - let inp_fire: Bool = wire(); - connect(inp_fire, ReadyValid::fire(inp)); + let inp_firing: Bool = wire(); + connect(inp_firing, ReadyValid::firing(inp)); #[hdl] - let out_fire: Bool = wire(); - connect(out_fire, ReadyValid::fire(out)); + let out_firing: Bool = wire(); + connect(out_firing, ReadyValid::firing(out)); #[hdl] let indexes_equal: Bool = wire(); connect(indexes_equal, inp_index_reg.cmp_eq(out_index_reg)); @@ -101,7 +101,7 @@ pub fn queue( connect(read_port.en, true); connect(read_port.clk, cd.clk); connect(write_port.addr, inp_index_reg); - connect(write_port.en, inp_fire); + connect(write_port.en, inp_firing); connect(write_port.clk, cd.clk); connect(write_port.data, HdlOption::unwrap_or(inp.data, ty.uninit())); connect(write_port.mask, splat_mask(ty, true.to_expr())); @@ -126,12 +126,12 @@ pub fn queue( } #[hdl] - if inp_fire.cmp_ne(out_fire) { - connect(maybe_full_reg, inp_fire); + if inp_firing.cmp_ne(out_firing) { + connect(maybe_full_reg, inp_firing); } #[hdl] - if inp_fire { + if inp_firing { #[hdl] if inp_index_reg.cmp_eq(capacity.get() - 1) { connect_any(inp_index_reg, 0_hdl_u0); @@ -141,7 +141,7 @@ pub fn queue( } #[hdl] - if out_fire { + if out_firing { #[hdl] if out_index_reg.cmp_eq(capacity.get() - 1) { connect_any(out_index_reg, 0_hdl_u0); @@ -258,9 +258,9 @@ mod tests { connect(next_expected_count, expected_count_reg); connect(expected_count_reg, next_expected_count); #[hdl] - if ReadyValid::fire(dut.inp) & !ReadyValid::fire(dut.out) { + if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { connect_any(next_expected_count, expected_count_reg + 1u8); - } else if !ReadyValid::fire(dut.inp) & ReadyValid::fire(dut.out) { + } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { connect_any(next_expected_count, expected_count_reg - 1u8); } hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); @@ -289,7 +289,7 @@ mod tests { let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8); #[hdl] - if let HdlSome(data) = ReadyValid::fire_data(dut.inp) { + if let HdlSome(data) = ReadyValid::firing_data(dut.inp) { #[hdl] if inp_index_reg.cmp_lt(index_max) { connect_any(inp_index_reg, inp_index_reg + 1u8); @@ -311,7 +311,7 @@ mod tests { let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8); #[hdl] - if let HdlSome(data) = ReadyValid::fire_data(dut.out) { + if let HdlSome(data) = ReadyValid::firing_data(dut.out) { #[hdl] if out_index_reg.cmp_lt(index_max) { connect_any(out_index_reg, out_index_reg + 1u8); From f403eed7c087fc8a345cfd48941315d0e44a18dc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 6 Oct 2024 20:08:39 -0700 Subject: [PATCH 054/190] only run tests once, since they are quite slow --- .forgejo/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 68ded33..3173827 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -65,5 +65,5 @@ jobs: with: save-if: ${{ github.ref == 'refs/heads/master' }} - run: cargo test - - run: cargo test --features=unstable-doc + - run: cargo build --tests --features=unstable-doc - run: cargo doc --features=unstable-doc From aec383c0af018cfaec0d12207a25d8bc4e74fa75 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 6 Oct 2024 20:57:42 -0700 Subject: [PATCH 055/190] try to fix ccache --- .forgejo/workflows/test.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 3173827..1246015 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -37,11 +37,17 @@ jobs: - name: Cache ccache dir uses: https://code.forgejo.org/actions/cache@v3 with: - path: ~/.ccache + path: .ccache key: ${{ runner.os }}-${{ github.sha }} restore-keys: | ${{ runner.os }}- - - run: | + - name: Setup ccache + run: | + ccache --set-config=cache_dir="${{ github.workspace }}/.ccache" + ccache --set-config=compression=true + ccache --set-config=compression_level=6 + ccache -M 4G + ccache -z export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" echo "$PATH" >> "$GITHUB_PATH" - run: | From eed0afc6ab2f656794eaf3fbfa2978abed21e0b7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 19:05:20 -0700 Subject: [PATCH 056/190] add some utility From> impls --- crates/fayalite/src/intern.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index 81d8434..9bd4d56 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -764,6 +764,24 @@ where } } +impl From> for Vec { + fn from(value: Interned<[T], C>) -> Self { + Vec::from(&*value.guard()) + } +} + +impl From> for Box<[T]> { + fn from(value: Interned<[T], C>) -> Self { + Box::from(&*value.guard()) + } +} + +impl From> for String { + fn from(value: Interned) -> Self { + String::from(&*value.guard()) + } +} + impl> + Default> Default for Interned<[I], C> where From 30b9a5e48d531c6be2d08707cfcceacdb694a21b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 19:06:01 -0700 Subject: [PATCH 057/190] change NameId to have an opaque Id so output firrtl doesn't depend on how many modules of the same name were ever created --- crates/fayalite/src/firrtl.rs | 35 ++- crates/fayalite/src/module.rs | 196 +++++---------- .../src/module/transform/simplify_enums.rs | 7 +- .../src/module/transform/simplify_memories.rs | 19 +- crates/fayalite/tests/module.rs | 224 +++++++++--------- crates/fayalite/visit_types.json | 2 +- 6 files changed, 204 insertions(+), 279 deletions(-) diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index ef955ea..bc75ccc 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -27,7 +27,7 @@ use crate::{ simplify_memories::simplify_memories, }, AnnotatedModuleIO, Block, ExternModuleBody, ExternModuleParameter, - ExternModuleParameterValue, Module, ModuleBody, NameId, NormalModuleBody, Stmt, + ExternModuleParameterValue, Module, ModuleBody, NameOptId, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, @@ -187,9 +187,9 @@ struct NameMaker { } impl NameMaker { - fn make(&mut self, name: NameId) -> Ident { - let mut num: usize = name.1; - let name = String::from(&*name.0); + fn make(&mut self, name: impl Into) -> Ident { + let mut num = 0usize; + let name: String = name.into(); // remove all invalid characters -- all valid characters are ASCII, so we can just remove invalid bytes let mut name = String::from_iter( name.bytes() @@ -221,7 +221,7 @@ impl NameMaker { #[derive(Default)] struct Namespace { name_maker: NameMaker, - map: HashMap, + map: HashMap, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -247,10 +247,11 @@ impl From for Ident { } impl Namespace { - fn get(&mut self, name: NameId) -> Ident { + fn get(&mut self, name: impl Into) -> Ident { + let name: NameOptId = name.into(); #[cold] - fn make(name_maker: &mut NameMaker, name: NameId) -> Ident { - name_maker.make(name) + fn make(name_maker: &mut NameMaker, name: NameOptId) -> Ident { + name_maker.make(name.0) } *self .map @@ -258,7 +259,7 @@ impl Namespace { .or_insert_with(|| make(&mut self.name_maker, name)) } fn make_new(&mut self, prefix: &str) -> Ident { - self.name_maker.make(NameId(prefix.intern(), 0)) + self.name_maker.make(prefix) } } @@ -368,7 +369,7 @@ impl TypeState { Ident(Intern::intern_owned(format!("Ty{id}"))) } fn get_bundle_field(&mut self, ty: Bundle, name: Interned) -> Ident { - self.bundle_ns(ty).borrow_mut().get(NameId(name, 0)) + self.bundle_ns(ty).borrow_mut().get(name) } fn bundle_def(&self, ty: Bundle) -> (Ident, Rc>) { self.bundle_defs.get_or_make(ty, |&ty, definitions| { @@ -382,7 +383,7 @@ impl TypeState { if flipped { body.push_str("flip "); } - write!(body, "{}: ", ns.get(NameId(name, 0))).unwrap(); + write!(body, "{}: ", ns.get(name)).unwrap(); body.push_str(&self.ty(ty)); } body.push('}'); @@ -406,7 +407,7 @@ impl TypeState { for EnumVariant { name, ty } in ty.variants() { body.push_str(separator); separator = ", "; - write!(body, "{}", variants.get(NameId(name, 0))).unwrap(); + write!(body, "{}", variants.get(name)).unwrap(); if let Some(ty) = ty { body.push_str(": "); body.push_str(&self.ty(ty)); @@ -428,11 +429,7 @@ impl TypeState { self.enum_def(ty).0 } fn get_enum_variant(&mut self, ty: Enum, name: Interned) -> Ident { - self.enum_def(ty) - .1 - .variants - .borrow_mut() - .get(NameId(name, 0)) + self.enum_def(ty).1.variants.borrow_mut().get(name) } fn ty(&self, ty: T) -> String { match ty.canonical() { @@ -929,7 +926,7 @@ impl<'a> Exporter<'a> { ) in expr.field_values().into_iter().zip(ty.fields()) { debug_assert!(!flipped, "can't have bundle literal with flipped field -- this should have been caught in BundleLiteral::new_unchecked"); - let name = bundle_ns.borrow_mut().get(NameId(name, 0)); + let name = bundle_ns.borrow_mut().get(name); let field_value = self.expr(Expr::canonical(field_value), definitions, const_ty); definitions.add_definition_line(format_args!("connect {ident}.{name}, {field_value}")); } @@ -2199,7 +2196,7 @@ impl<'a> Exporter<'a> { } in module.module_io().iter() { self.targeted_annotations(module_name, vec![], annotations); - let name = self.module.ns.get(NameId(module_io.name(), 0)); + let name = self.module.ns.get(module_io.name_id()); let ty = self.type_state.ty(module_io.ty()); if module_io.is_input() { writeln!( diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 872c75f..2ba3800 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -36,9 +36,10 @@ use std::{ iter::FusedIterator, marker::PhantomData, mem, + num::NonZeroU64, ops::Deref, rc::Rc, - sync::Mutex, + sync::atomic::AtomicU64, }; pub mod transform; @@ -602,8 +603,48 @@ impl BlockStack { } } +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct Id(NonZeroU64); + +impl Id { + pub fn new() -> Self { + static NEXT_ID: AtomicU64 = AtomicU64::new(1); + Self( + NonZeroU64::new(NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)) + .expect("Id::new ran out of ids"), + ) + } +} + #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct NameId(pub Interned, pub usize); +pub struct NameOptId(pub Interned, pub Option); + +impl fmt::Debug for NameOptId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl fmt::Display for NameOptId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl From> for NameOptId { + fn from(name: Interned) -> Self { + Self(name, None) + } +} + +impl From for NameOptId { + fn from(name_id: NameId) -> Self { + Self(name_id.0, Some(name_id.1)) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct NameId(pub Interned, pub Id); impl fmt::Debug for NameId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -613,13 +654,7 @@ impl fmt::Debug for NameId { impl fmt::Display for NameId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.is_empty() { - write!(f, "{}", self.1) - } else if self.1 == 0 { - f.write_str(&self.0) - } else { - write!(f, "{}_{}", self.0, self.1) - } + f.write_str(&self.0) } } @@ -648,106 +683,6 @@ impl fmt::Debug for TargetName { } } -#[derive(Default)] -pub struct NameIdGen(HashMap, usize>); - -impl NameIdGen { - pub fn gen(&mut self, name: Interned) -> NameId { - let next_id = self.0.entry(name).or_default(); - let id = *next_id; - *next_id += 1; - NameId(name, id) - } - pub fn mark_as_used(&mut self, name_id: NameId) { - let next_id = self.0.entry(name_id.0).or_default(); - *next_id = (*next_id).max(name_id.1 + 1); - } - pub fn for_module(module: Module) -> Self { - let mut retval = Self::default(); - let Module { - name: _, - source_location: _, - body, - io_ty: _, - module_io, - module_annotations: _, - } = module; - for module_io in &module_io { - retval.mark_as_used(module_io.module_io.name_id()); - } - match body { - ModuleBody::Extern(ExternModuleBody { - verilog_name: _, - parameters: _, - }) => {} - ModuleBody::Normal(NormalModuleBody { body }) => { - let mut blocks = vec![body]; - while let Some(block) = blocks.pop() { - let Block { memories, stmts } = block; - for memory in memories { - retval.mark_as_used(memory.scoped_name().1); - } - for stmt in &stmts { - blocks.extend_from_slice(stmt.sub_stmt_blocks()); - match stmt { - Stmt::Connect(StmtConnect { - lhs: _, - rhs: _, - source_location: _, - }) - | Stmt::Formal(StmtFormal { - kind: _, - clk: _, - pred: _, - en: _, - text: _, - source_location: _, - }) - | Stmt::If(StmtIf { - cond: _, - source_location: _, - blocks: _, - }) - | Stmt::Match(StmtMatch { - expr: _, - source_location: _, - blocks: _, - }) => {} - Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { - annotations: _, - instance, - })) => { - retval.mark_as_used(instance.name_id()); - } - Stmt::Declaration(StmtDeclaration::Reg(StmtReg { - annotations: _, - reg, - })) => { - retval.mark_as_used(reg.name_id()); - } - Stmt::Declaration(StmtDeclaration::Wire(StmtWire { - annotations: _, - wire, - })) => { - retval.mark_as_used(wire.name_id()); - } - } - } - } - } - } - retval - } - pub fn gen_module_name(name: Interned) -> NameId { - static MODULE_NAME_GEN: Mutex> = Mutex::new(None); - MODULE_NAME_GEN - .lock() - .unwrap() - .get_or_insert_with(NameIdGen::default) - .gen(name) - } -} - #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Instance { scoped_name: ScopedNameId, @@ -836,7 +771,6 @@ struct ModuleBuilderImpl { body: ModuleBodyBuilding, io: Vec>, io_indexes: HashMap, usize>, - name_id_gen: NameIdGen, module_annotations: Vec, } @@ -1941,9 +1875,7 @@ impl RegBuilder, Option>, T> { ty, } = self; ModuleBuilder::with(|module_builder| { - let mut impl_ = module_builder.impl_.borrow_mut(); - let scoped_name = ScopedNameId(module_builder.name, impl_.name_id_gen.gen(name)); - drop(impl_); + let scoped_name = ScopedNameId(module_builder.name, NameId(name, Id::new())); let reg = Reg::new_unchecked(scoped_name, source_location, ty, clock_domain, init); let retval = reg.to_expr(); // convert before borrow_mut since ModuleBuilder could be reentered by T::canonical() @@ -1991,8 +1923,13 @@ impl ModuleBuilder { is_input: bool, ty: IO, ) -> Expr { - let module_io = - ModuleIO::::new_unchecked(self.name, name.intern(), source_location, is_input, ty); + let module_io = ModuleIO::::new_unchecked( + self.name, + NameId(name.intern(), Id::new()), + source_location, + is_input, + ty, + ); let retval = module_io.to_expr(); let module_io = module_io.canonical(); let mut impl_ = self.impl_.borrow_mut(); @@ -2080,7 +2017,7 @@ impl ModuleBuilder { module_kind: ModuleKind, f: impl FnOnce(&Self) -> Result<(), E>, ) -> Result>, E> { - let name = NameIdGen::gen_module_name(name.intern()); + let name = NameId(name.intern(), Id::new()); let body = match module_kind { ModuleKind::Extern => ModuleBody::Extern(ExternModuleBody { verilog_name: name.0, @@ -2105,7 +2042,6 @@ impl ModuleBuilder { body, io: vec![], io_indexes: HashMap::new(), - name_id_gen: NameIdGen::default(), module_annotations: vec![], }), }; @@ -2277,9 +2213,7 @@ pub fn annotate(target: Expr, annotations: impl IntoAnnotations) { #[track_caller] pub fn wire_with_loc(name: &str, source_location: SourceLocation, ty: T) -> Expr { ModuleBuilder::with(|m| { - let mut impl_ = m.impl_.borrow_mut(); - let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern())); - drop(impl_); + let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); let wire = Wire::::new_unchecked(scoped_name, source_location, ty); let retval = wire.to_expr(); let canonical_wire = wire.canonical(); @@ -2311,9 +2245,7 @@ fn incomplete_declaration( source_location: SourceLocation, ) -> Rc> { ModuleBuilder::with(|m| { - let mut impl_ = m.impl_.borrow_mut(); - let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern())); - drop(impl_); + let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); let retval = Rc::new(RefCell::new(IncompleteDeclaration::Incomplete { name: scoped_name, source_location, @@ -2489,9 +2421,7 @@ pub fn instance_with_loc( source_location: SourceLocation, ) -> Expr { ModuleBuilder::with(|m| { - let mut impl_ = m.impl_.borrow_mut(); - let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern())); - drop(impl_); + let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); let instance = Instance:: { scoped_name, instantiated, @@ -2530,9 +2460,7 @@ fn memory_impl( source_location: SourceLocation, ) -> MemBuilder { ModuleBuilder::with(|m| { - let mut impl_ = m.impl_.borrow_mut(); - let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern())); - drop(impl_); + let scoped_name = ScopedNameId(m.name, NameId(name.intern(), Id::new())); let (retval, target_mem) = MemBuilder::new(scoped_name, source_location, mem_element_type); let mut impl_ = m.impl_.borrow_mut(); let body = impl_.body.builder_normal_body(); @@ -2608,6 +2536,7 @@ pub fn memory( pub struct ModuleIO { containing_module_name: NameId, bundle_field: BundleField, + id: Id, ty: T, source_location: SourceLocation, } @@ -2627,12 +2556,14 @@ impl ModuleIO { let Self { containing_module_name, bundle_field, + id, ty: _, source_location, } = *self; ModuleIO { containing_module_name, bundle_field, + id, ty: bundle_field.ty, source_location, } @@ -2656,7 +2587,7 @@ impl ModuleIO { self.bundle_field.name } pub fn name_id(&self) -> NameId { - NameId(self.bundle_field.name, 0) + NameId(self.bundle_field.name, self.id) } pub fn scoped_name(&self) -> ScopedNameId { ScopedNameId(self.containing_module_name, self.name_id()) @@ -2666,7 +2597,7 @@ impl ModuleIO { } pub fn new_unchecked( containing_module_name: NameId, - name: Interned, + name: NameId, source_location: SourceLocation, is_input: bool, ty: T, @@ -2674,10 +2605,11 @@ impl ModuleIO { Self { containing_module_name, bundle_field: BundleField { - name, + name: name.0, flipped: is_input, ty: ty.canonical(), }, + id: name.1, ty, source_location, } diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index edb4b97..f398006 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -14,8 +14,7 @@ use crate::{ memory::{DynPortType, Mem, MemPort}, module::{ transform::visit::{Fold, Folder}, - Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, - StmtWire, + Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire, }, source_location::SourceLocation, ty::{CanonicalType, Type}, @@ -92,13 +91,12 @@ enum EnumTypeState { } struct ModuleState { - name_id_gen: NameIdGen, module_name: NameId, } impl ModuleState { fn gen_name(&mut self, name: &str) -> ScopedNameId { - ScopedNameId(self.module_name, self.name_id_gen.gen(name.intern())) + ScopedNameId(self.module_name, NameId(name.intern(), Id::new())) } } @@ -632,7 +630,6 @@ impl Folder for State { fn fold_module(&mut self, v: Module) -> Result, Self::Error> { self.module_state_stack.push(ModuleState { - name_id_gen: NameIdGen::for_module(v.canonical()), module_name: v.name_id(), }); let retval = Fold::default_fold(v, self); diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 19fb0ec..71e459c 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -10,7 +10,7 @@ use crate::{ memory::{Mem, MemPort, PortType}, module::{ transform::visit::{Fold, Folder}, - Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire, + Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtWire, }, source_location::SourceLocation, ty::{CanonicalType, Type}, @@ -417,7 +417,6 @@ impl SplitMemState<'_, '_> { struct ModuleState { output_module: Option>>, - name_id_gen: NameIdGen, memories: HashMap, } @@ -626,10 +625,10 @@ impl ModuleState { mem_name_path: &str, split_state: &SplitState<'_>, ) -> Mem { - let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!( - "{}{mem_name_path}", - input_mem.scoped_name().1 .0 - ))); + let mem_name = NameId( + Intern::intern_owned(format!("{}{mem_name_path}", input_mem.scoped_name().1 .0)), + Id::new(), + ); let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name); let output_element_type = match single_type { SingleType::UInt(ty) => ty.canonical(), @@ -753,9 +752,10 @@ impl ModuleState { let port_ty = port.ty(); let NameId(mem_name, _) = input_mem.scoped_name().1; let port_name = port.port_name(); - let wire_name = self - .name_id_gen - .gen(Intern::intern_owned(format!("{mem_name}_{port_name}"))); + let wire_name = NameId( + Intern::intern_owned(format!("{mem_name}_{port_name}")), + Id::new(), + ); let wire = Wire::new_unchecked( ScopedNameId(input_mem.scoped_name().0, wire_name), port.source_location(), @@ -887,7 +887,6 @@ impl Folder for State { module, ModuleState { output_module: None, - name_id_gen: NameIdGen::for_module(*module), memories: HashMap::new(), }, ); diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 69438f2..b0b1fb6 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -175,9 +175,9 @@ circuit check_array_repeat: #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 assert_export_firrtl! { m => - "/test/check_array_repeat_1.fir": r"FIRRTL version 3.2.0 -circuit check_array_repeat_1: - module check_array_repeat_1: @[module-XXXXXXXXXX.rs 1:1] + "/test/check_array_repeat.fir": r"FIRRTL version 3.2.0 +circuit check_array_repeat: + module check_array_repeat: @[module-XXXXXXXXXX.rs 1:1] input i: UInt<8> @[module-XXXXXXXXXX.rs 2:1] output o: UInt<8>[4] @[module-XXXXXXXXXX.rs 3:1] wire _array_literal_expr: UInt<8>[4] @@ -1672,9 +1672,9 @@ fn test_memory_of_arrays() { circuit check_memory_of_arrays: %[[ { "class": "firrtl.annotations.MemoryFileInlineAnnotation", - "filename": "/test/check_memory_of_arrays/mem_1.mem", + "filename": "/test/check_memory_of_arrays/mem.mem", "hexOrBinary": "h", - "target": "~check_memory_of_arrays|check_memory_of_arrays>mem_1" + "target": "~check_memory_of_arrays|check_memory_of_arrays>mem" } ]] type Ty0 = {addr: UInt<4>, en: UInt<1>, clk: Clock, flip data: UInt<8>[2][3]} @@ -1688,7 +1688,7 @@ circuit check_memory_of_arrays: %[[ input wdata: UInt<8>[2][3] @[module-XXXXXXXXXX.rs 5:1] input wmask: UInt<1>[2][3] @[module-XXXXXXXXXX.rs 6:1] input clk: Clock @[module-XXXXXXXXXX.rs 7:1] - mem mem_1: @[module-XXXXXXXXXX.rs 8:1] + mem `mem`: @[module-XXXXXXXXXX.rs 8:1] data-type => UInt<8>[6] depth => 16 read-latency => 0 @@ -1698,30 +1698,30 @@ circuit check_memory_of_arrays: %[[ writer => w1 wire mem_r0: Ty0 @[module-XXXXXXXXXX.rs 9:1] wire mem_w1: Ty1 @[module-XXXXXXXXXX.rs 14:1] - connect mem_r0.data[0][0], mem_1.r0.data[0] @[module-XXXXXXXXXX.rs 9:1] - connect mem_r0.data[0][1], mem_1.r0.data[1] @[module-XXXXXXXXXX.rs 9:1] - connect mem_r0.data[1][0], mem_1.r0.data[2] @[module-XXXXXXXXXX.rs 9:1] - connect mem_r0.data[1][1], mem_1.r0.data[3] @[module-XXXXXXXXXX.rs 9:1] - connect mem_r0.data[2][0], mem_1.r0.data[4] @[module-XXXXXXXXXX.rs 9:1] - connect mem_r0.data[2][1], mem_1.r0.data[5] @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.w1.data[0], mem_w1.data[0][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[0], mem_w1.mask[0][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.data[1], mem_w1.data[0][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[1], mem_w1.mask[0][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.data[2], mem_w1.data[1][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[2], mem_w1.mask[1][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.data[3], mem_w1.data[1][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[3], mem_w1.mask[1][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.data[4], mem_w1.data[2][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[4], mem_w1.mask[2][0] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.data[5], mem_w1.data[2][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[5], mem_w1.mask[2][1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + connect mem_r0.data[0][0], `mem`.r0.data[0] @[module-XXXXXXXXXX.rs 9:1] + connect mem_r0.data[0][1], `mem`.r0.data[1] @[module-XXXXXXXXXX.rs 9:1] + connect mem_r0.data[1][0], `mem`.r0.data[2] @[module-XXXXXXXXXX.rs 9:1] + connect mem_r0.data[1][1], `mem`.r0.data[3] @[module-XXXXXXXXXX.rs 9:1] + connect mem_r0.data[2][0], `mem`.r0.data[4] @[module-XXXXXXXXXX.rs 9:1] + connect mem_r0.data[2][1], `mem`.r0.data[5] @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.w1.data[0], mem_w1.data[0][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[0], mem_w1.mask[0][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[1], mem_w1.data[0][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[1], mem_w1.mask[0][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[2], mem_w1.data[1][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[2], mem_w1.mask[1][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[3], mem_w1.data[1][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[3], mem_w1.mask[1][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[4], mem_w1.data[2][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[4], mem_w1.mask[2][0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[5], mem_w1.data[2][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[5], mem_w1.mask[2][1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] connect mem_r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect mem_r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] @@ -1732,7 +1732,7 @@ circuit check_memory_of_arrays: %[[ connect mem_w1.data, wdata @[module-XXXXXXXXXX.rs 18:1] connect mem_w1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] "#, - "/test/check_memory_of_arrays/mem_1.mem": r"000000000000 + "/test/check_memory_of_arrays/mem.mem": r"000000000000 020103020101 04080c080402 061b1b120903 @@ -2445,9 +2445,9 @@ fn test_memory_of_enum() { circuit check_memory_of_enum: %[[ { "class": "firrtl.annotations.MemoryFileInlineAnnotation", - "filename": "/test/check_memory_of_enum/mem_1.mem", + "filename": "/test/check_memory_of_enum/mem.mem", "hexOrBinary": "b", - "target": "~check_memory_of_enum|check_memory_of_enum>mem_1" + "target": "~check_memory_of_enum|check_memory_of_enum>mem" } ]] type Ty0 = {|A, B: UInt<8>, C: UInt<1>[3]|} @@ -2462,7 +2462,7 @@ circuit check_memory_of_enum: %[[ input wdata: Ty0 @[module-XXXXXXXXXX.rs 5:1] input wmask: UInt<1> @[module-XXXXXXXXXX.rs 6:1] input clk: Clock @[module-XXXXXXXXXX.rs 7:1] - mem mem_1: @[module-XXXXXXXXXX.rs 8:1] + mem `mem`: @[module-XXXXXXXXXX.rs 8:1] data-type => UInt<10> depth => 16 read-latency => 0 @@ -2474,10 +2474,10 @@ circuit check_memory_of_enum: %[[ wire mem_w1: Ty2 @[module-XXXXXXXXXX.rs 14:1] wire _cast_bits_to_enum_expr: Ty0 wire _cast_bits_to_enum_expr_body: UInt<8> - connect _cast_bits_to_enum_expr_body, head(mem_1.r0.data, 8) - when eq(UInt<2>(0), tail(mem_1.r0.data, 8)): + connect _cast_bits_to_enum_expr_body, head(`mem`.r0.data, 8) + when eq(UInt<2>(0), tail(`mem`.r0.data, 8)): connect _cast_bits_to_enum_expr, {|A, B: UInt<8>, C: UInt<1>[3]|}(A) - else when eq(UInt<2>(1), tail(mem_1.r0.data, 8)): + else when eq(UInt<2>(1), tail(`mem`.r0.data, 8)): connect _cast_bits_to_enum_expr, {|A, B: UInt<8>, C: UInt<1>[3]|}(B, _cast_bits_to_enum_expr_body) else: wire _cast_bits_to_array_expr: UInt<1>[3] @@ -2504,14 +2504,14 @@ circuit check_memory_of_enum: %[[ 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) - connect mem_1.w1.data, _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data, _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] ; connect different types: ; lhs: UInt<4> ; rhs: UInt<8> @@ -2528,7 +2528,7 @@ circuit check_memory_of_enum: %[[ connect mem_w1.data, wdata @[module-XXXXXXXXXX.rs 18:1] connect mem_w1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] "#, - "/test/check_memory_of_enum/mem_1.mem": r"0000000000 + "/test/check_memory_of_enum/mem.mem": r"0000000000 0000000110 0000001010 0000010010 @@ -2602,7 +2602,7 @@ circuit check_memory_of_enum: %[[ reader => r0 writer => w1 wire mem_r0: Ty2 @[module-XXXXXXXXXX.rs 9:1] - wire mem_w1_1: Ty4 @[module-XXXXXXXXXX.rs 14:1] + wire mem_w1: Ty4 @[module-XXXXXXXXXX.rs 14:1] wire _cast_bits_to_enum_expr: Ty0 when eq(UInt<2>(0), tail(mem_tag.r0.data, 0)): connect _cast_bits_to_enum_expr, {|A, B, C|}(A) @@ -2612,7 +2612,7 @@ circuit check_memory_of_enum: %[[ connect _cast_bits_to_enum_expr, {|A, B, C|}(C) connect mem_r0.data.tag, _cast_bits_to_enum_expr @[module-XXXXXXXXXX.rs 9:1] wire _cast_enum_to_bits_expr: UInt<2> - match mem_w1_1.data.tag: + match mem_w1.data.tag: A: connect _cast_enum_to_bits_expr, UInt<2>(0) B: @@ -2620,29 +2620,29 @@ circuit check_memory_of_enum: %[[ C: connect _cast_enum_to_bits_expr, UInt<2>(2) connect mem_tag.w1.data, _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.mask, mem_w1_1.mask.tag @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.mask, mem_w1.mask.tag @[module-XXXXXXXXXX.rs 14:1] connect mem_tag.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] connect mem_tag.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] connect mem_tag.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_tag.w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] connect mem_r0.data.body, mem_body.r0.data @[module-XXXXXXXXXX.rs 9:1] - connect mem_body.w1.data, mem_w1_1.data.body @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.mask, mem_w1_1.mask.body @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.data, mem_w1.data.body @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.mask, mem_w1.mask.body @[module-XXXXXXXXXX.rs 14:1] connect mem_body.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] connect mem_body.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] connect mem_body.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_body.w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] - wire mem_w1: Ty9 @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.data, mem_w1.data @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.mask.tag, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.mask.body, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + wire mem_w1_1: Ty9 @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.data, mem_w1_1.data @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.mask.tag, mem_w1_1.mask @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.mask.body, mem_w1_1.mask @[module-XXXXXXXXXX.rs 14:1] ; connect different types: ; lhs: UInt<4> ; rhs: UInt<8> @@ -2653,11 +2653,11 @@ circuit check_memory_of_enum: %[[ ; connect different types: ; lhs: UInt<4> ; rhs: UInt<8> - connect mem_w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] - connect mem_w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] - connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] - connect mem_w1.data, wdata @[module-XXXXXXXXXX.rs 18:1] - connect mem_w1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] + connect mem_w1_1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] + connect mem_w1_1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] + connect mem_w1_1.clk, clk @[module-XXXXXXXXXX.rs 17:1] + connect mem_w1_1.data, wdata @[module-XXXXXXXXXX.rs 18:1] + connect mem_w1_1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] "#, "/test/check_memory_of_enum/mem_body.mem": r"00 01 @@ -2748,32 +2748,32 @@ circuit check_memory_of_enum: %[[ reader => r0 writer => w1 wire mem_r0: Ty1 @[module-XXXXXXXXXX.rs 9:1] - wire mem_w1_1: Ty3 @[module-XXXXXXXXXX.rs 14:1] + wire mem_w1: Ty3 @[module-XXXXXXXXXX.rs 14:1] connect mem_r0.data.tag, mem_tag.r0.data @[module-XXXXXXXXXX.rs 9:1] - connect mem_tag.w1.data, mem_w1_1.data.tag @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.mask, mem_w1_1.mask.tag @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.data, mem_w1.data.tag @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.mask, mem_w1.mask.tag @[module-XXXXXXXXXX.rs 14:1] connect mem_tag.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] connect mem_tag.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] connect mem_tag.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_tag.w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_tag.w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_tag.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] connect mem_r0.data.body, mem_body.r0.data @[module-XXXXXXXXXX.rs 9:1] - connect mem_body.w1.data, mem_w1_1.data.body @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.mask, mem_w1_1.mask.body @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.data, mem_w1.data.body @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.mask, mem_w1.mask.body @[module-XXXXXXXXXX.rs 14:1] connect mem_body.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] connect mem_body.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] connect mem_body.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_body.w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_body.w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] - wire mem_w1: Ty8 @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.data, mem_w1.data @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.mask.tag, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] - connect mem_w1_1.mask.body, mem_w1.mask @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_body.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + wire mem_w1_1: Ty8 @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.addr, mem_w1_1.addr @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.en, mem_w1_1.en @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.clk, mem_w1_1.clk @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.data, mem_w1_1.data @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.mask.tag, mem_w1_1.mask @[module-XXXXXXXXXX.rs 14:1] + connect mem_w1.mask.body, mem_w1_1.mask @[module-XXXXXXXXXX.rs 14:1] ; connect different types: ; lhs: UInt<4> ; rhs: UInt<8> @@ -2784,11 +2784,11 @@ circuit check_memory_of_enum: %[[ ; connect different types: ; lhs: UInt<4> ; rhs: UInt<8> - connect mem_w1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] - connect mem_w1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] - connect mem_w1.clk, clk @[module-XXXXXXXXXX.rs 17:1] - connect mem_w1.data, wdata @[module-XXXXXXXXXX.rs 18:1] - connect mem_w1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] + connect mem_w1_1.addr, waddr @[module-XXXXXXXXXX.rs 15:1] + connect mem_w1_1.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 16:1] + connect mem_w1_1.clk, clk @[module-XXXXXXXXXX.rs 17:1] + connect mem_w1_1.data, wdata @[module-XXXXXXXXXX.rs 18:1] + connect mem_w1_1.mask, wmask @[module-XXXXXXXXXX.rs 19:1] "#, "/test/check_memory_of_enum/mem_body.mem": r"00 01 @@ -2950,7 +2950,7 @@ circuit check_memory_of_array_of_enum: input wdata: Ty0[2] @[module-XXXXXXXXXX.rs 5:1] input wmask: UInt<1>[2] @[module-XXXXXXXXXX.rs 6:1] input clk: Clock @[module-XXXXXXXXXX.rs 7:1] - mem mem_1: @[module-XXXXXXXXXX.rs 8:1] + mem `mem`: @[module-XXXXXXXXXX.rs 8:1] data-type => UInt<10>[2] depth => 256 read-latency => 0 @@ -2962,10 +2962,10 @@ circuit check_memory_of_array_of_enum: wire mem_w1: Ty2 @[module-XXXXXXXXXX.rs 14:1] wire _cast_bits_to_enum_expr: Ty0 wire _cast_bits_to_enum_expr_body: UInt<8> - connect _cast_bits_to_enum_expr_body, head(mem_1.r0.data[0], 8) - when eq(UInt<2>(0), tail(mem_1.r0.data[0], 8)): + connect _cast_bits_to_enum_expr_body, head(`mem`.r0.data[0], 8) + when eq(UInt<2>(0), tail(`mem`.r0.data[0], 8)): connect _cast_bits_to_enum_expr, {|A, B: UInt<8>, C: UInt<1>[3]|}(A) - else when eq(UInt<2>(1), tail(mem_1.r0.data[0], 8)): + else when eq(UInt<2>(1), tail(`mem`.r0.data[0], 8)): connect _cast_bits_to_enum_expr, {|A, B: UInt<8>, C: UInt<1>[3]|}(B, _cast_bits_to_enum_expr_body) else: wire _cast_bits_to_array_expr: UInt<1>[3] @@ -2980,10 +2980,10 @@ circuit check_memory_of_array_of_enum: connect mem_r0.data[0], _cast_bits_to_enum_expr @[module-XXXXXXXXXX.rs 9:1] wire _cast_bits_to_enum_expr_1: Ty0 wire _cast_bits_to_enum_expr_body_1: UInt<8> - connect _cast_bits_to_enum_expr_body_1, head(mem_1.r0.data[1], 8) - when eq(UInt<2>(0), tail(mem_1.r0.data[1], 8)): + connect _cast_bits_to_enum_expr_body_1, head(`mem`.r0.data[1], 8) + when eq(UInt<2>(0), tail(`mem`.r0.data[1], 8)): connect _cast_bits_to_enum_expr_1, {|A, B: UInt<8>, C: UInt<1>[3]|}(A) - else when eq(UInt<2>(1), tail(mem_1.r0.data[1], 8)): + else when eq(UInt<2>(1), tail(`mem`.r0.data[1], 8)): connect _cast_bits_to_enum_expr_1, {|A, B: UInt<8>, C: UInt<1>[3]|}(B, _cast_bits_to_enum_expr_body_1) else: wire _cast_bits_to_array_expr_1: UInt<1>[3] @@ -3010,8 +3010,8 @@ circuit check_memory_of_array_of_enum: 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) - connect mem_1.w1.data[0], _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[0], mem_w1.mask[0] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[0], _cast_enum_to_bits_expr @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[0], mem_w1.mask[0] @[module-XXXXXXXXXX.rs 14:1] wire _cast_enum_to_bits_expr_1: UInt<10> match mem_w1.data[1]: A: @@ -3026,14 +3026,14 @@ circuit check_memory_of_array_of_enum: 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 mem_1.w1.data[1], _cast_enum_to_bits_expr_1 @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.mask[1], mem_w1.mask[1] @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] - connect mem_1.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] - connect mem_1.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.data[1], _cast_enum_to_bits_expr_1 @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.mask[1], mem_w1.mask[1] @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.r0.addr, mem_r0.addr @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.clk, mem_r0.clk @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.r0.en, mem_r0.en @[module-XXXXXXXXXX.rs 9:1] + connect `mem`.w1.addr, mem_w1.addr @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.clk, mem_w1.clk @[module-XXXXXXXXXX.rs 14:1] + connect `mem`.w1.en, mem_w1.en @[module-XXXXXXXXXX.rs 14:1] connect mem_r0.addr, raddr @[module-XXXXXXXXXX.rs 10:1] connect mem_r0.en, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 11:1] connect mem_r0.clk, clk @[module-XXXXXXXXXX.rs 12:1] @@ -3344,11 +3344,11 @@ circuit check_uninit: simplify_enums: None, ..ExportOptions::default() }, - "/test/check_uninit_1.fir": r"FIRRTL version 3.2.0 -circuit check_uninit_1: + "/test/check_uninit.fir": r"FIRRTL version 3.2.0 +circuit check_uninit: type Ty0 = {} type Ty1 = {|HdlNone, HdlSome: Ty0|} - module check_uninit_1: @[module-XXXXXXXXXX.rs 1:1] + module check_uninit: @[module-XXXXXXXXXX.rs 1:1] output o: Ty1[3] @[module-XXXXXXXXXX.rs 2:1] wire _uninit_expr: Ty1[3] invalidate _uninit_expr diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 366ee2f..182efd9 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -28,7 +28,7 @@ "$kind": "Struct", "$constructor": "ModuleIO::new_unchecked", "containing_module_name_id()": "Visible", - "name()": "Visible", + "name_id()": "Visible", "source_location()": "Visible", "is_input()": "Visible", "ty()": "Visible" From 44ca1a607a04a5eddf327b4ffbaa9c54c6b6b5d1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 20:27:39 -0700 Subject: [PATCH 058/190] remove unused AGCContext --- crates/fayalite/src/intern.rs | 128 ---------------------------------- 1 file changed, 128 deletions(-) diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index 9bd4d56..c38f26f 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -198,7 +198,6 @@ impl LazyInterned { pub trait InternedCompare { type InternedCompareKey: Ord + Hash; fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey; } /// Warning: doesn't do what you want with `T = dyn Trait`, @@ -272,9 +271,6 @@ impl InternedCompare for T { fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { PtrEqWithMetadata(this) } - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey { - PtrEqWithMetadata(this.as_ptr()) - } } impl InternedCompare for [T] { @@ -282,9 +278,6 @@ impl InternedCompare for [T] { fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { PtrEqWithMetadata(this) } - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey { - PtrEqWithMetadata(this.as_ptr()) - } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -295,9 +288,6 @@ impl InternedCompare for BitSlice { fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { BitSlicePtrEq(this.as_bitptr(), this.len()) } - fn interned_compare_key_weak(_this: &std::sync::Weak) -> Self::InternedCompareKey { - unreachable!("not currently implementable since Weak can't be constructed") - } } impl InternedCompare for str { @@ -305,9 +295,6 @@ impl InternedCompare for str { fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { PtrEqWithMetadata(this) } - fn interned_compare_key_weak(this: &std::sync::Weak) -> Self::InternedCompareKey { - PtrEqWithMetadata(this.as_ptr()) - } } pub trait InternContext: 'static + Send + Sync + Hash + Ord + fmt::Debug + Clone { @@ -408,121 +395,6 @@ impl BitSliceInternContext for GlobalContext { } } -#[derive(Clone)] -pub struct AGCContext(std::sync::Arc); - -impl AGCContext { - pub fn new() -> Self { - Self(std::sync::Arc::new(TypeIdMap::new())) - } -} - -impl Default for AGCContext { - fn default() -> Self { - Self::new() - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct AGCContextId(*const ()); - -impl fmt::Debug for AGCContextId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl AGCContext { - pub fn id(&self) -> AGCContextId { - AGCContextId(std::sync::Arc::as_ptr(&self.0).cast()) - } -} - -impl Hash for AGCContext { - fn hash(&self, state: &mut H) { - self.id().hash(state); - } -} - -impl Ord for AGCContext { - fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(&other.id()) - } -} - -impl PartialOrd for AGCContext { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Eq for AGCContext {} - -impl PartialEq for AGCContext { - fn eq(&self, other: &Self) -> bool { - self.id().eq(&other.id()) - } -} - -impl fmt::Debug for AGCContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AGCContext") - .field("id", &self.id()) - .finish_non_exhaustive() - } -} - -impl InternContext for AGCContext { - type InternedImpl = std::sync::Weak; - type InternedGuardImpl = std::sync::Arc; - type AllContextsAreIdentical = ConstBool; - - fn interned_compare_key( - v: &Self::InternedImpl, - ) -> T::InternedCompareKey { - T::interned_compare_key_weak(v) - } - fn guard( - v: &Self::InternedImpl, - ) -> Self::InternedGuardImpl { - v.upgrade().expect("expired") - } - fn try_guard( - v: &Self::InternedImpl, - ) -> Option> { - v.upgrade() - } - fn unguard( - v: &Self::InternedGuardImpl, - ) -> Self::InternedImpl { - std::sync::Arc::downgrade(v) - } - fn alloc_str(&self, value: Cow<'_, str>) -> Self::InternedGuardImpl { - match value { - Cow::Borrowed(value) => value.into(), - Cow::Owned(value) => value.into(), - } - } - fn alloc_slice( - &self, - value: Cow<'_, [T]>, - ) -> Self::InternedGuardImpl<[T]> { - match value { - Cow::Borrowed(value) => value.into(), - Cow::Owned(value) => value.into(), - } - } - fn alloc_sized( - &self, - value: Cow<'_, T>, - ) -> Self::InternedGuardImpl { - std::sync::Arc::new(value.into_owned()) - } - fn interner(&self) -> &Interner { - self.0.get_or_insert_default() - } -} - pub trait Intern: Any + Send + Sync { fn intern_with_ctx(&self, context: &C) -> Interned; fn intern_sized_with_ctx(self, context: &C) -> Interned From f12322aa2a858533cc413048f2f42350b596e515 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 21:33:56 -0700 Subject: [PATCH 059/190] remove interning contexts --- .../src/hdl_bundle.rs | 4 +- .../src/hdl_type_common.rs | 6 +- crates/fayalite/src/array.rs | 6 +- crates/fayalite/src/bundle.rs | 2 +- crates/fayalite/src/expr/ops.rs | 8 +- crates/fayalite/src/int.rs | 2 +- crates/fayalite/src/intern.rs | 575 +++++------------- crates/fayalite/src/ty.rs | 2 +- 8 files changed, 154 insertions(+), 451 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f3a589a..b13aa08 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -686,7 +686,7 @@ impl ToTokens for ParsedBundle { let __retval = #mask_type_match_variant_ident { #(#match_variant_body_fields)* }; - ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) + ::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } #[automatically_derived] @@ -761,7 +761,7 @@ impl ToTokens for ParsedBundle { let __retval = #match_variant_ident { #(#match_variant_body_fields)* }; - ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) + ::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval)) } } } 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 e7561fe..239f32b 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -2718,7 +2718,7 @@ impl ParsedGenerics { type Output = #next_target #next_type_generics; fn index(&self, #param_token: #index_type) -> &Self::Output { - ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Interned::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) } @@ -2778,7 +2778,7 @@ impl ParsedGenerics { type Output = #next_target #next_target_args; fn index(&self, #param_token: #param_ident) -> &Self::Output { - ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Interned::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) } @@ -2848,7 +2848,7 @@ impl ParsedGenerics { type Output = #next_target #next_target_args; fn index(&self, #param_token: __Param) -> &Self::Output { - ::fayalite::intern::Interned::<_>::into_inner( + ::fayalite::intern::Interned::into_inner( ::fayalite::intern::Intern::intern_sized(#output_expr), ) } diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index d543322..a3aea79 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -187,7 +187,7 @@ impl TypeWithDeref for ArrayType { let base = Expr::as_dyn_array(*this); let base_ty = Expr::ty(base); let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr())); - Interned::<_>::into_inner(Intern::intern_sized( + Interned::into_inner(Intern::intern_sized( Len::ArrayMatch::::try_from(retval) .ok() .expect("unreachable"), @@ -202,7 +202,7 @@ impl Index for ArrayWithoutGenerics { type Output = ArrayWithoutLen; fn index(&self, element: T) -> &Self::Output { - Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) + Interned::into_inner(Intern::intern_sized(ArrayWithoutLen { element })) } } @@ -215,6 +215,6 @@ impl Index for ArrayWithoutLen { type Output = ArrayType; fn index(&self, len: L) -> &Self::Output { - Interned::<_>::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) + Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len))) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 95c87f9..199c2b2 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -377,7 +377,7 @@ macro_rules! impl_tuples { impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) { fn expr_deref(this: &Expr) -> &Self::MatchVariant { let _ = this; - Interned::<_>::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized()) + Interned::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized()) } } impl<$($T: StaticType,)*> StaticType for ($($T,)*) { diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index 6069f20..3579641 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -2057,7 +2057,7 @@ impl ExprIndex for ArrayType, index: usize) -> &Expr { - Interned::<_>::into_inner( + Interned::into_inner( ArrayIndex::::new(Expr::as_dyn_array(*this), index) .to_expr() .intern_sized(), @@ -2154,7 +2154,7 @@ impl ExprIndex>> type Output = ElementType; fn expr_index(this: &Expr, index: Expr>) -> &Expr { - Interned::<_>::into_inner( + Interned::into_inner( DynArrayIndex::::new(Expr::as_dyn_array(*this), Expr::as_dyn_int(index)) .to_expr() .intern_sized(), @@ -2279,7 +2279,7 @@ macro_rules! impl_int_slice { let base = Expr::as_dyn_int(*this); let base_ty = Expr::ty(base); let range = base_ty.slice_index_to_range(index); - Interned::<_>::into_inner($name::new(base, range).to_expr().intern_sized()) + Interned::into_inner($name::new(base, range).to_expr().intern_sized()) } } @@ -2291,7 +2291,7 @@ macro_rules! impl_int_slice { let base = Expr::as_dyn_int(*this); let base_ty = Expr::ty(base); assert!(index < base_ty.width()); - Interned::<_>::into_inner( + Interned::into_inner( $name::new(base, index..(index + 1)) .to_expr() .cast_to_static::() diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index e0d258a..f529198 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -301,7 +301,7 @@ macro_rules! impl_int { type Output = $name; fn index(&self, width: Width) -> &Self::Output { - Interned::<_>::into_inner(Intern::intern_sized($name::new(width))) + Interned::into_inner(Intern::intern_sized($name::new(width))) } } diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index c38f26f..3780ad3 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -1,10 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information #![allow(clippy::type_complexity)] -use crate::{ - intern::type_map::TypeIdMap, - util::{ConstBool, GenericConstBool}, -}; +use crate::intern::type_map::TypeIdMap; use bitvec::{ptr::BitPtr, slice::BitSlice, vec::BitVec}; use hashbrown::{hash_map::RawEntryMut, HashMap, HashTable}; use serde::{Deserialize, Serialize}; @@ -297,221 +294,94 @@ impl InternedCompare for str { } } -pub trait InternContext: 'static + Send + Sync + Hash + Ord + fmt::Debug + Clone { - type InternedImpl: 'static + Send + Sync + Clone; - type InternedGuardImpl: 'static - + Send - + Sync - + Clone - + Borrow; - type AllContextsAreIdentical: GenericConstBool; - fn interned_compare_key( - v: &Self::InternedImpl, - ) -> T::InternedCompareKey { - T::interned_compare_key_ref(Self::guard(v).borrow()) +pub trait Intern: Any + Send + Sync { + fn intern(&self) -> Interned; + fn intern_sized(self) -> Interned + where + Self: Clone, + { + Self::intern_owned(self) } - fn guard( - v: &Self::InternedImpl, - ) -> Self::InternedGuardImpl; - fn try_guard( - v: &Self::InternedImpl, - ) -> Option>; - fn into_guard( - v: Self::InternedImpl, - ) -> Self::InternedGuardImpl { - Self::guard(&v) + fn intern_owned(this: ::Owned) -> Interned + where + Self: ToOwned, + { + Self::intern_cow(Cow::Owned(this)) } - fn unguard( - v: &Self::InternedGuardImpl, - ) -> Self::InternedImpl; - fn unguard_move( - v: Self::InternedGuardImpl, - ) -> Self::InternedImpl { - Self::unguard(&v) + fn intern_cow(this: Cow<'_, Self>) -> Interned + where + Self: ToOwned, + { + this.intern() } - fn alloc_str(&self, value: Cow<'_, str>) -> Self::InternedGuardImpl; - fn alloc_slice( - &self, - value: Cow<'_, [T]>, - ) -> Self::InternedGuardImpl<[T]>; - fn alloc_sized( - &self, - value: Cow<'_, T>, - ) -> Self::InternedGuardImpl; - fn interner(&self) -> &Interner; } -pub trait BitSliceInternContext: InternContext { - fn alloc_bit_slice(&self, value: Cow<'_, BitSlice>) -> Self::InternedGuardImpl; +pub struct Interner { + map: Mutex>, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] -pub struct GlobalContext; - -impl InternContext for GlobalContext { - type InternedImpl = &'static T; - type InternedGuardImpl = &'static T; - type AllContextsAreIdentical = ConstBool; - - fn guard( - v: &Self::InternedImpl, - ) -> Self::InternedGuardImpl { - *v - } - fn try_guard( - v: &Self::InternedImpl, - ) -> Option> { - Some(*v) - } - fn unguard( - v: &Self::InternedGuardImpl, - ) -> Self::InternedImpl { - *v - } - fn alloc_str(&self, value: Cow<'_, str>) -> Self::InternedGuardImpl { - value.into_owned().leak() - } - fn alloc_slice( - &self, - value: Cow<'_, [T]>, - ) -> Self::InternedGuardImpl<[T]> { - value.into_owned().leak() - } - fn alloc_sized( - &self, - value: Cow<'_, T>, - ) -> Self::InternedGuardImpl { - Box::leak(Box::new(value.into_owned())) - } - fn interner(&self) -> &Interner { +impl Interner { + fn get() -> &'static Interner { static TYPE_ID_MAP: TypeIdMap = TypeIdMap::new(); TYPE_ID_MAP.get_or_insert_default() } } -impl BitSliceInternContext for GlobalContext { - fn alloc_bit_slice(&self, value: Cow<'_, BitSlice>) -> Self::InternedGuardImpl { - value.into_owned().leak() - } -} - -pub trait Intern: Any + Send + Sync { - fn intern_with_ctx(&self, context: &C) -> Interned; - fn intern_sized_with_ctx(self, context: &C) -> Interned - where - Self: Clone, - { - Self::intern_owned_with_ctx(self, context) - } - fn intern_owned_with_ctx(this: ::Owned, context: &C) -> Interned - where - Self: ToOwned, - { - Self::intern_cow_with_ctx(Cow::Owned(this), context) - } - fn intern_cow_with_ctx(this: Cow<'_, Self>, context: &C) -> Interned - where - Self: ToOwned, - { - this.intern_with_ctx(context) - } - fn intern(&self) -> Interned - where - C: InternContext> + Default, - { - self.intern_with_ctx(&C::default()) - } - fn intern_sized(self) -> Interned - where - Self: Clone, - C: InternContext> + Default, - { - self.intern_sized_with_ctx(&C::default()) - } - fn intern_owned(this: ::Owned) -> Interned - where - Self: ToOwned, - C: InternContext> + Default, - { - Self::intern_owned_with_ctx(this, &C::default()) - } - fn intern_cow(this: Cow<'_, Self>) -> Interned - where - Self: ToOwned, - C: InternContext> + Default, - { - Self::intern_cow_with_ctx(this, &C::default()) - } -} - -pub struct Interner { - map: Mutex, ()>>, - _phantom: PhantomData, -} - -impl Default for Interner { +impl Default for Interner { fn default() -> Self { Self { map: Default::default(), - _phantom: Default::default(), } } } -impl Interner { - fn intern) -> C::InternedGuardImpl>( +impl Interner { + fn intern) -> &'static T>( &self, alloc: F, value: Cow<'_, T>, - ) -> Interned { + ) -> Interned { let mut map = self.map.lock().unwrap(); let hasher = map.hasher().clone(); let hash = hasher.hash_one(&*value); - let inner = match map - .raw_entry_mut() - .from_hash(hash, |k| k.borrow() == &*value) - { - RawEntryMut::Occupied(entry) => C::unguard(entry.key()), - RawEntryMut::Vacant(entry) => C::unguard( - entry - .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(k.borrow())) - .0, - ), + let inner = match map.raw_entry_mut().from_hash(hash, |k| **k == *value) { + RawEntryMut::Occupied(entry) => *entry.key(), + RawEntryMut::Vacant(entry) => { + *entry + .insert_with_hasher(hash, alloc(value), (), |k| hasher.hash_one(&**k)) + .0 + } }; - Interned { - inner, - _phantom: PhantomData, - } + Interned { inner } } } -impl Interner { - fn intern_sized(&self, context: &C, value: Cow<'_, T>) -> Interned { - self.intern(|value| context.alloc_sized(value), value) +impl Interner { + fn intern_sized(&self, value: Cow<'_, T>) -> Interned { + self.intern(|value| Box::leak(Box::new(value.into_owned())), value) } } -impl Interner<[T], C> { - fn intern_slice(&self, context: &C, value: Cow<'_, [T]>) -> Interned<[T], C> { - self.intern(|value| context.alloc_slice(value), value) +impl Interner<[T]> { + fn intern_slice(&self, value: Cow<'_, [T]>) -> Interned<[T]> { + self.intern(|value| value.into_owned().leak(), value) } } -impl Interner { - fn intern_bit_slice(&self, context: &C, value: Cow<'_, BitSlice>) -> Interned { - self.intern(|value| context.alloc_bit_slice(value), value) +impl Interner { + fn intern_bit_slice(&self, value: Cow<'_, BitSlice>) -> Interned { + self.intern(|value| value.into_owned().leak(), value) } } -impl Interner { - fn intern_str(&self, context: &C, value: Cow<'_, str>) -> Interned { - self.intern(|value| context.alloc_str(value), value) +impl Interner { + fn intern_str(&self, value: Cow<'_, str>) -> Interned { + self.intern(|value| value.into_owned().leak(), value) } } -pub struct Interned { - inner: C::InternedImpl, - _phantom: PhantomData<&'static C>, +pub struct Interned { + inner: &'static T, } macro_rules! forward_fmt_trait { @@ -522,23 +392,9 @@ macro_rules! forward_fmt_trait { } } - impl fmt::$Tr - for Interned - { + impl fmt::$Tr for Interned { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(guard) = C::try_guard(&self.inner) { - guard.borrow().fmt(f) - } else { - write!(f, "") - } - } - } - - impl fmt::$Tr - for Guard - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.borrow().fmt(f) + self.inner.fmt(f) } } }; @@ -554,18 +410,16 @@ forward_fmt_trait!(UpperExp); forward_fmt_trait!(UpperHex); #[derive(Clone, Debug)] -pub struct InternedSliceIter { - slice: Interned<[T], C>, +pub struct InternedSliceIter { + slice: Interned<[T]>, index: std::ops::Range, } -impl Iterator for InternedSliceIter { +impl Iterator for InternedSliceIter { type Item = T; fn next(&mut self) -> Option { - self.index - .next() - .map(|index| self.slice.guard()[index].clone()) + self.index.next().map(|index| self.slice[index].clone()) } fn size_hint(&self) -> (usize, Option) { @@ -573,233 +427,186 @@ impl Iterator for InternedSl } } -impl DoubleEndedIterator - for InternedSliceIter -{ +impl DoubleEndedIterator for InternedSliceIter { fn next_back(&mut self) -> Option { self.index .next_back() - .map(|index| self.slice.guard()[index].clone()) + .map(|index| self.slice[index].clone()) } } -impl FusedIterator for InternedSliceIter {} +impl FusedIterator for InternedSliceIter {} -impl ExactSizeIterator - for InternedSliceIter -{ -} +impl ExactSizeIterator for InternedSliceIter {} -impl IntoIterator for Interned<[T], C> { +impl IntoIterator for Interned<[T]> { type Item = T; - type IntoIter = InternedSliceIter; + type IntoIter = InternedSliceIter; fn into_iter(self) -> Self::IntoIter { InternedSliceIter { - index: 0..self.guard().len(), + index: 0..self.len(), slice: self, } } } -impl<'a, T: 'static + Send + Sync, C: InternContext> IntoIterator for &'a Interned<[T], C> -where - C::InternedImpl<[T]>: Borrow<[T]>, -{ +impl<'a, T: 'static + Send + Sync> IntoIterator for &'a Interned<[T]> { type Item = &'a T; type IntoIter = std::slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.inner.borrow().iter() + self.inner.iter() } } -impl<'a, T: 'static + Send + Sync, C: InternContext> IntoIterator for &'a mut Interned<[T], C> -where - C::InternedImpl<[T]>: Borrow<[T]>, -{ +impl<'a, T: 'static + Send + Sync> IntoIterator for &'a mut Interned<[T]> { type Item = &'a T; type IntoIter = std::slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.inner.borrow().iter() + self.inner.iter() } } -impl> + Default> - FromIterator for Interned<[I], C> +impl FromIterator for Interned<[I]> where - [I]: Intern, + [I]: Intern, { fn from_iter>(iter: T) -> Self { Intern::intern_owned(Vec::from_iter(iter)) } } -impl From> for Vec { - fn from(value: Interned<[T], C>) -> Self { - Vec::from(&*value.guard()) +impl From> for Vec { + fn from(value: Interned<[T]>) -> Self { + Vec::from(&*value) } } -impl From> for Box<[T]> { - fn from(value: Interned<[T], C>) -> Self { - Box::from(&*value.guard()) +impl From> for Box<[T]> { + fn from(value: Interned<[T]>) -> Self { + Box::from(&*value) } } -impl From> for String { - fn from(value: Interned) -> Self { - String::from(&*value.guard()) +impl From> for String { + fn from(value: Interned) -> Self { + String::from(&*value) } } -impl> + Default> Default - for Interned<[I], C> +impl Default for Interned<[I]> where - [I]: Intern, + [I]: Intern, { fn default() -> Self { [][..].intern() } } -impl> + Default> Default - for Interned -where - str: Intern, -{ +impl Default for Interned { fn default() -> Self { "".intern() } } -impl> + Default> Default - for Interned -where - BitSlice: Intern, -{ +impl Default for Interned { fn default() -> Self { <&BitSlice>::default().intern() } } -impl> + Default> - Default for Interned +impl Default for Interned where - I: Intern, + I: Intern, { fn default() -> Self { I::default().intern() } } -impl Interned { +impl Interned { pub fn cast_unchecked( this: Self, - f: impl FnOnce(C::InternedImpl) -> C::InternedImpl, - ) -> Interned { + f: impl FnOnce(&'static T) -> &'static U, + ) -> Interned { Interned { inner: f(this.inner), - _phantom: PhantomData, } } pub fn try_cast_unchecked( this: Self, - f: impl FnOnce(C::InternedImpl) -> Result, E>, - ) -> Result, E> { + f: impl FnOnce(&'static T) -> Result<&'static U, E>, + ) -> Result, E> { Ok(Interned { inner: f(this.inner)?, - _phantom: PhantomData, }) } - pub fn into_inner(this: Self) -> C::InternedImpl { + pub fn into_inner(this: Self) -> &'static T { this.inner } - pub fn get_ref(this: &Self) -> &C::InternedImpl { + pub fn get_ref(this: &Self) -> &&'static T { &this.inner } - pub fn guard(&self) -> Guard { - Guard { - inner: C::guard(&self.inner), - _phantom: PhantomData, - } - } } -impl Clone for Interned { +impl Clone for Interned { fn clone(&self) -> Self { - Interned { - inner: self.inner.clone(), - _phantom: PhantomData, - } + *self } } -impl Copy for Interned where - C::InternedImpl: Copy -{ -} +impl Copy for Interned where &'static T: Copy {} -impl Deref for Interned +impl Deref for Interned where - C::InternedImpl: Borrow, + &'static T: Borrow, { type Target = T; fn deref(&self) -> &Self::Target { - self.inner.borrow() + self.inner } } -impl PartialEq - for Interned -{ +impl PartialEq for Interned { fn eq(&self, other: &Self) -> bool { - ::interned_compare_key(&self.inner) - == ::interned_compare_key(&other.inner) + T::interned_compare_key_ref(self.inner) == T::interned_compare_key_ref(other.inner) } } -impl Eq for Interned {} +impl Eq for Interned {} -impl PartialOrd - for Interned -{ +impl PartialOrd for Interned { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Interned { +impl Ord for Interned { fn cmp(&self, other: &Self) -> Ordering { - ::interned_compare_key(&self.inner) - .cmp(&::interned_compare_key(&other.inner)) + T::interned_compare_key_ref(self.inner).cmp(&T::interned_compare_key_ref(other.inner)) } } -impl Hash - for Interned -{ +impl Hash for Interned { fn hash(&self, state: &mut H) { - ::interned_compare_key(&self.inner).hash(state); + T::interned_compare_key_ref(self.inner).hash(state); } } -impl Serialize for Interned { +impl Serialize for Interned { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - self.guard().serialize(serializer) + T::serialize(self, serializer) } } -impl< - 'de, - T: 'static + Send + Sync + Deserialize<'de> + Clone + Intern, - C: InternContext> + Default, - > Deserialize<'de> for Interned +impl<'de, T: 'static + Send + Sync + Deserialize<'de> + Clone + Intern> Deserialize<'de> + for Interned { fn deserialize(deserializer: D) -> Result where @@ -809,13 +616,9 @@ impl< } } -impl< - 'de, - T: 'static + Send + Sync + Clone, - C: InternContext> + Default, - > Deserialize<'de> for Interned<[T], C> +impl<'de, T: 'static + Send + Sync + Clone> Deserialize<'de> for Interned<[T]> where - [T]: Intern, + [T]: Intern, Vec: Deserialize<'de>, { fn deserialize(deserializer: D) -> Result @@ -826,11 +629,7 @@ where } } -impl<'de, C: BitSliceInternContext> + Default> - Deserialize<'de> for Interned -where - BitSlice: Intern, -{ +impl<'de> Deserialize<'de> for Interned { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -839,11 +638,7 @@ where } } -impl<'de, C: InternContext> + Default> Deserialize<'de> - for Interned -where - str: Intern, -{ +impl<'de> Deserialize<'de> for Interned { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -852,175 +647,83 @@ where } } -pub struct Guard { - inner: C::InternedGuardImpl, - _phantom: PhantomData<&'static C>, -} - -impl Guard { - pub fn cast_unchecked( - this: Self, - f: impl FnOnce(C::InternedGuardImpl) -> C::InternedGuardImpl, - ) -> Guard { - Guard { - inner: f(this.inner), - _phantom: PhantomData, - } - } - pub fn try_cast_unchecked( - this: Self, - f: impl FnOnce(C::InternedGuardImpl) -> Result, E>, - ) -> Result, E> { - Ok(Guard { - inner: f(this.inner)?, - _phantom: PhantomData, - }) - } - pub fn into_inner(this: Self) -> C::InternedGuardImpl { - this.inner - } - pub fn get_ref(this: &Self) -> &C::InternedGuardImpl { - &this.inner - } - pub fn unguard(&self) -> Interned { - Interned { - inner: C::unguard(&self.inner), - _phantom: PhantomData, - } - } -} - -impl Clone for Guard { - fn clone(&self) -> Self { - Guard { - inner: self.inner.clone(), - _phantom: PhantomData, - } - } -} - -impl Copy for Guard where - C::InternedGuardImpl: Copy -{ -} - -impl Deref for Guard { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.borrow() - } -} - -impl PartialEq - for Guard -{ - fn eq(&self, other: &Self) -> bool { - T::interned_compare_key_ref(self.inner.borrow()) - == T::interned_compare_key_ref(other.inner.borrow()) - } -} - -impl Eq for Guard {} - -impl PartialOrd - for Guard -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Guard { - fn cmp(&self, other: &Self) -> Ordering { - T::interned_compare_key_ref(self.inner.borrow()) - .cmp(&T::interned_compare_key_ref(other.inner.borrow())) - } -} - -impl Hash for Guard { - fn hash(&self, state: &mut H) { - T::interned_compare_key_ref(self.inner.borrow()).hash(state); - } -} - -impl Intern for T { - fn intern_with_ctx(&self, context: &C) -> Interned { - Self::intern_cow_with_ctx(Cow::Borrowed(self), context) +impl Intern for T { + fn intern(&self) -> Interned { + Self::intern_cow(Cow::Borrowed(self)) } - fn intern_owned_with_ctx(this: ::Owned, context: &C) -> Interned + fn intern_owned(this: ::Owned) -> Interned where Self: ToOwned, { - Self::intern_cow_with_ctx(Cow::Owned(this), context) + Self::intern_cow(Cow::Owned(this)) } - fn intern_cow_with_ctx(this: Cow<'_, Self>, context: &C) -> Interned + fn intern_cow(this: Cow<'_, Self>) -> Interned where Self: ToOwned, { - context.interner().intern_sized(context, this) + Interner::get().intern_sized(this) } } -impl Intern for [T] { - fn intern_with_ctx(&self, context: &C) -> Interned { - Self::intern_cow_with_ctx(Cow::Borrowed(self), context) +impl Intern for [T] { + fn intern(&self) -> Interned { + Self::intern_cow(Cow::Borrowed(self)) } - fn intern_owned_with_ctx(this: ::Owned, context: &C) -> Interned + fn intern_owned(this: ::Owned) -> Interned where Self: ToOwned, { - Self::intern_cow_with_ctx(Cow::Owned(this), context) + Self::intern_cow(Cow::Owned(this)) } - fn intern_cow_with_ctx(this: Cow<'_, Self>, context: &C) -> Interned + fn intern_cow(this: Cow<'_, Self>) -> Interned where Self: ToOwned, { - context.interner().intern_slice(context, this) + Interner::get().intern_slice(this) } } -impl Intern for BitSlice { - fn intern_with_ctx(&self, context: &C) -> Interned { - Self::intern_cow_with_ctx(Cow::Borrowed(self), context) +impl Intern for BitSlice { + fn intern(&self) -> Interned { + Self::intern_cow(Cow::Borrowed(self)) } - fn intern_owned_with_ctx(this: ::Owned, context: &C) -> Interned + fn intern_owned(this: ::Owned) -> Interned where Self: ToOwned, { - Self::intern_cow_with_ctx(Cow::Owned(this), context) + Self::intern_cow(Cow::Owned(this)) } - fn intern_cow_with_ctx(this: Cow<'_, Self>, context: &C) -> Interned + fn intern_cow(this: Cow<'_, Self>) -> Interned where Self: ToOwned, { - context.interner().intern_bit_slice(context, this) + Interner::get().intern_bit_slice(this) } } -impl Intern for str { - fn intern_with_ctx(&self, context: &C) -> Interned { - Self::intern_cow_with_ctx(Cow::Borrowed(self), context) +impl Intern for str { + fn intern(&self) -> Interned { + Self::intern_cow(Cow::Borrowed(self)) } - fn intern_owned_with_ctx(this: ::Owned, context: &C) -> Interned + fn intern_owned(this: ::Owned) -> Interned where Self: ToOwned, { - Self::intern_cow_with_ctx(Cow::Owned(this), context) + Self::intern_cow(Cow::Owned(this)) } - fn intern_cow_with_ctx(this: Cow<'_, Self>, context: &C) -> Interned + fn intern_cow(this: Cow<'_, Self>) -> Interned where Self: ToOwned, { - context.interner().intern_str(context, this) + Interner::get().intern_str(this) } } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 380d2e6..69080c9 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -330,6 +330,6 @@ impl Index for AsMaskWithoutGenerics { type Output = T::MaskType; fn index(&self, ty: T) -> &Self::Output { - Interned::<_>::into_inner(Intern::intern_sized(ty.mask_type())) + Interned::into_inner(Intern::intern_sized(ty.mask_type())) } } From ed1aea41f31776568aef509157dbb5708b855e3f Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 21:49:18 -0700 Subject: [PATCH 060/190] clean up some clippy warnings --- .../fayalite-proc-macros-impl/src/hdl_bundle.rs | 3 +-- crates/fayalite-proc-macros-impl/src/hdl_enum.rs | 2 +- .../src/hdl_type_common.rs | 6 +++--- crates/fayalite/src/bundle.rs | 10 +++++++++- crates/fayalite/src/enum_.rs | 9 +++++++-- crates/fayalite/src/int.rs | 4 ++-- crates/fayalite/src/memory.rs | 4 ++-- crates/fayalite/src/module.rs | 15 ++++++++------- .../src/module/transform/simplify_enums.rs | 11 ++++------- .../src/module/transform/simplify_memories.rs | 2 +- crates/fayalite/src/util/scoped_ref.rs | 6 ++++++ crates/fayalite/tests/module.rs | 2 +- 12 files changed, 45 insertions(+), 29 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index b13aa08..cf08c7e 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -58,8 +58,7 @@ impl ParsedBundle { } *mutability = FieldMutability::None; colon_token.get_or_insert(Token![:](ident.span())); - let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)); - options + errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs)) } fn parse(item: ItemStruct) -> syn::Result { let ItemStruct { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index adddd74..1d16177 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -600,7 +600,7 @@ impl ToTokens for ParsedEnum { static_generics.split_for_impl(); let static_type_body_variants = Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| { - if let Some(_) = field { + if field.is_some() { quote_spanned! {span=> #ident: ::fayalite::ty::StaticType::TYPE, } 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 239f32b..e36f68e 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1410,7 +1410,7 @@ impl ParseTypes for ParsedType { let mut args = None; let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { let PathSegment { ident, arguments } = segment; - if let Some(_) = args { + if args.is_some() { parser .errors() .error(&ident, "associated types/consts are not yet implemented"); @@ -1594,7 +1594,7 @@ impl ParseTypes for ParsedConstGenericType { let mut args = None; let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { let PathSegment { ident, arguments } = segment; - if let Some(_) = args { + if args.is_some() { parser .errors() .error(&ident, "associated types/consts are not yet implemented"); @@ -3554,7 +3554,7 @@ impl SplitForImpl for Generics { Self::TypeGenerics<'_>, Self::WhereClause<'_>, ) { - Generics::split_for_impl(&self) + Generics::split_for_impl(self) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 199c2b2..b338322 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -144,6 +144,12 @@ impl BundleTypePropertiesBuilder { } } +impl Default for BundleTypePropertiesBuilder { + fn default() -> Self { + Self::new() + } +} + impl Bundle { #[track_caller] pub fn new(fields: Interned<[BundleField]>) -> Self { @@ -342,6 +348,7 @@ macro_rules! impl_tuples { std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*))) } fn mask_type(&self) -> Self::MaskType { + #![allow(clippy::unused_unit)] let ($($var,)*) = self; ($($var.mask_type(),)*) } @@ -350,6 +357,7 @@ macro_rules! impl_tuples { } #[track_caller] fn from_canonical(canonical_type: CanonicalType) -> Self { + #![allow(clippy::unused_unit)] let CanonicalType::Bundle(bundle) = canonical_type else { panic!("expected bundle"); }; @@ -358,7 +366,7 @@ macro_rules! impl_tuples { }; $(let BundleField { name, flipped, ty } = $var; assert_eq!(&*name, stringify!($num)); - assert_eq!(flipped, false); + assert!(!flipped); let $var = $T::from_canonical(ty);)* ($($var,)*) } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 13724ef..2ed0b8e 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -169,6 +169,12 @@ impl EnumTypePropertiesBuilder { } } +impl Default for EnumTypePropertiesBuilder { + fn default() -> Self { + Self::new() + } +} + impl Enum { #[track_caller] pub fn new(variants: Interned<[EnumVariant]>) -> Self { @@ -399,9 +405,8 @@ impl HdlOption { else { unreachable!(); }; - let value = f(value).map_err(|e| { + let value = f(value).inspect_err(|_| { and_then_out.complete(()); // avoid error - e })?; let and_then_out = and_then_out.complete(Expr::ty(value)); connect(and_then_out, value); diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index f529198..dd631d0 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -147,7 +147,7 @@ where fn try_from_usize(v: usize) -> Option { if v == VALUE { - Some(Self::SizeType::default()) + Some(ConstUsize) } else { None } @@ -560,7 +560,7 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { } fn bits_to_expr(bits: Cow<'_, BitSlice>) -> Expr; fn le_bytes_to_expr_wrapping(bytes: &[u8], bit_width: usize) -> Expr { - let bitslice = BitSlice::::from_slice(&bytes); + let bitslice = BitSlice::::from_slice(bytes); let bitslice = &bitslice[..bit_width.min(bitslice.len())]; let mut bits = BitVec::new(); bits.extend_from_bitslice(bitslice); diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 1762f57..f583a8c 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -634,7 +634,7 @@ impl Mem { self.0.source_location } pub fn array_type(self) -> ArrayType { - self.0.array_type.clone() + self.0.array_type } pub fn initial_value(self) -> Option> { self.0.initial_value @@ -987,7 +987,7 @@ impl MemBuilder { #[allow(clippy::result_unit_err)] pub fn get_array_type(&self) -> Result, ()> { Ok(ArrayType::new( - self.mem_element_type.clone(), + self.mem_element_type, Len::from_usize(self.get_depth()?), )) } diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 2ba3800..7d6949c 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -607,6 +607,10 @@ impl BlockStack { pub struct Id(NonZeroU64); impl Id { + #[allow( + clippy::new_without_default, + reason = "returns a different value each time, so there isn't really a default value" + )] pub fn new() -> Self { static NEXT_ID: AtomicU64 = AtomicU64::new(1); Self( @@ -945,7 +949,7 @@ impl From> for NormalModuleBody { Stmt::Declaration(decl) => { let annotations = annotations_map .remove(&decl) - .map(|v| Intern::intern_owned(v)) + .map(Intern::intern_owned) .unwrap_or_default(); match decl { StmtDeclaration::Wire(StmtWire { @@ -1615,10 +1619,7 @@ impl AssertValidityState { let module = self.module; if block == 0 { for module_io in &*module.module_io { - self.insert_new_base( - TargetBase::intern_sized(module_io.module_io.clone().into()), - block, - ); + self.insert_new_base(TargetBase::intern_sized(module_io.module_io.into()), block); } } let Block { memories, stmts } = self.blocks[block]; @@ -1934,7 +1935,7 @@ impl ModuleBuilder { let module_io = module_io.canonical(); let mut impl_ = self.impl_.borrow_mut(); let impl_ = &mut *impl_; - impl_.io_indexes.insert(module_io.clone(), impl_.io.len()); + impl_.io_indexes.insert(module_io, impl_.io.len()); impl_.io.push(AnnotatedModuleIO { annotations: vec![], module_io, @@ -2478,7 +2479,7 @@ pub fn memory_array_with_loc( mem_array_type: ArrayType, source_location: SourceLocation, ) -> MemBuilder { - let mut retval = memory_impl(name, mem_array_type.element().clone(), source_location); + let mut retval = memory_impl(name, mem_array_type.element(), source_location); retval.depth(mem_array_type.len()); retval } diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index f398006..bb57cf0 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -271,12 +271,12 @@ impl State { .into()), EnumTypeState::TagUIntAndBody(_) => { let int_tag_expr = Expr::>::from_canonical(folded_expr).tag; - Ok(match_int_tag(int_tag_expr, source_location, &folded_blocks).into()) + Ok(match_int_tag(int_tag_expr, source_location, folded_blocks).into()) } EnumTypeState::UInt(_) => { let int_tag_expr = Expr::::from_canonical(folded_expr) [..unfolded_enum_type.discriminant_bit_width()]; - Ok(match_int_tag(int_tag_expr, source_location, &folded_blocks).into()) + Ok(match_int_tag(int_tag_expr, source_location, folded_blocks).into()) } EnumTypeState::Unchanged => Ok(StmtMatch { expr: Expr::from_canonical(folded_expr), @@ -929,13 +929,10 @@ impl Folder for State { unreachable!() } - fn fold_enum_literal( + fn fold_enum_literal>( &mut self, _v: ops::EnumLiteral, - ) -> Result, Self::Error> - where - T: Fold, - { + ) -> Result, Self::Error> { unreachable!() } diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index 71e459c..e8f9cbf 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -766,7 +766,7 @@ impl ModuleState { output_stmts.push( StmtWire { annotations: Default::default(), - wire: canonical_wire.clone(), + wire: canonical_wire, } .into(), ); diff --git a/crates/fayalite/src/util/scoped_ref.rs b/crates/fayalite/src/util/scoped_ref.rs index 7705377..27f2f89 100644 --- a/crates/fayalite/src/util/scoped_ref.rs +++ b/crates/fayalite/src/util/scoped_ref.rs @@ -106,3 +106,9 @@ impl ScopedRef { self.0.with_opt(f) } } + +impl Default for ScopedRef { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index b0b1fb6..222f7ba 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -2258,7 +2258,7 @@ pub fn check_memory_of_bundle() { let wmask: (Bool, Bool) = m.input(); #[hdl] let clk: Clock = m.input(); - let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ i / 2).cast_to_static()))); + let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static()))); #[hdl] let mut mem = memory_with_init(mem_init); let read_port = mem.new_read_port(); From 017c14a2f15d8ccc0c32ba4d1a1032d6a8b704e7 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 22:05:30 -0700 Subject: [PATCH 061/190] don't use #[allow(..., reason = "...")] since that's not stable yet on rust 1.80.1 --- crates/fayalite/src/module.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 7d6949c..7387832 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -607,10 +607,8 @@ impl BlockStack { pub struct Id(NonZeroU64); impl Id { - #[allow( - clippy::new_without_default, - reason = "returns a different value each time, so there isn't really a default value" - )] + // returns a different value each time, so there isn't really a default value + #[allow(clippy::new_without_default)] pub fn new() -> Self { static NEXT_ID: AtomicU64 = AtomicU64::new(1); Self( From 99180eb3b4f37164994cea8a6b17291dfa4fed46 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 21:59:50 -0700 Subject: [PATCH 062/190] fix clippy lints in generated code --- crates/fayalite-proc-macros-impl/src/hdl_bundle.rs | 4 ++-- .../fayalite-proc-macros-impl/src/hdl_type_common.rs | 1 + crates/fayalite-proc-macros-impl/src/module.rs | 11 ++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index cf08c7e..eefa8be 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -584,7 +584,7 @@ impl ToTokens for ParsedBundle { |((index, field), flip)| { let ident: &Ident = field.ident().as_ref().unwrap(); let ident_str = ident.to_string(); - let flipped = flip.is_some(); + let not_flipped = flip.is_none().then(|| Token![!](span)); quote_spanned! {span=> #ident: { let ::fayalite::bundle::BundleField { @@ -593,7 +593,7 @@ impl ToTokens for ParsedBundle { ty: __ty, } = #fields_token[#index]; ::fayalite::__std::assert_eq!(&*__name, #ident_str); - ::fayalite::__std::assert_eq!(__flipped, #flipped); + ::fayalite::__std::assert!(#not_flipped __flipped); ::fayalite::ty::Type::from_canonical(__ty) }, } 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 e36f68e..b6ab88d 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -66,6 +66,7 @@ impl Drop for WrappedInConst<'_> { fn drop(&mut self) { let inner = &self.inner; quote_spanned! {self.span=> + #[allow(clippy::type_complexity)] const _: () = { #inner }; diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 6363bb3..0852f58 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -450,12 +450,21 @@ impl ModuleFn { let fn_name_str = fn_name.to_string(); let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl(); let body_turbofish_type_generics = body_type_generics.as_turbofish(); + let body_lambda = if param_names.is_empty() { + quote! { + __body #body_turbofish_type_generics + } + } else { + quote! { + |m| __body #body_turbofish_type_generics(m, #(#param_names,)*) + } + }; let block = parse_quote! {{ #body_fn ::fayalite::module::ModuleBuilder::run( #fn_name_str, #module_kind_value, - |m| __body #body_turbofish_type_generics(m, #(#param_names,)*), + #body_lambda, ) }}; let outer_fn = ItemFn { From bf907c38721ebc804d6aaf0bf5ed6798d8604dab Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 7 Oct 2024 23:31:24 -0700 Subject: [PATCH 063/190] cache results of formal proofs --- Cargo.lock | 48 +++++++++++++ Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/cli.rs | 141 ++++++++++++++++++++++++++++++++----- 4 files changed, 175 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa6fb6b..1e6f88c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.1.0" @@ -109,6 +121,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "serde", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -118,6 +144,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cc" +version = "1.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -170,6 +205,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -242,6 +283,7 @@ name = "fayalite" version = "0.2.0" dependencies = [ "bitvec", + "blake3", "clap", "eyre", "fayalite-proc-macros", @@ -521,6 +563,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 15d13b6..699d57f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ fayalite-proc-macros-impl = { version = "=0.2.0", path = "crates/fayalite-proc-m fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" } base16ct = "0.2.0" bitvec = { version = "1.0.1", features = ["serde"] } +blake3 = { version = "1.5.4", features = ["serde"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] } eyre = "0.6.12" hashbrown = "0.14.3" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 21089c0..828d759 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -15,6 +15,7 @@ version.workspace = true [dependencies] bitvec.workspace = true +blake3.workspace = true clap.workspace = true eyre.workspace = true fayalite-proc-macros.workspace = true diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index fa1b247..a3649cc 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -12,6 +12,7 @@ use clap::{ Parser, Subcommand, ValueEnum, ValueHint, }; use eyre::{eyre, Report}; +use serde::{Deserialize, Serialize}; use std::{ error, ffi::OsString, @@ -197,8 +198,9 @@ impl BaseArgs { &self, _acquired_job: &mut AcquiredJob, mut command: process::Command, + mut captured_output: Option<&mut String>, ) -> io::Result { - if self.redirect_output_for_rust_test { + if self.redirect_output_for_rust_test || captured_output.is_some() { let (reader, writer) = os_pipe::pipe()?; let mut reader = io::BufReader::new(reader); command.stderr(writer.try_clone()?); @@ -209,6 +211,9 @@ impl BaseArgs { Ok(loop { let status = child.try_wait()?; streaming_read_utf8(&mut reader, |s| { + if let Some(captured_output) = captured_output.as_deref_mut() { + captured_output.push_str(s); + } // use print! so output goes to Rust test output capture print!("{s}"); io::Result::Ok(()) @@ -369,6 +374,7 @@ pub struct VerilogArgs { pub struct VerilogOutput { pub firrtl: FirrtlOutput, pub verilog_files: Vec, + pub contents_hash: Option, } impl VerilogOutput { @@ -386,6 +392,7 @@ impl VerilogArgs { let file_separator_prefix = "\n// ----- 8< ----- FILE \""; let file_separator_suffix = "\" ----- 8< -----\n\n"; let mut input = &*input; + output.contents_hash = Some(blake3::hash(input.as_bytes())); let main_verilog_file = output.main_verilog_file(); let mut file_name: Option<&Path> = Some(&main_verilog_file); loop { @@ -428,6 +435,7 @@ impl VerilogArgs { let output = VerilogOutput { firrtl: firrtl_output, verilog_files: vec![], + contents_hash: None, }; let mut cmd = process::Command::new(firtool); cmd.arg(output.firrtl.firrtl_file()); @@ -442,7 +450,7 @@ impl VerilogArgs { } cmd.args(firtool_extra_args); cmd.current_dir(&output.firrtl.output_dir); - let status = firrtl.base.run_external_command(acquired_job, cmd)?; + let status = firrtl.base.run_external_command(acquired_job, cmd, None)?; if status.success() { self.process_unadjusted_verilog_file(output) } else { @@ -541,7 +549,7 @@ pub struct FormalArgs { )] pub sby: PathBuf, #[arg(long)] - pub sby_extra_args: Vec, + pub sby_extra_args: Vec, #[arg(long, default_value_t)] pub mode: FormalMode, #[arg(long, default_value_t = Self::DEFAULT_DEPTH)] @@ -550,6 +558,8 @@ pub struct FormalArgs { pub solver: String, #[arg(long)] pub smtbmc_extra_args: Vec, + #[arg(long, default_value_t = true, env = "FAYALITE_CACHE_RESULTS")] + pub cache_results: bool, #[command(flatten)] _formal_adjust_args: FormalAdjustArgs, } @@ -564,16 +574,18 @@ impl fmt::Debug for FormalArgs { depth, solver, smtbmc_extra_args, + cache_results, _formal_adjust_args: _, } = self; f.debug_struct("FormalArgs") - .field("verilog", &verilog) - .field("sby", &sby) - .field("sby_extra_args", &sby_extra_args) - .field("mode", &mode) - .field("depth", &depth) - .field("solver", &solver) - .field("smtbmc_extra_args", &smtbmc_extra_args) + .field("verilog", verilog) + .field("sby", sby) + .field("sby_extra_args", sby_extra_args) + .field("mode", mode) + .field("depth", depth) + .field("solver", solver) + .field("smtbmc_extra_args", smtbmc_extra_args) + .field("cache_results", cache_results) .finish_non_exhaustive() } } @@ -592,6 +604,48 @@ impl FormalOutput { pub fn sby_file(&self) -> PathBuf { self.verilog.firrtl.file_with_ext("sby") } + pub fn cache_file(&self) -> PathBuf { + self.verilog.firrtl.file_with_ext("cache.json") + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub struct FormalCacheOutput {} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum FormalCacheVersion { + V1, +} + +impl FormalCacheVersion { + pub const CURRENT: Self = Self::V1; +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub struct FormalCache { + pub version: FormalCacheVersion, + pub contents_hash: blake3::Hash, + pub stdout_stderr: String, + pub result: Result, +} + +impl FormalCache { + pub fn new( + version: FormalCacheVersion, + contents_hash: blake3::Hash, + stdout_stderr: String, + result: Result, + ) -> Self { + Self { + version, + contents_hash, + stdout_stderr, + result, + } + } } impl FormalArgs { @@ -604,6 +658,7 @@ impl FormalArgs { depth, smtbmc_extra_args, solver, + cache_results: _, _formal_adjust_args: _, } = self; let smtbmc_options = smtbmc_extra_args.join(" "); @@ -644,7 +699,19 @@ impl FormalArgs { verilog: verilog_output, }; let sby_file = output.sby_file(); - std::fs::write(&sby_file, self.sby_contents(&output)?)?; + let sby_contents = self.sby_contents(&output)?; + let contents_hash = output.verilog.contents_hash.map(|verilog_hash| { + let mut hasher = blake3::Hasher::new(); + hasher.update(verilog_hash.as_bytes()); + hasher.update(sby_contents.as_bytes()); + hasher.update(&(self.sby_extra_args.len() as u64).to_le_bytes()); + for sby_extra_arg in self.sby_extra_args.iter() { + hasher.update(&(sby_extra_arg.len() as u64).to_le_bytes()); + hasher.update(sby_extra_arg.as_bytes()); + } + hasher.finalize() + }); + std::fs::write(&sby_file, sby_contents)?; let mut cmd = process::Command::new(&self.sby); cmd.arg("-f"); cmd.arg(sby_file.file_name().unwrap()); @@ -656,20 +723,62 @@ impl FormalArgs { NonZeroUsize::new(1).unwrap() }; let new_minimum_count = AcquiredJob::max_available_job_count().min(new_minimum_count); + let mut captured_output = String::new(); + let cache_file = output.cache_file(); + let do_cache = if let Some(contents_hash) = contents_hash.filter(|_| self.cache_results) { + if let Some(FormalCache { + version: FormalCacheVersion::CURRENT, + contents_hash: cache_contents_hash, + stdout_stderr, + result, + }) = fs::read(&cache_file) + .ok() + .and_then(|v| serde_json::from_slice(&v).ok()) + { + if cache_contents_hash == contents_hash { + println!("Using cached formal result:\n{stdout_stderr}"); + return match result { + Ok(FormalCacheOutput {}) => Ok(output), + Err(error) => Err(CliError(eyre::Report::msg(error))), + }; + } + } + true + } else { + false + }; + let _ = fs::remove_file(&cache_file); let status = acquired_job.increase_job_count(new_minimum_count, |acquired_job| { - self.verilog - .firrtl - .base - .run_external_command(acquired_job, cmd) + self.verilog.firrtl.base.run_external_command( + acquired_job, + cmd, + do_cache.then_some(&mut captured_output), + ) })?; - if status.success() { + let result = if status.success() { Ok(output) } else { Err(CliError(eyre!( "running {} failed: {status}", self.sby.display() ))) + }; + if do_cache { + fs::write( + cache_file, + serde_json::to_string_pretty(&FormalCache { + version: FormalCacheVersion::CURRENT, + contents_hash: contents_hash.unwrap(), + stdout_stderr: captured_output, + result: match &result { + Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}), + Err(error) => Err(error.to_string()), + }, + }) + .expect("serialization shouldn't ever fail"), + )?; } + result } } From 59cef3f398322efd7d2d6da05a812e97b68c198a Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 10 Oct 2024 20:48:09 -0700 Subject: [PATCH 064/190] add PhantomData as a hdl bundle --- .../src/hdl_type_common.rs | 192 ++++++++++++++++-- crates/fayalite/src/bundle.rs | 82 ++++++++ crates/fayalite/tests/hdl_types.rs | 3 + 3 files changed, 255 insertions(+), 22 deletions(-) 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 b6ab88d..506c153 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1273,6 +1273,130 @@ make_parsed_type_or_const! { } } +#[derive(Debug, Clone)] +pub(crate) struct ParsedTypePhantomData { + pub(crate) phantom_data: known_items::PhantomData, + pub(crate) lt_token: Token![<], + pub(crate) ty: Box, + pub(crate) gt_token: Token![>], +} + +impl_fold! { + struct ParsedTypePhantomData<> { + phantom_data: known_items::PhantomData, + lt_token: Token![<], + ty: Box, + gt_token: Token![>], + } +} + +impl ParsedTypePhantomData { + pub(crate) fn try_from_named( + named: ParsedTypeNamed, + parser: &mut TypesParser<'_>, + ) -> Result, ParseFailed> { + let ParsedTypeNamed { path, args } = named; + let parsed_path = known_items::PhantomData::parse_path(path); + let phantom_data = match parsed_path { + Ok(phantom_data) => phantom_data, + Err(path) => return Ok(Err(ParsedTypeNamed { path, args })), + }; + let Some(ParsedGenericArguments { + colon2_token: _, + lt_token, + args, + gt_token, + }) = args + else { + parser + .errors() + .error(phantom_data, "PhantomData requires generic arguments"); + return Err(ParseFailed); + }; + let args_len = args.len(); + if args_len != 1 { + parser.errors().error( + phantom_data, + format_args!( + "wrong number of generic arguments supplied: got {args_len}, expected 1" + ), + ); + return Err(ParseFailed); + } + let ty = args.into_iter().next().unwrap(); + let ParsedGenericArgument::Type(ty) = ty else { + parser.errors().error(ty, "expected a type"); + return Err(ParseFailed); + }; + Ok(Ok(Self { + phantom_data, + lt_token, + ty: Box::new(ty), + gt_token, + })) + } +} + +impl From for Type { + fn from(value: ParsedTypePhantomData) -> Type { + let ParsedTypePhantomData { + phantom_data, + lt_token, + ty, + gt_token, + } = value; + let path = phantom_data.path; + let mut args = Punctuated::new(); + args.push(GenericArgument::Type(ty.into())); + let args = AngleBracketedGenericArguments { + colon2_token: Some(Token![::](lt_token.span)), + lt_token, + args, + gt_token, + }; + let mut segments = path.segments; + segments.last_mut().unwrap().arguments = PathArguments::AngleBracketed(args); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: path.leading_colon, + segments, + }, + }) + } +} + +impl MakeHdlTypeExpr for ParsedTypePhantomData { + fn make_hdl_type_expr(&self, _context: &MakeHdlTypeExprContext) -> Expr { + let ParsedTypePhantomData { + phantom_data, + lt_token: _, + ty: _, + gt_token: _, + } = self; + Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: phantom_data.path.clone(), + }) + } +} + +impl ToTokens for ParsedTypePhantomData { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + phantom_data, + lt_token, + ty, + gt_token, + } = self; + phantom_data.to_tokens(tokens); + lt_token.to_tokens(tokens); + ty.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + #[derive(Debug, Clone)] pub(crate) enum ParsedType { Delimited(ParsedTypeDelimited), @@ -1280,6 +1404,7 @@ pub(crate) enum ParsedType { NamedParam(ParsedTypeNamedParam), Tuple(ParsedTypeTuple), ConstUsize(ParsedTypeConstUsize), + PhantomData(ParsedTypePhantomData), Array(ParsedTypeArray), UInt(ParsedTypeUInt), SInt(ParsedTypeSInt), @@ -1294,6 +1419,7 @@ impl_fold! { NamedParam(ParsedTypeNamedParam), Tuple(ParsedTypeTuple), ConstUsize(ParsedTypeConstUsize), + PhantomData(ParsedTypePhantomData), Array(ParsedTypeArray), UInt(ParsedTypeUInt), SInt(ParsedTypeSInt), @@ -1310,6 +1436,7 @@ impl From for Type { ParsedType::NamedParam(v) => v.into(), ParsedType::Tuple(v) => v.into(), ParsedType::ConstUsize(v) => v.into(), + ParsedType::PhantomData(v) => v.into(), ParsedType::Array(v) => v.into(), ParsedType::UInt(v) => v.into(), ParsedType::SInt(v) => v.into(), @@ -1360,6 +1487,7 @@ impl ToTokens for ParsedType { ParsedType::Named(ty) => ty.to_tokens(tokens), ParsedType::Tuple(ty) => ty.to_tokens(tokens), ParsedType::ConstUsize(ty) => ty.to_tokens(tokens), + ParsedType::PhantomData(ty) => ty.to_tokens(tokens), ParsedType::Array(ty) => ty.to_tokens(tokens), ParsedType::UInt(ty) => ty.to_tokens(tokens), ParsedType::SInt(ty) => ty.to_tokens(tokens), @@ -1467,6 +1595,10 @@ impl ParseTypes for ParsedType { Ok(v) => return Ok(Self::ConstUsize(v)), Err(named) => named, }; + let named = match ParsedTypePhantomData::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::PhantomData(v)), + Err(named) => named, + }; let named = match ParsedTypeUInt::try_from_named(named, parser)? { Ok(v) => return Ok(Self::UInt(v)), Err(named) => named, @@ -1784,7 +1916,7 @@ pub(crate) mod known_items { #[allow(non_snake_case, dead_code)] pub(crate) fn $known_item(span: Span) -> $known_item { - let segments = $known_item::PATH_SEGMENTS.iter() + let segments = $known_item::PATH_SEGMENTS[0].iter() .copied() .map(|seg| PathSegment::from(Ident::new(seg, span))) .collect(); @@ -1810,20 +1942,21 @@ pub(crate) mod known_items { return Ok(Self { span: ident.span(), path }); } } - if path.segments.len() == Self::PATH_SEGMENTS.len() - && path - .segments - .iter() - .zip(Self::PATH_SEGMENTS) - .all(|(seg, expected)| { - matches!(seg.arguments, PathArguments::None) - && seg.ident == *expected - }) - { - Ok(Self { span: path.segments.last().unwrap().ident.span(), path }) - } else { - Err(path) + for &path_segments in Self::PATH_SEGMENTS.iter() { + if path.segments.len() == path_segments.len() + && path + .segments + .iter() + .zip(path_segments) + .all(|(seg, expected)| { + matches!(seg.arguments, PathArguments::None) + && seg.ident == *expected + }) + { + return Ok(Self { span: path.segments.last().unwrap().ident.span(), path }); + } } + Err(path) } #[allow(dead_code)] pub(crate) fn parse_path_with_arguments(mut path: Path) -> Result<(Self, PathArguments), Path> { @@ -1878,19 +2011,24 @@ pub(crate) mod known_items { } macro_rules! impl_known_item { - ($([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => { - impl_known_item!([$($(::$head)*)? ::$next] $(::$tail)+); - }; - ([$(::$seg:ident)+] ::$known_item:ident) => { + ($(#[alias = $(::$alias:ident)+])* [$(::$seg:ident)+] ::$known_item:ident) => { impl_known_item_body!($known_item); impl $known_item { - pub(crate) const PATH_SEGMENTS: &'static [&'static str] = &[ - $(stringify!($seg),)+ - stringify!($known_item), + pub(crate) const PATH_SEGMENTS: &'static [&'static [&'static str]] = &[ + &[ + $(stringify!($seg),)+ + stringify!($known_item), + ], + $(&[ + $(stringify!($alias),)+ + ],)* ]; } }; + ($(#[alias = $(::$alias:ident)+])* $([$(::$head:ident)*])? ::$next:ident $(::$tail:ident)+) => { + impl_known_item!($(#[alias = $(::$alias)+])* [$($(::$head)*)? ::$next] $(::$tail)+); + }; } impl_known_item!(::fayalite::array::Array); @@ -1911,7 +2049,16 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::ty::Type); impl_known_item!(::fayalite::ty::Type::MaskType); impl_known_item!(::fayalite::util::ConstUsize); - impl_known_item!(::fayalite::__std::primitive::usize); + impl_known_item!( + #[alias = ::std::primitive::usize] + #[alias = ::core::primitive::usize] + ::fayalite::__std::primitive::usize + ); + impl_known_item!( + #[alias = ::std::marker::PhantomData] + #[alias = ::core::marker::PhantomData] + ::fayalite::__std::marker::PhantomData + ); } macro_rules! impl_bounds { @@ -3969,6 +4116,7 @@ impl MakeHdlTypeExpr for ParsedType { Self::NamedParam(v) => v.make_hdl_type_expr(context), Self::Tuple(v) => v.make_hdl_type_expr(context), Self::ConstUsize(v) => v.make_hdl_type_expr(context), + Self::PhantomData(v) => v.make_hdl_type_expr(context), Self::Array(v) => v.make_hdl_type_expr(context), Self::UInt(v) => v.make_hdl_type_expr(context), Self::SInt(v) => v.make_hdl_type_expr(context), diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index b338322..843eb6c 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -446,3 +446,85 @@ impl_tuples! { {#[num = 11, field = field_11] v11: T11} ] } + +impl Type for PhantomData { + type BaseType = Bundle; + type MaskType = (); + type MatchVariant = PhantomData; + type MatchActiveScope = (); + type MatchVariantAndInactiveScope = MatchVariantWithoutScope; + type MatchVariantsIter = std::iter::Once; + fn match_variants( + this: Expr, + source_location: SourceLocation, + ) -> Self::MatchVariantsIter { + let _ = this; + let _ = source_location; + std::iter::once(MatchVariantWithoutScope(PhantomData)) + } + fn mask_type(&self) -> Self::MaskType { + () + } + fn canonical(&self) -> CanonicalType { + Bundle::new(self.fields()).canonical() + } + #[track_caller] + fn from_canonical(canonical_type: CanonicalType) -> Self { + let CanonicalType::Bundle(bundle) = canonical_type else { + panic!("expected bundle"); + }; + assert!( + bundle.fields().is_empty(), + "bundle has wrong number of fields" + ); + PhantomData + } + fn source_location() -> SourceLocation { + SourceLocation::builtin() + } +} + +pub struct PhantomDataBuilder(PhantomData); + +impl Default for PhantomDataBuilder { + fn default() -> Self { + Self(PhantomData) + } +} + +impl ToExpr for PhantomDataBuilder { + type Type = PhantomData; + + fn to_expr(&self) -> Expr { + PhantomData.to_expr() + } +} + +impl BundleType for PhantomData { + type Builder = PhantomDataBuilder; + type FilledBuilder = PhantomDataBuilder; + fn fields(&self) -> Interned<[BundleField]> { + Interned::default() + } +} + +impl TypeWithDeref for PhantomData { + fn expr_deref(_this: &Expr) -> &Self::MatchVariant { + &PhantomData + } +} + +impl StaticType for PhantomData { + const TYPE: Self = PhantomData; + const MASK_TYPE: Self::MaskType = (); + const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; + const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; +} + +impl ToExpr for PhantomData { + type Type = PhantomData; + + fn to_expr(&self) -> Expr { + BundleLiteral::new(PhantomData, Interned::default()).to_expr() + } +} diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 3f11de3..bd7c0fd 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -1,6 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::prelude::*; +use std::marker::PhantomData; #[hdl(outline_generated)] pub struct S { @@ -8,6 +9,8 @@ pub struct S { b: UInt<3>, pub(crate) c: ArrayType, Len>, pub d: T2, + pub e: PhantomData, + pub f: PhantomData, } #[hdl(outline_generated)] From 1a2149b0406fd29fa2032532dfba3b371cf74a26 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 10 Oct 2024 20:53:29 -0700 Subject: [PATCH 065/190] silence warnings for field names that start with _ --- crates/fayalite-proc-macros-impl/src/hdl_bundle.rs | 2 +- crates/fayalite/tests/hdl_types.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index eefa8be..79326e2 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -340,7 +340,7 @@ impl ToTokens for Builder { })); quote_spanned! {self.ident.span()=> #[automatically_derived] - #[allow(non_camel_case_types, dead_code)] + #[allow(non_camel_case_types, non_snake_case, dead_code)] impl #impl_generics #unfilled_ty #where_clause { diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index bd7c0fd..bed1a82 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -9,8 +9,7 @@ pub struct S { b: UInt<3>, pub(crate) c: ArrayType, Len>, pub d: T2, - pub e: PhantomData, - pub f: PhantomData, + pub _phantom: PhantomData<(T, Len)>, } #[hdl(outline_generated)] From d0694cbd521496fe019ed8a755e77f3361f83289 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 10 Oct 2024 22:58:15 -0700 Subject: [PATCH 066/190] add disabled test for #[hdl] struct S4 which type errors --- crates/fayalite/tests/hdl_types.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index bed1a82..b8cbff3 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -32,6 +32,13 @@ pub struct S2 { pub v: E, } +#[cfg(todo)] +#[hdl(outline_generated)] +pub struct S4 { + pub v: UIntType, + pub v2: SIntType, +} + // check that #[hdl] properly handles hygiene macro_rules! types_in_macros { ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => { From 4909724995ff630fa028e5481577dafcb53384ed Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 10 Oct 2024 23:34:46 -0700 Subject: [PATCH 067/190] add more thorough checks that bounds are properly handled on #[hdl] structs --- crates/fayalite/tests/hdl_types.rs | 95 +++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 7 deletions(-) diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index b8cbff3..71450fc 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -1,6 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use fayalite::prelude::*; +#[cfg(todo)] +use fayalite::{ + bundle::BundleType, + enum_::EnumType, + int::{BoolOrIntType, IntType}, + ty::StaticType, +}; use std::marker::PhantomData; #[hdl(outline_generated)] @@ -32,13 +39,6 @@ pub struct S2 { pub v: E, } -#[cfg(todo)] -#[hdl(outline_generated)] -pub struct S4 { - pub v: UIntType, - pub v2: SIntType, -} - // check that #[hdl] properly handles hygiene macro_rules! types_in_macros { ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => { @@ -104,3 +104,84 @@ macro_rules! types_in_macros { } types_in_macros!(); + +mod bound_kind { + use fayalite::prelude::*; + + #[hdl] + pub struct Type { + v: T, + } + + #[hdl] + pub struct Size { + v: UIntType, + } +} + +macro_rules! check_bounds { + ($name:ident<$(#[$field:ident, $kind:ident] $var:ident: $($bound:ident +)*),*>) => { + #[cfg(todo)] + #[hdl(outline_generated)] + struct $name<$($var: $($bound +)*,)*> { + $($field: bound_kind::$kind<$var>,)* + } + }; +} + +check_bounds!(CheckBoundsS0<#[a, Size] A: Size +>); +check_bounds!(CheckBoundsS1<#[a, Size] A: KnownSize +>); +check_bounds!(CheckBoundsT0<#[a, Type] A: Type +>); +check_bounds!(CheckBoundsT1<#[a, Type] A: BoolOrIntType +>); +check_bounds!(CheckBoundsT2<#[a, Type] A: BundleType +>); +check_bounds!(CheckBoundsT3<#[a, Type] A: EnumType +>); +check_bounds!(CheckBoundsT4<#[a, Type] A: IntType +>); +check_bounds!(CheckBoundsT5<#[a, Type] A: StaticType +>); +check_bounds!(CheckBoundsSS0<#[a, Size] A: Size +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsST0<#[a, Size] A: Size +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsST1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTS0<#[a, Type] A: Type +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTS4<#[a, Type] A: IntType +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +>); +check_bounds!(CheckBoundsTT0<#[a, Type] A: Type +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +>); +check_bounds!(CheckBoundsSSS0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsSSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsSST0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsSST1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsSTS0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsSTS1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsSTT0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsSTT1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTSS0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTSS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTSS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTSS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTSS4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTSS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTST0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTST1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTST2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTST3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTST4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTST5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTS0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTS1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTS2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTS3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTS4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTS5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Size] C: Size +>); +check_bounds!(CheckBoundsTTT0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); +check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>); From d0229fbcfb11666b5abf7ae487ffe335866f9326 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Fri, 11 Oct 2024 17:30:49 -0700 Subject: [PATCH 068/190] get #[hdl] struct S to work --- crates/fayalite/src/array.rs | 2 +- crates/fayalite/src/int.rs | 51 ++++++++++++++++++------------ crates/fayalite/tests/hdl_types.rs | 4 +-- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index a3aea79..f617f91 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -85,7 +85,7 @@ impl ArrayType { } } -impl ArrayType { +impl> ArrayType { pub fn new_static(element: T) -> Self { Self::new(element, Len::SizeType::default()) } diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index dd631d0..03b2c88 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -32,8 +32,23 @@ mod sealed { pub const DYN_SIZE: usize = !0; pub type DynSize = ConstUsize; -pub trait KnownSize: GenericConstUsize + Size { +pub trait KnownSize: + GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default +{ const SIZE: Self; + type ArrayMatch: AsRef<[Expr]> + + AsMut<[Expr]> + + BorrowMut<[Expr]> + + 'static + + Send + + Sync + + Eq + + Clone + + std::hash::Hash + + std::fmt::Debug + + IntoIterator> + + TryFrom>> + + Into>>; } macro_rules! known_widths { @@ -44,6 +59,7 @@ macro_rules! known_widths { v }> { const SIZE: Self = Self; + type ArrayMatch = [Expr; Self::VALUE]; } }; ([2 $($rest:tt)*] $($bits:literal)+) => { @@ -55,6 +71,7 @@ macro_rules! known_widths { known_widths!([$($rest)*] 1); impl KnownSize for ConstUsize<{2 $(* $rest)*}> { const SIZE: Self = Self; + type ArrayMatch = [Expr; Self::VALUE]; } }; } @@ -124,30 +141,24 @@ impl sealed::SizeSealed for ConstUsize {} impl sealed::SizeTypeSealed for ConstUsize {} -impl SizeType for ConstUsize -where - ConstUsize: KnownSize, -{ - type Size = ConstUsize; +impl SizeType for T { + type Size = T; } -impl Size for ConstUsize -where - ConstUsize: KnownSize, -{ - type ArrayMatch = [Expr; VALUE]; +impl Size for T { + type ArrayMatch = ::ArrayMatch; - const KNOWN_VALUE: Option = Some(VALUE); + const KNOWN_VALUE: Option = Some(T::VALUE); - type SizeType = ConstUsize; + type SizeType = T; fn as_usize(_size_type: Self::SizeType) -> usize { - VALUE + T::VALUE } fn try_from_usize(v: usize) -> Option { - if v == VALUE { - Some(ConstUsize) + if v == T::VALUE { + Some(T::SIZE) } else { None } @@ -252,9 +263,7 @@ macro_rules! impl_int { impl $name { pub fn new_static() -> Self { - Self { - width: Width::SizeType::default(), - } + Self { width: Width::SIZE } } } @@ -526,9 +535,9 @@ pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed { fn new(width: ::SizeType) -> Self; fn new_static() -> Self where - Self::Width: KnownSize, + Self::Width: KnownSize + Size, { - Self::new(::SizeType::default()) + Self::new(Self::Width::default()) } fn as_same_width_sint(self) -> SIntType { SIntType::new(Self::Width::from_usize(self.width())) diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 71450fc..4509146 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -1,11 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use fayalite::prelude::*; -#[cfg(todo)] use fayalite::{ bundle::BundleType, enum_::EnumType, int::{BoolOrIntType, IntType}, + prelude::*, ty::StaticType, }; use std::marker::PhantomData; @@ -121,7 +120,6 @@ mod bound_kind { macro_rules! check_bounds { ($name:ident<$(#[$field:ident, $kind:ident] $var:ident: $($bound:ident +)*),*>) => { - #[cfg(todo)] #[hdl(outline_generated)] struct $name<$($var: $($bound +)*,)*> { $($field: bound_kind::$kind<$var>,)* From 3939ce2360138c75c53972368069c8f882111571 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 14 Oct 2024 17:47:58 -0700 Subject: [PATCH 069/190] add Bundle and Enum to prelude --- crates/fayalite/src/prelude.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 46d9e6e..9e7a85e 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -6,9 +6,10 @@ pub use crate::{ DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, }, array::{Array, ArrayType}, + bundle::Bundle, cli::Cli, clock::{Clock, ClockDomain, ToClock}, - enum_::{HdlNone, HdlOption, HdlSome}, + enum_::{Enum, HdlNone, HdlOption, HdlSome}, expr::{ repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr, ReduceBits, ToExpr, From 3d0f95cfe5a910f79177babf8fe12483f83e96f9 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 15 Oct 2024 01:48:48 -0700 Subject: [PATCH 070/190] formal: add workaround for wires disappearing because yosys optimizes them out --- crates/fayalite/src/cli.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index a3649cc..1dace37 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -687,7 +687,11 @@ impl FormalArgs { } writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap(); } - writeln!(retval, "prep -top {top_module}").unwrap(); + // workaround for wires disappearing -- set `keep` on all wires + writeln!(retval, "hierarchy -top {top_module}").unwrap(); + writeln!(retval, "proc").unwrap(); + writeln!(retval, "setattr -set keep 1 w:\\*").unwrap(); + writeln!(retval, "prep").unwrap(); Ok(retval) } fn run_impl( From 42effd1132091e2f14af416c64412d4047c76aff Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 15 Oct 2024 20:32:33 -0700 Subject: [PATCH 071/190] switch to using a make job server for managing test parallelism --- Cargo.lock | 60 ++++++++ Cargo.toml | 2 + crates/fayalite/Cargo.toml | 2 + crates/fayalite/src/cli.rs | 129 +---------------- crates/fayalite/src/util.rs | 1 + crates/fayalite/src/util/job_server.rs | 193 +++++++++++++++++++++++++ 6 files changed, 265 insertions(+), 122 deletions(-) create mode 100644 crates/fayalite/src/util/job_server.rs diff --git a/Cargo.lock b/Cargo.lock index 1e6f88c..7a2a2ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -230,6 +230,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "derive_destructure2" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -285,10 +306,12 @@ dependencies = [ "bitvec", "blake3", "clap", + "ctor", "eyre", "fayalite-proc-macros", "fayalite-visit-gen", "hashbrown", + "jobslot", "num-bigint", "num-traits", "os_pipe", @@ -350,6 +373,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.1" @@ -410,6 +444,20 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "jobslot" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d" +dependencies = [ + "cfg-if", + "derive_destructure2", + "getrandom", + "libc", + "scopeguard", + "windows-sys 0.59.0", +] + [[package]] name = "libc" version = "0.2.153" @@ -520,6 +568,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.202" @@ -672,6 +726,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "which" version = "6.0.1" diff --git a/Cargo.toml b/Cargo.toml index 699d57f..da104c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,11 @@ base16ct = "0.2.0" bitvec = { version = "1.0.1", features = ["serde"] } blake3 = { version = "1.5.4", features = ["serde"] } clap = { version = "4.5.9", features = ["derive", "env", "string"] } +ctor = "0.2.8" eyre = "0.6.12" hashbrown = "0.14.3" indexmap = { version = "2.2.6", features = ["serde"] } +jobslot = "0.2.19" num-bigint = "0.4.4" num-traits = "0.2.16" os_pipe = "1.2.1" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 828d759..5724a80 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -17,9 +17,11 @@ version.workspace = true bitvec.workspace = true blake3.workspace = true clap.workspace = true +ctor.workspace = true eyre.workspace = true fayalite-proc-macros.workspace = true hashbrown.workspace = true +jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true os_pipe.workspace = true diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 1dace37..a236c77 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -5,7 +5,7 @@ use crate::{ firrtl::{self, ExportOptions}, intern::Interned, module::Module, - util::streaming_read_utf8::streaming_read_utf8, + util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8}, }; use clap::{ builder::{OsStringValueParser, TypedValueParser}, @@ -18,10 +18,8 @@ use std::{ ffi::OsString, fmt::{self, Write}, fs, io, mem, - num::NonZeroUsize, path::{Path, PathBuf}, process, - sync::{Condvar, Mutex, OnceLock}, }; use tempfile::TempDir; @@ -49,107 +47,10 @@ impl From for CliError { } } -#[derive(Debug)] -pub struct AcquiredJob { - job_count: NonZeroUsize, -} - -impl Drop for AcquiredJob { - fn drop(&mut self) { - Self::change_job_count(Some(self.job_count), None); - } -} - -impl AcquiredJob { - pub fn max_available_job_count() -> NonZeroUsize { - static RETVAL: OnceLock = OnceLock::new(); - *RETVAL.get_or_init(|| { - std::thread::available_parallelism().unwrap_or(NonZeroUsize::new(1).unwrap()) - }) - } - fn change_job_count(released: Option, acquired: Option) { - static AVAILABLE_JOB_COUNT: OnceLock> = OnceLock::new(); - static COND_VAR: Condvar = Condvar::new(); - let mut available_job_count_lock = AVAILABLE_JOB_COUNT - .get_or_init(|| Mutex::new(Self::max_available_job_count().get())) - .lock() - .unwrap(); - if let Some(released) = released { - *available_job_count_lock = available_job_count_lock - .checked_add(released.get()) - .expect("tried to release too many jobs"); - COND_VAR.notify_all(); - } - if let Some(acquired) = acquired { - loop { - match available_job_count_lock.checked_sub(acquired.get()) { - Some(jobs_left) => { - *available_job_count_lock = jobs_left; - break; - } - None => { - available_job_count_lock = COND_VAR.wait(available_job_count_lock).unwrap() - } - } - } - } - } - pub fn job_count(&self) -> NonZeroUsize { - self.job_count - } - pub fn increase_job_count( - &mut self, - new_minimum_count: NonZeroUsize, - f: impl FnOnce(&mut AcquiredJob) -> R, - ) -> R { - if new_minimum_count <= self.job_count { - return f(self); - } - struct ReleaseOnDrop<'a> { - acquired_job: &'a mut AcquiredJob, - old_job_count: NonZeroUsize, - } - impl Drop for ReleaseOnDrop<'_> { - fn drop(&mut self) { - AcquiredJob::change_job_count( - NonZeroUsize::new(self.acquired_job.job_count.get() - self.old_job_count.get()), - None, - ); - self.acquired_job.job_count = self.old_job_count; - } - } - let release_on_drop = ReleaseOnDrop { - old_job_count: self.job_count, - acquired_job: self, - }; - // release our current jobs when acquiring new jobs to avoid deadlock - Self::change_job_count(Some(release_on_drop.old_job_count), Some(new_minimum_count)); - release_on_drop.acquired_job.job_count = new_minimum_count; - let retval = f(release_on_drop.acquired_job); - drop(release_on_drop); - retval - } - pub fn acquire_jobs(job_count: NonZeroUsize) -> Self { - Self::change_job_count(None, Some(job_count)); - Self { job_count } - } - pub fn run_command( - &mut self, - mut cmd: std::process::Command, - f: impl FnOnce(&mut std::process::Command) -> std::io::Result, - ) -> std::io::Result { - // TODO: if we implement a make job server, add the proper env vars to cmd - f(&mut cmd) - } -} - pub trait RunPhase { type Output; fn run(&self, arg: Arg) -> Result { - self.run_with_job( - arg, - &mut AcquiredJob::acquire_jobs(NonZeroUsize::new(1).unwrap()), - ) + self.run_with_job(arg, &mut AcquiredJob::acquire()) } fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result; } @@ -492,14 +393,6 @@ impl FormalMode { FormalMode::Cover => "cover", } } - fn needs_extra_job(self) -> bool { - match self { - FormalMode::BMC => false, - FormalMode::Prove => true, - FormalMode::Live => false, - FormalMode::Cover => false, - } - } } impl fmt::Display for FormalMode { @@ -721,12 +614,6 @@ impl FormalArgs { cmd.arg(sby_file.file_name().unwrap()); cmd.args(&self.sby_extra_args); cmd.current_dir(&output.verilog.firrtl.output_dir); - let new_minimum_count = if self.mode.needs_extra_job() { - NonZeroUsize::new(2).unwrap() - } else { - NonZeroUsize::new(1).unwrap() - }; - let new_minimum_count = AcquiredJob::max_available_job_count().min(new_minimum_count); let mut captured_output = String::new(); let cache_file = output.cache_file(); let do_cache = if let Some(contents_hash) = contents_hash.filter(|_| self.cache_results) { @@ -752,13 +639,11 @@ impl FormalArgs { false }; let _ = fs::remove_file(&cache_file); - let status = acquired_job.increase_job_count(new_minimum_count, |acquired_job| { - self.verilog.firrtl.base.run_external_command( - acquired_job, - cmd, - do_cache.then_some(&mut captured_output), - ) - })?; + let status = self.verilog.firrtl.base.run_external_command( + acquired_job, + cmd, + do_cache.then_some(&mut captured_output), + )?; let result = if status.success() { Ok(output) } else { diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index 95f5793..f66654f 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -27,4 +27,5 @@ pub use misc::{ interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, }; +pub mod job_server; pub mod ready_valid; diff --git a/crates/fayalite/src/util/job_server.rs b/crates/fayalite/src/util/job_server.rs new file mode 100644 index 0000000..376ddc0 --- /dev/null +++ b/crates/fayalite/src/util/job_server.rs @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + +use ctor::ctor; +use jobslot::{Acquired, Client}; +use std::{ + ffi::OsString, + mem, + num::NonZeroUsize, + sync::{Condvar, Mutex, Once, OnceLock}, + thread::spawn, +}; + +fn get_or_make_client() -> &'static Client { + #[ctor] + static CLIENT: OnceLock = unsafe { + match Client::from_env() { + Some(client) => OnceLock::from(client), + None => OnceLock::new(), + } + }; + + CLIENT.get_or_init(|| { + let mut available_parallelism = None; + let mut args = std::env::args_os().skip(1); + while let Some(arg) = args.next() { + const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads"; + if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) { + match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) { + Some(b'=') => { + let mut arg = arg.into_encoded_bytes(); + arg.drain(..=TEST_THREADS_OPTION.len()); + available_parallelism = Some(arg); + break; + } + None => { + available_parallelism = args.next().map(OsString::into_encoded_bytes); + break; + } + _ => {} + } + } + } + let available_parallelism = if let Some(available_parallelism) = available_parallelism + .as_deref() + .and_then(|v| std::str::from_utf8(v).ok()) + .and_then(|v| v.parse().ok()) + { + available_parallelism + } else if let Ok(available_parallelism) = std::thread::available_parallelism() { + available_parallelism + } else { + NonZeroUsize::new(1).unwrap() + }; + Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server") + }) +} + +struct State { + waiting_count: usize, + available: Vec, + implicit_available: bool, +} + +impl State { + fn total_available(&self) -> usize { + self.available.len() + self.implicit_available as usize + } + fn additional_waiting(&self) -> usize { + self.waiting_count.saturating_sub(self.total_available()) + } +} + +static STATE: Mutex = Mutex::new(State { + waiting_count: 0, + available: Vec::new(), + implicit_available: true, +}); +static COND_VAR: Condvar = Condvar::new(); + +#[derive(Debug)] +enum AcquiredJobInner { + FromJobServer(Acquired), + ImplicitJob, +} + +#[derive(Debug)] +pub struct AcquiredJob { + job: AcquiredJobInner, +} + +impl AcquiredJob { + fn start_acquire_thread() { + static STARTED_THREAD: Once = Once::new(); + STARTED_THREAD.call_once(|| { + spawn(|| { + let mut acquired = None; + let client = get_or_make_client(); + let mut state = STATE.lock().unwrap(); + loop { + state = if state.additional_waiting() == 0 { + if acquired.is_some() { + drop(state); + drop(acquired.take()); // drop Acquired outside of lock + STATE.lock().unwrap() + } else { + COND_VAR.wait(state).unwrap() + } + } else if acquired.is_some() { + // allocate space before moving Acquired to ensure we + // drop Acquired outside of the lock on panic + state.available.reserve(1); + state.available.push(acquired.take().unwrap()); + COND_VAR.notify_all(); + state + } else { + drop(state); + acquired = Some( + client + .acquire() + .expect("can't acquire token from job server"), + ); + STATE.lock().unwrap() + }; + } + }); + }); + } + fn acquire_inner(block: bool) -> Option { + Self::start_acquire_thread(); + let mut state = STATE.lock().unwrap(); + loop { + if let Some(acquired) = state.available.pop() { + return Some(Self { + job: AcquiredJobInner::FromJobServer(acquired), + }); + } + if state.implicit_available { + state.implicit_available = false; + return Some(Self { + job: AcquiredJobInner::ImplicitJob, + }); + } + if !block { + return None; + } + state.waiting_count += 1; + state = COND_VAR.wait(state).unwrap(); + state.waiting_count -= 1; + } + } + pub fn try_acquire() -> Option { + Self::acquire_inner(false) + } + pub fn acquire() -> Self { + Self::acquire_inner(true).expect("failed to acquire token") + } + pub fn run_command( + &mut self, + cmd: std::process::Command, + f: impl FnOnce(&mut std::process::Command) -> std::io::Result, + ) -> std::io::Result { + get_or_make_client().configure_make_and_run_with_fifo(cmd, f) + } +} + +impl Drop for AcquiredJob { + fn drop(&mut self) { + let mut state = STATE.lock().unwrap(); + match &self.job { + AcquiredJobInner::FromJobServer(_) => { + if state.waiting_count > state.available.len() + state.implicit_available as usize { + // allocate space before moving Acquired to ensure we + // drop Acquired outside of the lock on panic + state.available.reserve(1); + let AcquiredJobInner::FromJobServer(acquired) = + mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob) + else { + unreachable!() + }; + state.available.push(acquired); + COND_VAR.notify_all(); + } + } + AcquiredJobInner::ImplicitJob => { + state.implicit_available = true; + if state.waiting_count > state.available.len() { + COND_VAR.notify_all(); + } + } + } + } +} From cb17913004259b6e0908a477a67e5a6b621b5c1c Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 15 Oct 2024 21:32:38 -0700 Subject: [PATCH 072/190] limit sby to one thread each since it seems not to respect job count in parallel mode --- crates/fayalite/src/cli.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index a236c77..1030474 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -610,6 +610,7 @@ impl FormalArgs { }); std::fs::write(&sby_file, sby_contents)?; let mut cmd = process::Command::new(&self.sby); + cmd.arg("-j1"); // sby seems not to respect job count in parallel mode cmd.arg("-f"); cmd.arg(sby_file.file_name().unwrap()); cmd.args(&self.sby_extra_args); From 0c9c48a06651671d74a0c5114625aa2e3674a54b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 17 Oct 2024 21:05:18 -0700 Subject: [PATCH 073/190] split out deps into separate workflow with better caching using deps.yml from cpu.git --- .forgejo/workflows/deps.yml | 77 +++++++++++++++++++++++++++++++++++++ .forgejo/workflows/test.yml | 41 +++++++------------- 2 files changed, 90 insertions(+), 28 deletions(-) create mode 100644 .forgejo/workflows/deps.yml diff --git a/.forgejo/workflows/deps.yml b/.forgejo/workflows/deps.yml new file mode 100644 index 0000000..3b7e537 --- /dev/null +++ b/.forgejo/workflows/deps.yml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +# See Notices.txt for copyright information +on: + workflow_call: + outputs: + cache-primary-key: + value: ${{ jobs.deps.outputs.cache-primary-key }} + +jobs: + deps: + runs-on: debian-12 + outputs: + cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }} + steps: + - uses: https://code.forgejo.org/actions/checkout@v3 + with: + fetch-depth: 0 + - uses: https://code.forgejo.org/actions/cache/restore@v3 + id: restore-deps + with: + path: deps + key: deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }} + lookup-only: true + - name: Install Apt packages + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + apt-get update -qq + apt-get install -qq \ + bison \ + build-essential \ + ccache \ + clang \ + cvc5 \ + flex \ + gawk \ + g++ \ + git \ + libboost-filesystem-dev \ + libboost-python-dev \ + libboost-system-dev \ + libffi-dev \ + libreadline-dev \ + lld \ + pkg-config \ + python3 \ + python3-click \ + tcl-dev \ + zlib1g-dev + - name: Install Firtool + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + mkdir -p deps + wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz + sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz' + tar -C deps -xvaf deps/firrtl.tar.gz + rm -rf deps/firtool + mv deps/firtool-1.86.0 deps/firtool + - name: Get SymbiYosys + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby + - name: Build Z3 + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3 + (cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local) + make -C deps/z3/build -j"$(nproc)" + - name: Build Yosys + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys + make -C deps/yosys -j"$(nproc)" + - uses: https://code.forgejo.org/actions/cache/save@v3 + if: steps.restore-deps.outputs.cache-hit != 'true' + with: + path: deps + key: ${{ steps.restore-deps.outputs.cache-primary-key }} diff --git a/.forgejo/workflows/test.yml b/.forgejo/workflows/test.yml index 1246015..969d691 100644 --- a/.forgejo/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -3,8 +3,11 @@ on: [push, pull_request] jobs: + deps: + uses: ./.forgejo/workflows/deps.yml test: runs-on: debian-12 + needs: deps steps: - uses: https://code.forgejo.org/actions/checkout@v3 with: @@ -33,39 +36,21 @@ jobs: python3-click \ tcl-dev \ z3 \ - zlib1g-dev \ - - name: Cache ccache dir - uses: https://code.forgejo.org/actions/cache@v3 - with: - path: .ccache - key: ${{ runner.os }}-${{ github.sha }} - restore-keys: | - ${{ runner.os }}- - - name: Setup ccache - run: | - ccache --set-config=cache_dir="${{ github.workspace }}/.ccache" - ccache --set-config=compression=true - ccache --set-config=compression_level=6 - ccache -M 4G - ccache -z - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - echo "$PATH" >> "$GITHUB_PATH" + zlib1g-dev - run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 source "$HOME/.cargo/env" echo "$PATH" >> "$GITHUB_PATH" + - uses: https://code.forgejo.org/actions/cache/restore@v3 + with: + path: deps + key: ${{ needs.deps.outputs.cache-primary-key }} + fail-on-cache-miss: true - run: | - git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git - make -C sby install - - run: | - git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git - make -C yosys -j2 - make -C yosys install - - run: | - wget -O firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz - sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 firrtl.tar.gz' - tar -xvaf firrtl.tar.gz - export PATH="$(realpath firtool-1.86.0/bin):$PATH" + make -C deps/z3/build install + make -C deps/sby install + make -C deps/yosys install + export PATH="$(realpath deps/firtool/bin):$PATH" echo "$PATH" >> "$GITHUB_PATH" - uses: https://github.com/Swatinem/rust-cache@v2 with: From 5bd0de48b70aae1fea8ea9ab65704f7b86524488 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 30 Oct 2024 19:36:05 -0700 Subject: [PATCH 074/190] change to version 0.2.1 --- Cargo.lock | 8 ++++---- Cargo.toml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a2a2ac..500bd34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -301,7 +301,7 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fayalite" -version = "0.2.0" +version = "0.2.1" dependencies = [ "bitvec", "blake3", @@ -324,14 +324,14 @@ dependencies = [ [[package]] name = "fayalite-proc-macros" -version = "0.2.0" +version = "0.2.1" dependencies = [ "fayalite-proc-macros-impl", ] [[package]] name = "fayalite-proc-macros-impl" -version = "0.2.0" +version = "0.2.1" dependencies = [ "base16ct", "num-bigint", @@ -345,7 +345,7 @@ dependencies = [ [[package]] name = "fayalite-visit-gen" -version = "0.2.0" +version = "0.2.1" dependencies = [ "indexmap", "prettyplease", diff --git a/Cargo.toml b/Cargo.toml index da104c2..b6b8616 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ resolver = "2" members = ["crates/*"] [workspace.package] -version = "0.2.0" +version = "0.2.1" license = "LGPL-3.0-or-later" edition = "2021" repository = "https://git.libre-chip.org/libre-chip/fayalite" @@ -14,9 +14,9 @@ categories = ["simulation", "development-tools", "compilers"] rust-version = "1.80.1" [workspace.dependencies] -fayalite-proc-macros = { version = "=0.2.0", path = "crates/fayalite-proc-macros" } -fayalite-proc-macros-impl = { version = "=0.2.0", path = "crates/fayalite-proc-macros-impl" } -fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" } +fayalite-proc-macros = { version = "=0.2.1", path = "crates/fayalite-proc-macros" } +fayalite-proc-macros-impl = { version = "=0.2.1", path = "crates/fayalite-proc-macros-impl" } +fayalite-visit-gen = { version = "=0.2.1", path = "crates/fayalite-visit-gen" } base16ct = "0.2.0" bitvec = { version = "1.0.1", features = ["serde"] } blake3 = { version = "1.5.4", features = ["serde"] } From 20cf0abbcc01d5734ffcfca4c4533d7c72f3a821 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 30 Oct 2024 20:46:11 -0700 Subject: [PATCH 075/190] fix using #[hdl] types like S<{ 1 + 2 }> --- crates/fayalite-proc-macros-impl/src/hdl_type_common.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 506c153..3f3f817 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -4163,7 +4163,13 @@ impl MakeHdlTypeExpr for ParsedExpr { match self { ParsedExpr::Delimited(expr) => expr.make_hdl_type_expr(context), ParsedExpr::NamedParamConst(expr) => expr.make_hdl_type_expr(context), - ParsedExpr::Other(expr) => (**expr).clone(), + ParsedExpr::Other(expr) => { + let span = expr.span(); + let const_usize = known_items::ConstUsize(span); + parse_quote_spanned! {expr.span()=> + #const_usize::<{ #expr }> + } + } } } } From ee15fd2b9415b68d97cb68074f581c50fa5e1485 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 30 Oct 2024 20:47:10 -0700 Subject: [PATCH 076/190] support #[hdl] type aliases --- .../src/hdl_type_alias.rs | 133 ++++++++++++++++++ crates/fayalite-proc-macros-impl/src/lib.rs | 8 +- crates/fayalite/tests/hdl_types.rs | 8 ++ crates/fayalite/tests/ui/hdl_types.stderr | 2 +- 4 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs new file mode 100644 index 0000000..e5d5f7b --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use crate::{ + hdl_type_common::{ + get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, + TypesParser, + }, + kw, Errors, HdlAttr, +}; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{parse_quote_spanned, Attribute, Generics, Ident, ItemType, Token, Type, Visibility}; + +#[derive(Clone, Debug)] +pub(crate) struct ParsedTypeAlias { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) vis: Visibility, + pub(crate) type_token: Token![type], + pub(crate) ident: Ident, + pub(crate) generics: MaybeParsed, + pub(crate) eq_token: Token![=], + pub(crate) ty: MaybeParsed, + pub(crate) semi_token: Token![;], +} + +impl ParsedTypeAlias { + fn parse(item: ItemType) -> syn::Result { + let ItemType { + mut attrs, + vis, + type_token, + ident, + mut generics, + eq_token, + ty, + semi_token, + } = item; + let mut errors = Errors::new(); + let mut options = errors + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) + .unwrap_or_default(); + errors.ok(options.body.validate()); + let ItemOptions { + outline_generated: _, + target: _, + custom_bounds, + no_static, + no_runtime_generics: _, + } = options.body; + if let Some((no_static,)) = no_static { + errors.error(no_static, "no_static 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)) { + MaybeParsed::Parsed(generics) + } else { + MaybeParsed::Unrecognized(generics) + }; + let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors); + errors.finish()?; + Ok(Self { + attrs, + options, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + }) + } +} + +impl ToTokens for ParsedTypeAlias { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + } = self; + let ItemOptions { + outline_generated: _, + target, + custom_bounds: _, + no_static: _, + no_runtime_generics, + } = &options.body; + let target = get_target(target, ident); + let mut type_attrs = attrs.clone(); + type_attrs.push(parse_quote_spanned! {ident.span()=> + #[allow(type_alias_bounds)] + }); + ItemType { + attrs: type_attrs, + vis: vis.clone(), + type_token: *type_token, + ident: ident.clone(), + generics: generics.into(), + eq_token: *eq_token, + ty: Box::new(ty.clone().into()), + semi_token: *semi_token, + } + .to_tokens(tokens); + if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) = + (generics, ty, no_runtime_generics) + { + generics.make_runtime_generics(tokens, vis, ident, &target, |context| { + ty.make_hdl_type_expr(context) + }) + } + } +} + +pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result { + let item = ParsedTypeAlias::parse(item)?; + let outline_generated = item.options.body.outline_generated; + let mut contents = item.to_token_stream(); + if outline_generated.is_some() { + contents = crate::outline_generated(contents, "hdl-type-alias-"); + } + Ok(contents) +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 194dbf3..0ffd4d4 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -16,6 +16,7 @@ use syn::{ mod fold; mod hdl_bundle; mod hdl_enum; +mod hdl_type_alias; mod hdl_type_common; mod module; @@ -850,6 +851,7 @@ macro_rules! options { }; } +use crate::hdl_type_alias::hdl_type_alias_impl; pub(crate) use options; pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream { @@ -906,14 +908,16 @@ pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result syn::Result { let kw = kw::hdl::default(); - let item = syn::parse2::(quote! { #[#kw(#attr)] #item })?; + let item = quote! { #[#kw(#attr)] #item }; + let item = syn::parse2::(item)?; match item { Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item), Item::Fn(item) => hdl_module_impl(item), + Item::Type(item) => hdl_type_alias_impl(item), _ => Err(syn::Error::new( Span::call_site(), - "top-level #[hdl] can only be used on structs, enums, or functions", + "top-level #[hdl] can only be used on structs, enums, type aliases, or functions", )), } } diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 4509146..b191016 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -31,6 +31,8 @@ pub enum E { A, B(UInt<3>), C(T), + D(TyAlias2), + E(TyAlias, { 1 + 2 }>), } #[hdl(outline_generated)] @@ -38,6 +40,12 @@ pub struct S2 { pub v: E, } +#[hdl(outline_generated)] +pub type TyAlias = Array, C>; + +#[hdl(outline_generated)] +pub type TyAlias2 = TyAlias, ConstUsize<24>, 5>; + // check that #[hdl] properly handles hygiene macro_rules! types_in_macros { ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => { diff --git a/crates/fayalite/tests/ui/hdl_types.stderr b/crates/fayalite/tests/ui/hdl_types.stderr index a65d796..d3a3617 100644 --- a/crates/fayalite/tests/ui/hdl_types.stderr +++ b/crates/fayalite/tests/ui/hdl_types.stderr @@ -1,4 +1,4 @@ -error: top-level #[hdl] can only be used on structs, enums, or functions +error: top-level #[hdl] can only be used on structs, enums, type aliases, or functions --> tests/ui/hdl_types.rs:5:1 | 5 | #[hdl] From 3d5d8c54b6f2b7bbb0a31458c00139359ca3006e Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 30 Oct 2024 20:55:02 -0700 Subject: [PATCH 077/190] add repository to cache key --- .forgejo/workflows/deps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/deps.yml b/.forgejo/workflows/deps.yml index 3b7e537..ffaca53 100644 --- a/.forgejo/workflows/deps.yml +++ b/.forgejo/workflows/deps.yml @@ -19,7 +19,7 @@ jobs: id: restore-deps with: path: deps - key: deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }} + key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }} lookup-only: true - name: Install Apt packages if: steps.restore-deps.outputs.cache-hit != 'true' From c1f1a8b74982965acb86274d1a621fcdb1a4d751 Mon Sep 17 00:00:00 2001 From: Cesar Strauss Date: Wed, 20 Nov 2024 17:39:00 -0300 Subject: [PATCH 078/190] Add test module exercising formal verification. --- crates/fayalite/tests/formal.rs | 133 ++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 crates/fayalite/tests/formal.rs diff --git a/crates/fayalite/tests/formal.rs b/crates/fayalite/tests/formal.rs new file mode 100644 index 0000000..46b8292 --- /dev/null +++ b/crates/fayalite/tests/formal.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +//! Formal tests in Fayalite + +use fayalite::{ + cli::FormalMode, + clock::{Clock, ClockDomain}, + expr::{CastTo, HdlPartialEq}, + firrtl::ExportOptions, + formal::{any_seq, formal_reset, hdl_assert, hdl_assume}, + hdl_module, + int::{Bool, UInt}, + module::{connect, connect_any, reg_builder, wire}, + reset::ToReset, + testing::assert_formal, +}; + +/// Test hidden state +/// +/// Hidden state can cause problems for induction, since the formal engine +/// can assign invalid values to the state registers, making it traverse +/// valid but unreachable states. +/// +/// One solution is to go sufficiently in the past so the engine is forced +/// to eventually take a reachable state. This may be hampered by +/// existence of loops, then assumptions may be added to break them. +/// +/// Another solution is to "open the black box" and add additional +/// assertions involving the hidden state, so that the unreachable states +/// become invalid as well. +/// +/// Both approaches are taken here. +/// +/// See [Claire Wolf's presentation] and [Zipcpu blog article]. +/// +/// [Claire Wolf's presentation]: https://web.archive.org/web/20200115081517fw_/http://www.clifford.at/papers/2017/smtbmc-sby/ +/// [Zipcpu blog article]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html +mod hidden_state { + use super::*; + /// Test hidden state by shift registers + /// + /// The code implement the ideas from an article in the [Zipcpu blog]. Two + /// shift registers are fed from the same input, so they should always have + /// the same value. However the only observable is a comparison of their + /// last bit, all the others are hidden. To complicate matters, an enable + /// signal causes a loop in state space. + /// + /// [Zipcpu blog]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html + #[test] + fn shift_register() { + enum ConstraintMode { + WithExtraAssertions, + WithExtraAssumptions, + } + use ConstraintMode::*; + #[hdl_module] + fn test_module(constraint_mode: ConstraintMode) { + #[hdl] + let clk: Clock = m.input(); + #[hdl] + let cd = wire(); + connect( + cd, + #[hdl] + ClockDomain { + clk, + rst: formal_reset().to_reset(), + }, + ); + // input signal for the shift registers + #[hdl] + let i: Bool = wire(); + connect(i, any_seq(Bool)); + // shift enable signal + #[hdl] + let en: Bool = wire(); + connect(en, any_seq(Bool)); + // comparison output + #[hdl] + let o: Bool = wire(); + // shift registers, with enable + #[hdl] + let r1 = reg_builder().clock_domain(cd).reset(0u8); + #[hdl] + let r2 = reg_builder().clock_domain(cd).reset(0u8); + #[hdl] + if en { + connect_any(r1, (r1 << 1) | i.cast_to(UInt[1])); + connect_any(r2, (r2 << 1) | i.cast_to(UInt[1])); + } + // compare last bits of both shift registers + connect(o, r1[7].cmp_eq(r2[7])); + + // what we want to prove: last bits are always equal + hdl_assert(clk, o, ""); + + // additional terms below are only needed to assist with the induction proof + match constraint_mode { + WithExtraAssertions => { + // "Open the box": add assertions about hidden state. + // In this case, the hidden bits are also always equal. + hdl_assert(clk, r1.cmp_eq(r2), ""); + } + WithExtraAssumptions => { + // Break the loop, do not allow "en" to remain low forever + #[hdl] + let past_en_reg = reg_builder().clock_domain(cd).reset(false); + connect(past_en_reg, en); + hdl_assume(clk, past_en_reg | en, ""); + } + } + } + // we need a minimum of 16 steps so we can constrain all eight shift register bits, + // given that we are allowed to disable the shift once every two cycles. + assert_formal( + "shift_register_with_assumptions", + test_module(WithExtraAssumptions), + FormalMode::Prove, + 16, + None, + ExportOptions::default(), + ); + // here a couple of cycles is enough + assert_formal( + "shift_register_with_assertions", + test_module(WithExtraAssertions), + FormalMode::Prove, + 2, + None, + ExportOptions::default(), + ); + } +} From 3ea0d9892412cbdae1dc8b886d9339e51c532c52 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 20 Nov 2024 22:51:40 -0800 Subject: [PATCH 079/190] always write formal cache json --- crates/fayalite/src/cli.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/fayalite/src/cli.rs b/crates/fayalite/src/cli.rs index 1030474..66741ef 100644 --- a/crates/fayalite/src/cli.rs +++ b/crates/fayalite/src/cli.rs @@ -653,21 +653,19 @@ impl FormalArgs { self.sby.display() ))) }; - if do_cache { - fs::write( - cache_file, - serde_json::to_string_pretty(&FormalCache { - version: FormalCacheVersion::CURRENT, - contents_hash: contents_hash.unwrap(), - stdout_stderr: captured_output, - result: match &result { - Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}), - Err(error) => Err(error.to_string()), - }, - }) - .expect("serialization shouldn't ever fail"), - )?; - } + fs::write( + cache_file, + serde_json::to_string_pretty(&FormalCache { + version: FormalCacheVersion::CURRENT, + contents_hash: contents_hash.unwrap(), + stdout_stderr: captured_output, + result: match &result { + Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}), + Err(error) => Err(error.to_string()), + }, + }) + .expect("serialization shouldn't ever fail"), + )?; result } } From 6f904148c492a4a5d00ba4030f65b4c7e58140b8 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 01:19:07 -0800 Subject: [PATCH 080/190] WIP adding simulator --- crates/fayalite/src/lib.rs | 1 + crates/fayalite/src/sim.rs | 206 +++++++++++ crates/fayalite/src/sim/interpreter.rs | 464 +++++++++++++++++++++++++ 3 files changed, 671 insertions(+) create mode 100644 crates/fayalite/src/sim.rs create mode 100644 crates/fayalite/src/sim/interpreter.rs diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index eedb1bb..fba7ada 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -46,6 +46,7 @@ pub mod module; pub mod prelude; pub mod reg; pub mod reset; +pub mod sim; pub mod source_location; pub mod testing; pub mod ty; diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs new file mode 100644 index 0000000..3aa69a6 --- /dev/null +++ b/crates/fayalite/src/sim.rs @@ -0,0 +1,206 @@ +//! Fayalite Simulation + +use crate::{ + bundle::{Bundle, BundleType}, + enum_::Enum, + expr::Expr, + int::Bool, + intern::{Intern, Interned}, + module::{ + Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf, + StmtMatch, + }, + sim::interpreter::{Insn, Insns, InsnsBuilding}, + source_location::SourceLocation, +}; +use hashbrown::HashMap; +use std::{cell::RefCell, rc::Rc}; + +mod interpreter; + +#[derive(Debug)] +struct CompilingModule { + compiled_module: Option, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CondStack { + Always, + IfTrue { + parent: Interned, + cond: Expr, + source_location: SourceLocation, + }, + IfFalse { + parent: Interned, + cond: Expr, + source_location: SourceLocation, + }, + MatchArm { + parent: Interned, + enum_expr: Expr, + variant_index: usize, + source_location: SourceLocation, + }, +} + +#[derive(Debug)] +pub struct Compiler { + insns: Insns, + modules: HashMap>, Rc>>, + module_queue: Vec>>, +} + +impl Compiler { + pub fn new() -> Self { + Self { + insns: Insns::new(), + modules: HashMap::new(), + module_queue: Vec::new(), + } + } + pub fn add_module(&mut self, module: Interned>) { + self.modules.entry(module).or_insert_with(|| { + self.module_queue.push(module); + Rc::new(RefCell::new(CompilingModule { + compiled_module: None, + })) + }); + } + fn compile_block( + &mut self, + module: Interned>, + block: Block, + cond_stack: Interned, + ) { + let Block { memories, stmts } = block; + for memory in memories { + todo!("implement memory"); + } + for stmt in stmts { + match stmt { + Stmt::Connect(StmtConnect { + lhs, + rhs, + source_location, + }) => todo!(), + Stmt::Formal(StmtFormal { + kind, + clk, + pred, + en, + text, + source_location, + }) => todo!("implement simulating formal statements"), + Stmt::If(StmtIf { + cond, + source_location, + blocks: [then_block, else_block], + }) => { + self.compile_block( + module, + then_block, + CondStack::IfTrue { + parent: cond_stack, + cond, + source_location, + } + .intern_sized(), + ); + self.compile_block( + module, + else_block, + CondStack::IfFalse { + parent: cond_stack, + cond, + source_location, + } + .intern_sized(), + ); + } + Stmt::Match(StmtMatch { + expr, + source_location, + blocks, + }) => { + for (variant_index, block) in blocks.into_iter().enumerate() { + self.compile_block( + module, + block, + CondStack::MatchArm { + parent: cond_stack, + enum_expr: expr, + variant_index, + source_location, + } + .intern_sized(), + ); + } + } + Stmt::Declaration(stmt_declaration) => todo!(), + } + } + } + fn compile_module(&mut self, module: Interned>) -> CompiledModule { + let step_entry_pc = self.insns.next_insn_pc(); + match module.body() { + ModuleBody::Normal(NormalModuleBody { body }) => { + self.compile_block(module, body, CondStack::Always.intern_sized()) + } + ModuleBody::Extern(_extern_module_body) => { + todo!("simulating extern module: {}", module.name_id()); + } + } + self.insns.push(Insn::Return, module.source_location()); + CompiledModule { step_entry_pc } + } + pub fn run(mut self) -> Compiled { + while let Some(module) = self.module_queue.pop() { + let compiling_module = self.modules[&module].clone(); + let mut compiling_module = compiling_module.borrow_mut(); + let CompilingModule { compiled_module } = &mut *compiling_module; + assert!(compiled_module.is_none()); + *compiled_module = Some(self.compile_module(module)); + } + Compiled { + insns: Insns::from(self.insns).intern_sized(), + modules: self + .modules + .into_iter() + .map(|(module, compiling_module)| { + ( + module, + Rc::into_inner(compiling_module) + .expect("only reference is Compiler::modules") + .into_inner() + .compiled_module + .expect("module is compiled by Compiler::run"), + ) + }) + .collect(), + } + } +} + +#[derive(Debug)] +struct CompiledModule { + step_entry_pc: usize, +} + +#[derive(Debug)] +pub struct Compiled { + insns: Interned, + modules: HashMap>, CompiledModule>, +} + +impl Compiled { + pub fn new(module: Module) -> Self { + let mut compiler = Compiler::new(); + compiler.add_module(module.canonical().intern()); + compiler.run() + } +} + +pub struct Simulation { + state: interpreter::State, +} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs new file mode 100644 index 0000000..eed64e4 --- /dev/null +++ b/crates/fayalite/src/sim/interpreter.rs @@ -0,0 +1,464 @@ +use crate::{ + intern::{Intern, Interned}, + source_location::SourceLocation, +}; +use num_bigint::BigInt; +use std::{convert::Infallible, fmt, hash::Hash}; + +pub(crate) type SmallUInt = u64; +pub(crate) type SmallSInt = i64; + +macro_rules! impl_insns { + ( + #[next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] + $vis:vis fn $State:ident::$run:ident(&mut $self:ident, $insns:ident: &[$Insn:ident]) -> $run_ret_ty:ty { + #[pc] + let mut $pc:ident: usize = $pc_init:expr; + setup! { + $($setup:tt)* + } + $(let mut $local:ident = $local_init:expr;)* + main_loop!(); + cleanup! { + $($cleanup:tt)* + } + } + $( + $(#[$insn_meta:meta])* + $insn_name:ident $({ + $( + $(#[$field_meta:meta])* + $field_name:ident: $field_ty:ty, + )* + })? => $block:block + )+ + ) => { + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + $vis enum $Insn { + $( + $(#[$insn_meta])* + $insn_name $({ + $( + $(#[$field_meta])* + $field_name: $field_ty, + )* + })?, + )+ + } + + impl $State { + $vis fn $run(&mut $self, $insns: &[$Insn]) -> $run_ret_ty { + let mut $pc: usize = $pc_init; + $($setup)* + let mut insn = $insns[$pc]; + let retval = 'main_loop: loop { + macro_rules! $next_macro { + () => { + $pc += 1; + insn = $insns[$pc]; + continue 'main_loop; + }; + } + macro_rules! $branch_macro { + ($next_pc:expr) => { + $pc = $next_pc; + insn = $insns[$pc]; + continue 'main_loop; + }; + } + let _: Infallible = match insn { + $( + $Insn::$insn_name $({ + $( + $field_name, + )* + })? => { + $block + } + )+ + }; + }; + $($cleanup)* + retval + } + } + }; +} + +pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { + type BoxedSlice: fmt::Debug + + Clone + + Eq + + std::ops::Deref; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuildingDone; + +impl InsnsBuildingKind for InsnsBuildingDone { + type BoxedSlice = Interned<[T]>; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnsBuilding; + +impl InsnsBuildingKind for InsnsBuilding { + type BoxedSlice = Vec; +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct Insns { + insns: BK::BoxedSlice, + small_stack_size: usize, + big_stack_size: usize, + small_state_debug_names: BK::BoxedSlice>, + big_state_debug_names: BK::BoxedSlice>, + insn_source_locations: BK::BoxedSlice, +} + +struct InsnsDebug<'a> { + insns: &'a [Insn], + insn_source_locations: &'a [SourceLocation], +} + +impl<'a> fmt::Debug for InsnsDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug_list = f.debug_list(); + let mut last_source_location = None; + for (insn, source_location) in self.insns.iter().zip(self.insn_source_locations) { + if Some(source_location) != last_source_location { + debug_list.entry(&format_args!("// at: {source_location}\n{insn:?}")); + } else { + debug_list.entry(insn); + } + last_source_location = Some(source_location); + } + debug_list.finish() + } +} + +impl fmt::Debug for Insns { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + insns, + small_stack_size, + big_stack_size, + small_state_debug_names, + big_state_debug_names, + insn_source_locations, + } = self; + f.debug_struct("Insns") + .field("small_stack_size", small_stack_size) + .field("big_stack_size", big_stack_size) + .field("small_state_debug_names", small_state_debug_names) + .field("big_state_debug_names", big_state_debug_names) + .field( + "insns", + &InsnsDebug { + insns, + insn_source_locations, + }, + ) + .finish_non_exhaustive() + } +} + +impl Insns { + pub(crate) fn new() -> Self { + Self { + insns: Vec::new(), + small_stack_size: 0, + big_stack_size: 0, + small_state_debug_names: Vec::new(), + big_state_debug_names: Vec::new(), + insn_source_locations: Vec::new(), + } + } + pub(crate) fn last_insn_pc(&self) -> usize { + self.insns + .len() + .checked_sub(1) + .expect("no last instruction") + } + pub(crate) fn next_insn_pc(&self) -> usize { + self.insns.len() + } + pub(crate) fn push(&mut self, insn: Insn, source_location: SourceLocation) { + self.insns.push(insn); + self.insn_source_locations.push(source_location); + } +} + +impl From> for Insns { + fn from(input: Insns) -> Self { + let Insns { + insns, + small_stack_size, + big_stack_size, + small_state_debug_names, + big_state_debug_names, + insn_source_locations, + } = input; + Insns { + insns: Intern::intern_owned(insns), + small_stack_size, + big_stack_size, + small_state_debug_names: Intern::intern_owned(small_state_debug_names), + big_state_debug_names: Intern::intern_owned(big_state_debug_names), + insn_source_locations: Intern::intern_owned(insn_source_locations), + } + } +} + +#[derive(Debug)] +pub(crate) struct State { + pub(crate) insns: Interned, + pub(crate) pc: usize, + pub(crate) small_stack: Box<[SmallUInt]>, + pub(crate) small_stack_top: usize, + pub(crate) small_states: Box<[SmallUInt]>, + pub(crate) big_stack: Box<[BigInt]>, + pub(crate) big_stack_top: usize, + pub(crate) big_states: Box<[BigInt]>, +} + +impl State { + pub(crate) fn setup_call(&mut self, entry_pc: usize) { + let Self { + insns: _, + pc, + small_stack: _, + small_stack_top, + small_states: _, + big_stack: _, + big_stack_top, + big_states: _, + } = self; + *pc = entry_pc; + *small_stack_top = 0; + *big_stack_top = 0; + } +} + +impl_insns! { + #[next_macro = next, branch_macro = branch] + pub(crate) fn State::run(&mut self, insns: &[Insn]) -> () { + #[pc] + let mut pc: usize = self.pc; + setup! { + let small_states = &mut *self.small_states; + let small_stack = &mut *self.small_stack; + let mut small_stack_top = self.small_stack_top; + macro_rules! small_stack_push { + ($v:expr) => { + { + small_stack[small_stack_top] = $v; + small_stack_top += 1; + } + }; + } + macro_rules! small_stack_pop { + () => { + { + small_stack_top -= 1; + small_stack[small_stack_top] + } + }; + } + let big_states = &mut *self.big_states; + let big_stack = &mut *self.big_stack; + let mut big_stack_top = self.big_stack_top; + macro_rules! big_stack_push { + ($v:expr) => { + { + big_stack[big_stack_top] = $v; + big_stack_top += 1; + } + }; + } + macro_rules! big_stack_pop { + () => { + { + big_stack_top -= 1; + big_stack[big_stack_top] + } + }; + } + } + main_loop!(); + cleanup! { + self.pc = pc; + self.small_stack_top = small_stack_top; + } + } + ReadSmall { + index: u32, + } => { + small_stack_push!(small_states[index as usize]); + next!(); + } + SExtSmall { + /// number of bits in a [`SmallSInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = small_stack_pop!() as SmallSInt; + value <<= unused_bit_count; + value >>= unused_bit_count; + small_stack_push!(value as SmallUInt); + next!(); + } + ZExtSmall { + /// number of bits in a [`SmallUInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = small_stack_pop!(); + value <<= unused_bit_count; + value >>= unused_bit_count; + small_stack_push!(value); + next!(); + } + AndSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs & rhs; + small_stack_push!(value); + next!(); + } + OrSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs | rhs; + small_stack_push!(value); + next!(); + } + XorSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs ^ rhs; + small_stack_push!(value); + next!(); + } + NotSmall => { + let value = small_stack_pop!(); + small_stack_push!(!value); + next!(); + } + CmpEqSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs == rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpNeSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs != rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLTSmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs < rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLESmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs <= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGTSmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs > rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGESmallUInt => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = (lhs >= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLTSmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs < rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpLESmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs <= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGTSmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs > rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + CmpGESmallSInt => { + let rhs = small_stack_pop!() as SmallSInt; + let lhs = small_stack_pop!() as SmallSInt; + let value = (lhs >= rhs) as SmallUInt; + small_stack_push!(value); + next!(); + } + AddSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_add(rhs); + small_stack_push!(value); + next!(); + } + SubSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_sub(rhs); + small_stack_push!(value); + next!(); + } + NegSmall => { + let value = small_stack_pop!(); + let value = value.wrapping_neg(); + small_stack_push!(value); + next!(); + } + MulSmall => { + let rhs = small_stack_pop!(); + let lhs = small_stack_pop!(); + let value = lhs.wrapping_mul(rhs); + small_stack_push!(value); + next!(); + } + ConstU32Small { + value: u32, + } => { + small_stack_push!(value as SmallUInt); + next!(); + } + ConstS32Small { + value: i32, + } => { + small_stack_push!(value as SmallUInt); + next!(); + } + WriteSmall { + index: u32, + } => { + small_states[index as usize] = small_stack_pop!(); + next!(); + } + Return => { + break; + } +} From 479d59b28722c79873e7c5fa1caf1230001b7ee1 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 21:46:34 -0800 Subject: [PATCH 081/190] WIP implementing simulator --- crates/fayalite/src/expr/target.rs | 2 +- crates/fayalite/src/sim.rs | 389 ++++++++-- crates/fayalite/src/sim/interpreter.rs | 961 ++++++++++++++++++++----- 3 files changed, 1104 insertions(+), 248 deletions(-) diff --git a/crates/fayalite/src/expr/target.rs b/crates/fayalite/src/expr/target.rs index 0f85f62..e95ff84 100644 --- a/crates/fayalite/src/expr/target.rs +++ b/crates/fayalite/src/expr/target.rs @@ -313,7 +313,7 @@ impl TargetChild { } } -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum Target { Base(Interned), Child(TargetChild), diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 3aa69a6..3b55379 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1,28 +1,32 @@ //! Fayalite Simulation use crate::{ - bundle::{Bundle, BundleType}, + bundle::{Bundle, BundleField, BundleType}, enum_::Enum, - expr::Expr, - int::Bool, - intern::{Intern, Interned}, - module::{ - Block, Module, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtFormal, StmtIf, - StmtMatch, + expr::{ + target::{ + Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, + }, + Expr, + }, + int::Bool, + intern::{Intern, Interned, Memoize}, + module::{ + AnnotatedModuleIO, Block, Instance, Module, ModuleBody, NormalModuleBody, Stmt, + StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, + }, + sim::interpreter::{ + Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, StatePartLayout, TypeIndex, + TypeIndexRange, TypeLayout, }, - sim::interpreter::{Insn, Insns, InsnsBuilding}, source_location::SourceLocation, + ty::CanonicalType, }; use hashbrown::HashMap; -use std::{cell::RefCell, rc::Rc}; +use std::fmt; mod interpreter; -#[derive(Debug)] -struct CompilingModule { - compiled_module: Option, -} - #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] enum CondStack { Always, @@ -44,32 +48,279 @@ enum CondStack { }, } +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +enum InstantiatedModule { + Base(Interned>), + Child { + parent: Interned, + instance: Interned>, + }, +} + +impl InstantiatedModule { + fn leaf_module(self) -> Interned> { + match self { + InstantiatedModule::Base(base) => base, + InstantiatedModule::Child { instance, .. } => instance.instantiated(), + } + } + fn write_path(self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + InstantiatedModule::Base(base) => fmt::Debug::fmt(&base.name_id(), f), + InstantiatedModule::Child { parent, instance } => { + parent.write_path(f)?; + write!(f, ".{}", instance.name_id()) + } + } + } +} + +impl fmt::Debug for InstantiatedModule { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InstantiatedModule(")?; + self.write_path(f)?; + write!(f, ": {})", self.leaf_module().name_id()) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct TargetInInstantiatedModule { + instantiated_module: InstantiatedModule, + target: Target, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledBundleField { + offset: TypeIndex, + ty: CompiledTypeLayout, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +enum CompiledTypeLayoutBody { + Scalar, + Array { + /// debug names are ignored, use parent's layout instead + element: Interned, + }, + Bundle { + /// debug names are ignored, use parent's layout instead + fields: Interned<[CompiledBundleField]>, + }, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledTypeLayout { + ty: CanonicalType, + layout: TypeLayout, + body: CompiledTypeLayoutBody, +} + +impl CompiledTypeLayout { + fn with_prefixed_debug_names(self, prefix: &str) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_prefixed_debug_names(prefix), + body, + } + } + fn get(ty: CanonicalType) -> Self { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = CanonicalType; + type InputOwned = CanonicalType; + type Output = CompiledTypeLayout; + + fn inner(self, input: &Self::Input) -> Self::Output { + match input { + CanonicalType::UInt(_) + | CanonicalType::SInt(_) + | CanonicalType::Bool(_) + | CanonicalType::Enum(_) + | CanonicalType::AsyncReset(_) + | CanonicalType::SyncReset(_) + | CanonicalType::Reset(_) + | CanonicalType::Clock(_) => { + let mut layout = TypeLayout::empty(); + let debug_data = SlotDebugData { name: "".intern() }; + if input.bit_width() > interpreter::SmallUInt::BITS as usize { + layout.big_slots = StatePartLayout::scalar(debug_data); + } else { + layout.small_slots = StatePartLayout::scalar(debug_data); + }; + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Scalar, + } + } + CanonicalType::Array(array) => { + let mut layout = TypeLayout::empty(); + let element = CompiledTypeLayout::get(array.element()).intern_sized(); + for index in 0..array.len() { + layout.allocate( + &element + .layout + .with_prefixed_debug_names(&format!("[{index}]")), + ); + } + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Array { element }, + } + } + CanonicalType::Bundle(bundle) => { + let mut layout = TypeLayout::empty(); + let fields = bundle + .fields() + .iter() + .map( + |BundleField { + name, + flipped: _, + ty, + }| { + let ty = CompiledTypeLayout::get(*ty); + let offset = layout + .allocate( + &ty.layout + .with_prefixed_debug_names(&format!(".{name}")), + ) + .start(); + CompiledBundleField { offset, ty } + }, + ) + .collect(); + CompiledTypeLayout { + ty: *input, + layout: layout.into(), + body: CompiledTypeLayoutBody::Bundle { fields }, + } + } + } + } + } + MyMemoize.get_owned(ty) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct CompiledValue { + layout: CompiledTypeLayout, + range: TypeIndexRange, + write: Option<(CompiledTypeLayout, TypeIndexRange)>, +} + +impl CompiledValue { + fn map( + self, + mut f: impl FnMut(CompiledTypeLayout, TypeIndexRange) -> (CompiledTypeLayout, TypeIndexRange), + ) -> Self { + let (layout, range) = f(self.layout, self.range); + Self { + layout, + range, + write: self.write.map(|(layout, range)| f(layout, range)), + } + } +} + #[derive(Debug)] pub struct Compiler { insns: Insns, - modules: HashMap>, Rc>>, - module_queue: Vec>>, + base_module: Interned>, + modules: HashMap, + compiled_values: HashMap, } impl Compiler { - pub fn new() -> Self { + pub fn new(base_module: Interned>) -> Self { Self { insns: Insns::new(), + base_module, modules: HashMap::new(), - module_queue: Vec::new(), + compiled_values: HashMap::new(), } } - pub fn add_module(&mut self, module: Interned>) { - self.modules.entry(module).or_insert_with(|| { - self.module_queue.push(module); - Rc::new(RefCell::new(CompilingModule { - compiled_module: None, - })) - }); + fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue { + if let Some(&retval) = self.compiled_values.get(&target) { + return retval; + } + match target.target { + Target::Base(base) => { + let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty()); + let layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}", + target.instantiated_module, + base.target_name() + )); + let range = self.insns.allocate_variable(&layout.layout); + let write = match *base { + TargetBase::ModuleIO(_) + | TargetBase::MemPort(_) + | TargetBase::Wire(_) + | TargetBase::Instance(_) => None, + TargetBase::Reg(_) => { + let write_layout = unprefixed_layout.with_prefixed_debug_names(&format!( + "{:?}.{:?}$next", + target.instantiated_module, + base.target_name() + )); + Some(( + write_layout, + self.insns.allocate_variable(&write_layout.layout), + )) + } + }; + CompiledValue { + range, + layout, + write, + } + } + Target::Child(target_child) => { + let parent = self.compile_value(TargetInInstantiatedModule { + instantiated_module: target.instantiated_module, + target: *target_child.parent(), + }); + match *target_child.path_element() { + TargetPathElement::BundleField(TargetPathBundleField { name }) => { + parent.map(|layout, range| { + let CompiledTypeLayout { + ty: CanonicalType::Bundle(bundle), + layout: _, + body: CompiledTypeLayoutBody::Bundle { fields }, + } = layout + else { + unreachable!(); + }; + let field_index = bundle.name_indexes()[&name]; + ( + fields[field_index].ty, + range.slice(TypeIndexRange::new( + fields[field_index].offset, + fields[field_index].ty.layout.len(), + )), + ) + }) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => parent + .map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(element.layout.len(), index)) + }), + TargetPathElement::DynArrayElement(_) => unreachable!(), + } + } + } } fn compile_block( &mut self, - module: Interned>, + parent_module: Interned, block: Block, cond_stack: Interned, ) { @@ -98,7 +349,7 @@ impl Compiler { blocks: [then_block, else_block], }) => { self.compile_block( - module, + parent_module, then_block, CondStack::IfTrue { parent: cond_stack, @@ -108,7 +359,7 @@ impl Compiler { .intern_sized(), ); self.compile_block( - module, + parent_module, else_block, CondStack::IfFalse { parent: cond_stack, @@ -125,7 +376,7 @@ impl Compiler { }) => { for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( - module, + parent_module, block, CondStack::MatchArm { parent: cond_stack, @@ -137,67 +388,79 @@ impl Compiler { ); } } - Stmt::Declaration(stmt_declaration) => todo!(), + Stmt::Declaration(declaration) => match declaration { + StmtDeclaration::Wire(wire) => todo!(), + StmtDeclaration::Reg(reg) => todo!(), + StmtDeclaration::Instance(StmtInstance { + annotations, + instance, + }) => { + self.compile_module( + InstantiatedModule::Child { + parent: parent_module, + instance: instance.intern_sized(), + } + .intern_sized(), + ); + todo!() + } + }, } } } - fn compile_module(&mut self, module: Interned>) -> CompiledModule { - let step_entry_pc = self.insns.next_insn_pc(); - match module.body() { + fn compile_module(&mut self, module: Interned) -> &CompiledModule { + let module_io = module + .leaf_module() + .module_io() + .iter() + .map( + |&AnnotatedModuleIO { + annotations: _, + module_io, + }| { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *module, + target: Target::from(module_io), + }) + }, + ) + .collect(); + match module.leaf_module().body() { ModuleBody::Normal(NormalModuleBody { body }) => { self.compile_block(module, body, CondStack::Always.intern_sized()) } ModuleBody::Extern(_extern_module_body) => { - todo!("simulating extern module: {}", module.name_id()); + todo!("simulating extern module: {:?}", module); } } - self.insns.push(Insn::Return, module.source_location()); - CompiledModule { step_entry_pc } + let hashbrown::hash_map::Entry::Vacant(entry) = self.modules.entry(*module) else { + unreachable!("compiled same instantiated module twice"); + }; + entry.insert(CompiledModule { module_io }) } pub fn run(mut self) -> Compiled { - while let Some(module) = self.module_queue.pop() { - let compiling_module = self.modules[&module].clone(); - let mut compiling_module = compiling_module.borrow_mut(); - let CompilingModule { compiled_module } = &mut *compiling_module; - assert!(compiled_module.is_none()); - *compiled_module = Some(self.compile_module(module)); - } + self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); Compiled { insns: Insns::from(self.insns).intern_sized(), - modules: self - .modules - .into_iter() - .map(|(module, compiling_module)| { - ( - module, - Rc::into_inner(compiling_module) - .expect("only reference is Compiler::modules") - .into_inner() - .compiled_module - .expect("module is compiled by Compiler::run"), - ) - }) - .collect(), + modules: self.modules, } } } #[derive(Debug)] struct CompiledModule { - step_entry_pc: usize, + module_io: Interned<[CompiledValue]>, } #[derive(Debug)] pub struct Compiled { - insns: Interned, - modules: HashMap>, CompiledModule>, + insns: Interned>, + modules: HashMap, } impl Compiled { pub fn new(module: Module) -> Self { - let mut compiler = Compiler::new(); - compiler.add_module(module.canonical().intern()); - compiler.run() + Compiler::new(module.canonical().intern()).run() } } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index eed64e4..06d0025 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -3,21 +3,26 @@ use crate::{ source_location::SourceLocation, }; use num_bigint::BigInt; -use std::{convert::Infallible, fmt, hash::Hash}; +use std::{ + convert::Infallible, + fmt, + hash::Hash, + marker::PhantomData, + ops::{Deref, DerefMut, Index, IndexMut}, +}; pub(crate) type SmallUInt = u64; pub(crate) type SmallSInt = i64; macro_rules! impl_insns { ( - #[next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] - $vis:vis fn $State:ident::$run:ident(&mut $self:ident, $insns:ident: &[$Insn:ident]) -> $run_ret_ty:ty { - #[pc] - let mut $pc:ident: usize = $pc_init:expr; + #[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] + $vis:vis fn $State:ident::$run:ident(&mut $self:ident) -> $run_ret_ty:ty { + #[state] + let mut $state:ident = $state_init:expr; setup! { $($setup:tt)* } - $(let mut $local:ident = $local_init:expr;)* main_loop!(); cleanup! { $($cleanup:tt)* @@ -47,22 +52,22 @@ macro_rules! impl_insns { } impl $State { - $vis fn $run(&mut $self, $insns: &[$Insn]) -> $run_ret_ty { - let mut $pc: usize = $pc_init; + $vis fn $run(&mut $self) -> $run_ret_ty { + let mut $state = $state_init; $($setup)* - let mut insn = $insns[$pc]; + let mut insn = $state.insns[$state.pc]; let retval = 'main_loop: loop { macro_rules! $next_macro { () => { - $pc += 1; - insn = $insns[$pc]; + $state.pc += 1; + insn = $state.insns[$state.pc]; continue 'main_loop; }; } macro_rules! $branch_macro { ($next_pc:expr) => { - $pc = $next_pc; - insn = $insns[$pc]; + $state.pc = $next_pc; + insn = $state.insns[$state.pc]; continue 'main_loop; }; } @@ -86,34 +91,37 @@ macro_rules! impl_insns { } pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { - type BoxedSlice: fmt::Debug + type Vec: fmt::Debug + Clone + Eq - + std::ops::Deref; + + Hash + + Send + + Sync + + 'static + + Default + + Deref + + FromIterator; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct InsnsBuildingDone; impl InsnsBuildingKind for InsnsBuildingDone { - type BoxedSlice = Interned<[T]>; + type Vec = Interned<[T]>; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct InsnsBuilding; impl InsnsBuildingKind for InsnsBuilding { - type BoxedSlice = Vec; + type Vec = Vec; } #[derive(Clone, PartialEq, Eq, Hash)] -pub(crate) struct Insns { - insns: BK::BoxedSlice, - small_stack_size: usize, - big_stack_size: usize, - small_state_debug_names: BK::BoxedSlice>, - big_state_debug_names: BK::BoxedSlice>, - insn_source_locations: BK::BoxedSlice, +pub(crate) struct Insns { + insns: BK::Vec, + insn_source_locations: BK::Vec, + state_layout: StateLayout, } struct InsnsDebug<'a> { @@ -141,17 +149,11 @@ impl fmt::Debug for Insns { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { insns, - small_stack_size, - big_stack_size, - small_state_debug_names, - big_state_debug_names, insn_source_locations, + state_layout, } = self; f.debug_struct("Insns") - .field("small_stack_size", small_stack_size) - .field("big_stack_size", big_stack_size) - .field("small_state_debug_names", small_state_debug_names) - .field("big_state_debug_names", big_state_debug_names) + .field("state_layout", state_layout) .field( "insns", &InsnsDebug { @@ -163,15 +165,662 @@ impl fmt::Debug for Insns { } } +pub(crate) trait StatePartKind: + Send + Sync + Ord + Hash + fmt::Debug + 'static + Copy + Default +{ + const NAME: &'static str; + type DebugData: Send + Sync + Eq + Hash + fmt::Debug + 'static + Copy; + type State: fmt::Debug + 'static + Clone; + type BorrowedState<'a>: 'a; + fn new_state(len: StatePartLen) -> Self::State; + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; +} + +macro_rules! make_state_part_kinds { + ( + $( + #[state, field = $state_field:ident] + impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt + )+ + $( + #[type, field = $type_field:ident] + impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt + )+ + ) => { + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $StateKind; + + impl $StateStatePartKind for $StateKind $state_impl_body + )+ + + $( + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] + pub(crate) struct $TypeKind; + + impl $TypeStatePartKind for $TypeKind $type_impl_body + )+ + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLayout { + pub(crate) ty: TypeLayout, + $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)+ + } + + impl Copy for StateLayout {} + + impl StateLayout { + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_field: self.$state_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_field.is_empty())+ + } + pub(crate) fn empty() -> Self { + Self { + ty: TypeLayout::empty(), + $($state_field: StatePartLayout::empty(),)+ + } + } + } + + impl StateLayout { + pub(crate) fn next_index(&self) -> StateIndex { + StateIndex { + ty: self.ty.next_index(), + $($state_field: self.$state_field.next_index(),)+ + } + } + pub(crate) fn allocate( + &mut self, + layout: &StateLayout, + ) -> StateIndexRange { + StateIndexRange { + ty: self.ty.allocate(&layout.ty), + $($state_field: self.$state_field.allocate(&layout.$state_field),)+ + } + } + } + + impl From> for StateLayout { + fn from(v: StateLayout) -> Self { + Self { + ty: v.ty.into(), + $($state_field: v.$state_field.into(),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndexRange { + pub(crate) ty: TypeIndexRange, + $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)+ + } + + impl StateIndexRange { + pub(crate) fn start(self) -> StateIndex { + StateIndex { + ty: self.ty.start(), + $($state_field: self.$state_field.start(),)+ + } + } + pub(crate) fn len(self) -> StateLen { + StateLen { + ty: self.ty.len(), + $($state_field: self.$state_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + self.ty.is_empty() $(&& self.$state_field.is_empty())+ + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateLen { + pub(crate) ty: TypeLen, + $(pub(crate) $state_field: StatePartLen<$StateKind>,)+ + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct StateIndex { + pub(crate) ty: TypeIndex, + $(pub(crate) $state_field: StatePartIndex<$StateKind>,)+ + } + + #[derive(Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLayout { + $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)+ + } + + impl Copy for TypeLayout {} + + impl TypeLayout { + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_field.is_empty())&&+ + } + pub(crate) fn empty() -> Self { + Self { + $($type_field: StatePartLayout::empty(),)+ + } + } + } + + impl TypeLayout { + pub(crate) fn next_index(&self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.next_index(),)+ + } + } + pub(crate) fn allocate( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + TypeIndexRange { + $($type_field: self.$type_field.allocate(&layout.$type_field),)+ + } + } + } + + impl From> for TypeLayout { + fn from(v: TypeLayout) -> Self { + Self { + $($type_field: v.$type_field.into(),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndexRange { + $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)+ + } + + impl TypeIndexRange { + pub(crate) fn new(start: TypeIndex, len: TypeLen) -> Self { + Self { + $($type_field: StatePartIndexRange { + start: start.$type_field, + len: len.$type_field, + },)+ + } + } + pub(crate) fn start(self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.start(),)+ + } + } + pub(crate) fn len(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.len(),)+ + } + } + pub(crate) fn is_empty(self) -> bool { + $(self.$type_field.is_empty()) &&+ + } + pub(crate) fn slice(self, index: TypeIndexRange) -> Self { + Self { + $($type_field: self.$type_field.slice(index.$type_field),)+ + } + } + pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { + Self { + $($type_field: self.$type_field.index_array(element_size.$type_field, index),)+ + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeLen { + $(pub(crate) $type_field: StatePartLen<$TypeKind>,)+ + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeIndex { + $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)+ + } + + #[derive(Debug)] + pub(crate) struct State { + pub(crate) insns: Interned>, + pub(crate) pc: usize, + $(pub(crate) $state_field: StatePart<$StateKind>,)+ + $(pub(crate) $type_field: StatePart<$TypeKind>,)+ + } + + impl State { + pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { + BorrowedState { + orig_insns: self.insns, + insns: &self.insns.insns, + pc: self.pc, + orig_pc: &mut self.pc, + $($state_field: self.$state_field.borrow(),)+ + $($type_field: self.$type_field.borrow(),)+ + } + } + } + + #[derive(Debug)] + pub(crate) struct BorrowedState<'a> { + pub(crate) orig_insns: Interned>, + pub(crate) insns: &'a [Insn], + pub(crate) orig_pc: &'a mut usize, + pub(crate) pc: usize, + $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)+ + $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)+ + } + + impl Drop for BorrowedState<'_> { + fn drop(&mut self) { + *self.orig_pc = self.pc; + } + } + }; +} + +make_state_part_kinds! { + #[state, field = small_stack] + impl StatePartKind for StatePartKindSmallStack { + const NAME: &'static str = "SmallStack"; + type DebugData = (); + type State = Stack; + type BorrowedState<'a> = BorrowedStack<'a, SmallUInt>; + fn new_state(len: StatePartLen) -> Self::State { + Stack::new(len.value.try_into().expect("state is too big")) + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state.borrow() + } + } + #[state, field = big_stack] + impl StatePartKind for StatePartKindBigStack { + const NAME: &'static str = "BigStack"; + type DebugData = (); + type State = Stack; + type BorrowedState<'a> = BorrowedStack<'a, BigInt>; + fn new_state(len: StatePartLen) -> Self::State { + Stack::new(len.value.try_into().expect("state is too big")) + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state.borrow() + } + } + #[type, field = small_slots] + impl StatePartKind for StatePartKindSmallSlots { + const NAME: &'static str = "SmallSlots"; + type DebugData = SlotDebugData; + type State = Box<[SmallUInt]>; + type BorrowedState<'a> = &'a mut [SmallUInt]; + fn new_state(len: StatePartLen) -> Self::State { + vec![0; len.value.try_into().expect("state is too big")].into_boxed_slice() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + } + #[type, field = big_slots] + impl StatePartKind for StatePartKindBigSlots { + const NAME: &'static str = "BigSlots"; + type DebugData = SlotDebugData; + type State = Box<[BigInt]>; + type BorrowedState<'a> = &'a mut [BigInt]; + fn new_state(len: StatePartLen) -> Self::State { + std::iter::repeat_with(BigInt::default).take(len.value.try_into().expect("state is too big")).collect() + } + fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { + state + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct Stack { + storage: Box<[T]>, + cur_len: usize, +} + +impl Stack { + pub(crate) fn new(len: usize) -> Self + where + T: Default, + { + Self { + storage: std::iter::repeat_with(T::default).take(len).collect(), + cur_len: 0, + } + } + pub(crate) fn borrow(&mut self) -> BorrowedStack<'_, T> { + BorrowedStack { + storage: &mut self.storage, + cur_len: self.cur_len, + stack_cur_len: &mut self.cur_len, + } + } + pub(crate) fn clear(&mut self) { + self.cur_len = 0; + } +} + +#[derive(Debug)] +pub(crate) struct BorrowedStack<'a, T> { + storage: &'a mut [T], + cur_len: usize, + stack_cur_len: &'a mut usize, +} + +impl<'a, T> BorrowedStack<'a, T> { + pub(crate) fn push(&mut self, value: T) { + self.storage[self.cur_len] = value; + self.cur_len += 1; + } + pub(crate) fn pop(&mut self) -> T + where + T: Default, + { + self.cur_len -= 1; + std::mem::take(&mut self.storage[self.cur_len]) + } +} + +impl Drop for BorrowedStack<'_, T> { + fn drop(&mut self) { + *self.stack_cur_len = self.cur_len; + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) struct SlotDebugData { + pub(crate) name: Interned, +} + +impl SlotDebugData { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + let mut name = String::with_capacity(self.name.len() + prefix.len()); + name.push_str(prefix); + name.push_str(&self.name); + Self { + name: Intern::intern_owned(name), + } + } +} + +impl, BK: InsnsBuildingKind> StatePartLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_prefixed_debug_names(prefix)) + .collect(), + _phantom: PhantomData, + } + } +} + +impl TypeLayout { + pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { + Self { + small_slots: self.small_slots.with_prefixed_debug_names(prefix), + big_slots: self.big_slots.with_prefixed_debug_names(prefix), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartIndex { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl fmt::Debug for StatePartIndex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartIndex<{}>({})", K::NAME, self.value) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct StatePartLen { + pub(crate) value: u32, + pub(crate) _phantom: PhantomData, +} + +impl StatePartLen { + pub(crate) const fn new(value: u32) -> Self { + Self { + value, + _phantom: PhantomData, + } + } +} + +impl fmt::Debug for StatePartLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "StatePartLen<{}>({})", K::NAME, self.value) + } +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartLayout { + pub(crate) debug_data: BK::Vec, + pub(crate) _phantom: PhantomData, +} + +impl Copy for StatePartLayout {} + +impl StatePartLayout { + pub(crate) fn len(&self) -> StatePartLen { + StatePartLen::new( + self.debug_data + .len() + .try_into() + .expect("state part allocation layout is too big"), + ) + } + pub(crate) fn is_empty(&self) -> bool { + self.debug_data.is_empty() + } + pub(crate) fn empty() -> Self { + Self { + debug_data: Default::default(), + _phantom: PhantomData, + } + } +} + +impl From> + for StatePartLayout +{ + fn from(value: StatePartLayout) -> Self { + Self { + debug_data: Intern::intern_owned(value.debug_data), + _phantom: PhantomData, + } + } +} + +impl StatePartLayout { + pub(crate) fn scalar(debug_data: K::DebugData) -> Self { + Self { + debug_data: vec![debug_data], + _phantom: PhantomData, + } + } + pub(crate) fn next_index(&self) -> StatePartIndex { + StatePartIndex { + value: self.len().value, + _phantom: PhantomData, + } + } + pub(crate) fn allocate( + &mut self, + layout: &StatePartLayout, + ) -> StatePartIndexRange { + let start = self.next_index(); + let len = layout.len(); + let Self { + debug_data, + _phantom: _, + } = self; + debug_data.extend_from_slice(&layout.debug_data); + StatePartIndexRange { start, len } + } +} + +impl fmt::Debug for StatePartLayout { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { + debug_data, + _phantom: _, + } = self; + write!(f, "StatePartAllocationLayout<{}>", K::NAME)?; + f.debug_struct("") + .field("len", &debug_data.len()) + .field("debug_data", debug_data) + .finish_non_exhaustive() + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct StatePartIndexRange { + pub(crate) start: StatePartIndex, + pub(crate) len: StatePartLen, +} + +impl StatePartIndexRange { + pub(crate) fn start(self) -> StatePartIndex { + self.start + } + pub(crate) fn end(self) -> StatePartIndex { + StatePartIndex { + value: self + .start + .value + .checked_add(self.len.value) + .expect("state part allocation layout is too big"), + _phantom: PhantomData, + } + } + pub(crate) fn len(self) -> StatePartLen { + self.len + } + pub(crate) fn is_empty(self) -> bool { + self.len.value == 0 + } + pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { + assert!(index.end().value <= self.len.value, "index out of range"); + Self { + start: StatePartIndex { + value: self.start.value + index.start.value, + _phantom: PhantomData, + }, + len: index.len, + } + } + pub(crate) fn index_array(self, element_size: StatePartLen, index: usize) -> Self { + if element_size.value == 0 { + assert_eq!( + self.len.value, 0, + "array with zero-sized element must also be zero-sized", + ); + return self; + } + assert!( + self.len.value % element_size.value == 0, + "array's size must be a multiple of its element size", + ); + self.slice(StatePartIndexRange { + start: StatePartIndex { + value: index + .try_into() + .ok() + .and_then(|index| element_size.value.checked_mul(index)) + .expect("index out of range"), + _phantom: PhantomData, + }, + len: element_size, + }) + } +} + +impl fmt::Debug for StatePartIndexRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "StatePartIndexRange<{}> {{ start: {}, len: {} }}", + K::NAME, + self.start.value, + self.len.value + ) + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePart { + pub(crate) value: K::State, +} + +impl StatePart { + pub(crate) fn new(len: StatePartLen) -> Self { + Self { + value: K::new_state(len), + } + } + pub(crate) fn borrow<'a>(&'a mut self) -> BorrowedStatePart<'a, K> { + BorrowedStatePart { + value: K::borrow_state(&mut self.value), + } + } +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> { + pub(crate) value: K::BorrowedState<'a>, +} + +impl<'a, K: StatePartKind: Deref>>, T> + Index> for BorrowedStatePart<'a, K> +{ + type Output = T; + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.value[index.value as usize] + } +} + +impl<'a, K: StatePartKind: DerefMut>>, T> + IndexMut> for BorrowedStatePart<'a, K> +{ + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.value[index.value as usize] + } +} + +impl<'a, K: StatePartKind = BorrowedStack<'a, T>>, T: 'a> + BorrowedStatePart<'a, K> +{ + pub(crate) fn push(&mut self, value: T) { + self.value.push(value) + } + pub(crate) fn pop(&mut self) -> T + where + T: Default, + { + self.value.pop() + } +} + impl Insns { pub(crate) fn new() -> Self { Self { insns: Vec::new(), - small_stack_size: 0, - big_stack_size: 0, - small_state_debug_names: Vec::new(), - big_state_debug_names: Vec::new(), insn_source_locations: Vec::new(), + state_layout: StateLayout::empty(), } } pub(crate) fn last_insn_pc(&self) -> usize { @@ -187,39 +836,27 @@ impl Insns { self.insns.push(insn); self.insn_source_locations.push(source_location); } -} - -impl From> for Insns { - fn from(input: Insns) -> Self { - let Insns { - insns, - small_stack_size, - big_stack_size, - small_state_debug_names, - big_state_debug_names, - insn_source_locations, - } = input; - Insns { - insns: Intern::intern_owned(insns), - small_stack_size, - big_stack_size, - small_state_debug_names: Intern::intern_owned(small_state_debug_names), - big_state_debug_names: Intern::intern_owned(big_state_debug_names), - insn_source_locations: Intern::intern_owned(insn_source_locations), - } + pub(crate) fn allocate_variable( + &mut self, + layout: &TypeLayout, + ) -> TypeIndexRange { + self.state_layout.ty.allocate(layout) } } -#[derive(Debug)] -pub(crate) struct State { - pub(crate) insns: Interned, - pub(crate) pc: usize, - pub(crate) small_stack: Box<[SmallUInt]>, - pub(crate) small_stack_top: usize, - pub(crate) small_states: Box<[SmallUInt]>, - pub(crate) big_stack: Box<[BigInt]>, - pub(crate) big_stack_top: usize, - pub(crate) big_states: Box<[BigInt]>, +impl From> for Insns { + fn from(input: Insns) -> Self { + let Insns { + insns, + insn_source_locations, + state_layout, + } = input; + Insns { + insns: Intern::intern_owned(insns), + insn_source_locations: Intern::intern_owned(insn_source_locations), + state_layout: state_layout.into(), + } + } } impl State { @@ -227,235 +864,191 @@ impl State { let Self { insns: _, pc, - small_stack: _, - small_stack_top, - small_states: _, - big_stack: _, - big_stack_top, - big_states: _, + small_stack, + big_stack, + small_slots: _, + big_slots: _, } = self; *pc = entry_pc; - *small_stack_top = 0; - *big_stack_top = 0; + small_stack.value.clear(); + big_stack.value.clear(); } } impl_insns! { - #[next_macro = next, branch_macro = branch] - pub(crate) fn State::run(&mut self, insns: &[Insn]) -> () { - #[pc] - let mut pc: usize = self.pc; - setup! { - let small_states = &mut *self.small_states; - let small_stack = &mut *self.small_stack; - let mut small_stack_top = self.small_stack_top; - macro_rules! small_stack_push { - ($v:expr) => { - { - small_stack[small_stack_top] = $v; - small_stack_top += 1; - } - }; - } - macro_rules! small_stack_pop { - () => { - { - small_stack_top -= 1; - small_stack[small_stack_top] - } - }; - } - let big_states = &mut *self.big_states; - let big_stack = &mut *self.big_stack; - let mut big_stack_top = self.big_stack_top; - macro_rules! big_stack_push { - ($v:expr) => { - { - big_stack[big_stack_top] = $v; - big_stack_top += 1; - } - }; - } - macro_rules! big_stack_pop { - () => { - { - big_stack_top -= 1; - big_stack[big_stack_top] - } - }; - } - } + #[insn = Insn, next_macro = next, branch_macro = branch] + pub(crate) fn State::run(&mut self) -> () { + #[state] + let mut state = self.borrow(); + setup! {} main_loop!(); - cleanup! { - self.pc = pc; - self.small_stack_top = small_stack_top; - } + cleanup! {} } ReadSmall { - index: u32, + index: StatePartIndex, } => { - small_stack_push!(small_states[index as usize]); + state.small_stack.push(state.small_slots[index]); next!(); } SExtSmall { /// number of bits in a [`SmallSInt`] that aren't used unused_bit_count: u8, } => { - let mut value = small_stack_pop!() as SmallSInt; + let mut value = state.small_stack.pop() as SmallSInt; value <<= unused_bit_count; value >>= unused_bit_count; - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } ZExtSmall { /// number of bits in a [`SmallUInt`] that aren't used unused_bit_count: u8, } => { - let mut value = small_stack_pop!(); + let mut value = state.small_stack.pop(); value <<= unused_bit_count; value >>= unused_bit_count; - small_stack_push!(value); + state.small_stack.push(value); next!(); } AndSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs & rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } OrSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs | rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } XorSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs ^ rhs; - small_stack_push!(value); + state.small_stack.push(value); next!(); } NotSmall => { - let value = small_stack_pop!(); - small_stack_push!(!value); + let value = state.small_stack.pop(); + state.small_stack.push(!value); next!(); } CmpEqSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs == rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpNeSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs != rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLTSmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs < rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLESmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs <= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGTSmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs > rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGESmallUInt => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = (lhs >= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLTSmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs < rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpLESmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs <= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGTSmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs > rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } CmpGESmallSInt => { - let rhs = small_stack_pop!() as SmallSInt; - let lhs = small_stack_pop!() as SmallSInt; + let rhs = state.small_stack.pop() as SmallSInt; + let lhs = state.small_stack.pop() as SmallSInt; let value = (lhs >= rhs) as SmallUInt; - small_stack_push!(value); + state.small_stack.push(value); next!(); } AddSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_add(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } SubSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_sub(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } NegSmall => { - let value = small_stack_pop!(); + let value = state.small_stack.pop(); let value = value.wrapping_neg(); - small_stack_push!(value); + state.small_stack.push(value); next!(); } MulSmall => { - let rhs = small_stack_pop!(); - let lhs = small_stack_pop!(); + let rhs = state.small_stack.pop(); + let lhs = state.small_stack.pop(); let value = lhs.wrapping_mul(rhs); - small_stack_push!(value); + state.small_stack.push(value); next!(); } ConstU32Small { value: u32, } => { - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } ConstS32Small { value: i32, } => { - small_stack_push!(value as SmallUInt); + state.small_stack.push(value as SmallUInt); next!(); } WriteSmall { - index: u32, + index: StatePartIndex, } => { - small_states[index as usize] = small_stack_pop!(); + state.small_slots[index] = state.small_stack.pop(); next!(); } Return => { From b288d6f8f20d7bceee96375cb91ed8be336ee28b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 7 Nov 2024 21:50:31 -0800 Subject: [PATCH 082/190] add missing copyright headers --- crates/fayalite/src/sim.rs | 3 +++ crates/fayalite/src/sim/interpreter.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 3b55379..6114044 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + //! Fayalite Simulation use crate::{ diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 06d0025..f327510 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information + use crate::{ intern::{Intern, Interned}, source_location::SourceLocation, From 277d3e0d4d37a36eaba12fe684225dae38b0163d Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Sun, 10 Nov 2024 22:12:57 -0800 Subject: [PATCH 083/190] working on simulator --- Cargo.lock | 7 + Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/sim.rs | 709 +++++++++++++++++++++++-- crates/fayalite/src/sim/interpreter.rs | 665 +++++++++++++++++------ crates/fayalite/src/util.rs | 3 +- crates/fayalite/src/util/misc.rs | 16 + 7 files changed, 1212 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 500bd34..2fca6fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,6 +319,7 @@ dependencies = [ "serde_json", "tempfile", "trybuild", + "vec_map", "which", ] @@ -720,6 +721,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index b6b8616..44f5c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,4 +39,5 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] tempfile = "3.10.1" thiserror = "1.0.61" trybuild = "1.0" +vec_map = "0.8.2" which = "6.0.1" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 5724a80..8e90a74 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -28,6 +28,7 @@ os_pipe.workspace = true serde_json.workspace = true serde.workspace = true tempfile.workspace = true +vec_map.workspace = true which.workspace = true [dev-dependencies] diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 6114044..9b40458 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -4,29 +4,28 @@ //! Fayalite Simulation use crate::{ - bundle::{Bundle, BundleField, BundleType}, - enum_::Enum, + bundle::{BundleField, BundleType}, expr::{ target::{ Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, }, - Expr, + ExprEnum, }, - int::Bool, intern::{Intern, Interned, Memoize}, module::{ - AnnotatedModuleIO, Block, Instance, Module, ModuleBody, NormalModuleBody, Stmt, - StmtConnect, StmtDeclaration, StmtFormal, StmtIf, StmtInstance, StmtMatch, + AnnotatedModuleIO, Block, ModuleBody, NormalModuleBody, Stmt, StmtConnect, StmtDeclaration, + StmtFormal, StmtIf, StmtInstance, StmtMatch, StmtReg, StmtWire, }, + prelude::*, sim::interpreter::{ - Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, StatePartLayout, TypeIndex, - TypeIndexRange, TypeLayout, + Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, SmallUInt, StatePartIndex, + StatePartIndexMap, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, + TypeLen, TypeParts, MIN_BITS_FOR_NEEDING_BIG, }, - source_location::SourceLocation, - ty::CanonicalType, }; use hashbrown::HashMap; -use std::fmt; +use std::{fmt, marker::PhantomData, mem}; mod interpreter; @@ -35,17 +34,17 @@ enum CondStack { Always, IfTrue { parent: Interned, - cond: Expr, + cond: CompiledExpr, source_location: SourceLocation, }, IfFalse { parent: Interned, - cond: Expr, + cond: CompiledExpr, source_location: SourceLocation, }, MatchArm { parent: Interned, - enum_expr: Expr, + enum_expr: CompiledExpr, variant_index: usize, source_location: SourceLocation, }, @@ -95,7 +94,7 @@ struct TargetInInstantiatedModule { #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] struct CompiledBundleField { offset: TypeIndex, - ty: CompiledTypeLayout, + ty: CompiledTypeLayout, } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] @@ -103,7 +102,7 @@ enum CompiledTypeLayoutBody { Scalar, Array { /// debug names are ignored, use parent's layout instead - element: Interned, + element: Interned>, }, Bundle { /// debug names are ignored, use parent's layout instead @@ -112,13 +111,13 @@ enum CompiledTypeLayoutBody { } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledTypeLayout { - ty: CanonicalType, +struct CompiledTypeLayout { + ty: T, layout: TypeLayout, body: CompiledTypeLayoutBody, } -impl CompiledTypeLayout { +impl CompiledTypeLayout { fn with_prefixed_debug_names(self, prefix: &str) -> Self { let Self { ty, layout, body } = self; Self { @@ -127,13 +126,13 @@ impl CompiledTypeLayout { body, } } - fn get(ty: CanonicalType) -> Self { + fn get(ty: T) -> Self { #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] struct MyMemoize; impl Memoize for MyMemoize { type Input = CanonicalType; type InputOwned = CanonicalType; - type Output = CompiledTypeLayout; + type Output = CompiledTypeLayout; fn inner(self, input: &Self::Input) -> Self::Output { match input { @@ -146,8 +145,10 @@ impl CompiledTypeLayout { | CanonicalType::Reset(_) | CanonicalType::Clock(_) => { let mut layout = TypeLayout::empty(); - let debug_data = SlotDebugData { name: "".intern() }; - if input.bit_width() > interpreter::SmallUInt::BITS as usize { + let debug_data = SlotDebugData { + name: Interned::default(), + }; + if input.bit_width() >= interpreter::MIN_BITS_FOR_NEEDING_BIG { layout.big_slots = StatePartLayout::scalar(debug_data); } else { layout.small_slots = StatePartLayout::scalar(debug_data); @@ -205,24 +206,32 @@ impl CompiledTypeLayout { } } } - MyMemoize.get_owned(ty) + let CompiledTypeLayout { + ty: _, + layout, + body, + } = MyMemoize.get_owned(ty.canonical()); + Self { ty, layout, body } } } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -struct CompiledValue { - layout: CompiledTypeLayout, +struct CompiledValue { + layout: CompiledTypeLayout, range: TypeIndexRange, - write: Option<(CompiledTypeLayout, TypeIndexRange)>, + write: Option<(CompiledTypeLayout, TypeIndexRange)>, } -impl CompiledValue { - fn map( +impl CompiledValue { + fn map( self, - mut f: impl FnMut(CompiledTypeLayout, TypeIndexRange) -> (CompiledTypeLayout, TypeIndexRange), - ) -> Self { + mut f: impl FnMut( + CompiledTypeLayout, + TypeIndexRange, + ) -> (CompiledTypeLayout, TypeIndexRange), + ) -> CompiledValue { let (layout, range) = f(self.layout, self.range); - Self { + CompiledValue { layout, range, write: self.write.map(|(layout, range)| f(layout, range)), @@ -230,12 +239,98 @@ impl CompiledValue { } } +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExprDynIndex { + index_slot: StatePartIndex, + len: TypeLen, + stride: TypeLen, +} + +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +struct CompiledExpr { + static_part: CompiledValue, + dyn_indexes: Interned<[CompiledExprDynIndex]>, +} + +impl From> for CompiledExpr { + fn from(static_part: CompiledValue) -> Self { + Self { + static_part, + dyn_indexes: Interned::default(), + } + } +} + +impl CompiledExpr { + fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledExpr { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExpr { + static_part: static_part.map(|CompiledTypeLayout { ty, layout, body }, range| { + ( + CompiledTypeLayout { + ty: f(ty), + layout, + body, + }, + range, + ) + }), + dyn_indexes, + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +struct SlotSet(TypeParts); + +impl SlotSet { + fn is_empty(&self) -> bool { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.is_empty() && big_slots.is_empty() + } +} + +impl StatePartsValue for SlotSet { + type Value = Vec>; +} + +#[derive(Debug)] +struct Assignment { + inputs: SlotSet, + insns: Vec, +} + +#[derive(Debug, Default)] +struct StatePartAssignments { + written_slot_to_assignment_indexes_map: StatePartIndexMap>, +} + +#[derive(Debug, Default)] +struct SlotAssignments { + assignments: Vec, + parts: TypeParts, +} + +impl StatePartsValue for SlotAssignments { + type Value = StatePartAssignments; +} + #[derive(Debug)] pub struct Compiler { insns: Insns, base_module: Interned>, modules: HashMap, - compiled_values: HashMap, + compiled_values: HashMap>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + expanded_to_big: HashMap, CompiledValue>, + slots_assignments: SlotAssignments, } impl Compiler { @@ -245,13 +340,20 @@ impl Compiler { base_module, modules: HashMap::new(), compiled_values: HashMap::new(), + compiled_exprs: HashMap::new(), + compiled_exprs_to_values: HashMap::new(), + expanded_to_big: HashMap::new(), + slots_assignments: SlotAssignments::default(), } } - fn compile_value(&mut self, target: TargetInInstantiatedModule) -> CompiledValue { + fn compile_value( + &mut self, + target: TargetInInstantiatedModule, + ) -> CompiledValue { if let Some(&retval) = self.compiled_values.get(&target) { return retval; } - match target.target { + let retval = match target.target { Target::Base(base) => { let unprefixed_layout = CompiledTypeLayout::get(base.canonical_ty()); let layout = unprefixed_layout.with_prefixed_debug_names(&format!( @@ -319,7 +421,494 @@ impl Compiler { TargetPathElement::DynArrayElement(_) => unreachable!(), } } + }; + self.compiled_values.insert(target, retval); + retval + } + fn compiled_expr_to_value( + &mut self, + expr: CompiledExpr, + ) -> CompiledValue { + if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + return retval; } + let CompiledExpr { + static_part: mut retval, + dyn_indexes, + } = expr; + for CompiledExprDynIndex { + index_slot, + len, + stride, + } in dyn_indexes + { + todo!(); + } + self.compiled_exprs_to_values.insert(expr, retval); + retval + } + fn simple_nary_expr( + &mut self, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns: impl FnOnce( + &mut Self, + CompiledValue, + &mut [CompiledValue; N], + ) -> Vec, + ) -> CompiledValue { + let mut inputs = inputs.map(|input| { + let input = self.compile_expr(input); + self.compiled_expr_to_value(input) + }); + let layout = CompiledTypeLayout::get(dest_ty); + let range = self.insns.allocate_variable(&layout.layout); + let retval = CompiledValue { + layout, + range, + write: None, + }; + let insns = make_insns(self, retval, &mut inputs); + let mut inputs_set = SlotSet::default(); + for input in inputs { + let TypeIndexRange { + small_slots, + big_slots, + } = input.range; + inputs_set.0.small_slots.extend(small_slots.iter()); + inputs_set.0.big_slots.extend(big_slots.iter()); + } + let assignment_index = self.slots_assignments.assignments.len(); + self.slots_assignments.assignments.push(Assignment { + inputs: inputs_set, + insns, + }); + let TypeIndexRange { + small_slots, + big_slots, + } = range; + for i in small_slots.iter() { + self.slots_assignments + .parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(i) + .or_insert_with(Vec::new) + .push(assignment_index); + } + for i in big_slots.iter() { + self.slots_assignments + .parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(i) + .or_insert_with(Vec::new) + .push(assignment_index); + } + retval + } + fn expand_to_big(&mut self, expr: Expr) -> CompiledValue { + if let Some(&retval) = self.expanded_to_big.get(&expr) { + return retval; + } + let input = self.compile_expr(expr); + let input = self.compiled_expr_to_value(input); + let retval = match input.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: _, + } => input, + len => { + assert_eq!( + Some(StatePartLen { + value: 1, + _phantom: PhantomData, + }), + len.only_small() + ); + let signed = match Expr::ty(expr) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Enum(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + self.simple_nary_expr( + if signed { + SInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() + } else { + UInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() + }, + [expr], + |_this, dest, [input]| { + let dest = dest.range.big_slots.start; + let src = input.range.small_slots.start; + let unused_bit_count = + interpreter::SmallUInt::BITS as u8 - Expr::ty(expr).bit_width() as u8; + if signed { + vec![Insn::SExtSmallToBig { + dest, + src, + unused_bit_count, + }] + } else { + vec![Insn::ZExtSmallToBig { + dest, + src, + unused_bit_count, + }] + } + }, + ) + } + }; + self.expanded_to_big.insert(expr, retval); + retval + } + fn simple_nary_small_or_big_expr( + &mut self, + dest_ty: CanonicalType, + inputs: [Expr; N], + make_insns_small: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + make_insns_big: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + make_insns_big_to_small: impl FnOnce( + StatePartIndex, + [StatePartIndex; N], + ) -> Vec, + ) -> CompiledValue { + self.simple_nary_expr(dest_ty, inputs, |this, dest, compiled_inputs| { + let all_inputs_only_small = compiled_inputs + .iter() + .all(|input| input.range.len().only_small().is_some()); + if all_inputs_only_small { + if dest.range.len().only_small().is_some() { + // all small + assert_eq!(dest.range.len().small_slots.value, 1); + return make_insns_small( + dest.range.small_slots.start, + compiled_inputs.map( + |CompiledValue { + layout, + range, + write: _, + }| { + assert_eq!(range.small_slots.len().value, 1); + range.small_slots.start + }, + ), + ); + } else { + // inputs small, dest big -- expand inputs to big + for (&input, compiled_input) in inputs.iter().zip(&mut *compiled_inputs) { + *compiled_input = this.expand_to_big(input); + } + } + } + let big_inputs = compiled_inputs.map( + |CompiledValue { + layout, + range: + TypeIndexRange { + small_slots, + big_slots, + }, + write: _, + }| { + assert_eq!(small_slots.len().value, 0); + assert_eq!(big_slots.len().value, 1); + big_slots.start + }, + ); + if dest.range.len().only_small().is_some() { + // inputs big, dest small + assert_eq!(dest.range.len().small_slots.value, 1); + return make_insns_big_to_small(dest.range.small_slots.start, big_inputs); + } + let TypeIndexRange { + small_slots, + big_slots, + } = dest.range; + assert_eq!(small_slots.len().value, 0); + assert_eq!(big_slots.len().value, 1); + make_insns_big(big_slots.start, big_inputs) + }) + } + fn compile_expr(&mut self, expr: Expr) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&expr) { + return retval; + } + let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { + ExprEnum::UIntLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr.to_bigint().try_into().expect("const too big"), + }] + }, + |dest, []| { + vec![Insn::ConstBig { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + |_, _| unreachable!(), + ) + .into(), + ExprEnum::SIntLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr.to_bigint().try_into().expect("const too big"), + }] + }, + |dest, []| { + vec![Insn::ConstBig { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BoolLiteral(expr) => self + .simple_nary_small_or_big_expr( + Bool.canonical(), + [], + |dest, []| { + vec![Insn::ConstSmall { + dest, + value: expr as SmallUInt, + }] + }, + |_, _| unreachable!(), + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BundleLiteral(expr) => todo!(), + ExprEnum::ArrayLiteral(expr) => todo!(), + ExprEnum::EnumLiteral(expr) => todo!(), + ExprEnum::Uninit(expr) => todo!(), + ExprEnum::NotU(expr) => todo!(), + ExprEnum::NotS(expr) => todo!(), + ExprEnum::NotB(expr) => todo!(), + ExprEnum::Neg(expr) => self + .simple_nary_small_or_big_expr( + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::NegSmall { dest, src }], + |dest, [src]| vec![Insn::NegBig { dest, src }], + |_, _| unreachable!(), + ) + .into(), + ExprEnum::BitAndU(expr) => todo!(), + ExprEnum::BitAndS(expr) => todo!(), + ExprEnum::BitAndB(expr) => todo!(), + ExprEnum::BitOrU(expr) => todo!(), + ExprEnum::BitOrS(expr) => todo!(), + ExprEnum::BitOrB(expr) => todo!(), + ExprEnum::BitXorU(expr) => todo!(), + ExprEnum::BitXorS(expr) => todo!(), + ExprEnum::BitXorB(expr) => todo!(), + ExprEnum::AddU(expr) => todo!(), + ExprEnum::AddS(expr) => todo!(), + ExprEnum::SubU(expr) => todo!(), + ExprEnum::SubS(expr) => todo!(), + ExprEnum::MulU(expr) => todo!(), + ExprEnum::MulS(expr) => todo!(), + ExprEnum::DivU(expr) => todo!(), + ExprEnum::DivS(expr) => todo!(), + ExprEnum::RemU(expr) => todo!(), + ExprEnum::RemS(expr) => todo!(), + ExprEnum::DynShlU(expr) => todo!(), + ExprEnum::DynShlS(expr) => todo!(), + ExprEnum::DynShrU(expr) => todo!(), + ExprEnum::DynShrS(expr) => todo!(), + ExprEnum::FixedShlU(expr) => todo!(), + ExprEnum::FixedShlS(expr) => todo!(), + ExprEnum::FixedShrU(expr) => todo!(), + ExprEnum::FixedShrS(expr) => todo!(), + ExprEnum::CmpLtB(expr) => todo!(), + ExprEnum::CmpLeB(expr) => todo!(), + ExprEnum::CmpGtB(expr) => todo!(), + ExprEnum::CmpGeB(expr) => todo!(), + ExprEnum::CmpEqB(expr) => todo!(), + ExprEnum::CmpNeB(expr) => todo!(), + ExprEnum::CmpLtU(expr) => todo!(), + ExprEnum::CmpLeU(expr) => todo!(), + ExprEnum::CmpGtU(expr) => todo!(), + ExprEnum::CmpGeU(expr) => todo!(), + ExprEnum::CmpEqU(expr) => todo!(), + ExprEnum::CmpNeU(expr) => todo!(), + ExprEnum::CmpLtS(expr) => todo!(), + ExprEnum::CmpLeS(expr) => todo!(), + ExprEnum::CmpGtS(expr) => todo!(), + ExprEnum::CmpGeS(expr) => todo!(), + ExprEnum::CmpEqS(expr) => todo!(), + ExprEnum::CmpNeS(expr) => todo!(), + ExprEnum::CastUIntToUInt(expr) => todo!(), + ExprEnum::CastUIntToSInt(expr) => todo!(), + ExprEnum::CastSIntToUInt(expr) => todo!(), + ExprEnum::CastSIntToSInt(expr) => todo!(), + ExprEnum::CastBoolToUInt(expr) => todo!(), + ExprEnum::CastBoolToSInt(expr) => todo!(), + ExprEnum::CastUIntToBool(expr) => todo!(), + ExprEnum::CastSIntToBool(expr) => todo!(), + ExprEnum::CastBoolToSyncReset(expr) => todo!(), + ExprEnum::CastUIntToSyncReset(expr) => todo!(), + ExprEnum::CastSIntToSyncReset(expr) => todo!(), + ExprEnum::CastBoolToAsyncReset(expr) => todo!(), + ExprEnum::CastUIntToAsyncReset(expr) => todo!(), + ExprEnum::CastSIntToAsyncReset(expr) => todo!(), + ExprEnum::CastSyncResetToBool(expr) => todo!(), + ExprEnum::CastSyncResetToUInt(expr) => todo!(), + ExprEnum::CastSyncResetToSInt(expr) => todo!(), + ExprEnum::CastSyncResetToReset(expr) => todo!(), + ExprEnum::CastAsyncResetToBool(expr) => todo!(), + ExprEnum::CastAsyncResetToUInt(expr) => todo!(), + ExprEnum::CastAsyncResetToSInt(expr) => todo!(), + ExprEnum::CastAsyncResetToReset(expr) => todo!(), + ExprEnum::CastResetToBool(expr) => todo!(), + ExprEnum::CastResetToUInt(expr) => todo!(), + ExprEnum::CastResetToSInt(expr) => todo!(), + ExprEnum::CastBoolToClock(expr) => todo!(), + ExprEnum::CastUIntToClock(expr) => todo!(), + ExprEnum::CastSIntToClock(expr) => todo!(), + ExprEnum::CastClockToBool(expr) => todo!(), + ExprEnum::CastClockToUInt(expr) => todo!(), + ExprEnum::CastClockToSInt(expr) => todo!(), + ExprEnum::FieldAccess(expr) => todo!(), + ExprEnum::VariantAccess(expr) => todo!(), + ExprEnum::ArrayIndex(expr) => todo!(), + ExprEnum::DynArrayIndex(expr) => todo!(), + ExprEnum::ReduceBitAndU(expr) => todo!(), + ExprEnum::ReduceBitAndS(expr) => todo!(), + ExprEnum::ReduceBitOrU(expr) => todo!(), + ExprEnum::ReduceBitOrS(expr) => todo!(), + ExprEnum::ReduceBitXorU(expr) => todo!(), + ExprEnum::ReduceBitXorS(expr) => todo!(), + ExprEnum::SliceUInt(expr) => todo!(), + ExprEnum::SliceSInt(expr) => todo!(), + ExprEnum::CastToBits(expr) => todo!(), + ExprEnum::CastBitsTo(expr) => todo!(), + ExprEnum::ModuleIO(expr) => todo!(), + ExprEnum::Instance(expr) => todo!(), + ExprEnum::Wire(expr) => todo!(), + ExprEnum::Reg(expr) => todo!(), + ExprEnum::MemPort(expr) => todo!(), + }; + self.compiled_exprs.insert(expr, retval); + retval + } + fn compile_connect( + &mut self, + parent_module: Interned, + cond_stack: Interned, + lhs: Expr, + mut rhs: Expr, + source_location: SourceLocation, + ) { + if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() { + match Expr::ty(lhs) { + CanonicalType::UInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::SInt(lhs_ty) => { + rhs = Expr::canonical(Expr::::from_canonical(rhs).cast_to(lhs_ty)); + } + CanonicalType::Bool(_) => unreachable!(), + CanonicalType::Array(lhs_ty) => { + let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.len(), rhs_ty.len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for index in 0..lhs_ty.len() { + self.compile_connect( + parent_module, + cond_stack, + lhs[index], + rhs[index], + source_location, + ); + } + return; + } + CanonicalType::Enum(lhs_ty) => { + let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + todo!("handle connect with different enum types"); + } + CanonicalType::Bundle(lhs_ty) => { + let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else { + unreachable!(); + }; + assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len()); + let lhs = Expr::::from_canonical(lhs); + let rhs = Expr::::from_canonical(rhs); + for ( + field_index, + ( + BundleField { + name, + flipped, + ty: _, + }, + rhs_field, + ), + ) in lhs_ty.fields().into_iter().zip(rhs_ty.fields()).enumerate() + { + assert_eq!(name, rhs_field.name); + assert_eq!(flipped, rhs_field.flipped); + let mut lhs_expr = + crate::expr::ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); + let mut rhs_expr = + crate::expr::ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + if flipped { + mem::swap(&mut lhs_expr, &mut rhs_expr); + } + self.compile_connect( + parent_module, + cond_stack, + lhs_expr, + rhs_expr, + source_location, + ); + } + return; + } + CanonicalType::AsyncReset(_) => unreachable!(), + CanonicalType::SyncReset(_) => unreachable!(), + CanonicalType::Reset(_) => unreachable!(), + CanonicalType::Clock(_) => unreachable!(), + } + } + let lhs = self.compile_expr(lhs); + let rhs = self.compile_expr(rhs); + let rhs = self.compiled_expr_to_value(rhs); + todo!(); } fn compile_block( &mut self, @@ -337,7 +926,7 @@ impl Compiler { lhs, rhs, source_location, - }) => todo!(), + }) => self.compile_connect(parent_module, cond_stack, lhs, rhs, source_location), Stmt::Formal(StmtFormal { kind, clk, @@ -351,6 +940,9 @@ impl Compiler { source_location, blocks: [then_block, else_block], }) => { + let cond = self + .compile_expr(Expr::canonical(cond)) + .map_ty(Bool::from_canonical); self.compile_block( parent_module, then_block, @@ -377,13 +969,16 @@ impl Compiler { source_location, blocks, }) => { + let enum_expr = self + .compile_expr(Expr::canonical(expr)) + .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, block, CondStack::MatchArm { parent: cond_stack, - enum_expr: expr, + enum_expr, variant_index, source_location, } @@ -392,19 +987,51 @@ impl Compiler { } } Stmt::Declaration(declaration) => match declaration { - StmtDeclaration::Wire(wire) => todo!(), - StmtDeclaration::Reg(reg) => todo!(), + StmtDeclaration::Wire(StmtWire { annotations, wire }) => { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: wire.into(), + }); + } + StmtDeclaration::Reg(StmtReg { annotations, reg }) => { + self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: reg.into(), + }); + todo!(); + } StmtDeclaration::Instance(StmtInstance { annotations, instance, }) => { - self.compile_module( + let CompiledValue { + layout: + CompiledTypeLayout { + ty: value_ty, + layout: ty_layout, + body: CompiledTypeLayoutBody::Bundle { fields }, + }, + range: value_range, + write: None, + } = self.compile_value(TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: instance.into(), + }) + else { + unreachable!(); + }; + let CompiledModule { module_io } = *self.compile_module( InstantiatedModule::Child { parent: parent_module, instance: instance.intern_sized(), } .intern_sized(), ); + for (module_io, CompiledBundleField { offset, ty }) in + module_io.into_iter().zip(fields) + { + todo!(); + } todo!() } }, @@ -452,7 +1079,7 @@ impl Compiler { #[derive(Debug)] struct CompiledModule { - module_io: Interned<[CompiledValue]>, + module_io: Interned<[CompiledValue]>, } #[derive(Debug)] diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index f327510..82d3c11 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -4,18 +4,22 @@ use crate::{ intern::{Intern, Interned}, source_location::SourceLocation, + util::get_many_mut, }; use num_bigint::BigInt; use std::{ + borrow::BorrowMut, convert::Infallible, fmt, - hash::Hash, + hash::{Hash, Hasher}, marker::PhantomData, ops::{Deref, DerefMut, Index, IndexMut}, }; +use vec_map::VecMap; pub(crate) type SmallUInt = u64; pub(crate) type SmallSInt = i64; +pub(crate) const MIN_BITS_FOR_NEEDING_BIG: usize = SmallUInt::BITS as usize + 1; macro_rules! impl_insns { ( @@ -39,7 +43,7 @@ macro_rules! impl_insns { $field_name:ident: $field_ty:ty, )* })? => $block:block - )+ + )* ) => { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] $vis enum $Insn { @@ -51,7 +55,7 @@ macro_rules! impl_insns { $field_name: $field_ty, )* })?, - )+ + )* } impl $State { @@ -83,7 +87,7 @@ macro_rules! impl_insns { })? => { $block } - )+ + )* }; }; $($cleanup)* @@ -179,35 +183,151 @@ pub(crate) trait StatePartKind: fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a>; } +pub(crate) trait StatePartsValue { + type Value; +} + +macro_rules! impl_state_parts_traits { + ( + struct $Struct:ident<$V:ident: $StatePartsValue:ident> { + $(#[flatten] + $flattened_field:ident: $flattened_field_ty:ty, + $(#[field_in_flattened] + $field_in_flattened:ident: $field_in_flattened_ty:ty,)* + )? + $($field:ident: $field_ty:ty,)* + } + ) => { + impl<$V: $StatePartsValue> Copy for $Struct<$V> + where + $($flattened_field_ty: Copy,)? + $($field_ty: Copy,)* + { + } + + impl<$V: $StatePartsValue> Clone for $Struct<$V> + where + $($flattened_field_ty: Clone,)? + $($field_ty: Clone,)* + { + fn clone(&self) -> Self { + Self { + $($flattened_field: self.$flattened_field.clone(),)? + $($field: self.$field.clone(),)* + } + } + } + + impl<$V: $StatePartsValue> fmt::Debug for $Struct<$V> + where + $($($field_in_flattened_ty: fmt::Debug,)*)? + $($field_ty: fmt::Debug,)* + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(stringify!($Struct)) + $($(.field(stringify!($field_in_flattened), &self.$flattened_field.$field_in_flattened))*)? + $(.field(stringify!($field), &self.$field))* + .finish() + } + } + + impl<$V: $StatePartsValue> PartialEq for $Struct<$V> + where + $($flattened_field_ty: PartialEq,)? + $($field_ty: PartialEq,)* + { + fn eq(&self, other: &Self) -> bool { + true $(&& self.$flattened_field == other.$flattened_field)? $(&& self.$field == other.$field)* + } + } + + impl<$V: $StatePartsValue> Eq for $Struct<$V> + where + $($flattened_field_ty: Eq,)? + $($field_ty: Eq,)* + { + } + + impl<$V: $StatePartsValue> Hash for $Struct<$V> + where + $($flattened_field_ty: Hash,)? + $($field_ty: Hash,)* + { + fn hash(&self, h: &mut H) { + $(self.$flattened_field.hash(h);)? + $(self.$field.hash(h);)* + } + } + + impl<$V: $StatePartsValue> Default for $Struct<$V> + where + $($flattened_field_ty: Default,)? + $($field_ty: Default,)* + { + fn default() -> Self { + Self { + $($flattened_field: Default::default(),)? + $($field: Default::default(),)* + } + } + } + }; +} + macro_rules! make_state_part_kinds { ( $( #[state, field = $state_field:ident] impl $StateStatePartKind:ident for $StateKind:ident $state_impl_body:tt - )+ + )* $( #[type, field = $type_field:ident] impl $TypeStatePartKind:ident for $TypeKind:ident $type_impl_body:tt - )+ + )* ) => { $( #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] pub(crate) struct $StateKind; impl $StateStatePartKind for $StateKind $state_impl_body - )+ + )* $( #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)] pub(crate) struct $TypeKind; impl $TypeStatePartKind for $TypeKind $type_impl_body - )+ + )* + + pub(crate) struct StateParts { + pub(crate) ty: TypeParts, + $(pub(crate) $state_field: V::Value<$StateKind>,)* + } + + impl_state_parts_traits! { + struct StateParts { + #[flatten] + ty: TypeParts, + $(#[field_in_flattened] + $type_field: V::Value<$TypeKind>,)* + $($state_field: V::Value<$StateKind>,)* + } + } + + pub(crate) struct TypeParts { + $(pub(crate) $type_field: V::Value<$TypeKind>,)* + } + + impl_state_parts_traits! { + struct TypeParts { + $($type_field: V::Value<$TypeKind>,)* + } + } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateLayout { pub(crate) ty: TypeLayout, - $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)+ + $(pub(crate) $state_field: StatePartLayout<$StateKind, BK>,)* } impl Copy for StateLayout {} @@ -216,16 +336,16 @@ macro_rules! make_state_part_kinds { pub(crate) fn len(self) -> StateLen { StateLen { ty: self.ty.len(), - $($state_field: self.$state_field.len(),)+ + $($state_field: self.$state_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())+ + self.ty.is_empty() $(&& self.$state_field.is_empty())* } pub(crate) fn empty() -> Self { Self { ty: TypeLayout::empty(), - $($state_field: StatePartLayout::empty(),)+ + $($state_field: StatePartLayout::empty(),)* } } } @@ -234,7 +354,7 @@ macro_rules! make_state_part_kinds { pub(crate) fn next_index(&self) -> StateIndex { StateIndex { ty: self.ty.next_index(), - $($state_field: self.$state_field.next_index(),)+ + $($state_field: self.$state_field.next_index(),)* } } pub(crate) fn allocate( @@ -243,7 +363,7 @@ macro_rules! make_state_part_kinds { ) -> StateIndexRange { StateIndexRange { ty: self.ty.allocate(&layout.ty), - $($state_field: self.$state_field.allocate(&layout.$state_field),)+ + $($state_field: self.$state_field.allocate(&layout.$state_field),)* } } } @@ -252,7 +372,7 @@ macro_rules! make_state_part_kinds { fn from(v: StateLayout) -> Self { Self { ty: v.ty.into(), - $($state_field: v.$state_field.into(),)+ + $($state_field: v.$state_field.into(),)* } } } @@ -260,42 +380,42 @@ macro_rules! make_state_part_kinds { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateIndexRange { pub(crate) ty: TypeIndexRange, - $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)+ + $(pub(crate) $state_field: StatePartIndexRange<$StateKind>,)* } impl StateIndexRange { pub(crate) fn start(self) -> StateIndex { StateIndex { ty: self.ty.start(), - $($state_field: self.$state_field.start(),)+ + $($state_field: self.$state_field.start(),)* } } pub(crate) fn len(self) -> StateLen { StateLen { ty: self.ty.len(), - $($state_field: self.$state_field.len(),)+ + $($state_field: self.$state_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { - self.ty.is_empty() $(&& self.$state_field.is_empty())+ + self.ty.is_empty() $(&& self.$state_field.is_empty())* } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateLen { pub(crate) ty: TypeLen, - $(pub(crate) $state_field: StatePartLen<$StateKind>,)+ + $(pub(crate) $state_field: StatePartLen<$StateKind>,)* } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct StateIndex { pub(crate) ty: TypeIndex, - $(pub(crate) $state_field: StatePartIndex<$StateKind>,)+ + $(pub(crate) $state_field: StatePartIndex<$StateKind>,)* } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeLayout { - $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)+ + $(pub(crate) $type_field: StatePartLayout<$TypeKind, BK>,)* } impl Copy for TypeLayout {} @@ -303,7 +423,7 @@ macro_rules! make_state_part_kinds { impl TypeLayout { pub(crate) fn len(self) -> TypeLen { TypeLen { - $($type_field: self.$type_field.len(),)+ + $($type_field: self.$type_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { @@ -311,7 +431,7 @@ macro_rules! make_state_part_kinds { } pub(crate) fn empty() -> Self { Self { - $($type_field: StatePartLayout::empty(),)+ + $($type_field: StatePartLayout::empty(),)* } } } @@ -319,7 +439,7 @@ macro_rules! make_state_part_kinds { impl TypeLayout { pub(crate) fn next_index(&self) -> TypeIndex { TypeIndex { - $($type_field: self.$type_field.next_index(),)+ + $($type_field: self.$type_field.next_index(),)* } } pub(crate) fn allocate( @@ -327,7 +447,7 @@ macro_rules! make_state_part_kinds { layout: &TypeLayout, ) -> TypeIndexRange { TypeIndexRange { - $($type_field: self.$type_field.allocate(&layout.$type_field),)+ + $($type_field: self.$type_field.allocate(&layout.$type_field),)* } } } @@ -335,14 +455,14 @@ macro_rules! make_state_part_kinds { impl From> for TypeLayout { fn from(v: TypeLayout) -> Self { Self { - $($type_field: v.$type_field.into(),)+ + $($type_field: v.$type_field.into(),)* } } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeIndexRange { - $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)+ + $(pub(crate) $type_field: StatePartIndexRange<$TypeKind>,)* } impl TypeIndexRange { @@ -351,17 +471,17 @@ macro_rules! make_state_part_kinds { $($type_field: StatePartIndexRange { start: start.$type_field, len: len.$type_field, - },)+ + },)* } } pub(crate) fn start(self) -> TypeIndex { TypeIndex { - $($type_field: self.$type_field.start(),)+ + $($type_field: self.$type_field.start(),)* } } pub(crate) fn len(self) -> TypeLen { TypeLen { - $($type_field: self.$type_field.len(),)+ + $($type_field: self.$type_field.len(),)* } } pub(crate) fn is_empty(self) -> bool { @@ -369,32 +489,32 @@ macro_rules! make_state_part_kinds { } pub(crate) fn slice(self, index: TypeIndexRange) -> Self { Self { - $($type_field: self.$type_field.slice(index.$type_field),)+ + $($type_field: self.$type_field.slice(index.$type_field),)* } } pub(crate) fn index_array(self, element_size: TypeLen, index: usize) -> Self { Self { - $($type_field: self.$type_field.index_array(element_size.$type_field, index),)+ + $($type_field: self.$type_field.index_array(element_size.$type_field, index),)* } } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeLen { - $(pub(crate) $type_field: StatePartLen<$TypeKind>,)+ + $(pub(crate) $type_field: StatePartLen<$TypeKind>,)* } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct TypeIndex { - $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)+ + $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* } #[derive(Debug)] pub(crate) struct State { pub(crate) insns: Interned>, pub(crate) pc: usize, - $(pub(crate) $state_field: StatePart<$StateKind>,)+ - $(pub(crate) $type_field: StatePart<$TypeKind>,)+ + $(pub(crate) $state_field: StatePart<$StateKind>,)* + $(pub(crate) $type_field: StatePart<$TypeKind>,)* } impl State { @@ -404,8 +524,8 @@ macro_rules! make_state_part_kinds { insns: &self.insns.insns, pc: self.pc, orig_pc: &mut self.pc, - $($state_field: self.$state_field.borrow(),)+ - $($type_field: self.$type_field.borrow(),)+ + $($state_field: self.$state_field.borrow(),)* + $($type_field: self.$type_field.borrow(),)* } } } @@ -416,8 +536,8 @@ macro_rules! make_state_part_kinds { pub(crate) insns: &'a [Insn], pub(crate) orig_pc: &'a mut usize, pub(crate) pc: usize, - $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)+ - $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)+ + $(pub(crate) $state_field: BorrowedStatePart<'a, $StateKind>,)* + $(pub(crate) $type_field: BorrowedStatePart<'a, $TypeKind>,)* } impl Drop for BorrowedState<'_> { @@ -429,7 +549,7 @@ macro_rules! make_state_part_kinds { } make_state_part_kinds! { - #[state, field = small_stack] + /*#[state, field = small_stack] impl StatePartKind for StatePartKindSmallStack { const NAME: &'static str = "SmallStack"; type DebugData = (); @@ -454,7 +574,7 @@ make_state_part_kinds! { fn borrow_state<'a>(state: &'a mut Self::State) -> Self::BorrowedState<'a> { state.borrow() } - } + }*/ #[type, field = small_slots] impl StatePartKind for StatePartKindSmallSlots { const NAME: &'static str = "SmallSlots"; @@ -483,6 +603,23 @@ make_state_part_kinds! { } } +impl TypeLen { + pub(crate) fn only_small(self) -> Option> { + let Self { + small_slots, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } = self + else { + return None; + }; + Some(small_slots) + } +} + #[derive(Debug, Clone)] pub(crate) struct Stack { storage: Box<[T]>, @@ -544,6 +681,11 @@ pub(crate) struct SlotDebugData { } impl SlotDebugData { + pub(crate) fn empty() -> Self { + Self { + name: Interned::default(), + } + } pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { let mut name = String::with_capacity(self.name.len() + prefix.len()); name.push_str(prefix); @@ -582,6 +724,12 @@ pub(crate) struct StatePartIndex { pub(crate) _phantom: PhantomData, } +impl StatePartIndex { + pub(crate) fn as_usize(self) -> usize { + self.value.try_into().expect("index too big") + } +} + impl fmt::Debug for StatePartIndex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "StatePartIndex<{}>({})", K::NAME, self.value) @@ -716,6 +864,12 @@ impl StatePartIndexRange { pub(crate) fn is_empty(self) -> bool { self.len.value == 0 } + pub(crate) fn iter(self) -> impl Iterator> { + (self.start.value..self.end().value).map(|value| StatePartIndex { + value, + _phantom: PhantomData, + }) + } pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { assert!(index.end().value <= self.len.value, "index out of range"); Self { @@ -787,6 +941,25 @@ pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> { pub(crate) value: K::BorrowedState<'a>, } +impl< + 'a, + K: StatePartKind< + BorrowedState<'a>: DerefMut + BorrowMut<[T]>>, + >, + T, + > BorrowedStatePart<'a, K> +{ + pub(crate) fn get_many_mut( + &mut self, + indexes: [StatePartIndex; N], + ) -> [&mut T; N] { + get_many_mut( + (*self.value).borrow_mut(), + indexes.map(|v| v.value as usize), + ) + } +} + impl<'a, K: StatePartKind: Deref>>, T> Index> for BorrowedStatePart<'a, K> { @@ -818,6 +991,139 @@ impl<'a, K: StatePartKind = BorrowedStack<'a, T>>, T: 'a> } } +#[derive(PartialEq, Eq, Hash, Clone)] +pub(crate) struct StatePartIndexMap { + pub(crate) map: VecMap, + _phantom: PhantomData, +} + +impl fmt::Debug for StatePartIndexMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl StatePartIndexMap { + pub(crate) fn new() -> Self { + Self { + map: VecMap::new(), + _phantom: PhantomData, + } + } + pub(crate) fn get(&self, key: StatePartIndex) -> Option<&V> { + self.map.get(key.as_usize()) + } + pub(crate) fn get_mut(&mut self, key: StatePartIndex) -> Option<&mut V> { + self.map.get_mut(key.as_usize()) + } + pub(crate) fn keys(&self) -> impl Iterator> + '_ { + self.map.keys().map(|k| StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }) + } + pub(crate) fn values(&self) -> vec_map::Values<'_, V> { + self.map.values() + } + pub(crate) fn values_mut(&mut self) -> vec_map::ValuesMut<'_, V> { + self.map.values_mut() + } + pub(crate) fn iter(&self) -> impl Iterator, &V)> + '_ { + self.map.iter().map(|(k, v)| { + ( + StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }, + v, + ) + }) + } + pub(crate) fn iter_mut(&mut self) -> impl Iterator, &mut V)> + '_ { + self.map.iter_mut().map(|(k, v)| { + ( + StatePartIndex { + value: k as u32, + _phantom: PhantomData, + }, + v, + ) + }) + } + pub(crate) fn len(&self) -> usize { + self.map.len() + } + pub(crate) fn is_empty(&self) -> bool { + self.map.is_empty() + } + pub(crate) fn insert(&mut self, key: StatePartIndex, value: V) -> Option { + self.map.insert(key.as_usize(), value) + } + pub(crate) fn remove(&mut self, key: StatePartIndex) -> Option { + self.map.remove(key.as_usize()) + } + pub(crate) fn entry(&mut self, key: StatePartIndex) -> StatePartIndexMapEntry<'_, K, V> { + match self.map.entry(key.as_usize()) { + vec_map::Entry::Vacant(v) => { + StatePartIndexMapEntry::Vacant(StatePartIndexMapVacantEntry(v, PhantomData)) + } + vec_map::Entry::Occupied(v) => { + StatePartIndexMapEntry::Occupied(StatePartIndexMapOccupiedEntry(v, PhantomData)) + } + } + } +} + +impl Default for StatePartIndexMap { + fn default() -> Self { + Self::new() + } +} + +impl Index> for StatePartIndexMap { + type Output = V; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.map[index.as_usize()] + } +} + +impl IndexMut> for StatePartIndexMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.map[index.as_usize()] + } +} + +pub(crate) struct StatePartIndexMapVacantEntry<'a, K: StatePartKind, V>( + vec_map::VacantEntry<'a, V>, + PhantomData, +); + +pub(crate) struct StatePartIndexMapOccupiedEntry<'a, K: StatePartKind, V>( + vec_map::OccupiedEntry<'a, V>, + PhantomData, +); + +pub(crate) enum StatePartIndexMapEntry<'a, K: StatePartKind, V> { + Vacant(StatePartIndexMapVacantEntry<'a, K, V>), + Occupied(StatePartIndexMapOccupiedEntry<'a, K, V>), +} + +impl<'a, K: StatePartKind, V> StatePartIndexMapEntry<'a, K, V> { + pub(crate) fn or_insert(self, default: V) -> &'a mut V { + match self { + StatePartIndexMapEntry::Vacant(v) => v.0.insert(default), + StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(), + } + } + pub(crate) fn or_insert_with(self, f: impl FnOnce() -> V) -> &'a mut V { + match self { + StatePartIndexMapEntry::Vacant(v) => v.0.insert(f()), + StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(), + } + } +} + impl Insns { pub(crate) fn new() -> Self { Self { @@ -867,14 +1173,10 @@ impl State { let Self { insns: _, pc, - small_stack, - big_stack, small_slots: _, big_slots: _, } = self; *pc = entry_pc; - small_stack.value.clear(); - big_stack.value.clear(); } } @@ -887,171 +1189,238 @@ impl_insns! { main_loop!(); cleanup! {} } - ReadSmall { - index: StatePartIndex, + CopySmall { + dest: StatePartIndex, + src: StatePartIndex, } => { - state.small_stack.push(state.small_slots[index]); + state.small_slots[dest] = state.small_slots[src]; + next!(); + } + CopyBig { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); next!(); } SExtSmall { + dest: StatePartIndex, + src: StatePartIndex, /// number of bits in a [`SmallSInt`] that aren't used unused_bit_count: u8, } => { - let mut value = state.small_stack.pop() as SmallSInt; + let mut value = state.small_slots[src] as SmallSInt; value <<= unused_bit_count; value >>= unused_bit_count; - state.small_stack.push(value as SmallUInt); + state.small_slots[dest] = value as SmallUInt; next!(); } ZExtSmall { + dest: StatePartIndex, + src: StatePartIndex, /// number of bits in a [`SmallUInt`] that aren't used unused_bit_count: u8, } => { - let mut value = state.small_stack.pop(); + let mut value = state.small_slots[src]; value <<= unused_bit_count; value >>= unused_bit_count; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - AndSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + SExtSmallToBig { + dest: StatePartIndex, + src: StatePartIndex, + /// number of bits in a [`SmallSInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = state.small_slots[src] as SmallSInt; + value <<= unused_bit_count; + value >>= unused_bit_count; + state.big_slots[dest] = value.into(); + next!(); + } + ZExtSmallToBig { + dest: StatePartIndex, + src: StatePartIndex, + /// number of bits in a [`SmallUInt`] that aren't used + unused_bit_count: u8, + } => { + let mut value = state.small_slots[src]; + value <<= unused_bit_count; + value >>= unused_bit_count; + state.big_slots[dest] = value.into(); + next!(); + } + AndSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs & rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - OrSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + OrSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs | rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - XorSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + XorSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs ^ rhs; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - NotSmall => { - let value = state.small_stack.pop(); - state.small_stack.push(!value); + NotSmall { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = state.small_slots[src]; + state.small_slots[dest] = !value; next!(); } - CmpEqSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpEqSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs == rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpNeSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpNeSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs != rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLTSmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpLTSmallUInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs < rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLESmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + CmpLESmallUInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = (lhs <= rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpGTSmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); - let value = (lhs > rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpGESmallUInt => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); - let value = (lhs >= rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpLTSmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; + CmpLTSmallSInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs] as SmallSInt; + let rhs = state.small_slots[rhs] as SmallSInt; let value = (lhs < rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpLESmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; + CmpLESmallSInt { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs] as SmallSInt; + let rhs = state.small_slots[rhs] as SmallSInt; let value = (lhs <= rhs) as SmallUInt; - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - CmpGTSmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; - let value = (lhs > rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - CmpGESmallSInt => { - let rhs = state.small_stack.pop() as SmallSInt; - let lhs = state.small_stack.pop() as SmallSInt; - let value = (lhs >= rhs) as SmallUInt; - state.small_stack.push(value); - next!(); - } - AddSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + AddSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_add(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - SubSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + SubSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_sub(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - NegSmall => { - let value = state.small_stack.pop(); + NegSmall { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = state.small_slots[src]; let value = value.wrapping_neg(); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - MulSmall => { - let rhs = state.small_stack.pop(); - let lhs = state.small_stack.pop(); + NegBig { + dest: StatePartIndex, + src: StatePartIndex, + } => { + let value = -&state.big_slots[src]; + state.big_slots[dest] = value; + next!(); + } + MulSmall { + dest: StatePartIndex, + lhs: StatePartIndex, + rhs: StatePartIndex, + } => { + let lhs = state.small_slots[lhs]; + let rhs = state.small_slots[rhs]; let value = lhs.wrapping_mul(rhs); - state.small_stack.push(value); + state.small_slots[dest] = value; next!(); } - ConstU32Small { - value: u32, + ConstSmall { + dest: StatePartIndex, + value: SmallUInt, } => { - state.small_stack.push(value as SmallUInt); + state.small_slots[dest] = value; next!(); } - ConstS32Small { - value: i32, + ConstBig { + dest: StatePartIndex, + value: Interned, } => { - state.small_stack.push(value as SmallUInt); - next!(); - } - WriteSmall { - index: StatePartIndex, - } => { - state.small_slots[index] = state.small_stack.pop(); + state.big_slots[dest].clone_from(&value); next!(); } Return => { diff --git a/crates/fayalite/src/util.rs b/crates/fayalite/src/util.rs index f66654f..131c54c 100644 --- a/crates/fayalite/src/util.rs +++ b/crates/fayalite/src/util.rs @@ -24,7 +24,8 @@ pub use scoped_ref::ScopedRef; #[doc(inline)] pub use misc::{ - interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, + get_many_mut, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, + DebugAsRawString, MakeMutSlice, }; pub mod job_server; diff --git a/crates/fayalite/src/util/misc.rs b/crates/fayalite/src/util/misc.rs index 884458f..85e376a 100644 --- a/crates/fayalite/src/util/misc.rs +++ b/crates/fayalite/src/util/misc.rs @@ -155,3 +155,19 @@ impl fmt::UpperHex for BitSliceWriteWithBase<'_> { self.fmt_with_base::<4, true>(f) } } + +#[inline] +#[track_caller] +pub fn get_many_mut(slice: &mut [T], indexes: [usize; N]) -> [&mut T; N] { + for i in 0..N { + for j in 0..i { + assert!(indexes[i] != indexes[j], "duplicate index"); + } + assert!(indexes[i] < slice.len(), "index out of bounds"); + } + // Safety: checked that no indexes are duplicates and no indexes are out of bounds + unsafe { + let base = slice.as_mut_ptr(); // convert to a raw pointer before loop to avoid aliasing with &mut [T] + std::array::from_fn(|i| &mut *base.add(indexes[i])) + } +} From f338f37d3ef6cf1ec5a9c2d4db03c50863a9dd93 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 12 Nov 2024 01:08:32 -0800 Subject: [PATCH 084/190] working on simulator --- Cargo.lock | 5 +- Cargo.toml | 2 +- crates/fayalite/src/sim.rs | 1230 +++++++++++++++++------- crates/fayalite/src/sim/interpreter.rs | 806 +++++++++++----- 4 files changed, 1454 insertions(+), 589 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fca6fc..c280158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,11 +473,10 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] diff --git a/Cargo.toml b/Cargo.toml index 44f5c5a..add4bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ eyre = "0.6.12" hashbrown = "0.14.3" indexmap = { version = "2.2.6", features = ["serde"] } jobslot = "0.2.19" -num-bigint = "0.4.4" +num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" prettyplease = "0.2.20" diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 9b40458..dbcb88a 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -18,38 +18,39 @@ use crate::{ }, prelude::*, sim::interpreter::{ - Insn, Insns, InsnsBuilding, InsnsBuildingDone, SlotDebugData, SmallUInt, StatePartIndex, - StatePartIndexMap, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, - StatePartLayout, StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, - TypeLen, TypeParts, MIN_BITS_FOR_NEEDING_BIG, + Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, + SlotDebugData, SmallUInt, StatePartIndex, StatePartIndexMap, StatePartIndexRange, + StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, StatePartLayout, + StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, + MIN_BITS_FOR_NEEDING_BIG, }, }; use hashbrown::HashMap; -use std::{fmt, marker::PhantomData, mem}; +use num_bigint::BigInt; +use std::{collections::BTreeSet, fmt, marker::PhantomData, mem}; mod interpreter; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -enum CondStack { - Always, +enum CondBody { IfTrue { - parent: Interned, cond: CompiledExpr, - source_location: SourceLocation, }, IfFalse { - parent: Interned, cond: CompiledExpr, - source_location: SourceLocation, }, MatchArm { - parent: Interned, enum_expr: CompiledExpr, variant_index: usize, - source_location: SourceLocation, }, } +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +struct Cond { + body: CondBody, + source_location: SourceLocation, +} + #[derive(PartialEq, Eq, Hash, Clone, Copy)] enum InstantiatedModule { Base(Interned>), @@ -147,12 +148,9 @@ impl CompiledTypeLayout { let mut layout = TypeLayout::empty(); let debug_data = SlotDebugData { name: Interned::default(), + ty: *input, }; - if input.bit_width() >= interpreter::MIN_BITS_FOR_NEEDING_BIG { - layout.big_slots = StatePartLayout::scalar(debug_data); - } else { - layout.small_slots = StatePartLayout::scalar(debug_data); - }; + layout.big_slots = StatePartLayout::scalar(debug_data); CompiledTypeLayout { ty: *input, layout: layout.into(), @@ -239,13 +237,94 @@ impl CompiledValue { } } +impl CompiledValue { + fn add_discriminant_to_set(self, offset: TypeIndex, inputs: &mut SlotSet) { + inputs.extend([self.range.offset(offset)]); + } +} + #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExprDynIndex { index_slot: StatePartIndex, - len: TypeLen, + len: usize, stride: TypeLen, } +impl CompiledExprDynIndex { + fn offsets(self) -> impl Iterator { + (0..self.len.try_into().unwrap()).map(move |index| TypeIndex { + small_slots: StatePartIndex { + value: self + .stride + .small_slots + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: self + .stride + .big_slots + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + }, + }) + } + fn for_each_offset(dyn_indexes: &[CompiledExprDynIndex], mut f: impl FnMut(TypeIndex)) { + fn helper( + dyn_indexes: &[CompiledExprDynIndex], + offset: TypeIndex, + f: &mut impl FnMut(TypeIndex), + ) { + if let [next, rest @ ..] = dyn_indexes { + for next_offset in next.offsets() { + helper( + rest, + TypeIndex { + small_slots: StatePartIndex { + value: next_offset + .small_slots + .value + .checked_add(offset.small_slots.value) + .expect("array too big"), + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: next_offset + .big_slots + .value + .checked_add(offset.big_slots.value) + .expect("array too big"), + _phantom: PhantomData, + }, + }, + f, + ); + } + } else { + f(offset); + } + } + helper( + dyn_indexes, + TypeIndex { + small_slots: StatePartIndex { + value: 0, + _phantom: PhantomData, + }, + big_slots: StatePartIndex { + value: 0, + _phantom: PhantomData, + }, + }, + &mut f, + ); + } +} + #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExpr { static_part: CompiledValue, @@ -281,6 +360,36 @@ impl CompiledExpr { dyn_indexes, } } + fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + inputs.extend([static_part.range.offset(offset)]); + }); + } + fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part: _, + dyn_indexes, + } = self; + self.add_target_without_indexes_to_set(inputs); + inputs.extend(dyn_indexes); + } +} + +impl CompiledExpr { + fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { + let Self { + static_part, + dyn_indexes, + } = self; + CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + static_part.add_discriminant_to_set(offset, inputs); + }); + inputs.extend(dyn_indexes); + } } #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] @@ -297,12 +406,74 @@ impl SlotSet { } impl StatePartsValue for SlotSet { - type Value = Vec>; + type Value = BTreeSet>; +} + +impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.0.small_slots.extend(iter); + } +} + +impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.0.big_slots.extend(iter); + } +} + +impl Extend> for SlotSet +where + Self: Extend>, +{ + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().flat_map(|v| v.iter())); + } +} + +impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |TypeIndexRange { + small_slots, + big_slots, + }| { + self.extend(small_slots.iter()); + self.extend(big_slots.iter()); + }, + ) + } +} + +impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.index_slot)); + } +} + +impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each(|cond_body| match cond_body { + CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { + cond.add_target_and_indexes_to_set(self); + } + CondBody::MatchArm { + enum_expr, + variant_index, + } => enum_expr.add_discriminant_and_indexes_to_set(self), + }) + } +} + +impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.body)) + } } #[derive(Debug)] struct Assignment { inputs: SlotSet, + conditions: Interned<[Cond]>, insns: Vec, } @@ -327,9 +498,9 @@ pub struct Compiler { base_module: Interned>, modules: HashMap, compiled_values: HashMap>, - compiled_exprs: HashMap, CompiledExpr>, - compiled_exprs_to_values: HashMap, CompiledValue>, - expanded_to_big: HashMap, CompiledValue>, + compiled_exprs: HashMap<(Interned<[Cond]>, Expr), CompiledExpr>, + compiled_exprs_to_values: + HashMap<(Interned<[Cond]>, CompiledExpr), CompiledValue>, slots_assignments: SlotAssignments, } @@ -342,7 +513,6 @@ impl Compiler { compiled_values: HashMap::new(), compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), - expanded_to_big: HashMap::new(), slots_assignments: SlotAssignments::default(), } } @@ -427,9 +597,10 @@ impl Compiler { } fn compiled_expr_to_value( &mut self, + conditions: Interned<[Cond]>, expr: CompiledExpr, ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { + if let Some(&retval) = self.compiled_exprs_to_values.get(&(conditions, expr)) { return retval; } let CompiledExpr { @@ -444,359 +615,678 @@ impl Compiler { { todo!(); } - self.compiled_exprs_to_values.insert(expr, retval); + self.compiled_exprs_to_values + .insert((conditions, expr), retval); retval } - fn simple_nary_expr( + fn add_assignment( &mut self, + conditions: Interned<[Cond]>, + insns: impl IntoIterator, + ) { + let insns = Vec::from_iter(insns); + let assignment_index = self.slots_assignments.assignments.len(); + let mut inputs = SlotSet::default(); + for insn in &insns { + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&small_slot)) => { + inputs.0.small_slots.insert(small_slot); + } + (InsnFieldKind::Input, InsnFieldType::BigSlot(&big_slot)) => { + inputs.0.big_slots.insert(big_slot); + } + (InsnFieldKind::Output, InsnFieldType::SmallSlot(&small_slot)) => self + .slots_assignments + .parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(small_slot) + .or_insert_with(Vec::new) + .push(assignment_index), + (InsnFieldKind::Output, InsnFieldType::BigSlot(&big_slot)) => self + .slots_assignments + .parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(big_slot) + .or_insert_with(Vec::new) + .push(assignment_index), + ( + _, + InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} + } + } + } + self.slots_assignments.assignments.push(Assignment { + inputs, + conditions, + insns, + }); + } + fn simple_nary_big_expr( + &mut self, + conditions: Interned<[Cond]>, dest_ty: CanonicalType, inputs: [Expr; N], make_insns: impl FnOnce( - &mut Self, - CompiledValue, - &mut [CompiledValue; N], + StatePartIndex, + [StatePartIndex; N], ) -> Vec, ) -> CompiledValue { - let mut inputs = inputs.map(|input| { - let input = self.compile_expr(input); - self.compiled_expr_to_value(input) + let inputs = inputs.map(|input| { + let input = self.compile_expr(conditions, input); + let input = self.compiled_expr_to_value(conditions, input); + let TypeIndexRange { + small_slots, + big_slots, + } = input.range; + assert_eq!(small_slots.len.value, 0); + assert_eq!(big_slots.len.value, 1); + big_slots.start }); let layout = CompiledTypeLayout::get(dest_ty); let range = self.insns.allocate_variable(&layout.layout); + let TypeIndexRange { + small_slots, + big_slots, + } = range; + assert_eq!(small_slots.len.value, 0); + assert_eq!(big_slots.len.value, 1); let retval = CompiledValue { layout, range, write: None, }; - let insns = make_insns(self, retval, &mut inputs); - let mut inputs_set = SlotSet::default(); - for input in inputs { - let TypeIndexRange { - small_slots, - big_slots, - } = input.range; - inputs_set.0.small_slots.extend(small_slots.iter()); - inputs_set.0.big_slots.extend(big_slots.iter()); - } - let assignment_index = self.slots_assignments.assignments.len(); - self.slots_assignments.assignments.push(Assignment { - inputs: inputs_set, - insns, - }); - let TypeIndexRange { - small_slots, - big_slots, - } = range; - for i in small_slots.iter() { - self.slots_assignments - .parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(i) - .or_insert_with(Vec::new) - .push(assignment_index); - } - for i in big_slots.iter() { - self.slots_assignments - .parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(i) - .or_insert_with(Vec::new) - .push(assignment_index); - } + self.add_assignment(conditions, make_insns(big_slots.start, inputs)); retval } - fn expand_to_big(&mut self, expr: Expr) -> CompiledValue { - if let Some(&retval) = self.expanded_to_big.get(&expr) { - return retval; - } - let input = self.compile_expr(expr); - let input = self.compiled_expr_to_value(input); - let retval = match input.range.len() { - TypeLen { - small_slots: - StatePartLen { - value: 0, - _phantom: _, - }, - big_slots: _, - } => input, - len => { - assert_eq!( - Some(StatePartLen { - value: 1, - _phantom: PhantomData, - }), - len.only_small() - ); - let signed = match Expr::ty(expr) { - CanonicalType::UInt(_) => false, - CanonicalType::SInt(_) => true, - CanonicalType::Bool(_) => false, - CanonicalType::Enum(_) => false, - CanonicalType::Array(_) => unreachable!(), - CanonicalType::Bundle(_) => unreachable!(), - CanonicalType::AsyncReset(_) => false, - CanonicalType::SyncReset(_) => false, - CanonicalType::Reset(_) => false, - CanonicalType::Clock(_) => false, - }; - self.simple_nary_expr( - if signed { - SInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() - } else { - UInt::new_dyn(MIN_BITS_FOR_NEEDING_BIG).canonical() - }, - [expr], - |_this, dest, [input]| { - let dest = dest.range.big_slots.start; - let src = input.range.small_slots.start; - let unused_bit_count = - interpreter::SmallUInt::BITS as u8 - Expr::ty(expr).bit_width() as u8; - if signed { - vec![Insn::SExtSmallToBig { - dest, - src, - unused_bit_count, - }] - } else { - vec![Insn::ZExtSmallToBig { - dest, - src, - unused_bit_count, - }] - } - }, - ) - } - }; - self.expanded_to_big.insert(expr, retval); - retval - } - fn simple_nary_small_or_big_expr( + fn compile_expr( &mut self, - dest_ty: CanonicalType, - inputs: [Expr; N], - make_insns_small: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - make_insns_big: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - make_insns_big_to_small: impl FnOnce( - StatePartIndex, - [StatePartIndex; N], - ) -> Vec, - ) -> CompiledValue { - self.simple_nary_expr(dest_ty, inputs, |this, dest, compiled_inputs| { - let all_inputs_only_small = compiled_inputs - .iter() - .all(|input| input.range.len().only_small().is_some()); - if all_inputs_only_small { - if dest.range.len().only_small().is_some() { - // all small - assert_eq!(dest.range.len().small_slots.value, 1); - return make_insns_small( - dest.range.small_slots.start, - compiled_inputs.map( - |CompiledValue { - layout, - range, - write: _, - }| { - assert_eq!(range.small_slots.len().value, 1); - range.small_slots.start - }, - ), - ); - } else { - // inputs small, dest big -- expand inputs to big - for (&input, compiled_input) in inputs.iter().zip(&mut *compiled_inputs) { - *compiled_input = this.expand_to_big(input); - } - } - } - let big_inputs = compiled_inputs.map( - |CompiledValue { - layout, - range: - TypeIndexRange { - small_slots, - big_slots, - }, - write: _, - }| { - assert_eq!(small_slots.len().value, 0); - assert_eq!(big_slots.len().value, 1); - big_slots.start - }, - ); - if dest.range.len().only_small().is_some() { - // inputs big, dest small - assert_eq!(dest.range.len().small_slots.value, 1); - return make_insns_big_to_small(dest.range.small_slots.start, big_inputs); - } - let TypeIndexRange { - small_slots, - big_slots, - } = dest.range; - assert_eq!(small_slots.len().value, 0); - assert_eq!(big_slots.len().value, 1); - make_insns_big(big_slots.start, big_inputs) - }) - } - fn compile_expr(&mut self, expr: Expr) -> CompiledExpr { - if let Some(&retval) = self.compiled_exprs.get(&expr) { + conditions: Interned<[Cond]>, + expr: Expr, + ) -> CompiledExpr { + if let Some(&retval) = self.compiled_exprs.get(&(conditions, expr)) { return retval; } + let mut cast_bit = |arg: Expr| { + let src_signed = match Expr::ty(arg) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + let dest_signed = match Expr::ty(expr) { + CanonicalType::UInt(_) => false, + CanonicalType::SInt(_) => true, + CanonicalType::Bool(_) => false, + CanonicalType::Array(_) => unreachable!(), + CanonicalType::Enum(_) => unreachable!(), + CanonicalType::Bundle(_) => unreachable!(), + CanonicalType::AsyncReset(_) => false, + CanonicalType::SyncReset(_) => false, + CanonicalType::Reset(_) => false, + CanonicalType::Clock(_) => false, + }; + self.simple_nary_big_expr(conditions, Expr::ty(expr), [arg], |dest, [src]| { + match (src_signed, dest_signed) { + (false, false) | (true, true) => { + vec![Insn::Copy { dest, src }] + } + (false, true) => vec![Insn::CastToSInt { + dest, + src, + dest_width: 1, + }], + (true, false) => vec![Insn::CastToUInt { + dest, + src, + dest_width: 1, + }], + } + }) + .into() + }; let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { ExprEnum::UIntLiteral(expr) => self - .simple_nary_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr.to_bigint().try_into().expect("const too big"), - }] - }, - |dest, []| { - vec![Insn::ConstBig { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }) .into(), ExprEnum::SIntLiteral(expr) => self - .simple_nary_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr.to_bigint().try_into().expect("const too big"), - }] - }, - |dest, []| { - vec![Insn::ConstBig { - dest, - value: expr.to_bigint().intern_sized(), - }] - }, - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }) .into(), ExprEnum::BoolLiteral(expr) => self - .simple_nary_small_or_big_expr( - Bool.canonical(), - [], - |dest, []| { - vec![Insn::ConstSmall { - dest, - value: expr as SmallUInt, - }] - }, - |_, _| unreachable!(), - |_, _| unreachable!(), - ) + .simple_nary_big_expr(conditions, Bool.canonical(), [], |dest, []| { + vec![Insn::Const { + dest, + value: BigInt::from(expr).intern_sized(), + }] + }) .into(), ExprEnum::BundleLiteral(expr) => todo!(), ExprEnum::ArrayLiteral(expr) => todo!(), ExprEnum::EnumLiteral(expr) => todo!(), ExprEnum::Uninit(expr) => todo!(), - ExprEnum::NotU(expr) => todo!(), - ExprEnum::NotS(expr) => todo!(), - ExprEnum::NotB(expr) => todo!(), - ExprEnum::Neg(expr) => self - .simple_nary_small_or_big_expr( - expr.ty().canonical(), + ExprEnum::NotU(expr) => self + .simple_nary_big_expr( + conditions, + Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], - |dest, [src]| vec![Insn::NegSmall { dest, src }], - |dest, [src]| vec![Insn::NegBig { dest, src }], - |_, _| unreachable!(), + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::NotS(expr) => self + .simple_nary_big_expr( + conditions, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::NotS { dest, src }], + ) + .into(), + ExprEnum::NotB(expr) => self + .simple_nary_big_expr( + conditions, + Expr::ty(expr.arg()).canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::NotU { + dest, + src, + width: 1, + }] + }, + ) + .into(), + ExprEnum::Neg(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| vec![Insn::Neg { dest, src }], + ) + .into(), + ExprEnum::BitAndU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitAndB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitOrB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::BitXorB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::AddS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], + ) + .into(), + ExprEnum::SubU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| { + vec![Insn::SubU { + dest, + lhs, + rhs, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), + ExprEnum::SubS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::MulS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DivS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::RemS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShlS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::DynShrS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], + ) + .into(), + ExprEnum::FixedShlU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShlS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shl { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrU(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::FixedShrS(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.lhs())], + |dest, [lhs]| { + vec![Insn::Shr { + dest, + lhs, + rhs: expr.rhs(), + }] + }, + ) + .into(), + ExprEnum::CmpLtB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeB(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeU(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLtS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpLeS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGtS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpGeS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + // swap both comparison direction and lhs/rhs + [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], + |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpEqS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CmpNeS(expr) => self + .simple_nary_big_expr( + conditions, + Bool.canonical(), + [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], + |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], + ) + .into(), + ExprEnum::CastUIntToUInt(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, ) .into(), - ExprEnum::BitAndU(expr) => todo!(), - ExprEnum::BitAndS(expr) => todo!(), - ExprEnum::BitAndB(expr) => todo!(), - ExprEnum::BitOrU(expr) => todo!(), - ExprEnum::BitOrS(expr) => todo!(), - ExprEnum::BitOrB(expr) => todo!(), - ExprEnum::BitXorU(expr) => todo!(), - ExprEnum::BitXorS(expr) => todo!(), - ExprEnum::BitXorB(expr) => todo!(), - ExprEnum::AddU(expr) => todo!(), - ExprEnum::AddS(expr) => todo!(), - ExprEnum::SubU(expr) => todo!(), - ExprEnum::SubS(expr) => todo!(), - ExprEnum::MulU(expr) => todo!(), - ExprEnum::MulS(expr) => todo!(), - ExprEnum::DivU(expr) => todo!(), - ExprEnum::DivS(expr) => todo!(), - ExprEnum::RemU(expr) => todo!(), - ExprEnum::RemS(expr) => todo!(), - ExprEnum::DynShlU(expr) => todo!(), - ExprEnum::DynShlS(expr) => todo!(), - ExprEnum::DynShrU(expr) => todo!(), - ExprEnum::DynShrS(expr) => todo!(), - ExprEnum::FixedShlU(expr) => todo!(), - ExprEnum::FixedShlS(expr) => todo!(), - ExprEnum::FixedShrU(expr) => todo!(), - ExprEnum::FixedShrS(expr) => todo!(), - ExprEnum::CmpLtB(expr) => todo!(), - ExprEnum::CmpLeB(expr) => todo!(), - ExprEnum::CmpGtB(expr) => todo!(), - ExprEnum::CmpGeB(expr) => todo!(), - ExprEnum::CmpEqB(expr) => todo!(), - ExprEnum::CmpNeB(expr) => todo!(), - ExprEnum::CmpLtU(expr) => todo!(), - ExprEnum::CmpLeU(expr) => todo!(), - ExprEnum::CmpGtU(expr) => todo!(), - ExprEnum::CmpGeU(expr) => todo!(), - ExprEnum::CmpEqU(expr) => todo!(), - ExprEnum::CmpNeU(expr) => todo!(), - ExprEnum::CmpLtS(expr) => todo!(), - ExprEnum::CmpLeS(expr) => todo!(), - ExprEnum::CmpGtS(expr) => todo!(), - ExprEnum::CmpGeS(expr) => todo!(), - ExprEnum::CmpEqS(expr) => todo!(), - ExprEnum::CmpNeS(expr) => todo!(), - ExprEnum::CastUIntToUInt(expr) => todo!(), ExprEnum::CastUIntToSInt(expr) => todo!(), - ExprEnum::CastSIntToUInt(expr) => todo!(), + ExprEnum::CastSIntToUInt(expr) => self + .simple_nary_big_expr( + conditions, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToUInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), ExprEnum::CastSIntToSInt(expr) => todo!(), - ExprEnum::CastBoolToUInt(expr) => todo!(), - ExprEnum::CastBoolToSInt(expr) => todo!(), - ExprEnum::CastUIntToBool(expr) => todo!(), - ExprEnum::CastSIntToBool(expr) => todo!(), - ExprEnum::CastBoolToSyncReset(expr) => todo!(), - ExprEnum::CastUIntToSyncReset(expr) => todo!(), - ExprEnum::CastSIntToSyncReset(expr) => todo!(), - ExprEnum::CastBoolToAsyncReset(expr) => todo!(), - ExprEnum::CastUIntToAsyncReset(expr) => todo!(), - ExprEnum::CastSIntToAsyncReset(expr) => todo!(), - ExprEnum::CastSyncResetToBool(expr) => todo!(), - ExprEnum::CastSyncResetToUInt(expr) => todo!(), - ExprEnum::CastSyncResetToSInt(expr) => todo!(), - ExprEnum::CastSyncResetToReset(expr) => todo!(), - ExprEnum::CastAsyncResetToBool(expr) => todo!(), - ExprEnum::CastAsyncResetToUInt(expr) => todo!(), - ExprEnum::CastAsyncResetToSInt(expr) => todo!(), - ExprEnum::CastAsyncResetToReset(expr) => todo!(), - ExprEnum::CastResetToBool(expr) => todo!(), - ExprEnum::CastResetToUInt(expr) => todo!(), - ExprEnum::CastResetToSInt(expr) => todo!(), - ExprEnum::CastBoolToClock(expr) => todo!(), - ExprEnum::CastUIntToClock(expr) => todo!(), - ExprEnum::CastSIntToClock(expr) => todo!(), - ExprEnum::CastClockToBool(expr) => todo!(), - ExprEnum::CastClockToUInt(expr) => todo!(), - ExprEnum::CastClockToSInt(expr) => todo!(), + ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToSyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToAsyncReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastAsyncResetToReset(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastResetToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastBoolToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastUIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastSIntToClock(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), + ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), ExprEnum::FieldAccess(expr) => todo!(), ExprEnum::VariantAccess(expr) => todo!(), ExprEnum::ArrayIndex(expr) => todo!(), @@ -817,13 +1307,13 @@ impl Compiler { ExprEnum::Reg(expr) => todo!(), ExprEnum::MemPort(expr) => todo!(), }; - self.compiled_exprs.insert(expr, retval); + self.compiled_exprs.insert((conditions, expr), retval); retval } fn compile_connect( &mut self, parent_module: Interned, - cond_stack: Interned, + conditions: Interned<[Cond]>, lhs: Expr, mut rhs: Expr, source_location: SourceLocation, @@ -847,7 +1337,7 @@ impl Compiler { for index in 0..lhs_ty.len() { self.compile_connect( parent_module, - cond_stack, + conditions, lhs[index], rhs[index], source_location, @@ -891,7 +1381,7 @@ impl Compiler { } self.compile_connect( parent_module, - cond_stack, + conditions, lhs_expr, rhs_expr, source_location, @@ -905,16 +1395,16 @@ impl Compiler { CanonicalType::Clock(_) => unreachable!(), } } - let lhs = self.compile_expr(lhs); - let rhs = self.compile_expr(rhs); - let rhs = self.compiled_expr_to_value(rhs); + let lhs = self.compile_expr(conditions, lhs); + let rhs = self.compile_expr(conditions, rhs); + let rhs = self.compiled_expr_to_value(conditions, rhs); todo!(); } fn compile_block( &mut self, parent_module: Interned, block: Block, - cond_stack: Interned, + conditions: Interned<[Cond]>, ) { let Block { memories, stmts } = block; for memory in memories { @@ -926,7 +1416,7 @@ impl Compiler { lhs, rhs, source_location, - }) => self.compile_connect(parent_module, cond_stack, lhs, rhs, source_location), + }) => self.compile_connect(parent_module, conditions, lhs, rhs, source_location), Stmt::Formal(StmtFormal { kind, clk, @@ -941,27 +1431,23 @@ impl Compiler { blocks: [then_block, else_block], }) => { let cond = self - .compile_expr(Expr::canonical(cond)) + .compile_expr(conditions, Expr::canonical(cond)) .map_ty(Bool::from_canonical); self.compile_block( parent_module, then_block, - CondStack::IfTrue { - parent: cond_stack, - cond, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfTrue { cond }, source_location, - } - .intern_sized(), + }])), ); self.compile_block( parent_module, else_block, - CondStack::IfFalse { - parent: cond_stack, - cond, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::IfFalse { cond }, source_location, - } - .intern_sized(), + }])), ); } Stmt::Match(StmtMatch { @@ -970,19 +1456,19 @@ impl Compiler { blocks, }) => { let enum_expr = self - .compile_expr(Expr::canonical(expr)) + .compile_expr(conditions, Expr::canonical(expr)) .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, block, - CondStack::MatchArm { - parent: cond_stack, - enum_expr, - variant_index, + Interned::from_iter(conditions.iter().copied().chain([Cond { + body: CondBody::MatchArm { + enum_expr, + variant_index, + }, source_location, - } - .intern_sized(), + }])), ); } } @@ -1057,7 +1543,7 @@ impl Compiler { .collect(); match module.leaf_module().body() { ModuleBody::Normal(NormalModuleBody { body }) => { - self.compile_block(module, body, CondStack::Always.intern_sized()) + self.compile_block(module, body, Interned::default()); } ModuleBody::Extern(_extern_module_body) => { todo!("simulating extern module: {:?}", module); @@ -1068,8 +1554,9 @@ impl Compiler { }; entry.insert(CompiledModule { module_io }) } - pub fn run(mut self) -> Compiled { + pub fn compile(mut self) -> Compiled { self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + Compiled { insns: Insns::from(self.insns).intern_sized(), modules: self.modules, @@ -1090,10 +1577,11 @@ pub struct Compiled { impl Compiled { pub fn new(module: Module) -> Self { - Compiler::new(module.canonical().intern()).run() + Compiler::new(module.canonical().intern()).compile() } } +#[derive(Debug)] pub struct Simulation { state: interpreter::State, } diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 82d3c11..8fc9038 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -2,11 +2,13 @@ // See Notices.txt for copyright information use crate::{ - intern::{Intern, Interned}, + intern::{Intern, Interned, Memoize}, source_location::SourceLocation, + ty::CanonicalType, util::get_many_mut, }; use num_bigint::BigInt; +use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ borrow::BorrowMut, convert::Infallible, @@ -21,6 +23,147 @@ pub(crate) type SmallUInt = u64; pub(crate) type SmallSInt = i64; pub(crate) const MIN_BITS_FOR_NEEDING_BIG: usize = SmallUInt::BITS as usize + 1; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) enum InsnFieldKind { + Input, + Output, + Immediate, + BranchTarget, +} + +pub(crate) trait InsnFieldTypeTransform: Eq + Hash + fmt::Debug + Send + Sync { + type Type: Eq + Hash + fmt::Debug + Send + Sync; + fn empty_type() -> Self::Type<[(); 0]>; +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] +pub(crate) struct InsnFieldTypeTransformUnit; + +impl InsnFieldTypeTransform for InsnFieldTypeTransformUnit { + type Type = (); + fn empty_type() -> Self::Type<[(); 0]> { + () + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformRef<'a>(PhantomData<&'a ()>); + +impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRef<'a> { + type Type = &'a FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + &[] + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformRefMut<'a>(PhantomData<&'a mut ()>); + +impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRefMut<'a> { + type Type = &'a mut FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + &mut [] + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnFieldTypeTransformValue; + +impl InsnFieldTypeTransform for InsnFieldTypeTransformValue { + type Type = FieldType; + fn empty_type() -> Self::Type<[(); 0]> { + [] + } +} + +pub trait InsnFieldTrait: Send + Sync + 'static + Copy + Eq + Hash + fmt::Debug { + const UNIT: InsnFieldType; + fn variant( + v: Transform::Type, + ) -> InsnFieldType; +} + +macro_rules! insn_field_enum { + ( + $enum_vis:vis enum $InsnFieldType:ident<$Transform:ident: $InsnFieldTypeTransform:ident> { + $($Variant:ident($Transform2:ident::$Type:ident<$variant_ty:ty>),)* + } + ) => { + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + $enum_vis enum $InsnFieldType<$Transform: $InsnFieldTypeTransform> { + $($Variant($Transform2::$Type<$variant_ty>),)* + } + + $(impl InsnFieldTrait for $variant_ty { + const UNIT: $InsnFieldType = $InsnFieldType::$Variant(()); + fn variant<$Transform2: $InsnFieldTypeTransform>( + v: $Transform2::$Type, + ) -> $InsnFieldType<$Transform2> { + $InsnFieldType::$Variant(v) + } + })* + }; +} + +insn_field_enum! { + pub(crate) enum InsnFieldType { + SmallSlot(Transform::Type>), + BigSlot(Transform::Type>), + SmallUInt(Transform::Type), + SmallSInt(Transform::Type), + InternedBigInt(Transform::Type>), + U8(Transform::Type), + USize(Transform::Type), + Empty(Transform::Type<[(); 0]>), + } +} + +impl InsnFieldType { + pub(crate) fn empty() -> Self { + Self::Empty(Transform::empty_type()) + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +pub(crate) struct InsnField { + pub(crate) ty: InsnFieldType, + pub(crate) kind: InsnFieldKind, +} + +impl Clone for InsnField +where + InsnFieldType: Clone, +{ + fn clone(&self) -> Self { + Self { + ty: self.ty.clone(), + kind: self.kind.clone(), + } + } +} + +impl Copy for InsnField where + InsnFieldType: Copy +{ +} + +fn make_array_into_iter( + input: [T; I], + mut default: impl FnMut() -> T, +) -> std::array::IntoIter { + const { + assert!(I <= N); + }; + let mut input = input.into_iter(); + let array = std::array::from_fn(|_| input.next().unwrap_or_else(&mut default)); + let mut retval = array.into_iter(); + // remove unneeded trailing elements + if I < N { + retval.nth_back(N - I - 1); + } + retval +} + macro_rules! impl_insns { ( #[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] @@ -39,6 +182,7 @@ macro_rules! impl_insns { $(#[$insn_meta:meta])* $insn_name:ident $({ $( + #[kind = $field_kind:ident] $(#[$field_meta:meta])* $field_name:ident: $field_ty:ty, )* @@ -58,6 +202,91 @@ macro_rules! impl_insns { )* } + impl $Insn { + $vis const MAX_FIELDS: usize = { + let mut retval = 0; + $($( + let fields = [$(stringify!($field_name),)*].len(); + if retval < fields { + retval = fields; + } + )?)* + retval + }; + } + + impl $Insn { + $vis const fn fields_unit(&self) -> &'static [InsnField] { + match self { + $( + $Insn::$insn_name {..} => &[ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::UNIT, + kind: InsnFieldKind::$field_kind, + },)*)? + ], + )* + } + } + $vis fn fields<'a>(&'a self) -> std::array::IntoIter>, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + $vis fn fields_mut<'a>(&'a mut self) -> std::array::IntoIter>, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + $vis fn into_fields(self) -> std::array::IntoIter, { $Insn::MAX_FIELDS }> { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => make_array_into_iter([ + $($(InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + },)*)? + ], + || InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }, + ), + )* + } + } + } + impl $State { $vis fn $run(&mut $self) -> $run_ret_ty { let mut $state = $state_init; @@ -497,6 +726,11 @@ macro_rules! make_state_part_kinds { $($type_field: self.$type_field.index_array(element_size.$type_field, index),)* } } + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_field: self.$type_field.offset(offset.$type_field),)* + } + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -509,6 +743,14 @@ macro_rules! make_state_part_kinds { $(pub(crate) $type_field: StatePartIndex<$TypeKind>,)* } + impl TypeIndex { + pub(crate) fn offset(self, offset: TypeIndex) -> Self { + Self { + $($type_field: self.$type_field.offset(offset.$type_field),)* + } + } + } + #[derive(Debug)] pub(crate) struct State { pub(crate) insns: Interned>, @@ -678,20 +920,17 @@ impl Drop for BorrowedStack<'_, T> { #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub(crate) struct SlotDebugData { pub(crate) name: Interned, + pub(crate) ty: CanonicalType, } impl SlotDebugData { - pub(crate) fn empty() -> Self { - Self { - name: Interned::default(), - } - } pub(crate) fn with_prefixed_debug_names(&self, prefix: &str) -> Self { let mut name = String::with_capacity(self.name.len() + prefix.len()); name.push_str(prefix); name.push_str(&self.name); Self { name: Intern::intern_owned(name), + ty: self.ty, } } } @@ -728,6 +967,15 @@ impl StatePartIndex { pub(crate) fn as_usize(self) -> usize { self.value.try_into().expect("index too big") } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + Self { + value: self + .value + .checked_add(offset.value) + .expect("offset too big"), + _phantom: PhantomData, + } + } } impl fmt::Debug for StatePartIndex { @@ -870,6 +1118,13 @@ impl StatePartIndexRange { _phantom: PhantomData, }) } + pub(crate) fn offset(self, offset: StatePartIndex) -> Self { + self.end().offset(offset); // check for overflow + Self { + start: self.start.offset(offset), + len: self.len, + } + } pub(crate) fn slice(self, index: StatePartIndexRange) -> Self { assert!(index.end().value <= self.len.value, "index out of range"); Self { @@ -1180,6 +1435,65 @@ impl State { } } +fn bigint_pow2(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + (BigInt::one() << *input).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn bigint_mask(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + ((BigInt::one() << *input) - BigInt::one()).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn bigint_not_mask(width: usize) -> Interned { + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + struct MyMemoize; + impl Memoize for MyMemoize { + type Input = usize; + type InputOwned = usize; + type Output = Interned; + + fn inner(self, input: &Self::Input) -> Self::Output { + (-BigInt::one() << *input).intern_sized() + } + } + MyMemoize.get(&width) +} + +fn cast_bigint_to_sint(src: &BigInt, dest_width: usize) -> BigInt { + if dest_width == 0 { + BigInt::ZERO + } else if src.bit((dest_width - 1) as u64) { + src | &*bigint_not_mask(dest_width) + } else { + src & &*bigint_mask(dest_width) + } +} + +fn cast_bigint_to_uint(src: &BigInt, dest_width: usize) -> BigInt { + src & &*bigint_mask(dest_width) +} + impl_insns! { #[insn = Insn, next_macro = next, branch_macro = branch] pub(crate) fn State::run(&mut self) -> () { @@ -1189,235 +1503,299 @@ impl_insns! { main_loop!(); cleanup! {} } - CopySmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - state.small_slots[dest] = state.small_slots[src]; - next!(); - } - CopyBig { + Copy { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] src: StatePartIndex, } => { - let [dest, src] = state.big_slots.get_many_mut([dest, src]); - dest.clone_from(src); + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } next!(); } - SExtSmall { - dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallSInt`] that aren't used - unused_bit_count: u8, - } => { - let mut value = state.small_slots[src] as SmallSInt; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.small_slots[dest] = value as SmallUInt; - next!(); - } - ZExtSmall { - dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallUInt`] that aren't used - unused_bit_count: u8, - } => { - let mut value = state.small_slots[src]; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.small_slots[dest] = value; - next!(); - } - SExtSmallToBig { + CastToSInt { + #[kind = Output] dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallSInt`] that aren't used - unused_bit_count: u8, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + dest_width: usize, } => { - let mut value = state.small_slots[src] as SmallSInt; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.big_slots[dest] = value.into(); + let value = cast_bigint_to_sint(&state.big_slots[src], dest_width); + state.big_slots[dest] = value; next!(); } - ZExtSmallToBig { + CastToUInt { + #[kind = Output] dest: StatePartIndex, - src: StatePartIndex, - /// number of bits in a [`SmallUInt`] that aren't used - unused_bit_count: u8, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + dest_width: usize, } => { - let mut value = state.small_slots[src]; - value <<= unused_bit_count; - value >>= unused_bit_count; - state.big_slots[dest] = value.into(); + let value = cast_bigint_to_uint(&state.big_slots[src], dest_width); + state.big_slots[dest] = value; next!(); } - AndSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs & rhs; - state.small_slots[dest] = value; - next!(); - } - OrSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs | rhs; - state.small_slots[dest] = value; - next!(); - } - XorSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs ^ rhs; - state.small_slots[dest] = value; - next!(); - } - NotSmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - let value = state.small_slots[src]; - state.small_slots[dest] = !value; - next!(); - } - CmpEqSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs == rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpNeSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs != rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLTSmallUInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs < rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLESmallUInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = (lhs <= rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLTSmallSInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs] as SmallSInt; - let rhs = state.small_slots[rhs] as SmallSInt; - let value = (lhs < rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - CmpLESmallSInt { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs] as SmallSInt; - let rhs = state.small_slots[rhs] as SmallSInt; - let value = (lhs <= rhs) as SmallUInt; - state.small_slots[dest] = value; - next!(); - } - AddSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_add(rhs); - state.small_slots[dest] = value; - next!(); - } - SubSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_sub(rhs); - state.small_slots[dest] = value; - next!(); - } - NegSmall { - dest: StatePartIndex, - src: StatePartIndex, - } => { - let value = state.small_slots[src]; - let value = value.wrapping_neg(); - state.small_slots[dest] = value; - next!(); - } - NegBig { + And { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] & &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Or { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] | &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Xor { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] ^ &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + NotS { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + let value = !&state.big_slots[src]; + state.big_slots[dest] = value; + next!(); + } + NotU { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + width: usize, + } => { + let value = &state.big_slots[src] ^ &*bigint_mask(width); + state.big_slots[dest] = value; + next!(); + } + Neg { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] src: StatePartIndex, } => { let value = -&state.big_slots[src]; state.big_slots[dest] = value; next!(); } - MulSmall { - dest: StatePartIndex, - lhs: StatePartIndex, - rhs: StatePartIndex, - } => { - let lhs = state.small_slots[lhs]; - let rhs = state.small_slots[rhs]; - let value = lhs.wrapping_mul(rhs); - state.small_slots[dest] = value; - next!(); - } - ConstSmall { - dest: StatePartIndex, - value: SmallUInt, - } => { - state.small_slots[dest] = value; - next!(); - } - ConstBig { + Add { + #[kind = Output] dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] + &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + SubS { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] - &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + SubU { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + #[kind = Immediate] + dest_width: usize, + } => { + let mut value = &state.big_slots[lhs] - &state.big_slots[rhs]; + value &= &*bigint_mask(dest_width); + state.big_slots[dest] = value; + next!(); + } + Mul { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] * &state.big_slots[rhs]; + state.big_slots[dest] = value; + next!(); + } + Div { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = state.big_slots[lhs].checked_div(&state.big_slots[rhs]).unwrap_or(BigInt::ZERO); + state.big_slots[dest] = value; + next!(); + } + Rem { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + // no checked_rem?! + let value = if state.big_slots[rhs].is_zero() { + BigInt::ZERO + } else { + &state.big_slots[lhs] % &state.big_slots[rhs] + }; + state.big_slots[dest] = value; + next!(); + } + DynShl { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let value = &state.big_slots[lhs] << state.big_slots[rhs].to_usize().expect("shl by invalid value"); + state.big_slots[dest] = value; + next!(); + } + DynShr { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + assert!(!state.big_slots[rhs].is_negative(), "shr by invalid value"); + let value = state.big_slots[rhs].to_usize().map_or(BigInt::ZERO, |rhs| &state.big_slots[lhs] >> rhs); + state.big_slots[dest] = value; + next!(); + } + Shl { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Immediate] + rhs: usize, + } => { + let value = &state.big_slots[lhs] << rhs; + state.big_slots[dest] = value; + next!(); + } + Shr { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Immediate] + rhs: usize, + } => { + let value = &state.big_slots[lhs] >> rhs; + state.big_slots[dest] = value; + next!(); + } + CmpEq { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs == rhs); + state.big_slots[dest] = value; + next!(); + } + CmpNe { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs != rhs); + state.big_slots[dest] = value; + next!(); + } + CmpLt { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs < rhs); + state.big_slots[dest] = value; + next!(); + } + CmpLe { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + lhs: StatePartIndex, + #[kind = Input] + rhs: StatePartIndex, + } => { + let lhs = &state.big_slots[lhs]; + let rhs = &state.big_slots[rhs]; + let value = BigInt::from(lhs <= rhs); + state.big_slots[dest] = value; + next!(); + } + Const { + #[kind = Output] + dest: StatePartIndex, + #[kind = Immediate] value: Interned, } => { state.big_slots[dest].clone_from(&value); From 3106a6fff6db72ab44bfccc62383c305ab79e492 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 12 Nov 2024 22:11:12 -0800 Subject: [PATCH 085/190] working on simulator... --- crates/fayalite/src/sim.rs | 1045 ++++++++++++++++-------- crates/fayalite/src/sim/interpreter.rs | 335 ++++++++ 2 files changed, 1044 insertions(+), 336 deletions(-) diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index dbcb88a..8dc087b 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -6,10 +6,12 @@ use crate::{ bundle::{BundleField, BundleType}, expr::{ + ops, target::{ - Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement, + GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, + TargetPathElement, }, - ExprEnum, + ExprEnum, Flow, }, intern::{Intern, Interned, Memoize}, module::{ @@ -19,15 +21,16 @@ use crate::{ prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, SmallUInt, StatePartIndex, StatePartIndexMap, StatePartIndexRange, - StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, StatePartLayout, - StatePartLen, StatePartsValue, TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, - MIN_BITS_FOR_NEEDING_BIG, + SlotDebugData, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, + StatePartIndexMap, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, + StatePartKindSmallSlots, StatePartLayout, StatePartsValue, TypeArrayIndex, + TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeParts, }, + ty::StaticType, }; use hashbrown::HashMap; use num_bigint::BigInt; -use std::{collections::BTreeSet, fmt, marker::PhantomData, mem}; +use std::{collections::BTreeSet, fmt}; mod interpreter; @@ -127,6 +130,14 @@ impl CompiledTypeLayout { body, } } + fn with_anonymized_debug_info(self) -> Self { + let Self { ty, layout, body } = self; + Self { + ty, + layout: layout.with_anonymized_debug_info(), + body, + } + } fn get(ty: T) -> Self { #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] struct MyMemoize; @@ -221,6 +232,9 @@ struct CompiledValue { } impl CompiledValue { + fn write(self) -> (CompiledTypeLayout, TypeIndexRange) { + self.write.unwrap_or((self.layout, self.range)) + } fn map( self, mut f: impl FnMut( @@ -235,6 +249,18 @@ impl CompiledValue { write: self.write.map(|(layout, range)| f(layout, range)), } } + fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledValue { + self.map(|CompiledTypeLayout { ty, layout, body }, range| { + ( + CompiledTypeLayout { + ty: f(ty), + layout, + body, + }, + range, + ) + }) + } } impl CompiledValue { @@ -243,139 +269,90 @@ impl CompiledValue { } } -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] -struct CompiledExprDynIndex { - index_slot: StatePartIndex, - len: usize, - stride: TypeLen, -} - -impl CompiledExprDynIndex { - fn offsets(self) -> impl Iterator { - (0..self.len.try_into().unwrap()).map(move |index| TypeIndex { - small_slots: StatePartIndex { - value: self - .stride - .small_slots - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: self - .stride - .big_slots - .value - .checked_mul(index) - .expect("array too big"), - _phantom: PhantomData, - }, +impl CompiledValue { + fn field_by_index(self, field_index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayout { + ty: _, + layout: _, + body: CompiledTypeLayoutBody::Bundle { fields }, + } = layout + else { + unreachable!(); + }; + ( + fields[field_index].ty, + range.slice(TypeIndexRange::new( + fields[field_index].offset, + fields[field_index].ty.layout.len(), + )), + ) }) } - fn for_each_offset(dyn_indexes: &[CompiledExprDynIndex], mut f: impl FnMut(TypeIndex)) { - fn helper( - dyn_indexes: &[CompiledExprDynIndex], - offset: TypeIndex, - f: &mut impl FnMut(TypeIndex), - ) { - if let [next, rest @ ..] = dyn_indexes { - for next_offset in next.offsets() { - helper( - rest, - TypeIndex { - small_slots: StatePartIndex { - value: next_offset - .small_slots - .value - .checked_add(offset.small_slots.value) - .expect("array too big"), - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: next_offset - .big_slots - .value - .checked_add(offset.big_slots.value) - .expect("array too big"), - _phantom: PhantomData, - }, - }, - f, - ); - } - } else { - f(offset); - } - } - helper( - dyn_indexes, - TypeIndex { - small_slots: StatePartIndex { - value: 0, - _phantom: PhantomData, - }, - big_slots: StatePartIndex { - value: 0, - _phantom: PhantomData, - }, - }, - &mut f, - ); + fn field_by_name(self, name: Interned) -> CompiledValue { + self.field_by_index(self.layout.ty.name_indexes()[&name]) + } +} + +impl CompiledValue { + fn element(self, index: usize) -> CompiledValue { + self.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(element.layout.len(), index)) + }) + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + CompiledExpr::from(self).element_dyn(index_slot) } } #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] struct CompiledExpr { static_part: CompiledValue, - dyn_indexes: Interned<[CompiledExprDynIndex]>, + indexes: TypeArrayIndexes, } impl From> for CompiledExpr { fn from(static_part: CompiledValue) -> Self { Self { static_part, - dyn_indexes: Interned::default(), + indexes: TypeArrayIndexes::default(), } } } impl CompiledExpr { - fn map_ty(self, mut f: impl FnMut(T) -> U) -> CompiledExpr { + fn map_ty(self, f: impl FnMut(T) -> U) -> CompiledExpr { let Self { static_part, - dyn_indexes, + indexes, } = self; CompiledExpr { - static_part: static_part.map(|CompiledTypeLayout { ty, layout, body }, range| { - ( - CompiledTypeLayout { - ty: f(ty), - layout, - body, - }, - range, - ) - }), - dyn_indexes, + static_part: static_part.map_ty(f), + indexes, } } fn add_target_without_indexes_to_set(self, inputs: &mut SlotSet) { let Self { static_part, - dyn_indexes, + indexes, } = self; - CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + indexes.as_ref().for_each_offset(|offset| { inputs.extend([static_part.range.offset(offset)]); }); } fn add_target_and_indexes_to_set(self, inputs: &mut SlotSet) { let Self { static_part: _, - dyn_indexes, + indexes, } = self; self.add_target_without_indexes_to_set(inputs); - inputs.extend(dyn_indexes); + inputs.extend(indexes.as_ref().iter()); } } @@ -383,12 +360,59 @@ impl CompiledExpr { fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { let Self { static_part, - dyn_indexes, + indexes, } = self; - CompiledExprDynIndex::for_each_offset(&dyn_indexes, |offset| { + indexes.as_ref().for_each_offset(|offset| { static_part.add_discriminant_to_set(offset, inputs); }); - inputs.extend(dyn_indexes); + inputs.extend(indexes.as_ref().iter()); + } +} + +impl CompiledExpr { + fn field_by_index(self, field_index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_index(field_index), + indexes: self.indexes, + } + } + fn field_by_name(self, name: Interned) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.field_by_name(name), + indexes: self.indexes, + } + } +} + +impl CompiledExpr { + fn element(self, index: usize) -> CompiledExpr { + CompiledExpr { + static_part: self.static_part.element(index), + indexes: self.indexes, + } + } + fn element_dyn( + self, + index_slot: StatePartIndex, + ) -> CompiledExpr { + let CompiledTypeLayoutBody::Array { element } = self.static_part.layout.body else { + unreachable!(); + }; + let stride = element.layout.len(); + let indexes = self.indexes.join(TypeArrayIndex::from_parts( + index_slot, + self.static_part.layout.ty.len(), + stride, + )); + CompiledExpr { + static_part: self.static_part.map(|layout, range| { + let CompiledTypeLayoutBody::Array { element } = layout.body else { + unreachable!(); + }; + (*element, range.index_array(stride, 0)) + }), + indexes, + } } } @@ -444,9 +468,23 @@ impl Extend for SlotSet { } } -impl Extend for SlotSet { - fn extend>(&mut self, iter: T) { - self.extend(iter.into_iter().map(|v| v.index_slot)); +impl Extend for SlotSet { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |TypeArrayIndex { + small_slots, + big_slots, + }| { + self.extend([small_slots]); + self.extend([big_slots]); + }, + ) + } +} + +impl Extend> for SlotSet { + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|v| v.index)); } } @@ -475,6 +513,7 @@ struct Assignment { inputs: SlotSet, conditions: Interned<[Cond]>, insns: Vec, + source_location: SourceLocation, } #[derive(Debug, Default)] @@ -488,19 +527,59 @@ struct SlotAssignments { parts: TypeParts, } +impl SlotAssignments { + fn for_assignment(&mut self, assignment_index: usize) -> SlotAssignmentsForAssignment<'_> { + SlotAssignmentsForAssignment { + parts: &mut self.parts, + assignment_index, + } + } +} + impl StatePartsValue for SlotAssignments { type Value = StatePartAssignments; } +struct SlotAssignmentsForAssignment<'a> { + parts: &'a mut TypeParts, + assignment_index: usize, +} + +impl Extend> for SlotAssignmentsForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|slot| { + self.parts + .small_slots + .written_slot_to_assignment_indexes_map + .entry(slot) + .or_insert_with(Vec::new) + .push(self.assignment_index) + }); + } +} + +impl Extend> for SlotAssignmentsForAssignment<'_> { + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|slot| { + self.parts + .big_slots + .written_slot_to_assignment_indexes_map + .entry(slot) + .or_insert_with(Vec::new) + .push(self.assignment_index) + }); + } +} + #[derive(Debug)] pub struct Compiler { insns: Insns, base_module: Interned>, modules: HashMap, compiled_values: HashMap>, - compiled_exprs: HashMap<(Interned<[Cond]>, Expr), CompiledExpr>, - compiled_exprs_to_values: - HashMap<(Interned<[Cond]>, CompiledExpr), CompiledValue>, + compiled_exprs: HashMap, CompiledExpr>, + compiled_exprs_to_values: HashMap, CompiledValue>, + decl_conditions: HashMap>, slots_assignments: SlotAssignments, } @@ -513,6 +592,7 @@ impl Compiler { compiled_values: HashMap::new(), compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), + decl_conditions: HashMap::new(), slots_assignments: SlotAssignments::default(), } } @@ -562,32 +642,11 @@ impl Compiler { }); match *target_child.path_element() { TargetPathElement::BundleField(TargetPathBundleField { name }) => { - parent.map(|layout, range| { - let CompiledTypeLayout { - ty: CanonicalType::Bundle(bundle), - layout: _, - body: CompiledTypeLayoutBody::Bundle { fields }, - } = layout - else { - unreachable!(); - }; - let field_index = bundle.name_indexes()[&name]; - ( - fields[field_index].ty, - range.slice(TypeIndexRange::new( - fields[field_index].offset, - fields[field_index].ty.layout.len(), - )), - ) - }) + parent.map_ty(Bundle::from_canonical).field_by_name(name) + } + TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => { + parent.map_ty(Array::from_canonical).element(index) } - TargetPathElement::ArrayElement(TargetPathArrayElement { index }) => parent - .map(|layout, range| { - let CompiledTypeLayoutBody::Array { element } = layout.body else { - unreachable!(); - }; - (*element, range.index_array(element.layout.len(), index)) - }), TargetPathElement::DynArrayElement(_) => unreachable!(), } } @@ -597,32 +656,76 @@ impl Compiler { } fn compiled_expr_to_value( &mut self, - conditions: Interned<[Cond]>, expr: CompiledExpr, + source_location: SourceLocation, ) -> CompiledValue { - if let Some(&retval) = self.compiled_exprs_to_values.get(&(conditions, expr)) { + if let Some(&retval) = self.compiled_exprs_to_values.get(&expr) { return retval; } + assert!( + expr.static_part.layout.ty.is_passive(), + "invalid expression passed to compiled_expr_to_value -- type must be passive", + ); let CompiledExpr { - static_part: mut retval, - dyn_indexes, + static_part, + indexes, } = expr; - for CompiledExprDynIndex { - index_slot, - len, - stride, - } in dyn_indexes - { - todo!(); - } - self.compiled_exprs_to_values - .insert((conditions, expr), retval); + let layout = static_part.layout.with_anonymized_debug_info(); + let retval = CompiledValue { + layout, + range: self.insns.allocate_variable(&layout.layout), + write: None, + }; + let TypeIndexRange { + small_slots, + big_slots, + } = retval.range; + self.add_assignment( + Interned::default(), + small_slots + .iter() + .zip(static_part.range.small_slots.iter()) + .map(|(dest, base)| { + if indexes.small_slots.is_empty() { + Insn::CopySmall { dest, src: base } + } else { + Insn::ReadSmallIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.small_slots, + }, + } + } + }) + .chain( + big_slots + .iter() + .zip(static_part.range.big_slots.iter()) + .map(|(dest, base)| { + if indexes.big_slots.is_empty() { + Insn::Copy { dest, src: base } + } else { + Insn::ReadIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.big_slots, + }, + } + } + }), + ), + source_location, + ); + self.compiled_exprs_to_values.insert(expr, retval); retval } fn add_assignment( &mut self, conditions: Interned<[Cond]>, insns: impl IntoIterator, + source_location: SourceLocation, ) { let insns = Vec::from_iter(insns); let assignment_index = self.slots_assignments.assignments.len(); @@ -630,28 +733,50 @@ impl Compiler { for insn in &insns { for InsnField { ty, kind } in insn.fields() { match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&small_slot)) => { - inputs.0.small_slots.insert(small_slot); + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { + inputs.extend([slot]); } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&big_slot)) => { - inputs.0.big_slots.insert(big_slot); + (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { + inputs.extend([slot]); } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&small_slot)) => self + ( + InsnFieldKind::Input, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => self .slots_assignments - .parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(small_slot) - .or_insert_with(Vec::new) - .push(assignment_index), - (InsnFieldKind::Output, InsnFieldType::BigSlot(&big_slot)) => self + .for_assignment(assignment_index) + .extend([slot]), + (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => self .slots_assignments - .parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(big_slot) - .or_insert_with(Vec::new) - .push(assignment_index), + .for_assignment(assignment_index) + .extend([slot]), + ( + InsnFieldKind::Output, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + self.slots_assignments + .for_assignment(assignment_index) + .extend([slot]) + }); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| { + self.slots_assignments + .for_assignment(assignment_index) + .extend([slot]) + }); + inputs.extend(array_indexed.indexes); + } ( _, InsnFieldType::SmallUInt(_) @@ -669,11 +794,12 @@ impl Compiler { inputs, conditions, insns, + source_location, }); } fn simple_nary_big_expr( &mut self, - conditions: Interned<[Cond]>, + instantiated_module: InstantiatedModule, dest_ty: CanonicalType, inputs: [Expr; N], make_insns: impl FnOnce( @@ -682,8 +808,9 @@ impl Compiler { ) -> Vec, ) -> CompiledValue { let inputs = inputs.map(|input| { - let input = self.compile_expr(conditions, input); - let input = self.compiled_expr_to_value(conditions, input); + let input = self.compile_expr(instantiated_module, input); + let input = self + .compiled_expr_to_value(input, instantiated_module.leaf_module().source_location()); let TypeIndexRange { small_slots, big_slots, @@ -705,15 +832,19 @@ impl Compiler { range, write: None, }; - self.add_assignment(conditions, make_insns(big_slots.start, inputs)); + self.add_assignment( + Interned::default(), + make_insns(big_slots.start, inputs), + instantiated_module.leaf_module().source_location(), + ); retval } fn compile_expr( &mut self, - conditions: Interned<[Cond]>, + instantiated_module: InstantiatedModule, expr: Expr, ) -> CompiledExpr { - if let Some(&retval) = self.compiled_exprs.get(&(conditions, expr)) { + if let Some(&retval) = self.compiled_exprs.get(&expr) { return retval; } let mut cast_bit = |arg: Expr| { @@ -741,7 +872,7 @@ impl Compiler { CanonicalType::Reset(_) => false, CanonicalType::Clock(_) => false, }; - self.simple_nary_big_expr(conditions, Expr::ty(expr), [arg], |dest, [src]| { + self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| { match (src_signed, dest_signed) { (false, false) | (true, true) => { vec![Insn::Copy { dest, src }] @@ -762,23 +893,33 @@ impl Compiler { }; let retval: CompiledExpr<_> = match *Expr::expr_enum(expr) { ExprEnum::UIntLiteral(expr) => self - .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }) + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) .into(), ExprEnum::SIntLiteral(expr) => self - .simple_nary_big_expr(conditions, expr.ty().canonical(), [], |dest, []| { - vec![Insn::Const { - dest, - value: expr.to_bigint().intern_sized(), - }] - }) + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [], + |dest, []| { + vec![Insn::Const { + dest, + value: expr.to_bigint().intern_sized(), + }] + }, + ) .into(), ExprEnum::BoolLiteral(expr) => self - .simple_nary_big_expr(conditions, Bool.canonical(), [], |dest, []| { + .simple_nary_big_expr(instantiated_module, Bool.canonical(), [], |dest, []| { vec![Insn::Const { dest, value: BigInt::from(expr).intern_sized(), @@ -791,7 +932,7 @@ impl Compiler { ExprEnum::Uninit(expr) => todo!(), ExprEnum::NotU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -805,7 +946,7 @@ impl Compiler { .into(), ExprEnum::NotS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::NotS { dest, src }], @@ -813,7 +954,7 @@ impl Compiler { .into(), ExprEnum::NotB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Expr::ty(expr.arg()).canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -827,7 +968,7 @@ impl Compiler { .into(), ExprEnum::Neg(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| vec![Insn::Neg { dest, src }], @@ -835,7 +976,7 @@ impl Compiler { .into(), ExprEnum::BitAndU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -843,7 +984,7 @@ impl Compiler { .into(), ExprEnum::BitAndS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -851,7 +992,7 @@ impl Compiler { .into(), ExprEnum::BitAndB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::And { dest, lhs, rhs }], @@ -859,7 +1000,7 @@ impl Compiler { .into(), ExprEnum::BitOrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -867,7 +1008,7 @@ impl Compiler { .into(), ExprEnum::BitOrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -875,7 +1016,7 @@ impl Compiler { .into(), ExprEnum::BitOrB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Or { dest, lhs, rhs }], @@ -883,7 +1024,7 @@ impl Compiler { .into(), ExprEnum::BitXorU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -891,7 +1032,7 @@ impl Compiler { .into(), ExprEnum::BitXorS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -899,7 +1040,7 @@ impl Compiler { .into(), ExprEnum::BitXorB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Xor { dest, lhs, rhs }], @@ -907,7 +1048,7 @@ impl Compiler { .into(), ExprEnum::AddU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], @@ -915,7 +1056,7 @@ impl Compiler { .into(), ExprEnum::AddS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Add { dest, lhs, rhs }], @@ -923,7 +1064,7 @@ impl Compiler { .into(), ExprEnum::SubU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| { @@ -938,7 +1079,7 @@ impl Compiler { .into(), ExprEnum::SubS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::SubS { dest, lhs, rhs }], @@ -946,7 +1087,7 @@ impl Compiler { .into(), ExprEnum::MulU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], @@ -954,7 +1095,7 @@ impl Compiler { .into(), ExprEnum::MulS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Mul { dest, lhs, rhs }], @@ -962,7 +1103,7 @@ impl Compiler { .into(), ExprEnum::DivU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], @@ -970,7 +1111,7 @@ impl Compiler { .into(), ExprEnum::DivS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Div { dest, lhs, rhs }], @@ -978,7 +1119,7 @@ impl Compiler { .into(), ExprEnum::RemU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], @@ -986,7 +1127,7 @@ impl Compiler { .into(), ExprEnum::RemS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::Rem { dest, lhs, rhs }], @@ -994,7 +1135,7 @@ impl Compiler { .into(), ExprEnum::DynShlU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], @@ -1002,7 +1143,7 @@ impl Compiler { .into(), ExprEnum::DynShlS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShl { dest, lhs, rhs }], @@ -1010,7 +1151,7 @@ impl Compiler { .into(), ExprEnum::DynShrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], @@ -1018,7 +1159,7 @@ impl Compiler { .into(), ExprEnum::DynShrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::DynShr { dest, lhs, rhs }], @@ -1026,7 +1167,7 @@ impl Compiler { .into(), ExprEnum::FixedShlU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1040,7 +1181,7 @@ impl Compiler { .into(), ExprEnum::FixedShlS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1054,7 +1195,7 @@ impl Compiler { .into(), ExprEnum::FixedShrU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1068,7 +1209,7 @@ impl Compiler { .into(), ExprEnum::FixedShrS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.lhs())], |dest, [lhs]| { @@ -1082,7 +1223,7 @@ impl Compiler { .into(), ExprEnum::CmpLtB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1090,7 +1231,7 @@ impl Compiler { .into(), ExprEnum::CmpLeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1098,7 +1239,7 @@ impl Compiler { .into(), ExprEnum::CmpGtB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1107,7 +1248,7 @@ impl Compiler { .into(), ExprEnum::CmpGeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1116,7 +1257,7 @@ impl Compiler { .into(), ExprEnum::CmpEqB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1124,7 +1265,7 @@ impl Compiler { .into(), ExprEnum::CmpNeB(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1132,7 +1273,7 @@ impl Compiler { .into(), ExprEnum::CmpLtU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1140,7 +1281,7 @@ impl Compiler { .into(), ExprEnum::CmpLeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1148,7 +1289,7 @@ impl Compiler { .into(), ExprEnum::CmpGtU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1157,7 +1298,7 @@ impl Compiler { .into(), ExprEnum::CmpGeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1166,7 +1307,7 @@ impl Compiler { .into(), ExprEnum::CmpEqU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1174,7 +1315,7 @@ impl Compiler { .into(), ExprEnum::CmpNeU(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1182,7 +1323,7 @@ impl Compiler { .into(), ExprEnum::CmpLtS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLt { dest, lhs, rhs }], @@ -1190,7 +1331,7 @@ impl Compiler { .into(), ExprEnum::CmpLeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpLe { dest, lhs, rhs }], @@ -1198,7 +1339,7 @@ impl Compiler { .into(), ExprEnum::CmpGtS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1207,7 +1348,7 @@ impl Compiler { .into(), ExprEnum::CmpGeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), // swap both comparison direction and lhs/rhs [Expr::canonical(expr.rhs()), Expr::canonical(expr.lhs())], @@ -1216,7 +1357,7 @@ impl Compiler { .into(), ExprEnum::CmpEqS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpEq { dest, lhs, rhs }], @@ -1224,7 +1365,7 @@ impl Compiler { .into(), ExprEnum::CmpNeS(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, Bool.canonical(), [Expr::canonical(expr.lhs()), Expr::canonical(expr.rhs())], |dest, [lhs, rhs]| vec![Insn::CmpNe { dest, lhs, rhs }], @@ -1232,7 +1373,7 @@ impl Compiler { .into(), ExprEnum::CastUIntToUInt(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -1244,10 +1385,23 @@ impl Compiler { }, ) .into(), - ExprEnum::CastUIntToSInt(expr) => todo!(), + ExprEnum::CastUIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), ExprEnum::CastSIntToUInt(expr) => self .simple_nary_big_expr( - conditions, + instantiated_module, expr.ty().canonical(), [Expr::canonical(expr.arg())], |dest, [src]| { @@ -1259,7 +1413,20 @@ impl Compiler { }, ) .into(), - ExprEnum::CastSIntToSInt(expr) => todo!(), + ExprEnum::CastSIntToSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + expr.ty().canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::CastToSInt { + dest, + src, + dest_width: expr.ty().width(), + }] + }, + ) + .into(), ExprEnum::CastBoolToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), ExprEnum::CastBoolToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), ExprEnum::CastUIntToBool(expr) => cast_bit(Expr::canonical(expr.arg())), @@ -1287,34 +1454,190 @@ impl Compiler { ExprEnum::CastClockToBool(expr) => cast_bit(Expr::canonical(expr.arg())), ExprEnum::CastClockToUInt(expr) => cast_bit(Expr::canonical(expr.arg())), ExprEnum::CastClockToSInt(expr) => cast_bit(Expr::canonical(expr.arg())), - ExprEnum::FieldAccess(expr) => todo!(), + ExprEnum::FieldAccess(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Bundle::from_canonical) + .field_by_index(expr.field_index()), ExprEnum::VariantAccess(expr) => todo!(), - ExprEnum::ArrayIndex(expr) => todo!(), + ExprEnum::ArrayIndex(expr) => self + .compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element(expr.element_index()), ExprEnum::DynArrayIndex(expr) => todo!(), - ExprEnum::ReduceBitAndU(expr) => todo!(), - ExprEnum::ReduceBitAndS(expr) => todo!(), - ExprEnum::ReduceBitOrU(expr) => todo!(), - ExprEnum::ReduceBitOrS(expr) => todo!(), - ExprEnum::ReduceBitXorU(expr) => todo!(), - ExprEnum::ReduceBitXorS(expr) => todo!(), + ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical( + expr.arg() + .cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)), + ), + ) + } + .into(), + ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 { + self.compile_expr(instantiated_module, Expr::canonical(false.to_expr())) + } else { + self.compile_expr( + instantiated_module, + Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))), + ) + } + .into(), + ExprEnum::ReduceBitXorU(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), + ExprEnum::ReduceBitXorS(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::<1>::TYPE.canonical(), + [Expr::canonical(expr.arg())], + |dest, [src]| { + vec![Insn::ReduceBitXor { + dest, + src, + input_width: Expr::ty(expr.arg()).width(), + }] + }, + ) + .into(), ExprEnum::SliceUInt(expr) => todo!(), ExprEnum::SliceSInt(expr) => todo!(), ExprEnum::CastToBits(expr) => todo!(), ExprEnum::CastBitsTo(expr) => todo!(), - ExprEnum::ModuleIO(expr) => todo!(), - ExprEnum::Instance(expr) => todo!(), - ExprEnum::Wire(expr) => todo!(), - ExprEnum::Reg(expr) => todo!(), - ExprEnum::MemPort(expr) => todo!(), + ExprEnum::ModuleIO(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Instance(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Wire(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::Reg(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), + ExprEnum::MemPort(expr) => self + .compile_value(TargetInInstantiatedModule { + instantiated_module, + target: expr.into(), + }) + .into(), }; - self.compiled_exprs.insert((conditions, expr), retval); + self.compiled_exprs.insert(expr, retval); retval } + fn compile_simple_connect( + &mut self, + conditions: Interned<[Cond]>, + lhs: CompiledExpr, + rhs: CompiledValue, + source_location: SourceLocation, + ) { + let CompiledExpr { + static_part: lhs_static_part, + indexes, + } = lhs; + let (lhs_layout, lhs_range) = lhs_static_part.write(); + assert!( + lhs_layout.ty.is_passive(), + "invalid expression passed to compile_simple_connect -- type must be passive", + ); + let TypeIndexRange { + small_slots, + big_slots, + } = lhs_range; + self.add_assignment( + conditions, + small_slots + .iter() + .zip(rhs.range.small_slots.iter()) + .map(|(base, src)| { + if indexes.small_slots.is_empty() { + Insn::CopySmall { dest: base, src } + } else { + Insn::WriteSmallIndexed { + dest: StatePartArrayIndexed { + base, + indexes: indexes.small_slots, + }, + src, + } + } + }) + .chain( + big_slots + .iter() + .zip(rhs.range.big_slots.iter()) + .map(|(base, src)| { + if indexes.big_slots.is_empty() { + Insn::Copy { dest: base, src } + } else { + Insn::WriteIndexed { + dest: StatePartArrayIndexed { + base, + indexes: indexes.big_slots, + }, + src, + } + } + }), + ), + source_location, + ); + } fn compile_connect( &mut self, - parent_module: Interned, - conditions: Interned<[Cond]>, + lhs_instantiated_module: InstantiatedModule, + lhs_conditions: Interned<[Cond]>, lhs: Expr, + rhs_instantiated_module: InstantiatedModule, + rhs_conditions: Interned<[Cond]>, mut rhs: Expr, source_location: SourceLocation, ) { @@ -1336,9 +1659,11 @@ impl Compiler { let rhs = Expr::::from_canonical(rhs); for index in 0..lhs_ty.len() { self.compile_connect( - parent_module, - conditions, + lhs_instantiated_module, + lhs_conditions, lhs[index], + rhs_instantiated_module, + rhs_conditions, rhs[index], source_location, ); @@ -1372,20 +1697,30 @@ impl Compiler { { assert_eq!(name, rhs_field.name); assert_eq!(flipped, rhs_field.flipped); - let mut lhs_expr = - crate::expr::ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); - let mut rhs_expr = - crate::expr::ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); + let lhs_expr = ops::FieldAccess::new_by_index(lhs, field_index).to_expr(); + let rhs_expr = ops::FieldAccess::new_by_index(rhs, field_index).to_expr(); if flipped { - mem::swap(&mut lhs_expr, &mut rhs_expr); + // swap lhs/rhs + self.compile_connect( + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + source_location, + ); + } else { + self.compile_connect( + lhs_instantiated_module, + lhs_conditions, + lhs_expr, + rhs_instantiated_module, + rhs_conditions, + rhs_expr, + source_location, + ); } - self.compile_connect( - parent_module, - conditions, - lhs_expr, - rhs_expr, - source_location, - ); } return; } @@ -1395,10 +1730,93 @@ impl Compiler { CanonicalType::Clock(_) => unreachable!(), } } - let lhs = self.compile_expr(conditions, lhs); - let rhs = self.compile_expr(conditions, rhs); - let rhs = self.compiled_expr_to_value(conditions, rhs); - todo!(); + let Some(target) = lhs.target() else { + unreachable!("connect lhs must have target"); + }; + let lhs_decl_conditions = self.decl_conditions[&TargetInInstantiatedModule { + instantiated_module: lhs_instantiated_module, + target: target.base().into(), + }]; + let lhs = self.compile_expr(lhs_instantiated_module, lhs); + let rhs = self.compile_expr(rhs_instantiated_module, rhs); + let rhs = self.compiled_expr_to_value(rhs, source_location); + self.compile_simple_connect( + lhs_conditions[lhs_decl_conditions.len()..].intern(), + lhs, + rhs, + source_location, + ); + } + fn compile_declaration( + &mut self, + declaration: StmtDeclaration, + parent_module: Interned, + conditions: Interned<[Cond]>, + ) { + let target_base: TargetBase = match &declaration { + StmtDeclaration::Wire(v) => v.wire.into(), + StmtDeclaration::Reg(v) => v.reg.into(), + StmtDeclaration::Instance(v) => v.instance.into(), + }; + let target = TargetInInstantiatedModule { + instantiated_module: *parent_module, + target: target_base.into(), + }; + self.decl_conditions.insert(target, conditions); + self.compile_value(target); + match declaration { + StmtDeclaration::Wire(StmtWire { annotations, wire }) => {} + StmtDeclaration::Reg(StmtReg { annotations, reg }) => { + todo!(); + } + StmtDeclaration::Instance(StmtInstance { + annotations, + instance, + }) => { + let inner_instantiated_module = InstantiatedModule::Child { + parent: parent_module, + instance: instance.intern_sized(), + } + .intern_sized(); + let instance_expr = instance.to_expr(); + self.compile_module(inner_instantiated_module); + for (field_index, module_io) in + instance.instantiated().module_io().into_iter().enumerate() + { + let instance_field = + ops::FieldAccess::new_by_index(instance_expr, field_index).to_expr(); + match Expr::flow(instance_field) { + Flow::Source => { + // we need to supply the value to the instance since the + // parent module expects to read from the instance + self.compile_connect( + *parent_module, + conditions, + instance_field, + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + instance.source_location(), + ); + } + Flow::Sink => { + // we need to take the value from the instance since the + // parent module expects to write to the instance + self.compile_connect( + *inner_instantiated_module, + Interned::default(), + module_io.module_io.to_expr(), + *parent_module, + conditions, + instance_field, + instance.source_location(), + ); + } + Flow::Duplex => unreachable!(), + } + } + } + } } fn compile_block( &mut self, @@ -1416,22 +1834,23 @@ impl Compiler { lhs, rhs, source_location, - }) => self.compile_connect(parent_module, conditions, lhs, rhs, source_location), - Stmt::Formal(StmtFormal { - kind, - clk, - pred, - en, - text, + }) => self.compile_connect( + *parent_module, + conditions, + lhs, + *parent_module, + conditions, + rhs, source_location, - }) => todo!("implement simulating formal statements"), + ), + Stmt::Formal(StmtFormal { .. }) => todo!("implement simulating formal statements"), Stmt::If(StmtIf { cond, source_location, blocks: [then_block, else_block], }) => { let cond = self - .compile_expr(conditions, Expr::canonical(cond)) + .compile_expr(*parent_module, Expr::canonical(cond)) .map_ty(Bool::from_canonical); self.compile_block( parent_module, @@ -1456,7 +1875,7 @@ impl Compiler { blocks, }) => { let enum_expr = self - .compile_expr(conditions, Expr::canonical(expr)) + .compile_expr(*parent_module, Expr::canonical(expr)) .map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( @@ -1472,55 +1891,9 @@ impl Compiler { ); } } - Stmt::Declaration(declaration) => match declaration { - StmtDeclaration::Wire(StmtWire { annotations, wire }) => { - self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: wire.into(), - }); - } - StmtDeclaration::Reg(StmtReg { annotations, reg }) => { - self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: reg.into(), - }); - todo!(); - } - StmtDeclaration::Instance(StmtInstance { - annotations, - instance, - }) => { - let CompiledValue { - layout: - CompiledTypeLayout { - ty: value_ty, - layout: ty_layout, - body: CompiledTypeLayoutBody::Bundle { fields }, - }, - range: value_range, - write: None, - } = self.compile_value(TargetInInstantiatedModule { - instantiated_module: *parent_module, - target: instance.into(), - }) - else { - unreachable!(); - }; - let CompiledModule { module_io } = *self.compile_module( - InstantiatedModule::Child { - parent: parent_module, - instance: instance.intern_sized(), - } - .intern_sized(), - ); - for (module_io, CompiledBundleField { offset, ty }) in - module_io.into_iter().zip(fields) - { - todo!(); - } - todo!() - } - }, + Stmt::Declaration(declaration) => { + self.compile_declaration(declaration, parent_module, conditions); + } } } } @@ -1556,7 +1929,7 @@ impl Compiler { } pub fn compile(mut self) -> Compiled { self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - + todo!("handle self.slots_assignments"); Compiled { insns: Insns::from(self.insns).intern_sized(), modules: self.modules, diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 8fc9038..e89628c 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -109,6 +109,8 @@ insn_field_enum! { pub(crate) enum InsnFieldType { SmallSlot(Transform::Type>), BigSlot(Transform::Type>), + SmallSlotArrayIndexed(Transform::Type>), + BigSlotArrayIndexed(Transform::Type>), SmallUInt(Transform::Type), SmallSInt(Transform::Type), InternedBigInt(Transform::Type>), @@ -787,6 +789,167 @@ macro_rules! make_state_part_kinds { *self.orig_pc = self.pc; } } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexes { + $(pub(crate) $type_field: Interned<[StatePartArrayIndex<$TypeKind>]>,)* + } + + impl TypeArrayIndexes { + pub(crate) fn as_ref(&self) -> TypeArrayIndexesRef<'_> { + TypeArrayIndexesRef { + $($type_field: &self.$type_field,)* + } + } + #[must_use] + pub(crate) fn join(self, next: TypeArrayIndex) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_field: Interned::from_iter(self.$type_field.iter().copied().chain([next.$type_field])),)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndex { + $(pub(crate) $type_field: StatePartArrayIndex<$TypeKind>,)* + } + + impl TypeArrayIndex { + pub(crate) fn from_parts(index: StatePartIndex, len: usize, stride: TypeLen) -> Self { + Self { + $($type_field: StatePartArrayIndex { + index, + len, + stride: stride.$type_field, + },)* + } + } + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len; + $(assert_eq!(self.$type_field.len, len, "array length mismatch");)* + len + } + pub(crate) fn index(self) -> StatePartIndex { + let index = self.small_slots.index; + $(assert_eq!(self.$type_field.index, index, "array index mismatch");)* + index + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn stride(self) -> TypeLen { + TypeLen { + $($type_field: self.$type_field.stride,)* + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] + pub(crate) struct TypeArrayIndexesRef<'a> { + $(pub(crate) $type_field: &'a [StatePartArrayIndex<$TypeKind>],)* + } + + impl<'a> TypeArrayIndexesRef<'a> { + pub(crate) fn len(self) -> usize { + let len = self.small_slots.len(); + $(assert_eq!(self.$type_field.len(), len, "indexes count mismatch");)* + len + } + pub(crate) fn is_empty(self) -> bool { + self.len() == 0 + } + pub(crate) fn iter(self) -> impl Iterator + 'a { + (0..self.len()).map(move |i| TypeArrayIndex { + $($type_field: self.$type_field[i],)* + }) + } + pub(crate) fn for_each_offset( + self, + mut f: impl FnMut(TypeIndex), + ) { + self.for_each_offset2(TypeIndex { + $($type_field: StatePartIndex { + value: 0, + _phantom: PhantomData, + },)* + }, &mut f); + } + pub(crate) fn split_first(self) -> Option<(TypeArrayIndex, Self)> { + $(let $type_field = self.$type_field.split_first()?;)* + let next = TypeArrayIndex { + $($type_field: *$type_field.0,)* + }; + let rest = TypeArrayIndexesRef { + $($type_field: $type_field.1,)* + }; + Some((next, rest)) + } + pub(crate) fn for_each_offset2( + self, + base_offset: TypeIndex, + f: &mut (impl FnMut(TypeIndex) + ?Sized), + ) { + if let Some((next, rest)) = self.split_first() { + let stride = next.stride(); + for index in 0..next.len().try_into().expect("array too big") { + let mut offset = TypeIndex { + $($type_field: StatePartIndex { + value: stride + .$type_field + .value + .checked_mul(index) + .expect("array too big"), + _phantom: PhantomData, + },)* + }; + $(offset.$type_field.value = + base_offset + .$type_field + .value + .checked_add(offset.$type_field.value) + .expect("array too big");)* + rest.for_each_offset2(offset, f); + } + } else { + $(assert!(self.$type_field.is_empty(), "indexes count mismatch");)* + f(base_offset); + } + } + } + + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub(crate) struct TypeArrayIndexed { + $(pub(crate) $type_field: StatePartArrayIndexed<$TypeKind>,)* + } + + impl TypeArrayIndexed { + pub(crate) fn from_parts(base: TypeIndex, indexes: TypeArrayIndexes) -> Self { + Self { + $($type_field: StatePartArrayIndexed { + base: base.$type_field, + indexes: indexes.$type_field, + },)* + } + } + pub(crate) fn base(self) -> TypeIndex { + TypeIndex { + $($type_field: self.$type_field.base,)* + } + } + pub(crate) fn indexes(self) -> TypeArrayIndexes { + TypeArrayIndexes { + $($type_field: self.$type_field.indexes,)* + } + } + } + + impl From for TypeArrayIndexed { + fn from(value: TypeIndex) -> Self { + TypeArrayIndexed { + $($type_field: value.$type_field.into(),)* + } + } + } }; } @@ -933,6 +1096,12 @@ impl SlotDebugData { ty: self.ty, } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + name: Interned::default(), + ty: self.ty, + } + } } impl, BK: InsnsBuildingKind> StatePartLayout { @@ -946,6 +1115,16 @@ impl, BK: InsnsBuildingKind> StatePa _phantom: PhantomData, } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + debug_data: self + .debug_data + .iter() + .map(|v| v.with_anonymized_debug_info()) + .collect(), + _phantom: PhantomData, + } + } } impl TypeLayout { @@ -955,6 +1134,63 @@ impl TypeLayout { big_slots: self.big_slots.with_prefixed_debug_names(prefix), } } + pub(crate) fn with_anonymized_debug_info(&self) -> Self { + Self { + small_slots: self.small_slots.with_anonymized_debug_info(), + big_slots: self.big_slots.with_anonymized_debug_info(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePartArrayIndex { + pub(crate) index: StatePartIndex, + pub(crate) len: usize, + pub(crate) stride: StatePartLen, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub(crate) struct StatePartArrayIndexed { + pub(crate) base: StatePartIndex, + pub(crate) indexes: Interned<[StatePartArrayIndex]>, +} + +impl From> for StatePartArrayIndexed { + fn from(base: StatePartIndex) -> Self { + Self { + base, + indexes: Interned::default(), + } + } +} + +impl StatePartArrayIndexed { + pub(crate) fn for_each_target2( + base: StatePartIndex, + indexes: &[StatePartArrayIndex], + f: &mut (impl FnMut(StatePartIndex) + ?Sized), + ) { + if let [next, rest @ ..] = indexes { + for i in 0..next.len.try_into().expect("array too big") { + Self::for_each_target2( + StatePartIndex { + value: base + .value + .checked_add(next.stride.value.checked_mul(i).expect("array too big")) + .expect("array too big"), + _phantom: PhantomData, + }, + rest, + f, + ); + } + } else { + f(base); + } + } + pub(crate) fn for_each_target(self, mut f: impl FnMut(StatePartIndex)) { + Self::for_each_target2(self.base, &self.indexes, &mut f); + } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1435,6 +1671,26 @@ impl State { } } +impl BorrowedState<'_> { + fn eval_array_indexed( + &self, + array_indexed: StatePartArrayIndexed, + ) -> Option> { + let StatePartArrayIndexed { + base: mut retval, + indexes, + } = array_indexed; + for StatePartArrayIndex { index, len, stride } in indexes { + let index = self.small_slots[index]; + if index >= len as SmallUInt { + return None; + } + retval.value += stride.value * index as u32; + } + Some(retval) + } +} + fn bigint_pow2(width: usize) -> Interned { #[derive(Copy, Clone, PartialEq, Eq, Hash)] struct MyMemoize; @@ -1515,6 +1771,69 @@ impl_insns! { } next!(); } + CopySmall { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + state.small_slots[dest] = state.small_slots[src]; + next!(); + } + ReadIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } + } else { + state.big_slots[dest] = BigInt::ZERO; + } + next!(); + } + ReadSmallIndexed { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartArrayIndexed, + } => { + if let Some(src) = state.eval_array_indexed(src) { + state.small_slots[dest] = state.small_slots[src]; + } else { + state.small_slots[dest] = 0; + } + next!(); + } + WriteIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + if dest != src { + let [dest, src] = state.big_slots.get_many_mut([dest, src]); + dest.clone_from(src); + } + } + next!(); + } + WriteSmallIndexed { + #[kind = Output] + dest: StatePartArrayIndexed, + #[kind = Input] + src: StatePartIndex, + } => { + if let Some(dest) = state.eval_array_indexed(dest) { + state.small_slots[dest] = state.small_slots[src]; + } + next!(); + } CastToSInt { #[kind = Output] dest: StatePartIndex, @@ -1792,6 +2111,22 @@ impl_insns! { state.big_slots[dest] = value; next!(); } + ReduceBitXor { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + input_width: usize, + } => { + let src = &state.big_slots[src]; + let src = src & &*bigint_mask(input_width); + let value = BigInt::from( + src.to_biguint().expect("known to be non-negative").count_ones() % 2, + ); + state.big_slots[dest] = value; + next!(); + } Const { #[kind = Output] dest: StatePartIndex, From a6e40839ac64d751e807b28e89ef11bd2f956b40 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 13 Nov 2024 04:14:04 -0800 Subject: [PATCH 086/190] simulator WIP: use petgraph for topological sort over assignments --- Cargo.lock | 17 + Cargo.toml | 1 + crates/fayalite/Cargo.toml | 1 + crates/fayalite/src/sim.rs | 959 +++++++++++++++++++++---- crates/fayalite/src/sim/interpreter.rs | 54 +- 5 files changed, 887 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c280158..c768882 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,6 +315,7 @@ dependencies = [ "num-bigint", "num-traits", "os_pipe", + "petgraph", "serde", "serde_json", "tempfile", @@ -358,6 +359,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "funty" version = "2.0.0" @@ -515,6 +522,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "prettyplease" version = "0.2.20" diff --git a/Cargo.toml b/Cargo.toml index add4bfe..a9cc312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ jobslot = "0.2.19" num-bigint = "0.4.6" num-traits = "0.2.16" os_pipe = "1.2.1" +petgraph = "0.6.5" prettyplease = "0.2.20" proc-macro2 = "1.0.83" quote = "1.0.36" diff --git a/crates/fayalite/Cargo.toml b/crates/fayalite/Cargo.toml index 8e90a74..2652792 100644 --- a/crates/fayalite/Cargo.toml +++ b/crates/fayalite/Cargo.toml @@ -25,6 +25,7 @@ jobslot.workspace = true num-bigint.workspace = true num-traits.workspace = true os_pipe.workspace = true +petgraph.workspace = true serde_json.workspace = true serde.workspace = true tempfile.workspace = true diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index 8dc087b..b647302 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -21,16 +21,19 @@ use crate::{ prelude::*, sim::interpreter::{ Insn, InsnField, InsnFieldKind, InsnFieldType, Insns, InsnsBuilding, InsnsBuildingDone, - SlotDebugData, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, - StatePartIndexMap, StatePartIndexRange, StatePartKind, StatePartKindBigSlots, - StatePartKindSmallSlots, StatePartLayout, StatePartsValue, TypeArrayIndex, - TypeArrayIndexes, TypeIndex, TypeIndexRange, TypeLayout, TypeParts, + SlotDebugData, SmallUInt, StatePartArrayIndex, StatePartArrayIndexed, StatePartIndex, + StatePartIndexRange, StatePartKind, StatePartKindBigSlots, StatePartKindSmallSlots, + StatePartLayout, StatePartLen, StatePartsValue, TypeArrayIndex, TypeArrayIndexes, + TypeIndex, TypeIndexRange, TypeLayout, TypeLen, TypeParts, }, ty::StaticType, }; use hashbrown::HashMap; use num_bigint::BigInt; -use std::{collections::BTreeSet, fmt}; +use petgraph::visit::{ + GraphBase, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers, VisitMap, Visitable, +}; +use std::{collections::BTreeSet, fmt, marker::PhantomData, mem, ops::IndexMut}; mod interpreter; @@ -416,6 +419,385 @@ impl CompiledExpr { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentOrSlotIndex { + AssignmentIndex(usize), + SmallSlots(StatePartIndex), + BigSlots(StatePartIndex), +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +enum AssignmentIO { + BigInput { + assignment_index: usize, + slot: StatePartIndex, + }, + SmallInput { + assignment_index: usize, + slot: StatePartIndex, + }, + BigOutput { + assignment_index: usize, + slot: StatePartIndex, + }, + SmallOutput { + assignment_index: usize, + slot: StatePartIndex, + }, +} + +#[derive(Debug, Default)] +struct Assignments { + assignments: Vec, + slot_readers: Option, + slot_writers: Option, +} + +impl Assignments { + fn finalize(&mut self, slots_len: TypeLen) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); + let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); + for (assignment_index, assignment) in self.assignments.iter().enumerate() { + slot_readers + .keys_for_assignment(assignment_index) + .extend([&assignment.inputs]); + slot_writers + .keys_for_assignment(assignment_index) + .extend([&assignment.outputs]); + } + self.slot_readers = Some(slot_readers); + self.slot_writers = Some(slot_writers); + } + fn push(&mut self, v: Assignment) { + assert!( + self.slot_readers.is_none() && self.slot_writers.is_none(), + "already finalized" + ); + self.assignments.push(v); + } + fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_readers + .as_ref() + .expect("Assignments::finalize should have been called") + } + fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { + self.slot_writers + .as_ref() + .expect("Assignments::finalize should have been called") + } +} + +impl GraphBase for Assignments { + type EdgeId = AssignmentIO; + type NodeId = AssignmentOrSlotIndex; +} + +struct AssignmentsNodeIdentifiers { + assignment_indexes: std::ops::Range, + small_slots: std::ops::Range, + big_slots: std::ops::Range, +} + +impl Iterator for AssignmentsNodeIdentifiers { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + let Self { + assignment_indexes, + small_slots, + big_slots, + } = self; + assignment_indexes + .next() + .map(AssignmentOrSlotIndex::AssignmentIndex) + .or_else(|| { + small_slots.next().map(|value| { + AssignmentOrSlotIndex::SmallSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + .or_else(|| { + big_slots.next().map(|value| { + AssignmentOrSlotIndex::BigSlots(StatePartIndex { + value, + _phantom: PhantomData, + }) + }) + }) + } +} + +impl<'a> IntoNodeIdentifiers for &'a Assignments { + type NodeIdentifiers = AssignmentsNodeIdentifiers; + + fn node_identifiers(self) -> Self::NodeIdentifiers { + let TypeLen { + small_slots, + big_slots, + } = self.slot_readers().len(); + AssignmentsNodeIdentifiers { + assignment_indexes: 0..self.assignments.len(), + small_slots: 0..small_slots.value, + big_slots: 0..big_slots.value, + } + } +} + +enum AssignmentsNeighborsDirected<'a> { + AssignmentIndexes(std::slice::Iter<'a, usize>), + Slots { + small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, + big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, + }, +} + +impl Iterator for AssignmentsNeighborsDirected<'_> { + type Item = AssignmentOrSlotIndex; + fn next(&mut self) -> Option { + match self { + AssignmentsNeighborsDirected::AssignmentIndexes(iter) => iter + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex), + AssignmentsNeighborsDirected::Slots { + small_slots, + big_slots, + } => small_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::SmallSlots) + .or_else(|| { + big_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::BigSlots) + }), + } + } +} + +impl<'a> IntoNeighbors for &'a Assignments { + type Neighbors = AssignmentsNeighborsDirected<'a>; + + fn neighbors(self, n: Self::NodeId) -> Self::Neighbors { + self.neighbors_directed(n, petgraph::Direction::Outgoing) + } +} + +impl<'a> IntoNeighborsDirected for &'a Assignments { + type NeighborsDirected = AssignmentsNeighborsDirected<'a>; + + fn neighbors_directed( + self, + n: Self::NodeId, + d: petgraph::Direction, + ) -> Self::NeighborsDirected { + use petgraph::Direction::*; + let slot_map = match d { + Outgoing => self.slot_readers(), + Incoming => self.slot_writers(), + }; + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + let assignment = &self.assignments[assignment_index]; + let SlotSet(TypeParts { + small_slots, + big_slots, + }) = match d { + Outgoing => &assignment.outputs, + Incoming => &assignment.inputs, + }; + AssignmentsNeighborsDirected::Slots { + small_slots: small_slots.iter(), + big_slots: big_slots.iter(), + } + } + AssignmentOrSlotIndex::SmallSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + AssignmentOrSlotIndex::BigSlots(slot) => { + AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) + } + } + } +} + +struct AssignmentsVisitMap { + assignments: Vec, + slots: DenseSlotSet, +} + +impl VisitMap for AssignmentsVisitMap { + fn visit(&mut self, n: AssignmentOrSlotIndex) -> bool { + match n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + !mem::replace(&mut self.assignments[assignment_index], true) + } + AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.insert(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.insert(slot), + } + } + + fn is_visited(&self, n: &AssignmentOrSlotIndex) -> bool { + match *n { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { + self.assignments[assignment_index] + } + AssignmentOrSlotIndex::SmallSlots(slot) => self.slots.contains(slot), + AssignmentOrSlotIndex::BigSlots(slot) => self.slots.contains(slot), + } + } +} + +impl Visitable for Assignments { + type Map = AssignmentsVisitMap; + + fn visit_map(self: &Self) -> Self::Map { + AssignmentsVisitMap { + assignments: vec![false; self.assignments.len()], + slots: DenseSlotSet::new(self.slot_readers().len()), + } + } + + fn reset_map(self: &Self, map: &mut Self::Map) { + let AssignmentsVisitMap { assignments, slots } = map; + assignments.clear(); + assignments.resize(self.assignments.len(), false); + if slots.len() != self.slot_readers().len() { + *slots = DenseSlotSet::new(self.slot_readers().len()); + } else { + slots.clear(); + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +struct DenseSlotSet(TypeParts); + +impl DenseSlotSet { + fn new(len: TypeLen) -> Self { + let TypeLen { + small_slots, + big_slots, + } = len; + Self(TypeParts { + small_slots: vec![false; small_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + big_slots: vec![false; big_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + }) + } + fn len(&self) -> TypeLen { + TypeLen { + small_slots: StatePartLen { + value: self.0.small_slots.len() as _, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: self.0.big_slots.len() as _, + _phantom: PhantomData, + }, + } + } + fn clear(&mut self) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.fill(false); + big_slots.fill(false); + } +} + +impl StatePartsValue for DenseSlotSet { + type Value = Box<[bool]>; +} + +trait DenseSlotSetMethods: Extend> { + fn contains(&self, k: StatePartIndex) -> bool; + fn remove(&mut self, k: StatePartIndex) -> bool { + self.take(k).is_some() + } + fn take(&mut self, k: StatePartIndex) -> Option>; + fn replace(&mut self, k: StatePartIndex) -> Option>; + fn insert(&mut self, k: StatePartIndex) -> bool { + self.replace(k).is_none() + } +} + +impl Extend> for DenseSlotSet +where + Self: DenseSlotSetMethods, +{ + fn extend>>(&mut self, iter: T) { + iter.into_iter().for_each(|v| { + self.insert(v); + }); + } +} + +impl DenseSlotSetMethods for DenseSlotSet { + fn contains(&self, k: StatePartIndex) -> bool { + self.0.small_slots[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(self.0.small_slots.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(&mut self.0.small_slots[k.as_usize()], true).then_some(k) + } +} + +impl DenseSlotSetMethods for DenseSlotSet { + fn contains(&self, k: StatePartIndex) -> bool { + self.0.big_slots[k.as_usize()] + } + + fn take( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(self.0.big_slots.get_mut(k.as_usize())?, false).then_some(k) + } + + fn replace( + &mut self, + k: StatePartIndex, + ) -> Option> { + mem::replace(&mut self.0.big_slots[k.as_usize()], true).then_some(k) + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +struct SlotVec(TypeParts); + +impl SlotVec { + fn is_empty(&self) -> bool { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.is_empty() && big_slots.is_empty() + } +} + +impl StatePartsValue for SlotVec { + type Value = Vec>; +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] struct SlotSet(TypeParts); @@ -427,6 +809,30 @@ impl SlotSet { }) = self; small_slots.is_empty() && big_slots.is_empty() } + fn for_each( + &self, + small_slots_fn: impl FnMut(StatePartIndex), + big_slots_fn: impl FnMut(StatePartIndex), + ) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().copied().for_each(small_slots_fn); + big_slots.iter().copied().for_each(big_slots_fn); + } + fn all( + &self, + small_slots_fn: impl FnMut(StatePartIndex) -> bool, + big_slots_fn: impl FnMut(StatePartIndex) -> bool, + ) -> bool { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().copied().all(small_slots_fn) + && big_slots.iter().copied().all(big_slots_fn) + } } impl StatePartsValue for SlotSet { @@ -511,63 +917,221 @@ impl Extend for SlotSet { #[derive(Debug)] struct Assignment { inputs: SlotSet, + outputs: SlotSet, conditions: Interned<[Cond]>, insns: Vec, source_location: SourceLocation, } -#[derive(Debug, Default)] -struct StatePartAssignments { - written_slot_to_assignment_indexes_map: StatePartIndexMap>, +#[derive(Debug)] +struct SlotToAssignmentIndexFullMap(TypeParts); + +impl StatePartsValue for SlotToAssignmentIndexFullMap { + type Value = Box<[Vec]>; } -#[derive(Debug, Default)] -struct SlotAssignments { - assignments: Vec, - parts: TypeParts, -} - -impl SlotAssignments { - fn for_assignment(&mut self, assignment_index: usize) -> SlotAssignmentsForAssignment<'_> { - SlotAssignmentsForAssignment { - parts: &mut self.parts, +impl SlotToAssignmentIndexFullMap { + fn new(len: TypeLen) -> Self { + let TypeLen { + small_slots, + big_slots, + } = len; + const VEC_NEW: Vec = Vec::new(); + Self(TypeParts { + small_slots: vec![VEC_NEW; small_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + big_slots: vec![VEC_NEW; big_slots.value.try_into().expect("length too big")] + .into_boxed_slice(), + }) + } + fn len(&self) -> TypeLen { + TypeLen { + small_slots: StatePartLen { + value: self.0.small_slots.len() as _, + _phantom: PhantomData, + }, + big_slots: StatePartLen { + value: self.0.big_slots.len() as _, + _phantom: PhantomData, + }, + } + } + fn keys_for_assignment( + &mut self, + assignment_index: usize, + ) -> SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + SlotToAssignmentIndexFullMapKeysForAssignment { + map: self, assignment_index, } } -} - -impl StatePartsValue for SlotAssignments { - type Value = StatePartAssignments; -} - -struct SlotAssignmentsForAssignment<'a> { - parts: &'a mut TypeParts, - assignment_index: usize, -} - -impl Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .small_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(self.assignment_index) + fn for_each( + &self, + mut small_slots_fn: impl FnMut(StatePartIndex, &[usize]), + mut big_slots_fn: impl FnMut(StatePartIndex, &[usize]), + ) { + let Self(TypeParts { + small_slots, + big_slots, + }) = self; + small_slots.iter().enumerate().for_each(|(k, v)| { + small_slots_fn( + StatePartIndex { + value: k as _, + _phantom: PhantomData, + }, + v, + ) + }); + big_slots.iter().enumerate().for_each(|(k, v)| { + big_slots_fn( + StatePartIndex { + value: k as _, + _phantom: PhantomData, + }, + v, + ) }); } } -impl Extend> for SlotAssignmentsForAssignment<'_> { - fn extend>>(&mut self, iter: T) { - iter.into_iter().for_each(|slot| { - self.parts - .big_slots - .written_slot_to_assignment_indexes_map - .entry(slot) - .or_insert_with(Vec::new) - .push(self.assignment_index) - }); +impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.0.small_slots[index.as_usize()] + } +} + +impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.0.small_slots[index.as_usize()] + } +} + +impl std::ops::Index> for SlotToAssignmentIndexFullMap { + type Output = Vec; + + fn index(&self, index: StatePartIndex) -> &Self::Output { + &self.0.big_slots[index.as_usize()] + } +} + +impl std::ops::IndexMut> for SlotToAssignmentIndexFullMap { + fn index_mut(&mut self, index: StatePartIndex) -> &mut Self::Output { + &mut self.0.big_slots[index.as_usize()] + } +} + +struct SlotToAssignmentIndexFullMapKeysForAssignment<'a> { + map: &'a mut SlotToAssignmentIndexFullMap, + assignment_index: usize, +} + +impl<'a, K: StatePartKind> Extend<&'a StatePartIndex> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> +where + Self: Extend>, +{ + fn extend>>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()); + } +} + +impl Extend> + for SlotToAssignmentIndexFullMapKeysForAssignment<'_> +where + SlotToAssignmentIndexFullMap: IndexMut, Output = Vec>, +{ + fn extend>>(&mut self, iter: T) { + iter.into_iter() + .for_each(|slot| self.map[slot].push(self.assignment_index)); + } +} + +impl<'a> Extend<&'a SlotSet> for SlotToAssignmentIndexFullMapKeysForAssignment<'_> { + fn extend>(&mut self, iter: T) { + iter.into_iter().for_each( + |SlotSet(TypeParts { + small_slots, + big_slots, + })| { + self.extend(small_slots); + self.extend(big_slots); + }, + ); + } +} + +impl Assignment { + fn new( + conditions: Interned<[Cond]>, + insns: Vec, + source_location: SourceLocation, + ) -> Self { + let mut inputs = SlotSet::default(); + let mut outputs = SlotSet::default(); + for insn in &insns { + for InsnField { ty, kind } in insn.fields() { + match (kind, ty) { + (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { + inputs.extend([slot]); + } + (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { + inputs.extend([slot]); + } + ( + InsnFieldKind::Input, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| inputs.extend([slot])); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => { + outputs.extend([slot]); + } + (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => { + outputs.extend([slot]); + } + ( + InsnFieldKind::Output, + InsnFieldType::SmallSlotArrayIndexed(&array_indexed), + ) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + } + (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { + array_indexed.for_each_target(|slot| { + outputs.extend([slot]); + }); + inputs.extend(array_indexed.indexes); + } + ( + _, + InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::USize(_) + | InsnFieldType::Empty(_), + ) + | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} + } + } + } + Self { + inputs, + outputs, + conditions, + insns, + source_location, + } } } @@ -580,7 +1144,9 @@ pub struct Compiler { compiled_exprs: HashMap, CompiledExpr>, compiled_exprs_to_values: HashMap, CompiledValue>, decl_conditions: HashMap>, - slots_assignments: SlotAssignments, + compiled_values_to_dyn_array_indexes: + HashMap, StatePartIndex>, + assignments: Assignments, } impl Compiler { @@ -593,7 +1159,8 @@ impl Compiler { compiled_exprs: HashMap::new(), compiled_exprs_to_values: HashMap::new(), decl_conditions: HashMap::new(), - slots_assignments: SlotAssignments::default(), + compiled_values_to_dyn_array_indexes: HashMap::new(), + assignments: Assignments::default(), } } fn compile_value( @@ -728,74 +1295,8 @@ impl Compiler { source_location: SourceLocation, ) { let insns = Vec::from_iter(insns); - let assignment_index = self.slots_assignments.assignments.len(); - let mut inputs = SlotSet::default(); - for insn in &insns { - for InsnField { ty, kind } in insn.fields() { - match (kind, ty) { - (InsnFieldKind::Input, InsnFieldType::SmallSlot(&slot)) => { - inputs.extend([slot]); - } - (InsnFieldKind::Input, InsnFieldType::BigSlot(&slot)) => { - inputs.extend([slot]); - } - ( - InsnFieldKind::Input, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Input, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| inputs.extend([slot])); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::SmallSlot(&slot)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - (InsnFieldKind::Output, InsnFieldType::BigSlot(&slot)) => self - .slots_assignments - .for_assignment(assignment_index) - .extend([slot]), - ( - InsnFieldKind::Output, - InsnFieldType::SmallSlotArrayIndexed(&array_indexed), - ) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - (InsnFieldKind::Output, InsnFieldType::BigSlotArrayIndexed(&array_indexed)) => { - array_indexed.for_each_target(|slot| { - self.slots_assignments - .for_assignment(assignment_index) - .extend([slot]) - }); - inputs.extend(array_indexed.indexes); - } - ( - _, - InsnFieldType::SmallUInt(_) - | InsnFieldType::SmallSInt(_) - | InsnFieldType::InternedBigInt(_) - | InsnFieldType::U8(_) - | InsnFieldType::USize(_) - | InsnFieldType::Empty(_), - ) - | (InsnFieldKind::Immediate | InsnFieldKind::BranchTarget, _) => {} - } - } - } - self.slots_assignments.assignments.push(Assignment { - inputs, - conditions, - insns, - source_location, - }); + self.assignments + .push(Assignment::new(conditions, insns, source_location)); } fn simple_nary_big_expr( &mut self, @@ -839,6 +1340,70 @@ impl Compiler { ); retval } + fn compiled_value_to_dyn_array_index( + &mut self, + compiled_value: CompiledValue, + source_location: SourceLocation, + ) -> StatePartIndex { + if let Some(&retval) = self + .compiled_values_to_dyn_array_indexes + .get(&compiled_value) + { + return retval; + } + let retval = match compiled_value.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => compiled_value.range.small_slots.start, + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + } => { + let debug_data = SlotDebugData { + name: Interned::default(), + ty: UInt::<{ SmallUInt::BITS as usize }>::TYPE.canonical(), + }; + let dest = self + .insns + .allocate_variable(&TypeLayout { + small_slots: StatePartLayout::scalar(debug_data), + big_slots: StatePartLayout::empty(), + }) + .small_slots + .start; + self.add_assignment( + Interned::default(), + vec![Insn::CastBigToArrayIndex { + dest, + src: compiled_value.range.big_slots.start, + }], + source_location, + ); + dest + } + _ => unreachable!(), + }; + self.compiled_values_to_dyn_array_indexes + .insert(compiled_value, retval); + retval + } fn compile_expr( &mut self, instantiated_module: InstantiatedModule, @@ -1463,7 +2028,21 @@ impl Compiler { .compile_expr(instantiated_module, Expr::canonical(expr.base())) .map_ty(Array::from_canonical) .element(expr.element_index()), - ExprEnum::DynArrayIndex(expr) => todo!(), + ExprEnum::DynArrayIndex(expr) => { + let element_index = + self.compile_expr(instantiated_module, Expr::canonical(expr.element_index())); + let element_index = self.compiled_expr_to_value( + element_index, + instantiated_module.leaf_module().source_location(), + ); + let index_slot = self.compiled_value_to_dyn_array_index( + element_index.map_ty(UInt::from_canonical), + instantiated_module.leaf_module().source_location(), + ); + self.compile_expr(instantiated_module, Expr::canonical(expr.base())) + .map_ty(Array::from_canonical) + .element_dyn(index_slot) + } ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 { self.compile_expr(instantiated_module, Expr::canonical(true.to_expr())) } else { @@ -1534,8 +2113,36 @@ impl Compiler { }, ) .into(), - ExprEnum::SliceUInt(expr) => todo!(), - ExprEnum::SliceSInt(expr) => todo!(), + ExprEnum::SliceUInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), + ExprEnum::SliceSInt(expr) => self + .simple_nary_big_expr( + instantiated_module, + UInt::new_dyn(expr.range().len()).canonical(), + [Expr::canonical(expr.base())], + |dest, [src]| { + vec![Insn::SliceInt { + dest, + src, + start: expr.range().start, + len: expr.range().len(), + }] + }, + ) + .into(), ExprEnum::CastToBits(expr) => todo!(), ExprEnum::CastBitsTo(expr) => todo!(), ExprEnum::ModuleIO(expr) => self @@ -1927,34 +2534,104 @@ impl Compiler { }; entry.insert(CompiledModule { module_io }) } - pub fn compile(mut self) -> Compiled { - self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); - todo!("handle self.slots_assignments"); + fn process_assignments(&mut self) { + self.assignments + .finalize(self.insns.state_layout().len().ty); + let assignments_queue: Vec = match petgraph::algo::toposort(&self.assignments, None) + { + Ok(nodes) => nodes + .into_iter() + .filter_map(|n| match n { + AssignmentOrSlotIndex::AssignmentIndex(v) => Some(v), + _ => None, + }) + .collect(), + Err(e) => match e.node_id() { + AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( + "combinatorial logic cycle detected at: {}", + self.assignments.assignments[assignment_index].source_location, + ), + AssignmentOrSlotIndex::SmallSlots(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.small_slots.debug_data[slot.as_usize()].name, + ), + AssignmentOrSlotIndex::BigSlots(slot) => panic!( + "combinatorial logic cycle detected through: {}", + self.insns.state_layout().ty.big_slots.debug_data[slot.as_usize()].name, + ), + }, + }; + todo!(); + } + pub fn compile(mut self) -> Compiled { + let base_module = + *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); + self.process_assignments(); Compiled { insns: Insns::from(self.insns).intern_sized(), - modules: self.modules, + base_module, + base_module_io_ty: self.base_module.io_ty(), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +struct CompiledModule { + module_io: Interned<[CompiledValue]>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Compiled { + insns: Interned>, + base_module: CompiledModule, + base_module_io_ty: T, +} + +impl Compiled { + pub fn new(module: Module) -> Self { + Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) + } + pub fn canonical(self) -> Compiled { + let Self { + insns, + base_module, + base_module_io_ty, + } = self; + Compiled { + insns, + base_module, + base_module_io_ty: Bundle::from_canonical(base_module_io_ty.canonical()), + } + } + pub fn from_canonical(canonical: Compiled) -> Self { + let Compiled { + insns, + base_module, + base_module_io_ty, + } = canonical; + Self { + insns, + base_module, + base_module_io_ty: T::from_canonical(base_module_io_ty.canonical()), } } } #[derive(Debug)] -struct CompiledModule { - module_io: Interned<[CompiledValue]>, +pub struct Simulation { + state: interpreter::State, + compiled: Compiled, } -#[derive(Debug)] -pub struct Compiled { - insns: Interned>, - modules: HashMap, -} - -impl Compiled { - pub fn new(module: Module) -> Self { - Compiler::new(module.canonical().intern()).compile() +impl Simulation { + pub fn from_compiled(compiled: Compiled) -> Self { + Self { + state: interpreter::State::new(compiled.insns), + compiled, + } + } + pub fn settle_step(&mut self) { + self.state.setup_call(0); + self.state.run(); } } - -#[derive(Debug)] -pub struct Simulation { - state: interpreter::State, -} diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index e89628c..5269131 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -362,6 +362,12 @@ pub(crate) struct Insns { state_layout: StateLayout, } +impl Insns { + pub(crate) fn state_layout(&self) -> &StateLayout { + &self.state_layout + } +} + struct InsnsDebug<'a> { insns: &'a [Insn], insn_source_locations: &'a [SourceLocation], @@ -564,13 +570,13 @@ macro_rules! make_state_part_kinds { impl Copy for StateLayout {} impl StateLayout { - pub(crate) fn len(self) -> StateLen { + pub(crate) fn len(&self) -> StateLen { StateLen { ty: self.ty.len(), $($state_field: self.$state_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { self.ty.is_empty() $(&& self.$state_field.is_empty())* } pub(crate) fn empty() -> Self { @@ -652,12 +658,12 @@ macro_rules! make_state_part_kinds { impl Copy for TypeLayout {} impl TypeLayout { - pub(crate) fn len(self) -> TypeLen { + pub(crate) fn len(&self) -> TypeLen { TypeLen { $($type_field: self.$type_field.len(),)* } } - pub(crate) fn is_empty(self) -> bool { + pub(crate) fn is_empty(&self) -> bool { $(self.$type_field.is_empty())&&+ } pub(crate) fn empty() -> Self { @@ -762,6 +768,14 @@ macro_rules! make_state_part_kinds { } impl State { + pub(crate) fn new(insns: Interned>) -> Self { + Self { + insns, + pc: 0, + $($state_field: StatePart::new(insns.state_layout.$state_field.len()),)* + $($type_field: StatePart::new(insns.state_layout.ty.$type_field.len()),)* + } + } pub(crate) fn borrow(&mut self) -> BorrowedState<'_> { BorrowedState { orig_insns: self.insns, @@ -1494,6 +1508,13 @@ impl fmt::Debug for StatePartIndexMap { } } +impl Extend<(StatePartIndex, V)> for StatePartIndexMap { + fn extend, V)>>(&mut self, iter: T) { + self.map + .extend(iter.into_iter().map(|(k, v)| (k.as_usize(), v))); + } +} + impl StatePartIndexMap { pub(crate) fn new() -> Self { Self { @@ -1834,6 +1855,17 @@ impl_insns! { } next!(); } + CastBigToArrayIndex { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + } => { + let src = &state.big_slots[src]; + let value = src.try_into().unwrap_or(SmallUInt::MAX); + state.small_slots[dest] = value; + next!(); + } CastToSInt { #[kind = Output] dest: StatePartIndex, @@ -2055,6 +2087,20 @@ impl_insns! { state.big_slots[dest] = value; next!(); } + SliceInt { + #[kind = Output] + dest: StatePartIndex, + #[kind = Input] + src: StatePartIndex, + #[kind = Immediate] + start: usize, + #[kind = Immediate] + len: usize, + } => { + let value = &state.big_slots[src] >> start; + state.big_slots[dest] = value & &*bigint_mask(len); + next!(); + } CmpEq { #[kind = Output] dest: StatePartIndex, From f54e55a14330d1e10e2b6da13510d77e016850bc Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Wed, 13 Nov 2024 21:20:15 -0800 Subject: [PATCH 087/190] Simulation::settle_step() works for simple modules --- crates/fayalite/src/sim.rs | 485 +++++++++++++++-------- crates/fayalite/src/sim/interpreter.rs | 378 +++++++++++++++++- crates/fayalite/tests/sim.rs | 524 +++++++++++++++++++++++++ 3 files changed, 1221 insertions(+), 166 deletions(-) create mode 100644 crates/fayalite/tests/sim.rs diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index b647302..7793dce 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -40,13 +40,13 @@ mod interpreter; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] enum CondBody { IfTrue { - cond: CompiledExpr, + cond: CompiledValue, }, IfFalse { - cond: CompiledExpr, + cond: CompiledValue, }, MatchArm { - enum_expr: CompiledExpr, + enum_expr: CompiledValue, variant_index: usize, }, } @@ -267,8 +267,8 @@ impl CompiledValue { } impl CompiledValue { - fn add_discriminant_to_set(self, offset: TypeIndex, inputs: &mut SlotSet) { - inputs.extend([self.range.offset(offset)]); + fn add_discriminant_to_set(self, inputs: &mut SlotSet) { + inputs.extend([self.range]); } } @@ -359,19 +359,6 @@ impl CompiledExpr { } } -impl CompiledExpr { - fn add_discriminant_and_indexes_to_set(self, inputs: &mut SlotSet) { - let Self { - static_part, - indexes, - } = self; - indexes.as_ref().for_each_offset(|offset| { - static_part.add_discriminant_to_set(offset, inputs); - }); - inputs.extend(indexes.as_ref().iter()); - } -} - impl CompiledExpr { fn field_by_index(self, field_index: usize) -> CompiledExpr { CompiledExpr { @@ -446,48 +433,118 @@ enum AssignmentIO { }, } -#[derive(Debug, Default)] -struct Assignments { - assignments: Vec, - slot_readers: Option, - slot_writers: Option, +#[derive(Debug)] +enum Assignments { + Accumulating { + assignments: Vec, + }, + Finalized { + assignments: Box<[Assignment]>, + slot_readers: SlotToAssignmentIndexFullMap, + slot_writers: SlotToAssignmentIndexFullMap, + assignment_immediate_predecessors: Box<[Box<[usize]>]>, + assignment_immediate_successors: Box<[Box<[usize]>]>, + }, +} + +impl Default for Assignments { + fn default() -> Self { + Self::Accumulating { + assignments: Vec::new(), + } + } } impl Assignments { fn finalize(&mut self, slots_len: TypeLen) { - assert!( - self.slot_readers.is_none() && self.slot_writers.is_none(), - "already finalized" - ); + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + let assignments = mem::take(assignments).into_boxed_slice(); let mut slot_readers = SlotToAssignmentIndexFullMap::new(slots_len); let mut slot_writers = SlotToAssignmentIndexFullMap::new(slots_len); - for (assignment_index, assignment) in self.assignments.iter().enumerate() { + let mut assignment_immediate_predecessors = vec![BTreeSet::new(); assignments.len()]; + let mut assignment_immediate_successors = vec![BTreeSet::new(); assignments.len()]; + for (assignment_index, assignment) in assignments.iter().enumerate() { slot_readers .keys_for_assignment(assignment_index) .extend([&assignment.inputs]); - slot_writers - .keys_for_assignment(assignment_index) - .extend([&assignment.outputs]); + let SlotSet(TypeParts { + small_slots, + big_slots, + }) = &assignment.outputs; + for &slot in small_slots { + if let Some(&pred) = slot_writers[slot].last() { + assignment_immediate_predecessors[assignment_index].insert(pred); + assignment_immediate_successors[pred].insert(assignment_index); + } + slot_writers[slot].push(assignment_index); + } + for &slot in big_slots { + if let Some(&pred) = slot_writers[slot].last() { + assignment_immediate_predecessors[assignment_index].insert(pred); + assignment_immediate_successors[pred].insert(assignment_index); + } + slot_writers[slot].push(assignment_index); + } } - self.slot_readers = Some(slot_readers); - self.slot_writers = Some(slot_writers); + *self = Self::Finalized { + assignments, + slot_readers, + slot_writers, + assignment_immediate_predecessors: assignment_immediate_predecessors + .into_iter() + .map(Box::from_iter) + .collect(), + assignment_immediate_successors: assignment_immediate_successors + .into_iter() + .map(Box::from_iter) + .collect(), + }; } fn push(&mut self, v: Assignment) { - assert!( - self.slot_readers.is_none() && self.slot_writers.is_none(), - "already finalized" - ); - self.assignments.push(v); + let Self::Accumulating { assignments } = self else { + unreachable!("already finalized"); + }; + assignments.push(v); + } + fn assignments(&self) -> &[Assignment] { + let Self::Finalized { assignments, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + assignments } fn slot_readers(&self) -> &SlotToAssignmentIndexFullMap { - self.slot_readers - .as_ref() - .expect("Assignments::finalize should have been called") + let Self::Finalized { slot_readers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_readers } fn slot_writers(&self) -> &SlotToAssignmentIndexFullMap { - self.slot_writers - .as_ref() - .expect("Assignments::finalize should have been called") + let Self::Finalized { slot_writers, .. } = self else { + unreachable!("Assignments::finalize should have been called"); + }; + slot_writers + } + fn assignment_immediate_predecessors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_predecessors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_predecessors + } + fn assignment_immediate_successors(&self) -> &[Box<[usize]>] { + let Self::Finalized { + assignment_immediate_successors, + .. + } = self + else { + unreachable!("Assignments::finalize should have been called"); + }; + assignment_immediate_successors } } @@ -541,42 +598,47 @@ impl<'a> IntoNodeIdentifiers for &'a Assignments { big_slots, } = self.slot_readers().len(); AssignmentsNodeIdentifiers { - assignment_indexes: 0..self.assignments.len(), + assignment_indexes: 0..self.assignments().len(), small_slots: 0..small_slots.value, big_slots: 0..big_slots.value, } } } -enum AssignmentsNeighborsDirected<'a> { - AssignmentIndexes(std::slice::Iter<'a, usize>), - Slots { - small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, - }, +struct AssignmentsNeighborsDirected<'a> { + assignment_indexes: std::slice::Iter<'a, usize>, + small_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, + big_slots: std::collections::btree_set::Iter<'a, StatePartIndex>, } impl Iterator for AssignmentsNeighborsDirected<'_> { type Item = AssignmentOrSlotIndex; fn next(&mut self) -> Option { - match self { - AssignmentsNeighborsDirected::AssignmentIndexes(iter) => iter - .next() - .copied() - .map(AssignmentOrSlotIndex::AssignmentIndex), - AssignmentsNeighborsDirected::Slots { - small_slots, - big_slots, - } => small_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::SmallSlots) - .or_else(|| { - big_slots - .next() - .copied() - .map(AssignmentOrSlotIndex::BigSlots) - }), + let Self { + assignment_indexes, + small_slots, + big_slots, + } = self; + if let retval @ Some(_) = assignment_indexes + .next() + .copied() + .map(AssignmentOrSlotIndex::AssignmentIndex) + { + retval + } else if let retval @ Some(_) = small_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::SmallSlots) + { + retval + } else if let retval @ Some(_) = big_slots + .next() + .copied() + .map(AssignmentOrSlotIndex::BigSlots) + { + retval + } else { + None } } } @@ -604,25 +666,39 @@ impl<'a> IntoNeighborsDirected for &'a Assignments { }; match n { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => { - let assignment = &self.assignments[assignment_index]; - let SlotSet(TypeParts { - small_slots, - big_slots, - }) = match d { - Outgoing => &assignment.outputs, - Incoming => &assignment.inputs, + let assignment = &self.assignments()[assignment_index]; + let ( + assignment_indexes, + SlotSet(TypeParts { + small_slots, + big_slots, + }), + ) = match d { + Outgoing => ( + &self.assignment_immediate_successors()[assignment_index], + &assignment.outputs, + ), + Incoming => ( + &self.assignment_immediate_predecessors()[assignment_index], + &assignment.inputs, + ), }; - AssignmentsNeighborsDirected::Slots { + AssignmentsNeighborsDirected { + assignment_indexes: assignment_indexes.iter(), small_slots: small_slots.iter(), big_slots: big_slots.iter(), } } - AssignmentOrSlotIndex::SmallSlots(slot) => { - AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) - } - AssignmentOrSlotIndex::BigSlots(slot) => { - AssignmentsNeighborsDirected::AssignmentIndexes(slot_map[slot].iter()) - } + AssignmentOrSlotIndex::SmallSlots(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + small_slots: Default::default(), + big_slots: Default::default(), + }, + AssignmentOrSlotIndex::BigSlots(slot) => AssignmentsNeighborsDirected { + assignment_indexes: slot_map[slot].iter(), + small_slots: Default::default(), + big_slots: Default::default(), + }, } } } @@ -659,7 +735,7 @@ impl Visitable for Assignments { fn visit_map(self: &Self) -> Self::Map { AssignmentsVisitMap { - assignments: vec![false; self.assignments.len()], + assignments: vec![false; self.assignments().len()], slots: DenseSlotSet::new(self.slot_readers().len()), } } @@ -667,7 +743,7 @@ impl Visitable for Assignments { fn reset_map(self: &Self, map: &mut Self::Map) { let AssignmentsVisitMap { assignments, slots } = map; assignments.clear(); - assignments.resize(self.assignments.len(), false); + assignments.resize(self.assignments().len(), false); if slots.len() != self.slot_readers().len() { *slots = DenseSlotSet::new(self.slot_readers().len()); } else { @@ -898,12 +974,12 @@ impl Extend for SlotSet { fn extend>(&mut self, iter: T) { iter.into_iter().for_each(|cond_body| match cond_body { CondBody::IfTrue { cond } | CondBody::IfFalse { cond } => { - cond.add_target_and_indexes_to_set(self); + self.extend([cond.range]); } CondBody::MatchArm { enum_expr, - variant_index, - } => enum_expr.add_discriminant_and_indexes_to_set(self), + variant_index: _, + } => enum_expr.add_discriminant_to_set(self), }) } } @@ -936,11 +1012,10 @@ impl SlotToAssignmentIndexFullMap { small_slots, big_slots, } = len; - const VEC_NEW: Vec = Vec::new(); Self(TypeParts { - small_slots: vec![VEC_NEW; small_slots.value.try_into().expect("length too big")] + small_slots: vec![Vec::new(); small_slots.value.try_into().expect("length too big")] .into_boxed_slice(), - big_slots: vec![VEC_NEW; big_slots.value.try_into().expect("length too big")] + big_slots: vec![Vec::new(); big_slots.value.try_into().expect("length too big")] .into_boxed_slice(), }) } @@ -1237,54 +1312,51 @@ impl Compiler { static_part, indexes, } = expr; - let layout = static_part.layout.with_anonymized_debug_info(); - let retval = CompiledValue { - layout, - range: self.insns.allocate_variable(&layout.layout), - write: None, + let retval = if indexes.as_ref().is_empty() { + CompiledValue { + layout: static_part.layout, + range: static_part.range, + write: None, + } + } else { + let layout = static_part.layout.with_anonymized_debug_info(); + let retval = CompiledValue { + layout, + range: self.insns.allocate_variable(&layout.layout), + write: None, + }; + let TypeIndexRange { + small_slots, + big_slots, + } = retval.range; + self.add_assignment( + Interned::default(), + small_slots + .iter() + .zip(static_part.range.small_slots.iter()) + .map(|(dest, base)| Insn::ReadSmallIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.small_slots, + }, + }) + .chain( + big_slots + .iter() + .zip(static_part.range.big_slots.iter()) + .map(|(dest, base)| Insn::ReadIndexed { + dest, + src: StatePartArrayIndexed { + base, + indexes: indexes.big_slots, + }, + }), + ), + source_location, + ); + retval }; - let TypeIndexRange { - small_slots, - big_slots, - } = retval.range; - self.add_assignment( - Interned::default(), - small_slots - .iter() - .zip(static_part.range.small_slots.iter()) - .map(|(dest, base)| { - if indexes.small_slots.is_empty() { - Insn::CopySmall { dest, src: base } - } else { - Insn::ReadSmallIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.small_slots, - }, - } - } - }) - .chain( - big_slots - .iter() - .zip(static_part.range.big_slots.iter()) - .map(|(dest, base)| { - if indexes.big_slots.is_empty() { - Insn::Copy { dest, src: base } - } else { - Insn::ReadIndexed { - dest, - src: StatePartArrayIndexed { - base, - indexes: indexes.big_slots, - }, - } - } - }), - ), - source_location, - ); self.compiled_exprs_to_values.insert(expr, retval); retval } @@ -2456,9 +2528,9 @@ impl Compiler { source_location, blocks: [then_block, else_block], }) => { - let cond = self - .compile_expr(*parent_module, Expr::canonical(cond)) - .map_ty(Bool::from_canonical); + let cond = self.compile_expr(*parent_module, Expr::canonical(cond)); + let cond = self.compiled_expr_to_value(cond, source_location); + let cond = cond.map_ty(Bool::from_canonical); self.compile_block( parent_module, then_block, @@ -2481,9 +2553,9 @@ impl Compiler { source_location, blocks, }) => { - let enum_expr = self - .compile_expr(*parent_module, Expr::canonical(expr)) - .map_ty(Enum::from_canonical); + let enum_expr = self.compile_expr(*parent_module, Expr::canonical(expr)); + let enum_expr = self.compiled_expr_to_value(enum_expr, source_location); + let enum_expr = enum_expr.map_ty(Enum::from_canonical); for (variant_index, block) in blocks.into_iter().enumerate() { self.compile_block( parent_module, @@ -2514,10 +2586,12 @@ impl Compiler { annotations: _, module_io, }| { - self.compile_value(TargetInInstantiatedModule { + let target = TargetInInstantiatedModule { instantiated_module: *module, target: Target::from(module_io), - }) + }; + self.decl_conditions.insert(target, Interned::default()); + self.compile_value(target) }, ) .collect(); @@ -2537,8 +2611,7 @@ impl Compiler { fn process_assignments(&mut self) { self.assignments .finalize(self.insns.state_layout().len().ty); - let assignments_queue: Vec = match petgraph::algo::toposort(&self.assignments, None) - { + let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { Ok(nodes) => nodes .into_iter() .filter_map(|n| match n { @@ -2549,7 +2622,7 @@ impl Compiler { Err(e) => match e.node_id() { AssignmentOrSlotIndex::AssignmentIndex(assignment_index) => panic!( "combinatorial logic cycle detected at: {}", - self.assignments.assignments[assignment_index].source_location, + self.assignments.assignments()[assignment_index].source_location, ), AssignmentOrSlotIndex::SmallSlots(slot) => panic!( "combinatorial logic cycle detected through: {}", @@ -2561,12 +2634,113 @@ impl Compiler { ), }, }; - todo!(); + struct CondStackEntry<'a> { + cond: &'a Cond, + end_label_index: usize, + } + let mut cond_stack = Vec::>::new(); + for assignment_index in assignments_order { + let Assignment { + inputs: _, + outputs: _, + conditions, + insns, + source_location, + } = &self.assignments.assignments()[assignment_index]; + let mut same_len = 0; + for (index, (entry, cond)) in cond_stack.iter().zip(conditions).enumerate() { + if entry.cond != cond { + break; + } + same_len = index + 1; + } + while cond_stack.len() > same_len { + let CondStackEntry { + cond: _, + end_label_index, + } = cond_stack.pop().expect("just checked len"); + self.insns.define_label_at_next_insn(end_label_index); + } + for cond in &conditions[cond_stack.len()..] { + let end_label_index = self.insns.new_label(); + match cond.body { + CondBody::IfTrue { cond: cond_value } + | CondBody::IfFalse { cond: cond_value } => { + let (branch_if_zero, branch_if_non_zero) = match cond_value.range.len() { + TypeLen { + small_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + } => ( + Insn::BranchIfSmallZero { + target: end_label_index, + value: cond_value.range.small_slots.start, + }, + Insn::BranchIfSmallNonZero { + target: end_label_index, + value: cond_value.range.small_slots.start, + }, + ), + TypeLen { + small_slots: + StatePartLen { + value: 0, + _phantom: _, + }, + big_slots: + StatePartLen { + value: 1, + _phantom: _, + }, + } => ( + Insn::BranchIfZero { + target: end_label_index, + value: cond_value.range.big_slots.start, + }, + Insn::BranchIfNonZero { + target: end_label_index, + value: cond_value.range.big_slots.start, + }, + ), + _ => unreachable!(), + }; + self.insns.push( + if let CondBody::IfTrue { .. } = cond.body { + branch_if_zero + } else { + branch_if_non_zero + }, + cond.source_location, + ); + } + CondBody::MatchArm { + enum_expr, + variant_index, + } => todo!(), + } + cond_stack.push(CondStackEntry { + cond, + end_label_index, + }); + } + for insn in insns { + self.insns.push(*insn, *source_location); + } + } } pub fn compile(mut self) -> Compiled { let base_module = *self.compile_module(InstantiatedModule::Base(self.base_module).intern_sized()); self.process_assignments(); + self.insns + .push(Insn::Return, self.base_module.source_location()); Compiled { insns: Insns::from(self.insns).intern_sized(), base_module, @@ -2588,7 +2762,7 @@ pub struct Compiled { } impl Compiled { - pub fn new(module: Module) -> Self { + pub fn new(module: Interned>) -> Self { Self::from_canonical(Compiler::new(module.canonical().intern()).compile()) } pub fn canonical(self) -> Compiled { @@ -2620,14 +2794,19 @@ impl Compiled { #[derive(Debug)] pub struct Simulation { state: interpreter::State, - compiled: Compiled, + base_module: CompiledModule, + base_module_io_ty: T, } impl Simulation { + pub fn new(module: Interned>) -> Self { + Self::from_compiled(Compiled::new(module)) + } pub fn from_compiled(compiled: Compiled) -> Self { Self { state: interpreter::State::new(compiled.insns), - compiled, + base_module: compiled.base_module, + base_module_io_ty: compiled.base_module_io_ty, } } pub fn settle_step(&mut self) { diff --git a/crates/fayalite/src/sim/interpreter.rs b/crates/fayalite/src/sim/interpreter.rs index 5269131..e713300 100644 --- a/crates/fayalite/src/sim/interpreter.rs +++ b/crates/fayalite/src/sim/interpreter.rs @@ -7,6 +7,7 @@ use crate::{ ty::CanonicalType, util::get_many_mut, }; +use hashbrown::HashMap; use num_bigint::BigInt; use num_traits::{One, Signed, ToPrimitive, Zero}; use std::{ @@ -166,6 +167,83 @@ fn make_array_into_iter( retval } +impl fmt::Debug for Insn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug_fmt::(f, None, None) + } +} + +impl Insn { + fn debug_fmt( + &self, + f: &mut fmt::Formatter<'_>, + labels: Option<&Labels>, + state_layout: Option<&StateLayout>, + ) -> fmt::Result { + let (insn_name, fields) = self.fields_with_names(); + write!(f, "{insn_name}")?; + if fields.len() == 0 { + return Ok(()); + } + writeln!(f, " {{")?; + for (field_name, field) in fields { + write!(f, " {field_name}: ")?; + match field.kind { + InsnFieldKind::BranchTarget => match field.ty { + InsnFieldType::USize(&label_index) => { + if let Some(labels) = labels { + write!(f, "L{label_index}")?; + if let Some(label) = labels.labels.get(label_index) { + if let Some(address) = label.address { + write!(f, " (at {address})")?; + } else { + write!(f, " (not yet defined)")?; + } + } else { + write!(f, " (invalid)")?; + } + writeln!(f, ",")?; + continue; + } + } + InsnFieldType::SmallSlot(_) + | InsnFieldType::BigSlot(_) + | InsnFieldType::SmallSlotArrayIndexed(_) + | InsnFieldType::BigSlotArrayIndexed(_) + | InsnFieldType::SmallUInt(_) + | InsnFieldType::SmallSInt(_) + | InsnFieldType::InternedBigInt(_) + | InsnFieldType::U8(_) + | InsnFieldType::Empty(_) => {} + }, + InsnFieldKind::Input | InsnFieldKind::Output | InsnFieldKind::Immediate => {} + } + match field.ty { + InsnFieldType::SmallSlot(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::BigSlot(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::SmallSlotArrayIndexed(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::BigSlotArrayIndexed(v) => { + v.debug_fmt(f, ",", " // ", "", state_layout)?; + } + InsnFieldType::SmallUInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::SmallSInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::InternedBigInt(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::U8(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::USize(v) => fmt::Debug::fmt(v, f)?, + InsnFieldType::Empty(v) => fmt::Debug::fmt(v, f)?, + } + writeln!(f, ",")?; + } + write!(f, "}}") + } +} + macro_rules! impl_insns { ( #[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident] @@ -191,7 +269,7 @@ macro_rules! impl_insns { })? => $block:block )* ) => { - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + #[derive(Copy, Clone, Eq, PartialEq, Hash)] $vis enum $Insn { $( $(#[$insn_meta])* @@ -249,6 +327,28 @@ macro_rules! impl_insns { )* } } + $vis fn fields_with_names<'a>(&'a self) -> (&'static str, std::array::IntoIter<(&'static str, InsnField>), { $Insn::MAX_FIELDS }>) { + match self { + $( + $Insn::$insn_name $({ + $($field_name,)* + })? => ( + stringify!($insn_name), + make_array_into_iter([ + $($((stringify!($field_name), InsnField { + ty: <$field_ty as InsnFieldTrait>::variant($field_name), + kind: InsnFieldKind::$field_kind, + }),)*)? + ], + || ("", InsnField { + ty: InsnFieldType::empty(), + kind: InsnFieldKind::Immediate, + }), + ), + ), + )* + } + } $vis fn fields_mut<'a>(&'a mut self) -> std::array::IntoIter>, { $Insn::MAX_FIELDS }> { match self { $( @@ -339,6 +439,8 @@ pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default { + Default + Deref + FromIterator; + type Labels: Default; + fn labels(labels: &Self::Labels) -> Option<&Labels>; } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] @@ -346,6 +448,10 @@ pub(crate) struct InsnsBuildingDone; impl InsnsBuildingKind for InsnsBuildingDone { type Vec = Interned<[T]>; + type Labels = (); + fn labels(_labels: &Self::Labels) -> Option<&Labels> { + None + } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] @@ -353,12 +459,27 @@ pub(crate) struct InsnsBuilding; impl InsnsBuildingKind for InsnsBuilding { type Vec = Vec; + type Labels = Labels; + fn labels(labels: &Self::Labels) -> Option<&Labels> { + Some(labels) + } +} + +struct Label { + address: Option, +} + +#[derive(Default)] +pub(crate) struct Labels { + labels: Vec