forked from libre-chip/fayalite
Compare commits
9 commits
86a1bb46be
...
450e1004b6
Author | SHA1 | Date | |
---|---|---|---|
450e1004b6 | |||
c0c5b550bc | |||
2fa0ea6192 | |||
bd75fdfefd | |||
50c86e18dc | |||
60734cc9d1 | |||
3458c21f44 | |||
43797db36e | |||
cdd84953d0 |
28 changed files with 1909 additions and 70 deletions
|
@ -12,10 +12,10 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||||
id: restore-deps
|
id: restore-deps
|
||||||
with:
|
with:
|
||||||
path: deps
|
path: deps
|
||||||
|
@ -58,19 +58,19 @@ jobs:
|
||||||
- name: Get SymbiYosys
|
- name: Get SymbiYosys
|
||||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby
|
git clone --depth=1 --branch=yosys-0.45 https://git.libre-chip.org/mirrors/sby deps/sby
|
||||||
- name: Build Z3
|
- name: Build Z3
|
||||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3
|
git clone --depth=1 --recursive --branch=z3-4.13.3 https://git.libre-chip.org/mirrors/z3 deps/z3
|
||||||
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
||||||
make -C deps/z3/build -j"$(nproc)"
|
make -C deps/z3/build -j"$(nproc)"
|
||||||
- name: Build Yosys
|
- name: Build Yosys
|
||||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
run: |
|
run: |
|
||||||
git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys
|
git clone --depth=1 --recursive --branch=0.45 https://git.libre-chip.org/mirrors/yosys deps/yosys
|
||||||
make -C deps/yosys -j"$(nproc)"
|
make -C deps/yosys -j"$(nproc)"
|
||||||
- uses: https://code.forgejo.org/actions/cache/save@v3
|
- uses: https://git.libre-chip.org/mirrors/cache/save@v3
|
||||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
path: deps
|
path: deps
|
||||||
|
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
runs-on: debian-12
|
runs-on: debian-12
|
||||||
needs: deps
|
needs: deps
|
||||||
steps:
|
steps:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
- uses: https://git.libre-chip.org/mirrors/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: |
|
- run: |
|
||||||
|
@ -41,7 +41,7 @@ jobs:
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.82.0
|
||||||
source "$HOME/.cargo/env"
|
source "$HOME/.cargo/env"
|
||||||
echo "$PATH" >> "$GITHUB_PATH"
|
echo "$PATH" >> "$GITHUB_PATH"
|
||||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
- uses: https://git.libre-chip.org/mirrors/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
path: deps
|
path: deps
|
||||||
key: ${{ needs.deps.outputs.cache-primary-key }}
|
key: ${{ needs.deps.outputs.cache-primary-key }}
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
make -C deps/yosys install
|
make -C deps/yosys install
|
||||||
export PATH="$(realpath deps/firtool/bin):$PATH"
|
export PATH="$(realpath deps/firtool/bin):$PATH"
|
||||||
echo "$PATH" >> "$GITHUB_PATH"
|
echo "$PATH" >> "$GITHUB_PATH"
|
||||||
- uses: https://github.com/Swatinem/rust-cache@v2
|
- uses: https://git.libre-chip.org/mirrors/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
- run: cargo test
|
- run: cargo test
|
||||||
|
|
|
@ -83,6 +83,7 @@ impl ParsedBundle {
|
||||||
custom_bounds,
|
custom_bounds,
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
|
cmp_eq: _,
|
||||||
} = options.body;
|
} = options.body;
|
||||||
let mut fields = match fields {
|
let mut fields = match fields {
|
||||||
syn::Fields::Named(fields) => fields,
|
syn::Fields::Named(fields) => fields,
|
||||||
|
@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle {
|
||||||
custom_bounds: _,
|
custom_bounds: _,
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
|
cmp_eq,
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let mut item_attrs = attrs.clone();
|
let mut item_attrs = attrs.clone();
|
||||||
|
@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.to_tokens(tokens);
|
.to_tokens(tokens);
|
||||||
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
|
let mut where_clause =
|
||||||
|
Generics::from(generics)
|
||||||
|
.where_clause
|
||||||
|
.unwrap_or_else(|| syn::WhereClause {
|
||||||
|
where_token: Token,
|
||||||
|
predicates: Punctuated::new(),
|
||||||
|
});
|
||||||
|
let mut fields_cmp_eq = vec![];
|
||||||
|
let mut fields_cmp_ne = vec![];
|
||||||
|
for field in fields.named() {
|
||||||
|
let field_ident = field.ident();
|
||||||
|
let field_ty = field.ty();
|
||||||
|
where_clause
|
||||||
|
.predicates
|
||||||
|
.push(parse_quote_spanned! {cmp_eq.span=>
|
||||||
|
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
|
||||||
|
});
|
||||||
|
fields_cmp_eq.push(quote_spanned! {span=>
|
||||||
|
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
|
||||||
|
});
|
||||||
|
fields_cmp_ne.push(quote_spanned! {span=>
|
||||||
|
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let cmp_eq_body;
|
||||||
|
let cmp_ne_body;
|
||||||
|
if fields_len == 0 {
|
||||||
|
cmp_eq_body = quote_spanned! {span=>
|
||||||
|
::fayalite::expr::ToExpr::to_expr(&true)
|
||||||
|
};
|
||||||
|
cmp_ne_body = quote_spanned! {span=>
|
||||||
|
::fayalite::expr::ToExpr::to_expr(&false)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
cmp_eq_body = quote_spanned! {span=>
|
||||||
|
#(#fields_cmp_eq)&*
|
||||||
|
};
|
||||||
|
cmp_ne_body = quote_spanned! {span=>
|
||||||
|
#(#fields_cmp_ne)|*
|
||||||
|
};
|
||||||
|
};
|
||||||
|
quote_spanned! {span=>
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
fn cmp_eq(
|
||||||
|
__lhs: ::fayalite::expr::Expr<Self>,
|
||||||
|
__rhs: ::fayalite::expr::Expr<Self>,
|
||||||
|
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||||
|
#cmp_eq_body
|
||||||
|
}
|
||||||
|
fn cmp_ne(
|
||||||
|
__lhs: ::fayalite::expr::Expr<Self>,
|
||||||
|
__rhs: ::fayalite::expr::Expr<Self>,
|
||||||
|
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
|
||||||
|
#cmp_ne_body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
}
|
||||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||||
let static_generics = generics.clone().for_static_type();
|
let static_generics = generics.clone().for_static_type();
|
||||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||||
|
|
|
@ -155,7 +155,11 @@ impl ParsedEnum {
|
||||||
custom_bounds,
|
custom_bounds,
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
|
cmp_eq,
|
||||||
} = options.body;
|
} = options.body;
|
||||||
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
|
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
|
||||||
|
}
|
||||||
attrs.retain(|attr| {
|
attrs.retain(|attr| {
|
||||||
if attr.path().is_ident("repr") {
|
if attr.path().is_ident("repr") {
|
||||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||||
|
@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum {
|
||||||
custom_bounds: _,
|
custom_bounds: _,
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
|
cmp_eq: _, // TODO: implement cmp_eq for enums
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let mut struct_attrs = attrs.clone();
|
let mut struct_attrs = attrs.clone();
|
||||||
|
|
|
@ -49,10 +49,14 @@ impl ParsedTypeAlias {
|
||||||
custom_bounds,
|
custom_bounds,
|
||||||
no_static,
|
no_static,
|
||||||
no_runtime_generics: _,
|
no_runtime_generics: _,
|
||||||
|
cmp_eq,
|
||||||
} = options.body;
|
} = options.body;
|
||||||
if let Some((no_static,)) = no_static {
|
if let Some((no_static,)) = no_static {
|
||||||
errors.error(no_static, "no_static is not valid on type aliases");
|
errors.error(no_static, "no_static is not valid on type aliases");
|
||||||
}
|
}
|
||||||
|
if let Some((cmp_eq,)) = cmp_eq {
|
||||||
|
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
|
||||||
|
}
|
||||||
let generics = if custom_bounds.is_some() {
|
let generics = if custom_bounds.is_some() {
|
||||||
MaybeParsed::Unrecognized(generics)
|
MaybeParsed::Unrecognized(generics)
|
||||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||||
|
@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias {
|
||||||
custom_bounds: _,
|
custom_bounds: _,
|
||||||
no_static: _,
|
no_static: _,
|
||||||
no_runtime_generics,
|
no_runtime_generics,
|
||||||
|
cmp_eq: _,
|
||||||
} = &options.body;
|
} = &options.body;
|
||||||
let target = get_target(target, ident);
|
let target = get_target(target, ident);
|
||||||
let mut type_attrs = attrs.clone();
|
let mut type_attrs = attrs.clone();
|
||||||
|
|
|
@ -26,6 +26,7 @@ crate::options! {
|
||||||
CustomBounds(custom_bounds),
|
CustomBounds(custom_bounds),
|
||||||
NoStatic(no_static),
|
NoStatic(no_static),
|
||||||
NoRuntimeGenerics(no_runtime_generics),
|
NoRuntimeGenerics(no_runtime_generics),
|
||||||
|
CmpEq(cmp_eq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2069,11 +2070,16 @@ macro_rules! impl_bounds {
|
||||||
$(
|
$(
|
||||||
$Variant:ident,
|
$Variant:ident,
|
||||||
)*
|
)*
|
||||||
|
$(
|
||||||
|
#[unknown]
|
||||||
|
$Unknown:ident,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
$vis enum $enum_type {
|
$vis enum $enum_type {
|
||||||
$($Variant(known_items::$Variant),)*
|
$($Variant(known_items::$Variant),)*
|
||||||
|
$($Unknown(syn::TypeParamBound),)?
|
||||||
}
|
}
|
||||||
|
|
||||||
$(impl From<known_items::$Variant> for $enum_type {
|
$(impl From<known_items::$Variant> for $enum_type {
|
||||||
|
@ -2086,28 +2092,54 @@ macro_rules! impl_bounds {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
$(Self::$Variant(v) => v.to_tokens(tokens),)*
|
||||||
|
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $enum_type {
|
impl $enum_type {
|
||||||
$vis fn parse_path(path: Path) -> Result<Self, Path> {
|
$vis fn parse_path(path: Path) -> Result<Self, Path> {
|
||||||
|
#![allow(unreachable_code)]
|
||||||
$(let path = match known_items::$Variant::parse_path(path) {
|
$(let path = match known_items::$Variant::parse_path(path) {
|
||||||
Ok(v) => return Ok(Self::$Variant(v)),
|
Ok(v) => return Ok(Self::$Variant(v)),
|
||||||
Err(path) => path,
|
Err(path) => path,
|
||||||
};)*
|
};)*
|
||||||
|
$(return Ok(Self::$Unknown(syn::TraitBound {
|
||||||
|
paren_token: None,
|
||||||
|
modifier: syn::TraitBoundModifier::None,
|
||||||
|
lifetimes: None,
|
||||||
|
path,
|
||||||
|
}.into()));)?
|
||||||
Err(path)
|
Err(path)
|
||||||
}
|
}
|
||||||
|
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
|
||||||
|
#![allow(unreachable_code)]
|
||||||
|
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
|
||||||
|
if let syn::TraitBound {
|
||||||
|
paren_token: _,
|
||||||
|
modifier: syn::TraitBoundModifier::None,
|
||||||
|
lifetimes: None,
|
||||||
|
path: _,
|
||||||
|
} = trait_bound {
|
||||||
|
match Self::parse_path(trait_bound.path) {
|
||||||
|
Ok(retval) => return Ok(retval),
|
||||||
|
Err(path) => trait_bound.path = path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type_param_bound = trait_bound.into();
|
||||||
|
}
|
||||||
|
$(return Ok(Self::$Unknown(type_param_bound));)?
|
||||||
|
Err(type_param_bound)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for $enum_type {
|
impl Parse for $enum_type {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| {
|
Self::parse_type_param_bound(input.parse()?)
|
||||||
syn::Error::new_spanned(
|
.map_err(|type_param_bound| syn::Error::new_spanned(
|
||||||
path,
|
type_param_bound,
|
||||||
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
|
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
|
||||||
)
|
))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2115,6 +2147,7 @@ macro_rules! impl_bounds {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
$vis struct $struct_type {
|
$vis struct $struct_type {
|
||||||
$($vis $Variant: Option<known_items::$Variant>,)*
|
$($vis $Variant: Option<known_items::$Variant>,)*
|
||||||
|
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for $struct_type {
|
impl ToTokens for $struct_type {
|
||||||
|
@ -2126,42 +2159,63 @@ macro_rules! impl_bounds {
|
||||||
separator = Some(<Token![+]>::default());
|
separator = Some(<Token![+]>::default());
|
||||||
v.to_tokens(tokens);
|
v.to_tokens(tokens);
|
||||||
})*
|
})*
|
||||||
|
$(for v in &self.$Unknown {
|
||||||
|
separator.to_tokens(tokens);
|
||||||
|
separator = Some(<Token![+]>::default());
|
||||||
|
v.to_tokens(tokens);
|
||||||
|
})*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = {
|
const _: () = {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
$vis struct Iter($vis $struct_type);
|
#[allow(non_snake_case)]
|
||||||
|
$vis struct Iter {
|
||||||
|
$($Variant: Option<known_items::$Variant>,)*
|
||||||
|
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
|
||||||
|
}
|
||||||
|
|
||||||
impl IntoIterator for $struct_type {
|
impl IntoIterator for $struct_type {
|
||||||
type Item = $enum_type;
|
type Item = $enum_type;
|
||||||
type IntoIter = Iter;
|
type IntoIter = Iter;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
Iter(self)
|
Iter {
|
||||||
|
$($Variant: self.$Variant,)*
|
||||||
|
$($Unknown: self.$Unknown.into_iter(),)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for Iter {
|
impl Iterator for Iter {
|
||||||
type Item = $enum_type;
|
type Item = $enum_type;
|
||||||
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
$(
|
$(
|
||||||
if let Some(value) = self.0.$Variant.take() {
|
if let Some(value) = self.$Variant.take() {
|
||||||
return Some($enum_type::$Variant(value));
|
return Some($enum_type::$Variant(value));
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
$(
|
||||||
|
if let Some(value) = self.$Unknown.next() {
|
||||||
|
return Some($enum_type::$Unknown(value));
|
||||||
|
}
|
||||||
|
)?
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_mut, unused_variables)]
|
#[allow(unused_mut, unused_variables)]
|
||||||
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
|
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
|
||||||
$(
|
$(
|
||||||
if let Some(value) = self.0.$Variant.take() {
|
if let Some(value) = self.$Variant.take() {
|
||||||
init = f(init, $enum_type::$Variant(value));
|
init = f(init, $enum_type::$Variant(value));
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
$(
|
||||||
|
if let Some(value) = self.$Unknown.next() {
|
||||||
|
init = f(init, $enum_type::$Unknown(value));
|
||||||
|
}
|
||||||
|
)?
|
||||||
init
|
init
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2173,6 +2227,9 @@ macro_rules! impl_bounds {
|
||||||
$($enum_type::$Variant(v) => {
|
$($enum_type::$Variant(v) => {
|
||||||
self.$Variant = Some(v);
|
self.$Variant = Some(v);
|
||||||
})*
|
})*
|
||||||
|
$($enum_type::$Unknown(v) => {
|
||||||
|
self.$Unknown.push(v);
|
||||||
|
})?
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2191,6 +2248,7 @@ macro_rules! impl_bounds {
|
||||||
$(if let Some(v) = v.$Variant {
|
$(if let Some(v) = v.$Variant {
|
||||||
self.$Variant = Some(v);
|
self.$Variant = Some(v);
|
||||||
})*
|
})*
|
||||||
|
$(self.$Unknown.extend(v.$Unknown);)*
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2244,6 +2302,8 @@ impl_bounds! {
|
||||||
Size,
|
Size,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
|
#[unknown]
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2257,6 +2317,8 @@ impl_bounds! {
|
||||||
ResetType,
|
ResetType,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
|
#[unknown]
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound {
|
||||||
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
|
||||||
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
|
||||||
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
|
||||||
|
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
||||||
ResetType,
|
ResetType,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
|
Unknown,
|
||||||
} = value;
|
} = value;
|
||||||
Self {
|
Self {
|
||||||
BoolOrIntType,
|
BoolOrIntType,
|
||||||
|
@ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
|
||||||
Size: None,
|
Size: None,
|
||||||
StaticType,
|
StaticType,
|
||||||
Type,
|
Type,
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2330,6 +2395,7 @@ impl ParsedTypeBound {
|
||||||
ParsedTypeBound::Type(known_items::Type(span)),
|
ParsedTypeBound::Type(known_items::Type(span)),
|
||||||
]),
|
]),
|
||||||
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
|
||||||
|
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2364,6 +2430,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
|
||||||
Size,
|
Size,
|
||||||
StaticType: None,
|
StaticType: None,
|
||||||
Type: None,
|
Type: None,
|
||||||
|
Unknown: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2391,6 +2458,7 @@ impl ParsedBounds {
|
||||||
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
|
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
|
||||||
let mut type_bounds = None;
|
let mut type_bounds = None;
|
||||||
let mut size_type_bounds = None;
|
let mut size_type_bounds = None;
|
||||||
|
let mut unknown_bounds = vec![];
|
||||||
self.into_iter().for_each(|bound| match bound.categorize() {
|
self.into_iter().for_each(|bound| match bound.categorize() {
|
||||||
ParsedBoundCategory::Type(bound) => {
|
ParsedBoundCategory::Type(bound) => {
|
||||||
type_bounds
|
type_bounds
|
||||||
|
@ -2402,15 +2470,37 @@ impl ParsedBounds {
|
||||||
.get_or_insert_with(ParsedSizeTypeBounds::default)
|
.get_or_insert_with(ParsedSizeTypeBounds::default)
|
||||||
.extend([bound]);
|
.extend([bound]);
|
||||||
}
|
}
|
||||||
|
ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound),
|
||||||
});
|
});
|
||||||
match (type_bounds, size_type_bounds) {
|
match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) {
|
||||||
(None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
(None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||||
Type: Some(known_items::Type(span)),
|
Type: Some(known_items::Type(span)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
(None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds),
|
(None, None, false) => {
|
||||||
(Some(bounds), None) => ParsedBoundsCategory::Type(bounds),
|
errors.error(
|
||||||
(Some(type_bounds), Some(size_type_bounds)) => {
|
unknown_bounds.remove(0),
|
||||||
|
"unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds",
|
||||||
|
);
|
||||||
|
ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||||
|
Unknown: unknown_bounds,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds),
|
||||||
|
(None, Some(bounds), false) => {
|
||||||
|
// TODO: implement
|
||||||
|
errors.error(
|
||||||
|
unknown_bounds.remove(0),
|
||||||
|
"unknown bounds with `Size` bounds are not implemented",
|
||||||
|
);
|
||||||
|
ParsedBoundsCategory::SizeType(bounds)
|
||||||
|
}
|
||||||
|
(Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds {
|
||||||
|
Unknown: unknown_bounds,
|
||||||
|
..bounds
|
||||||
|
}),
|
||||||
|
(Some(type_bounds), Some(size_type_bounds), _) => {
|
||||||
errors.error(
|
errors.error(
|
||||||
size_type_bounds
|
size_type_bounds
|
||||||
.Size
|
.Size
|
||||||
|
@ -2427,6 +2517,7 @@ impl ParsedBounds {
|
||||||
pub(crate) enum ParsedBoundCategory {
|
pub(crate) enum ParsedBoundCategory {
|
||||||
Type(ParsedTypeBound),
|
Type(ParsedTypeBound),
|
||||||
SizeType(ParsedSizeTypeBound),
|
SizeType(ParsedSizeTypeBound),
|
||||||
|
Unknown(syn::TypeParamBound),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParsedBound {
|
impl ParsedBound {
|
||||||
|
@ -2441,12 +2532,14 @@ impl ParsedBound {
|
||||||
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
|
||||||
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
|
||||||
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
|
||||||
|
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn implied_bounds(self) -> ParsedBounds {
|
fn implied_bounds(self) -> ParsedBounds {
|
||||||
match self.categorize() {
|
match self.categorize() {
|
||||||
ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
|
ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
|
||||||
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
|
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
|
||||||
|
ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3325,7 +3418,7 @@ impl ParsedGenerics {
|
||||||
| ParsedTypeBound::EnumType(_)
|
| ParsedTypeBound::EnumType(_)
|
||||||
| ParsedTypeBound::IntType(_)
|
| ParsedTypeBound::IntType(_)
|
||||||
| ParsedTypeBound::ResetType(_) => {
|
| ParsedTypeBound::ResetType(_) => {
|
||||||
errors.error(bound, "bound on mask type not implemented");
|
errors.error(bound, "bounds on mask types are not implemented");
|
||||||
}
|
}
|
||||||
ParsedTypeBound::StaticType(bound) => {
|
ParsedTypeBound::StaticType(bound) => {
|
||||||
if bounds.StaticType.is_none() {
|
if bounds.StaticType.is_none() {
|
||||||
|
@ -3337,6 +3430,12 @@ impl ParsedGenerics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParsedTypeBound::Type(_) => {}
|
ParsedTypeBound::Type(_) => {}
|
||||||
|
ParsedTypeBound::Unknown(_) => {
|
||||||
|
errors.error(
|
||||||
|
bound,
|
||||||
|
"unknown bounds on mask types are not implemented",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bounds.add_implied_bounds();
|
bounds.add_implied_bounds();
|
||||||
|
|
|
@ -72,13 +72,14 @@ mod kw {
|
||||||
custom_keyword!(cfg);
|
custom_keyword!(cfg);
|
||||||
custom_keyword!(cfg_attr);
|
custom_keyword!(cfg_attr);
|
||||||
custom_keyword!(clock_domain);
|
custom_keyword!(clock_domain);
|
||||||
|
custom_keyword!(cmp_eq);
|
||||||
custom_keyword!(connect_inexact);
|
custom_keyword!(connect_inexact);
|
||||||
custom_keyword!(custom_bounds);
|
custom_keyword!(custom_bounds);
|
||||||
custom_keyword!(flip);
|
custom_keyword!(flip);
|
||||||
custom_keyword!(hdl);
|
custom_keyword!(hdl);
|
||||||
custom_keyword!(hdl_module);
|
custom_keyword!(hdl_module);
|
||||||
custom_keyword!(input);
|
|
||||||
custom_keyword!(incomplete_wire);
|
custom_keyword!(incomplete_wire);
|
||||||
|
custom_keyword!(input);
|
||||||
custom_keyword!(instance);
|
custom_keyword!(instance);
|
||||||
custom_keyword!(m);
|
custom_keyword!(m);
|
||||||
custom_keyword!(memory);
|
custom_keyword!(memory);
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
expr::{
|
||||||
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
|
||||||
|
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
|
||||||
|
},
|
||||||
|
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||||
intern::{Intern, Interned, LazyInterned},
|
intern::{Intern, Interned, LazyInterned},
|
||||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
@ -12,7 +15,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
util::ConstUsize,
|
util::ConstUsize,
|
||||||
};
|
};
|
||||||
use std::ops::Index;
|
use std::{iter::FusedIterator, ops::Index};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||||
|
@ -148,10 +151,8 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
this: Expr<Self>,
|
this: Expr<Self>,
|
||||||
source_location: SourceLocation,
|
source_location: SourceLocation,
|
||||||
) -> Self::MatchVariantsIter {
|
) -> Self::MatchVariantsIter {
|
||||||
let base = Expr::as_dyn_array(this);
|
|
||||||
let base_ty = Expr::ty(base);
|
|
||||||
let _ = source_location;
|
let _ = source_location;
|
||||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
let retval = Vec::from_iter(this);
|
||||||
std::iter::once(MatchVariantWithoutScope(
|
std::iter::once(MatchVariantWithoutScope(
|
||||||
Len::ArrayMatch::<T>::try_from(retval)
|
Len::ArrayMatch::<T>::try_from(retval)
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -184,9 +185,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||||
|
|
||||||
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
||||||
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
||||||
let base = Expr::as_dyn_array(*this);
|
let retval = Vec::from_iter(*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::<T>::try_from(retval)
|
Len::ArrayMatch::<T>::try_from(retval)
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -218,3 +217,131 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
||||||
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
|
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
|
||||||
|
where
|
||||||
|
Lhs: ExprPartialEq<Rhs>,
|
||||||
|
{
|
||||||
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||||
|
let lhs_ty = Expr::ty(lhs);
|
||||||
|
let rhs_ty = Expr::ty(rhs);
|
||||||
|
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||||
|
lhs.into_iter()
|
||||||
|
.zip(rhs)
|
||||||
|
.map(|(l, r)| l.cmp_eq(r))
|
||||||
|
.collect::<Expr<Array<Bool>>>()
|
||||||
|
.cast_to_bits()
|
||||||
|
.all_one_bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
|
||||||
|
let lhs_ty = Expr::ty(lhs);
|
||||||
|
let rhs_ty = Expr::ty(rhs);
|
||||||
|
assert_eq!(lhs_ty.len(), rhs_ty.len());
|
||||||
|
lhs.into_iter()
|
||||||
|
.zip(rhs)
|
||||||
|
.map(|(l, r)| l.cmp_ne(r))
|
||||||
|
.collect::<Expr<Array<Bool>>>()
|
||||||
|
.cast_to_bits()
|
||||||
|
.any_one_bits()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
|
||||||
|
type Item = T;
|
||||||
|
type ExprIntoIter = ExprArrayIter<T, Len>;
|
||||||
|
|
||||||
|
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
|
||||||
|
ExprArrayIter {
|
||||||
|
base: e,
|
||||||
|
indexes: 0..Expr::ty(e).len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ExprArrayIter<T: Type, Len: Size> {
|
||||||
|
base: Expr<ArrayType<T, Len>>,
|
||||||
|
indexes: std::ops::Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> ExprArrayIter<T, Len> {
|
||||||
|
pub fn base(&self) -> Expr<ArrayType<T, Len>> {
|
||||||
|
self.base
|
||||||
|
}
|
||||||
|
pub fn indexes(&self) -> std::ops::Range<usize> {
|
||||||
|
self.indexes.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> Iterator for ExprArrayIter<T, Len> {
|
||||||
|
type Item = Expr<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.indexes.next().map(|i| self.base[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
self.indexes.size_hint()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(self) -> usize {
|
||||||
|
self.indexes.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last(mut self) -> Option<Self::Item> {
|
||||||
|
self.next_back()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
|
self.indexes.nth(n).map(|i| self.base[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fold<B, F>(self, init: B, mut f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.indexes.fold(init, |b, i| f(b, self.base[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> DoubleEndedIterator for ExprArrayIter<T, Len> {
|
||||||
|
fn next_back(&mut self) -> Option<Self::Item> {
|
||||||
|
self.indexes.next_back().map(|i| self.base[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
||||||
|
self.indexes.nth_back(n).map(|i| self.base[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rfold<B, F>(self, init: B, mut f: F) -> B
|
||||||
|
where
|
||||||
|
F: FnMut(B, Self::Item) -> B,
|
||||||
|
{
|
||||||
|
self.indexes.rfold(init, |b, i| f(b, self.base[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> ExactSizeIterator for ExprArrayIter<T, Len> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.indexes.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type, Len: Size> FusedIterator for ExprArrayIter<T, Len> {}
|
||||||
|
|
||||||
|
impl<A: StaticType> ExprFromIterator<Expr<A>> for Array<A> {
|
||||||
|
fn expr_from_iter<T: IntoIterator<Item = Expr<A>>>(iter: T) -> Expr<Self> {
|
||||||
|
ArrayLiteral::new(
|
||||||
|
A::TYPE,
|
||||||
|
iter.into_iter().map(|v| Expr::canonical(v)).collect(),
|
||||||
|
)
|
||||||
|
.to_expr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, A: StaticType> ExprFromIterator<&'a Expr<A>> for Array<A> {
|
||||||
|
fn expr_from_iter<T: IntoIterator<Item = &'a Expr<A>>>(iter: T) -> Expr<Self> {
|
||||||
|
iter.into_iter().copied().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{ops::BundleLiteral, Expr, ToExpr},
|
expr::{
|
||||||
|
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
|
||||||
|
CastToBits, Expr, ReduceBits, ToExpr,
|
||||||
|
},
|
||||||
|
int::{Bool, DynSize},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
sim::{SimValue, ToSimValue},
|
sim::{SimValue, ToSimValue},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_tuples {
|
macro_rules! impl_tuples {
|
||||||
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
|
(
|
||||||
|
[$({
|
||||||
|
#[
|
||||||
|
num = $num:tt,
|
||||||
|
field = $field:ident,
|
||||||
|
ty = $ty_var:ident: $Ty:ident,
|
||||||
|
lhs = $lhs_var:ident: $Lhs:ident,
|
||||||
|
rhs = $rhs_var:ident: $Rhs:ident
|
||||||
|
]
|
||||||
|
$var:ident: $T:ident
|
||||||
|
})*]
|
||||||
|
[]
|
||||||
|
) => {
|
||||||
impl_tuple_builder_fields! {
|
impl_tuple_builder_fields! {
|
||||||
{}
|
{}
|
||||||
[$({
|
[$({
|
||||||
|
@ -498,6 +514,29 @@ macro_rules! impl_tuples {
|
||||||
Self::into_sim_value(*self, ty)
|
Self::into_sim_value(*self, ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
|
||||||
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||||
|
let ($($lhs_var,)*) = *lhs;
|
||||||
|
let ($($rhs_var,)*) = *rhs;
|
||||||
|
ArrayLiteral::<Bool, DynSize>::new(
|
||||||
|
Bool,
|
||||||
|
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
|
||||||
|
)
|
||||||
|
.cast_to_bits()
|
||||||
|
.all_one_bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
||||||
|
let ($($lhs_var,)*) = *lhs;
|
||||||
|
let ($($rhs_var,)*) = *rhs;
|
||||||
|
ArrayLiteral::<Bool, DynSize>::new(
|
||||||
|
Bool,
|
||||||
|
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
|
||||||
|
)
|
||||||
|
.cast_to_bits()
|
||||||
|
.any_one_bits()
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||||
impl_tuples!([$($lhs)*] []);
|
impl_tuples!([$($lhs)*] []);
|
||||||
|
@ -507,18 +546,18 @@ macro_rules! impl_tuples {
|
||||||
|
|
||||||
impl_tuples! {
|
impl_tuples! {
|
||||||
[] [
|
[] [
|
||||||
{#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0}
|
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
|
||||||
{#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1}
|
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
|
||||||
{#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2}
|
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
|
||||||
{#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3}
|
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
|
||||||
{#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4}
|
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
|
||||||
{#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5}
|
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
|
||||||
{#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6}
|
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
|
||||||
{#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7}
|
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
|
||||||
{#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8}
|
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
|
||||||
{#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9}
|
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
|
||||||
{#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10}
|
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
|
||||||
{#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11}
|
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
expr::{
|
||||||
|
ops::{ExprPartialEq, VariantAccess},
|
||||||
|
Expr, ToExpr,
|
||||||
|
},
|
||||||
hdl,
|
hdl,
|
||||||
int::Bool,
|
int::Bool,
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
|
@ -360,6 +363,60 @@ pub enum HdlOption<T: Type> {
|
||||||
HdlSome(T),
|
HdlSome(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
|
||||||
|
#[hdl]
|
||||||
|
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||||
|
#[hdl]
|
||||||
|
let cmp_eq = wire();
|
||||||
|
#[hdl]
|
||||||
|
match lhs {
|
||||||
|
HdlSome(lhs) =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
|
||||||
|
HdlNone => connect(cmp_eq, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HdlNone =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(_) => connect(cmp_eq, false),
|
||||||
|
HdlNone => connect(cmp_eq, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmp_eq
|
||||||
|
}
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
|
||||||
|
#[hdl]
|
||||||
|
let cmp_ne = wire();
|
||||||
|
#[hdl]
|
||||||
|
match lhs {
|
||||||
|
HdlSome(lhs) =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
|
||||||
|
HdlNone => connect(cmp_ne, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HdlNone =>
|
||||||
|
{
|
||||||
|
#[hdl]
|
||||||
|
match rhs {
|
||||||
|
HdlSome(_) => connect(cmp_ne, true),
|
||||||
|
HdlNone => connect(cmp_ne, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmp_ne
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
|
||||||
HdlOption[T::TYPE].HdlNone()
|
HdlOption[T::TYPE].HdlNone()
|
||||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
||||||
transform::visit::{Fold, Folder, Visit, Visitor},
|
transform::visit::{Fold, Folder, Visit, Visitor},
|
||||||
Instance, ModuleIO,
|
Instance, ModuleIO,
|
||||||
},
|
},
|
||||||
|
phantom_const::PhantomConst,
|
||||||
reg::Reg,
|
reg::Reg,
|
||||||
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
reset::{AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset},
|
||||||
ty::{CanonicalType, StaticType, Type, TypeWithDeref},
|
ty::{CanonicalType, StaticType, Type, TypeWithDeref},
|
||||||
|
@ -109,6 +110,7 @@ expr_enum! {
|
||||||
UIntLiteral(Interned<UIntValue>),
|
UIntLiteral(Interned<UIntValue>),
|
||||||
SIntLiteral(Interned<SIntValue>),
|
SIntLiteral(Interned<SIntValue>),
|
||||||
BoolLiteral(bool),
|
BoolLiteral(bool),
|
||||||
|
PhantomConst(PhantomConst),
|
||||||
BundleLiteral(ops::BundleLiteral),
|
BundleLiteral(ops::BundleLiteral),
|
||||||
ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>),
|
ArrayLiteral(ops::ArrayLiteral<CanonicalType, DynSize>),
|
||||||
EnumLiteral(ops::EnumLiteral),
|
EnumLiteral(ops::EnumLiteral),
|
||||||
|
@ -755,3 +757,27 @@ pub fn repeat<T: Type, L: SizeType>(
|
||||||
)
|
)
|
||||||
.to_expr()
|
.to_expr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToExpr for PhantomConst<T> {
|
||||||
|
type Type = Self;
|
||||||
|
|
||||||
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
|
Expr {
|
||||||
|
__enum: ExprEnum::PhantomConst(self.canonical_phantom_const()).intern_sized(),
|
||||||
|
__ty: *self,
|
||||||
|
__flow: Flow::Source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> GetTarget for PhantomConst<T> {
|
||||||
|
fn target(&self) -> Option<Interned<Target>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + crate::phantom_const::PhantomConstValue> ToLiteralBits for PhantomConst<T> {
|
||||||
|
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
|
||||||
|
Ok(Interned::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,14 +11,15 @@ use crate::{
|
||||||
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
|
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField,
|
||||||
TargetPathDynArrayElement, TargetPathElement,
|
TargetPathDynArrayElement, TargetPathElement,
|
||||||
},
|
},
|
||||||
CastTo, Expr, ExprEnum, Flow, HdlPartialEq, HdlPartialOrd, NotALiteralExpr, ReduceBits,
|
CastBitsTo as _, CastTo, CastToBits as _, Expr, ExprEnum, Flow, HdlPartialEq,
|
||||||
ToExpr, ToLiteralBits,
|
HdlPartialOrd, NotALiteralExpr, ReduceBits, ToExpr, ToLiteralBits,
|
||||||
},
|
},
|
||||||
int::{
|
int::{
|
||||||
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt,
|
Bool, BoolOrIntType, DynSize, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt,
|
||||||
UIntType, UIntValue,
|
UIntType, UIntValue,
|
||||||
},
|
},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
|
phantom_const::{PhantomConst, PhantomConstValue},
|
||||||
reset::{
|
reset::{
|
||||||
AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset,
|
AsyncReset, Reset, ResetType, ResetTypeDispatch, SyncReset, ToAsyncReset, ToReset,
|
||||||
ToSyncReset,
|
ToSyncReset,
|
||||||
|
@ -1892,6 +1893,26 @@ impl ExprCastTo<Clock> for Clock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> ExprCastTo<()> for PhantomConst<T> {
|
||||||
|
fn cast_to(src: Expr<Self>, to_type: ()) -> Expr<()> {
|
||||||
|
src.cast_to_bits().cast_bits_to(to_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>> for () {
|
||||||
|
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
|
||||||
|
src.cast_to_bits().cast_bits_to(to_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue, U: ?Sized + PhantomConstValue> ExprCastTo<PhantomConst<T>>
|
||||||
|
for PhantomConst<U>
|
||||||
|
{
|
||||||
|
fn cast_to(src: Expr<Self>, to_type: PhantomConst<T>) -> Expr<PhantomConst<T>> {
|
||||||
|
src.cast_to_bits().cast_bits_to(to_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct FieldAccess<FieldType: Type = CanonicalType> {
|
pub struct FieldAccess<FieldType: Type = CanonicalType> {
|
||||||
base: Expr<Bundle>,
|
base: Expr<Bundle>,
|
||||||
|
@ -2708,3 +2729,47 @@ impl<T: Type> ToExpr for Uninit<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ExprIntoIterator: Type {
|
||||||
|
type Item: Type;
|
||||||
|
type ExprIntoIter: Iterator<Item = Expr<Self::Item>>;
|
||||||
|
|
||||||
|
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExprIntoIterator> IntoIterator for Expr<T> {
|
||||||
|
type Item = Expr<T::Item>;
|
||||||
|
type IntoIter = T::ExprIntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
T::expr_into_iter(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExprIntoIterator> IntoIterator for &'_ Expr<T> {
|
||||||
|
type Item = Expr<T::Item>;
|
||||||
|
type IntoIter = T::ExprIntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
T::expr_into_iter(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ExprIntoIterator> IntoIterator for &'_ mut Expr<T> {
|
||||||
|
type Item = Expr<T::Item>;
|
||||||
|
type IntoIter = T::ExprIntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
T::expr_into_iter(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExprFromIterator<A>: Type {
|
||||||
|
fn expr_from_iter<T: IntoIterator<Item = A>>(iter: T) -> Expr<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<This: ExprFromIterator<A>, A> FromIterator<A> for Expr<This> {
|
||||||
|
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
|
||||||
|
This::expr_from_iter(iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
target::{
|
target::{
|
||||||
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
|
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
|
||||||
},
|
},
|
||||||
Expr, ExprEnum,
|
CastBitsTo, Expr, ExprEnum,
|
||||||
},
|
},
|
||||||
formal::FormalKind,
|
formal::FormalKind,
|
||||||
int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue},
|
int::{Bool, DynSize, IntType, SIntValue, UInt, UIntValue},
|
||||||
|
@ -447,6 +447,7 @@ impl TypeState {
|
||||||
CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(),
|
CanonicalType::AsyncReset(AsyncReset {}) => "AsyncReset".into(),
|
||||||
CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(),
|
CanonicalType::SyncReset(SyncReset {}) => "UInt<1>".into(),
|
||||||
CanonicalType::Reset(Reset {}) => "Reset".into(),
|
CanonicalType::Reset(Reset {}) => "Reset".into(),
|
||||||
|
CanonicalType::PhantomConst(_) => "{}".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1152,6 +1153,7 @@ impl<'a> Exporter<'a> {
|
||||||
| CanonicalType::Clock(_)
|
| CanonicalType::Clock(_)
|
||||||
| CanonicalType::AsyncReset(_)
|
| CanonicalType::AsyncReset(_)
|
||||||
| CanonicalType::Reset(_) => format!("asUInt({value_str})"),
|
| CanonicalType::Reset(_) => format!("asUInt({value_str})"),
|
||||||
|
CanonicalType::PhantomConst(_) => "UInt<0>(0)".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn expr_cast_bits_to_bundle(
|
fn expr_cast_bits_to_bundle(
|
||||||
|
@ -1357,6 +1359,12 @@ impl<'a> Exporter<'a> {
|
||||||
CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"),
|
CanonicalType::AsyncReset(_) => format!("asAsyncReset({value_str})"),
|
||||||
CanonicalType::SyncReset(_) => value_str,
|
CanonicalType::SyncReset(_) => value_str,
|
||||||
CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"),
|
CanonicalType::Reset(_) => unreachable!("Reset is not bit castable to"),
|
||||||
|
CanonicalType::PhantomConst(_) => {
|
||||||
|
let retval = self.module.ns.make_new("_cast_bits_to_phantom_const_expr");
|
||||||
|
definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {{}}"));
|
||||||
|
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
|
||||||
|
return retval.to_string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn expr_unary<T: Type>(
|
fn expr_unary<T: Type>(
|
||||||
|
@ -1395,6 +1403,11 @@ impl<'a> Exporter<'a> {
|
||||||
ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal),
|
ExprEnum::UIntLiteral(literal) => self.uint_literal(&literal),
|
||||||
ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal),
|
ExprEnum::SIntLiteral(literal) => self.sint_literal(&literal),
|
||||||
ExprEnum::BoolLiteral(literal) => self.bool_literal(literal),
|
ExprEnum::BoolLiteral(literal) => self.bool_literal(literal),
|
||||||
|
ExprEnum::PhantomConst(ty) => self.expr(
|
||||||
|
UInt[0].zero().cast_bits_to(ty.canonical()),
|
||||||
|
definitions,
|
||||||
|
const_ty,
|
||||||
|
),
|
||||||
ExprEnum::ArrayLiteral(array_literal) => {
|
ExprEnum::ArrayLiteral(array_literal) => {
|
||||||
self.array_literal_expr(array_literal, definitions, const_ty)
|
self.array_literal_expr(array_literal, definitions, const_ty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,6 +96,7 @@ pub mod int;
|
||||||
pub mod intern;
|
pub mod intern;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
|
pub mod phantom_const;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod reg;
|
pub mod reg;
|
||||||
pub mod reset;
|
pub mod reset;
|
||||||
|
|
|
@ -1082,6 +1082,7 @@ pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
|
||||||
)
|
)
|
||||||
.to_expr(),
|
.to_expr(),
|
||||||
)),
|
)),
|
||||||
|
CanonicalType::PhantomConst(_) => Expr::from_canonical(Expr::canonical(().to_expr())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1490,6 +1490,9 @@ impl TargetState {
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
|
CanonicalType::PhantomConst(_) => TargetStateInner::Decomposed {
|
||||||
|
subtargets: HashMap::new(),
|
||||||
|
},
|
||||||
CanonicalType::Array(ty) => TargetStateInner::Decomposed {
|
CanonicalType::Array(ty) => TargetStateInner::Decomposed {
|
||||||
subtargets: (0..ty.len())
|
subtargets: (0..ty.len())
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
|
|
|
@ -155,6 +155,7 @@ impl ResetsLayout {
|
||||||
CanonicalType::SyncReset(_) => ResetsLayout::SyncReset,
|
CanonicalType::SyncReset(_) => ResetsLayout::SyncReset,
|
||||||
CanonicalType::Reset(_) => ResetsLayout::Reset,
|
CanonicalType::Reset(_) => ResetsLayout::Reset,
|
||||||
CanonicalType::Clock(_) => ResetsLayout::NoResets,
|
CanonicalType::Clock(_) => ResetsLayout::NoResets,
|
||||||
|
CanonicalType::PhantomConst(_) => ResetsLayout::NoResets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,7 +408,8 @@ impl Resets {
|
||||||
| CanonicalType::Bool(_)
|
| CanonicalType::Bool(_)
|
||||||
| CanonicalType::AsyncReset(_)
|
| CanonicalType::AsyncReset(_)
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Clock(_) => Ok(self.ty),
|
| CanonicalType::Clock(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => Ok(self.ty),
|
||||||
CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn(
|
CanonicalType::Array(ty) => Ok(CanonicalType::Array(Array::new_dyn(
|
||||||
self.array_elements().substituted_type(
|
self.array_elements().substituted_type(
|
||||||
reset_graph,
|
reset_graph,
|
||||||
|
@ -998,7 +1000,8 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
||||||
CanonicalType::Array(_)
|
CanonicalType::Array(_)
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::Reset(_) => unreachable!(),
|
| CanonicalType::Reset(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
|
$(CanonicalType::$Variant(ty) => Expr::expr_enum($arg.cast_to(ty)),)*
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1010,6 +1013,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::Reset(_) => unreachable!(),
|
| CanonicalType::Reset(_) => unreachable!(),
|
||||||
|
CanonicalType::PhantomConst(_) => Expr::expr_enum(arg),
|
||||||
$(CanonicalType::$Variant(_) => {
|
$(CanonicalType::$Variant(_) => {
|
||||||
let arg = Expr::<$Variant>::from_canonical(arg);
|
let arg = Expr::<$Variant>::from_canonical(arg);
|
||||||
match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
|
match_expr_ty!(arg, UInt, SInt, Bool, AsyncReset, SyncReset, Clock)
|
||||||
|
@ -1040,6 +1044,7 @@ impl<P: Pass> RunPass<P> for ExprEnum {
|
||||||
ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::UIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::SIntLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::BoolLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
|
ExprEnum::PhantomConst(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::BundleLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::ArrayLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
ExprEnum::EnumLiteral(expr) => Ok(expr.run_pass(pass_args)?.map(ExprEnum::from)),
|
||||||
|
@ -1670,7 +1675,8 @@ impl RunPassDispatch for AnyReg {
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_)
|
| CanonicalType::Bundle(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::Clock(_) => unreachable!(),
|
| CanonicalType::Clock(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1769,6 +1775,7 @@ impl_run_pass_copy!([] SVAttributeAnnotation);
|
||||||
impl_run_pass_copy!([] UInt);
|
impl_run_pass_copy!([] UInt);
|
||||||
impl_run_pass_copy!([] usize);
|
impl_run_pass_copy!([] usize);
|
||||||
impl_run_pass_copy!([] FormalKind);
|
impl_run_pass_copy!([] FormalKind);
|
||||||
|
impl_run_pass_copy!([] PhantomConst);
|
||||||
|
|
||||||
macro_rules! impl_run_pass_for_struct {
|
macro_rules! impl_run_pass_for_struct {
|
||||||
(
|
(
|
||||||
|
|
|
@ -69,7 +69,8 @@ fn contains_any_enum_types(ty: CanonicalType) -> bool {
|
||||||
| CanonicalType::AsyncReset(_)
|
| CanonicalType::AsyncReset(_)
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::Clock(_) => false,
|
| CanonicalType::Clock(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -512,7 +513,8 @@ impl State {
|
||||||
| CanonicalType::AsyncReset(_)
|
| CanonicalType::AsyncReset(_)
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_)
|
| CanonicalType::Reset(_)
|
||||||
| CanonicalType::Clock(_) => unreachable!(),
|
| CanonicalType::Clock(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -577,7 +579,8 @@ fn connect_port(
|
||||||
| (CanonicalType::Clock(_), _)
|
| (CanonicalType::Clock(_), _)
|
||||||
| (CanonicalType::AsyncReset(_), _)
|
| (CanonicalType::AsyncReset(_), _)
|
||||||
| (CanonicalType::SyncReset(_), _)
|
| (CanonicalType::SyncReset(_), _)
|
||||||
| (CanonicalType::Reset(_), _) => unreachable!(
|
| (CanonicalType::Reset(_), _)
|
||||||
|
| (CanonicalType::PhantomConst(_), _) => unreachable!(
|
||||||
"trying to connect memory ports:\n{:?}\n{:?}",
|
"trying to connect memory ports:\n{:?}\n{:?}",
|
||||||
Expr::ty(lhs),
|
Expr::ty(lhs),
|
||||||
Expr::ty(rhs),
|
Expr::ty(rhs),
|
||||||
|
@ -665,6 +668,7 @@ impl Folder for State {
|
||||||
ExprEnum::UIntLiteral(_)
|
ExprEnum::UIntLiteral(_)
|
||||||
| ExprEnum::SIntLiteral(_)
|
| ExprEnum::SIntLiteral(_)
|
||||||
| ExprEnum::BoolLiteral(_)
|
| ExprEnum::BoolLiteral(_)
|
||||||
|
| ExprEnum::PhantomConst(_)
|
||||||
| ExprEnum::BundleLiteral(_)
|
| ExprEnum::BundleLiteral(_)
|
||||||
| ExprEnum::ArrayLiteral(_)
|
| ExprEnum::ArrayLiteral(_)
|
||||||
| ExprEnum::Uninit(_)
|
| ExprEnum::Uninit(_)
|
||||||
|
@ -923,7 +927,8 @@ impl Folder for State {
|
||||||
| CanonicalType::Clock(_)
|
| CanonicalType::Clock(_)
|
||||||
| CanonicalType::AsyncReset(_)
|
| CanonicalType::AsyncReset(_)
|
||||||
| CanonicalType::SyncReset(_)
|
| CanonicalType::SyncReset(_)
|
||||||
| CanonicalType::Reset(_) => canonical_type.default_fold(self),
|
| CanonicalType::Reset(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => canonical_type.default_fold(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ enum MemSplit {
|
||||||
Bundle {
|
Bundle {
|
||||||
fields: Rc<[MemSplit]>,
|
fields: Rc<[MemSplit]>,
|
||||||
},
|
},
|
||||||
|
PhantomConst,
|
||||||
Single {
|
Single {
|
||||||
output_mem: Option<Mem>,
|
output_mem: Option<Mem>,
|
||||||
element_type: SingleType,
|
element_type: SingleType,
|
||||||
|
@ -76,6 +77,7 @@ impl MemSplit {
|
||||||
fn mark_changed_element_type(self) -> Self {
|
fn mark_changed_element_type(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
MemSplit::Bundle { fields: _ } => self,
|
MemSplit::Bundle { fields: _ } => self,
|
||||||
|
MemSplit::PhantomConst => self,
|
||||||
MemSplit::Single {
|
MemSplit::Single {
|
||||||
output_mem,
|
output_mem,
|
||||||
element_type,
|
element_type,
|
||||||
|
@ -97,6 +99,7 @@ impl MemSplit {
|
||||||
.map(|field| Self::new(field.ty).mark_changed_element_type())
|
.map(|field| Self::new(field.ty).mark_changed_element_type())
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
|
CanonicalType::PhantomConst(_) => MemSplit::PhantomConst,
|
||||||
CanonicalType::Array(ty) => {
|
CanonicalType::Array(ty) => {
|
||||||
let element = MemSplit::new(ty.element());
|
let element = MemSplit::new(ty.element());
|
||||||
if let Self::Single {
|
if let Self::Single {
|
||||||
|
@ -339,6 +342,7 @@ impl SplitMemState<'_, '_> {
|
||||||
self.split_state_stack.pop();
|
self.split_state_stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MemSplit::PhantomConst => {}
|
||||||
MemSplit::Single {
|
MemSplit::Single {
|
||||||
output_mem,
|
output_mem,
|
||||||
element_type: single_type,
|
element_type: single_type,
|
||||||
|
@ -538,7 +542,12 @@ impl ModuleState {
|
||||||
};
|
};
|
||||||
loop {
|
loop {
|
||||||
match input_element_type {
|
match input_element_type {
|
||||||
CanonicalType::Bundle(_) => unreachable!("bundle types are always split"),
|
CanonicalType::Bundle(_) => {
|
||||||
|
unreachable!("bundle types are always split")
|
||||||
|
}
|
||||||
|
CanonicalType::PhantomConst(_) => {
|
||||||
|
unreachable!("PhantomConst are always removed")
|
||||||
|
}
|
||||||
CanonicalType::Enum(_)
|
CanonicalType::Enum(_)
|
||||||
if input_array_types
|
if input_array_types
|
||||||
.first()
|
.first()
|
||||||
|
@ -743,7 +752,8 @@ impl ModuleState {
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| MemSplit::Bundle { .. }
|
| MemSplit::Bundle { .. }
|
||||||
| MemSplit::Array { .. } => {
|
| MemSplit::Array { .. }
|
||||||
|
| MemSplit::PhantomConst => {
|
||||||
let mut replacement_ports = Vec::with_capacity(input_mem.ports().len());
|
let mut replacement_ports = Vec::with_capacity(input_mem.ports().len());
|
||||||
let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len());
|
let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len());
|
||||||
let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len());
|
let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len());
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::{
|
||||||
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
||||||
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
||||||
},
|
},
|
||||||
|
phantom_const::PhantomConst,
|
||||||
reg::Reg,
|
reg::Reg,
|
||||||
reset::{AsyncReset, Reset, ResetType, SyncReset},
|
reset::{AsyncReset, Reset, ResetType, SyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
|
274
crates/fayalite/src/phantom_const.rs
Normal file
274
crates/fayalite/src/phantom_const.rs
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize},
|
||||||
|
source_location::SourceLocation,
|
||||||
|
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
fmt,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PhantomConstCanonicalValue {
|
||||||
|
parsed: serde_json::Value,
|
||||||
|
serialized: Interned<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PhantomConstCanonicalValue {
|
||||||
|
pub fn from_json_value(parsed: serde_json::Value) -> Self {
|
||||||
|
let serialized = Intern::intern_owned(
|
||||||
|
serde_json::to_string(&parsed)
|
||||||
|
.expect("conversion from json value to text shouldn't fail"),
|
||||||
|
);
|
||||||
|
Self { parsed, serialized }
|
||||||
|
}
|
||||||
|
pub fn as_json_value(&self) -> &serde_json::Value {
|
||||||
|
&self.parsed
|
||||||
|
}
|
||||||
|
pub fn as_str(&self) -> Interned<str> {
|
||||||
|
self.serialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PhantomConstCanonicalValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.serialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PhantomConstCanonicalValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str(&self.serialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for PhantomConstCanonicalValue {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.serialized == other.serialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for PhantomConstCanonicalValue {}
|
||||||
|
|
||||||
|
impl Hash for PhantomConstCanonicalValue {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.serialized.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for PhantomConstCanonicalValue {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.parsed.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PhantomConstCanonicalValue {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self::from_json_value(serde_json::Value::deserialize(
|
||||||
|
deserializer,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PhantomConstValue: Intern + InternedCompare + Serialize + fmt::Debug {
|
||||||
|
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PhantomConstValue for T
|
||||||
|
where
|
||||||
|
T: ?Sized + Intern + InternedCompare + Serialize + fmt::Debug,
|
||||||
|
Interned<T>: DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn deserialize_value<'de, D>(deserializer: D) -> Result<Interned<Self>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
<Interned<T> as Deserialize<'de>>::deserialize(deserializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper type that allows any Rust value to be smuggled as a HDL [`Type`].
|
||||||
|
/// This only works for values that can be [serialized][Serialize] to and [deserialized][Deserialize] from [JSON][serde_json].
|
||||||
|
pub struct PhantomConst<T: ?Sized + PhantomConstValue = PhantomConstCanonicalValue> {
|
||||||
|
value: LazyInterned<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> fmt::Debug for PhantomConst<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("PhantomConst").field(&self.get()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Clone for PhantomConst<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Copy for PhantomConst<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> PartialEq for PhantomConst<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.get() == other.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Eq for PhantomConst<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Hash for PhantomConst<T> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.get().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PhantomConstCanonicalMemoize<T: ?Sized, const IS_FROM_CANONICAL: bool>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Copy
|
||||||
|
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Clone
|
||||||
|
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Eq
|
||||||
|
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> PartialEq
|
||||||
|
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||||
|
{
|
||||||
|
fn eq(&self, _other: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized, const IS_FROM_CANONICAL: bool> Hash
|
||||||
|
for PhantomConstCanonicalMemoize<T, IS_FROM_CANONICAL>
|
||||||
|
{
|
||||||
|
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, false> {
|
||||||
|
type Input = Interned<T>;
|
||||||
|
type InputOwned = Interned<T>;
|
||||||
|
type Output = Interned<PhantomConstCanonicalValue>;
|
||||||
|
|
||||||
|
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||||
|
Intern::intern_sized(PhantomConstCanonicalValue::from_json_value(
|
||||||
|
serde_json::to_value(input)
|
||||||
|
.expect("serialization failed when constructing a canonical PhantomConst"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Memoize for PhantomConstCanonicalMemoize<T, true> {
|
||||||
|
type Input = Interned<PhantomConstCanonicalValue>;
|
||||||
|
type InputOwned = Interned<PhantomConstCanonicalValue>;
|
||||||
|
type Output = Interned<T>;
|
||||||
|
|
||||||
|
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||||
|
PhantomConstValue::deserialize_value(input.as_json_value())
|
||||||
|
.expect("deserialization failed ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> PhantomConst<T>
|
||||||
|
where
|
||||||
|
Interned<T>: Default,
|
||||||
|
{
|
||||||
|
pub const fn default() -> Self {
|
||||||
|
PhantomConst {
|
||||||
|
value: LazyInterned::new_lazy(&Interned::<T>::default),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> PhantomConst<T> {
|
||||||
|
pub fn new(value: Interned<T>) -> Self {
|
||||||
|
Self {
|
||||||
|
value: LazyInterned::Interned(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get(self) -> Interned<T> {
|
||||||
|
self.value.interned()
|
||||||
|
}
|
||||||
|
pub fn type_properties(self) -> TypeProperties {
|
||||||
|
<()>::TYPE_PROPERTIES
|
||||||
|
}
|
||||||
|
pub fn can_connect(self, other: Self) -> bool {
|
||||||
|
self == other
|
||||||
|
}
|
||||||
|
pub fn canonical_phantom_const(self) -> PhantomConst {
|
||||||
|
if let Some(&retval) = <dyn Any>::downcast_ref::<PhantomConst>(&self) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
<PhantomConst>::new(
|
||||||
|
PhantomConstCanonicalMemoize::<T, false>(PhantomData).get_owned(self.get()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn from_canonical_phantom_const(canonical_type: PhantomConst) -> Self {
|
||||||
|
if let Some(&retval) = <dyn Any>::downcast_ref::<Self>(&canonical_type) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
Self::new(
|
||||||
|
PhantomConstCanonicalMemoize::<T, true>(PhantomData).get_owned(canonical_type.get()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> Type for PhantomConst<T> {
|
||||||
|
type BaseType = PhantomConst;
|
||||||
|
type MaskType = ();
|
||||||
|
impl_match_variant_as_self!();
|
||||||
|
|
||||||
|
fn mask_type(&self) -> Self::MaskType {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn canonical(&self) -> CanonicalType {
|
||||||
|
CanonicalType::PhantomConst(self.canonical_phantom_const())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||||
|
let CanonicalType::PhantomConst(phantom_const) = canonical_type else {
|
||||||
|
panic!("expected PhantomConst");
|
||||||
|
};
|
||||||
|
Self::from_canonical_phantom_const(phantom_const)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_location() -> SourceLocation {
|
||||||
|
SourceLocation::builtin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
|
||||||
|
where
|
||||||
|
Interned<T>: Default,
|
||||||
|
{
|
||||||
|
const TYPE: Self = Self::default();
|
||||||
|
const MASK_TYPE: Self::MaskType = ();
|
||||||
|
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||||
|
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ pub use crate::{
|
||||||
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
|
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
|
||||||
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
|
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
|
||||||
},
|
},
|
||||||
|
phantom_const::PhantomConst,
|
||||||
reg::Reg,
|
reg::Reg,
|
||||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
|
|
@ -167,6 +167,14 @@ impl<T: Type> CompiledTypeLayout<T> {
|
||||||
body: CompiledTypeLayoutBody::Array { element },
|
body: CompiledTypeLayoutBody::Array { element },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CanonicalType::PhantomConst(_) => {
|
||||||
|
let unit_layout = CompiledTypeLayout::get(());
|
||||||
|
CompiledTypeLayout {
|
||||||
|
ty: *input,
|
||||||
|
layout: unit_layout.layout,
|
||||||
|
body: unit_layout.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
CanonicalType::Bundle(bundle) => {
|
CanonicalType::Bundle(bundle) => {
|
||||||
let mut layout = TypeLayout::empty();
|
let mut layout = TypeLayout::empty();
|
||||||
let fields = bundle
|
let fields = bundle
|
||||||
|
@ -1792,7 +1800,7 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
CanonicalType::Bundle(_) => unreachable!(),
|
CanonicalType::Bundle(_) | CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
CanonicalType::AsyncReset(_) => TraceAsyncReset {
|
CanonicalType::AsyncReset(_) => TraceAsyncReset {
|
||||||
location: self.make_trace_scalar_helper(
|
location: self.make_trace_scalar_helper(
|
||||||
instantiated_module,
|
instantiated_module,
|
||||||
|
@ -2009,6 +2017,13 @@ impl Compiler {
|
||||||
| CanonicalType::Clock(_) => {
|
| CanonicalType::Clock(_) => {
|
||||||
self.make_trace_scalar(instantiated_module, target, name, source_location)
|
self.make_trace_scalar(instantiated_module, target, name, source_location)
|
||||||
}
|
}
|
||||||
|
CanonicalType::PhantomConst(_) => TraceBundle {
|
||||||
|
name,
|
||||||
|
fields: Interned::default(),
|
||||||
|
ty: Bundle::new(Interned::default()),
|
||||||
|
flow: target.flow(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn make_trace_decl(
|
fn make_trace_decl(
|
||||||
|
@ -2469,6 +2484,9 @@ impl Compiler {
|
||||||
Expr::field(Expr::<Bundle>::from_canonical(expr.arg()), &field.name)
|
Expr::field(Expr::<Bundle>::from_canonical(expr.arg()), &field.name)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
CanonicalType::PhantomConst(_) => {
|
||||||
|
self.compile_cast_aggregate_to_bits(instantiated_module, [])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn compile_cast_bits_to(
|
fn compile_cast_bits_to(
|
||||||
|
@ -2518,6 +2536,10 @@ impl Compiler {
|
||||||
CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
CanonicalType::SyncReset(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
||||||
CanonicalType::Reset(_) => unreachable!(),
|
CanonicalType::Reset(_) => unreachable!(),
|
||||||
CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
CanonicalType::Clock(ty) => Expr::canonical(expr.arg().cast_to(ty)),
|
||||||
|
CanonicalType::PhantomConst(ty) => {
|
||||||
|
let _ = self.compile_expr(instantiated_module, Expr::canonical(expr.arg()));
|
||||||
|
Expr::canonical(ty.to_expr())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
|
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
|
||||||
self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location())
|
self.compiled_expr_to_value(retval, instantiated_module.leaf_module().source_location())
|
||||||
|
@ -2567,6 +2589,7 @@ impl Compiler {
|
||||||
CanonicalType::SyncReset(_) => false,
|
CanonicalType::SyncReset(_) => false,
|
||||||
CanonicalType::Reset(_) => false,
|
CanonicalType::Reset(_) => false,
|
||||||
CanonicalType::Clock(_) => false,
|
CanonicalType::Clock(_) => false,
|
||||||
|
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
let dest_signed = match Expr::ty(expr) {
|
let dest_signed = match Expr::ty(expr) {
|
||||||
CanonicalType::UInt(_) => false,
|
CanonicalType::UInt(_) => false,
|
||||||
|
@ -2579,6 +2602,7 @@ impl Compiler {
|
||||||
CanonicalType::SyncReset(_) => false,
|
CanonicalType::SyncReset(_) => false,
|
||||||
CanonicalType::Reset(_) => false,
|
CanonicalType::Reset(_) => false,
|
||||||
CanonicalType::Clock(_) => false,
|
CanonicalType::Clock(_) => false,
|
||||||
|
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
|
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
|
||||||
match (src_signed, dest_signed) {
|
match (src_signed, dest_signed) {
|
||||||
|
@ -2634,6 +2658,9 @@ impl Compiler {
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
|
ExprEnum::PhantomConst(_) => self
|
||||||
|
.compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default())
|
||||||
|
.into(),
|
||||||
ExprEnum::BundleLiteral(literal) => self
|
ExprEnum::BundleLiteral(literal) => self
|
||||||
.compile_aggregate_literal(
|
.compile_aggregate_literal(
|
||||||
instantiated_module,
|
instantiated_module,
|
||||||
|
@ -3537,6 +3564,7 @@ impl Compiler {
|
||||||
CanonicalType::SyncReset(_) => unreachable!(),
|
CanonicalType::SyncReset(_) => unreachable!(),
|
||||||
CanonicalType::Reset(_) => unreachable!(),
|
CanonicalType::Reset(_) => unreachable!(),
|
||||||
CanonicalType::Clock(_) => unreachable!(),
|
CanonicalType::Clock(_) => unreachable!(),
|
||||||
|
CanonicalType::PhantomConst(_) => unreachable!("PhantomConst mismatch"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let Some(target) = lhs.target() else {
|
let Some(target) = lhs.target() else {
|
||||||
|
@ -3901,6 +3929,7 @@ impl Compiler {
|
||||||
CanonicalType::SyncReset(_) => false,
|
CanonicalType::SyncReset(_) => false,
|
||||||
CanonicalType::Reset(_) => false,
|
CanonicalType::Reset(_) => false,
|
||||||
CanonicalType::Clock(_) => false,
|
CanonicalType::Clock(_) => false,
|
||||||
|
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
let width = data_layout.ty.bit_width();
|
let width = data_layout.ty.bit_width();
|
||||||
if let Some(MemoryPortReadInsns {
|
if let Some(MemoryPortReadInsns {
|
||||||
|
@ -5909,6 +5938,7 @@ impl SimValue<CanonicalType> {
|
||||||
CanonicalType::Clock(ty) => {
|
CanonicalType::Clock(ty) => {
|
||||||
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
Expr::canonical(Bool::bits_to_expr(Cow::Borrowed(bits)).cast_to(ty))
|
||||||
}
|
}
|
||||||
|
CanonicalType::PhantomConst(ty) => Expr::canonical(ty.to_expr()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6312,7 +6342,8 @@ impl ToSimValue<CanonicalType> for bool {
|
||||||
| CanonicalType::SInt(_)
|
| CanonicalType::SInt(_)
|
||||||
| CanonicalType::Array(_)
|
| CanonicalType::Array(_)
|
||||||
| CanonicalType::Enum(_)
|
| CanonicalType::Enum(_)
|
||||||
| CanonicalType::Bundle(_) => {
|
| CanonicalType::Bundle(_)
|
||||||
|
| CanonicalType::PhantomConst(_) => {
|
||||||
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
panic!("can't create SimValue from bool: expected value of type: {ty:?}");
|
||||||
}
|
}
|
||||||
CanonicalType::Bool(_)
|
CanonicalType::Bool(_)
|
||||||
|
@ -6977,6 +7008,7 @@ impl SimulationImpl {
|
||||||
CanonicalType::SyncReset(_) => false,
|
CanonicalType::SyncReset(_) => false,
|
||||||
CanonicalType::Reset(_) => false,
|
CanonicalType::Reset(_) => false,
|
||||||
CanonicalType::Clock(_) => false,
|
CanonicalType::Clock(_) => false,
|
||||||
|
CanonicalType::PhantomConst(_) => unreachable!(),
|
||||||
};
|
};
|
||||||
match compiled_value.range.len() {
|
match compiled_value.range.len() {
|
||||||
TypeLen::A_SMALL_SLOT => read_write_small_scalar(
|
TypeLen::A_SMALL_SLOT => read_write_small_scalar(
|
||||||
|
|
|
@ -9,8 +9,10 @@ use crate::{
|
||||||
expr::Expr,
|
expr::Expr,
|
||||||
int::{Bool, SInt, UInt},
|
int::{Bool, SInt, UInt},
|
||||||
intern::{Intern, Interned},
|
intern::{Intern, Interned},
|
||||||
|
phantom_const::PhantomConst,
|
||||||
reset::{AsyncReset, Reset, SyncReset},
|
reset::{AsyncReset, Reset, SyncReset},
|
||||||
source_location::SourceLocation,
|
source_location::SourceLocation,
|
||||||
|
util::ConstUsize,
|
||||||
};
|
};
|
||||||
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index};
|
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index};
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ pub enum CanonicalType {
|
||||||
SyncReset(SyncReset),
|
SyncReset(SyncReset),
|
||||||
Reset(Reset),
|
Reset(Reset),
|
||||||
Clock(Clock),
|
Clock(Clock),
|
||||||
|
PhantomConst(PhantomConst),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for CanonicalType {
|
impl fmt::Debug for CanonicalType {
|
||||||
|
@ -50,6 +53,7 @@ impl fmt::Debug for CanonicalType {
|
||||||
Self::SyncReset(v) => v.fmt(f),
|
Self::SyncReset(v) => v.fmt(f),
|
||||||
Self::Reset(v) => v.fmt(f),
|
Self::Reset(v) => v.fmt(f),
|
||||||
Self::Clock(v) => v.fmt(f),
|
Self::Clock(v) => v.fmt(f),
|
||||||
|
Self::PhantomConst(v) => v.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +71,7 @@ impl CanonicalType {
|
||||||
CanonicalType::SyncReset(v) => v.type_properties(),
|
CanonicalType::SyncReset(v) => v.type_properties(),
|
||||||
CanonicalType::Reset(v) => v.type_properties(),
|
CanonicalType::Reset(v) => v.type_properties(),
|
||||||
CanonicalType::Clock(v) => v.type_properties(),
|
CanonicalType::Clock(v) => v.type_properties(),
|
||||||
|
CanonicalType::PhantomConst(v) => v.type_properties(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_passive(self) -> bool {
|
pub fn is_passive(self) -> bool {
|
||||||
|
@ -143,6 +148,12 @@ impl CanonicalType {
|
||||||
};
|
};
|
||||||
lhs.can_connect(rhs)
|
lhs.can_connect(rhs)
|
||||||
}
|
}
|
||||||
|
CanonicalType::PhantomConst(lhs) => {
|
||||||
|
let CanonicalType::PhantomConst(rhs) = rhs else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
lhs.can_connect(rhs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +177,7 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FillInDefaultedGenerics {
|
pub trait FillInDefaultedGenerics {
|
||||||
type Type: Type;
|
type Type;
|
||||||
fn fill_in_defaulted_generics(self) -> Self::Type;
|
fn fill_in_defaulted_generics(self) -> Self::Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +189,22 @@ impl<T: Type> FillInDefaultedGenerics for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FillInDefaultedGenerics for usize {
|
||||||
|
type Type = usize;
|
||||||
|
|
||||||
|
fn fill_in_defaulted_generics(self) -> Self::Type {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const V: usize> FillInDefaultedGenerics for ConstUsize<V> {
|
||||||
|
type Type = ConstUsize<V>;
|
||||||
|
|
||||||
|
fn fill_in_defaulted_generics(self) -> Self::Type {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
pub trait TypeOrDefaultSealed {}
|
pub trait TypeOrDefaultSealed {}
|
||||||
pub trait BaseTypeSealed {}
|
pub trait BaseTypeSealed {}
|
||||||
|
@ -205,6 +232,7 @@ impl_base_type!(AsyncReset);
|
||||||
impl_base_type!(SyncReset);
|
impl_base_type!(SyncReset);
|
||||||
impl_base_type!(Reset);
|
impl_base_type!(Reset);
|
||||||
impl_base_type!(Clock);
|
impl_base_type!(Clock);
|
||||||
|
impl_base_type!(PhantomConst);
|
||||||
|
|
||||||
impl sealed::BaseTypeSealed for CanonicalType {}
|
impl sealed::BaseTypeSealed for CanonicalType {}
|
||||||
|
|
||||||
|
@ -299,6 +327,7 @@ impl Type for CanonicalType {
|
||||||
CanonicalType::SyncReset(v) => v.mask_type().canonical(),
|
CanonicalType::SyncReset(v) => v.mask_type().canonical(),
|
||||||
CanonicalType::Reset(v) => v.mask_type().canonical(),
|
CanonicalType::Reset(v) => v.mask_type().canonical(),
|
||||||
CanonicalType::Clock(v) => v.mask_type().canonical(),
|
CanonicalType::Clock(v) => v.mask_type().canonical(),
|
||||||
|
CanonicalType::PhantomConst(v) => v.mask_type().canonical(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn canonical(&self) -> CanonicalType {
|
fn canonical(&self) -> CanonicalType {
|
||||||
|
|
|
@ -29,4 +29,5 @@ pub use misc::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod job_server;
|
pub mod job_server;
|
||||||
|
pub mod prefix_sum;
|
||||||
pub mod ready_valid;
|
pub mod ready_valid;
|
||||||
|
|
839
crates/fayalite/src/util/prefix_sum.rs
Normal file
839
crates/fayalite/src/util/prefix_sum.rs
Normal file
|
@ -0,0 +1,839 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
|
// code derived from:
|
||||||
|
// https://web.archive.org/web/20250303054010/https://git.libre-soc.org/?p=nmutil.git;a=blob;f=src/nmutil/prefix_sum.py;hb=effeb28e5848392adddcdad1f6e7a098f2a44c9c
|
||||||
|
|
||||||
|
use crate::intern::{Intern, Interned, Memoize};
|
||||||
|
use std::{borrow::Cow, num::NonZeroUsize};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub struct PrefixSumOp {
|
||||||
|
pub lhs_index: usize,
|
||||||
|
pub rhs_and_dest_index: NonZeroUsize,
|
||||||
|
pub row: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct DiagramConfig {
|
||||||
|
pub space: Cow<'static, str>,
|
||||||
|
pub vertical_bar: Cow<'static, str>,
|
||||||
|
pub plus: Cow<'static, str>,
|
||||||
|
pub slant: Cow<'static, str>,
|
||||||
|
pub connect: Cow<'static, str>,
|
||||||
|
pub no_connect: Cow<'static, str>,
|
||||||
|
pub padding: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagramConfig {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
space: Cow::Borrowed(" "),
|
||||||
|
vertical_bar: Cow::Borrowed("|"),
|
||||||
|
plus: Cow::Borrowed("\u{2295}"), // ⊕
|
||||||
|
slant: Cow::Borrowed(r"\"),
|
||||||
|
connect: Cow::Borrowed("\u{25CF}"), // ●
|
||||||
|
no_connect: Cow::Borrowed("X"),
|
||||||
|
padding: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn draw(self, ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize) -> String {
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct DiagramCell {
|
||||||
|
slant: bool,
|
||||||
|
plus: bool,
|
||||||
|
tee: bool,
|
||||||
|
}
|
||||||
|
let mut ops_by_row: Vec<Vec<PrefixSumOp>> = Vec::new();
|
||||||
|
let mut last_row = 0;
|
||||||
|
ops.into_iter().for_each(|op| {
|
||||||
|
assert!(
|
||||||
|
op.lhs_index < op.rhs_and_dest_index.get(),
|
||||||
|
"invalid PrefixSumOp! lhs_index must be less \
|
||||||
|
than rhs_and_dest_index: {op:?}",
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
op.row >= last_row,
|
||||||
|
"invalid PrefixSumOp! row must \
|
||||||
|
not decrease (row last was: {last_row}): {op:?}",
|
||||||
|
);
|
||||||
|
let ops = if op.row > last_row || ops_by_row.is_empty() {
|
||||||
|
ops_by_row.push(vec![]);
|
||||||
|
ops_by_row.last_mut().expect("just pushed")
|
||||||
|
} else {
|
||||||
|
ops_by_row
|
||||||
|
.last_mut()
|
||||||
|
.expect("just checked if ops_by_row is empty")
|
||||||
|
};
|
||||||
|
if let Some(last) = ops.last() {
|
||||||
|
assert!(
|
||||||
|
op.rhs_and_dest_index < last.rhs_and_dest_index,
|
||||||
|
"invalid PrefixSumOp! rhs_and_dest_index must strictly \
|
||||||
|
decrease in a row:\nthis op: {op:?}\nlast op: {last:?}",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ops.push(op);
|
||||||
|
last_row = op.row;
|
||||||
|
});
|
||||||
|
let blank_row = || {
|
||||||
|
vec![
|
||||||
|
DiagramCell {
|
||||||
|
slant: false,
|
||||||
|
plus: false,
|
||||||
|
tee: false
|
||||||
|
};
|
||||||
|
item_count
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let mut cells = vec![blank_row()];
|
||||||
|
for ops in ops_by_row {
|
||||||
|
let max_distance = ops
|
||||||
|
.iter()
|
||||||
|
.map(
|
||||||
|
|&PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index,
|
||||||
|
..
|
||||||
|
}| { rhs_and_dest_index.get() - lhs_index },
|
||||||
|
)
|
||||||
|
.max()
|
||||||
|
.expect("ops is known to be non-empty");
|
||||||
|
cells.extend((0..max_distance).map(|_| blank_row()));
|
||||||
|
for op in ops {
|
||||||
|
let mut y = cells.len() - 1;
|
||||||
|
assert!(
|
||||||
|
op.rhs_and_dest_index.get() < item_count,
|
||||||
|
"invalid PrefixSumOp! rhs_and_dest_index must be \
|
||||||
|
less than item_count ({item_count}): {op:?}",
|
||||||
|
);
|
||||||
|
let mut x = op.rhs_and_dest_index.get();
|
||||||
|
cells[y][x].plus = true;
|
||||||
|
x -= 1;
|
||||||
|
y -= 1;
|
||||||
|
while op.lhs_index < x {
|
||||||
|
cells[y][x].slant = true;
|
||||||
|
x -= 1;
|
||||||
|
y -= 1;
|
||||||
|
}
|
||||||
|
cells[y][x].tee = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut retval = String::new();
|
||||||
|
let mut row_text = vec![String::new(); 2 * self.padding + 1];
|
||||||
|
for cells_row in cells {
|
||||||
|
for cell in cells_row {
|
||||||
|
// top padding
|
||||||
|
for y in 0..self.padding {
|
||||||
|
// top left padding
|
||||||
|
for x in 0..self.padding {
|
||||||
|
row_text[y] += if x == y && (cell.plus || cell.slant) {
|
||||||
|
&self.slant
|
||||||
|
} else {
|
||||||
|
&self.space
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// top vertical bar
|
||||||
|
row_text[y] += &self.vertical_bar;
|
||||||
|
// top right padding
|
||||||
|
for _ in 0..self.padding {
|
||||||
|
row_text[y] += &self.space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// center left padding
|
||||||
|
for _ in 0..self.padding {
|
||||||
|
row_text[self.padding] += &self.space;
|
||||||
|
}
|
||||||
|
// center
|
||||||
|
row_text[self.padding] += if cell.plus {
|
||||||
|
&self.plus
|
||||||
|
} else if cell.tee {
|
||||||
|
&self.connect
|
||||||
|
} else if cell.slant {
|
||||||
|
&self.no_connect
|
||||||
|
} else {
|
||||||
|
&self.vertical_bar
|
||||||
|
};
|
||||||
|
// center right padding
|
||||||
|
for _ in 0..self.padding {
|
||||||
|
row_text[self.padding] += &self.space;
|
||||||
|
}
|
||||||
|
let bottom_padding_start = self.padding + 1;
|
||||||
|
let bottom_padding_last = self.padding * 2;
|
||||||
|
// bottom padding
|
||||||
|
for y in bottom_padding_start..=bottom_padding_last {
|
||||||
|
// bottom left padding
|
||||||
|
for _ in 0..self.padding {
|
||||||
|
row_text[y] += &self.space;
|
||||||
|
}
|
||||||
|
// bottom vertical bar
|
||||||
|
row_text[y] += &self.vertical_bar;
|
||||||
|
// bottom right padding
|
||||||
|
for x in bottom_padding_start..=bottom_padding_last {
|
||||||
|
row_text[y] += if x == y && (cell.tee || cell.slant) {
|
||||||
|
&self.slant
|
||||||
|
} else {
|
||||||
|
&self.space
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for line in &mut row_text {
|
||||||
|
retval += line.trim_end();
|
||||||
|
retval += "\n";
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DiagramConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrefixSumOp {
|
||||||
|
pub fn diagram(ops: impl IntoIterator<Item = Self>, item_count: usize) -> String {
|
||||||
|
Self::diagram_with_config(ops, item_count, DiagramConfig::new())
|
||||||
|
}
|
||||||
|
pub fn diagram_with_config(
|
||||||
|
ops: impl IntoIterator<Item = Self>,
|
||||||
|
item_count: usize,
|
||||||
|
config: DiagramConfig,
|
||||||
|
) -> String {
|
||||||
|
config.draw(ops, item_count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub enum PrefixSumAlgorithm {
|
||||||
|
/// Uses the algorithm from:
|
||||||
|
/// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel
|
||||||
|
LowLatency,
|
||||||
|
/// Uses the algorithm from:
|
||||||
|
/// https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_2:_Work-efficient
|
||||||
|
WorkEfficient,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrefixSumAlgorithm {
|
||||||
|
fn ops_impl(self, item_count: usize) -> Vec<PrefixSumOp> {
|
||||||
|
let mut retval = Vec::new();
|
||||||
|
let mut distance = 1;
|
||||||
|
let mut row = 0;
|
||||||
|
while distance < item_count {
|
||||||
|
let double_distance = distance
|
||||||
|
.checked_mul(2)
|
||||||
|
.expect("prefix-sum item_count is too big");
|
||||||
|
let (start, step) = match self {
|
||||||
|
Self::LowLatency => (distance, 1),
|
||||||
|
Self::WorkEfficient => (double_distance - 1, double_distance),
|
||||||
|
};
|
||||||
|
for rhs_and_dest_index in (start..item_count).step_by(step).rev() {
|
||||||
|
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let lhs_index = rhs_and_dest_index.get() - distance;
|
||||||
|
retval.push(PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index,
|
||||||
|
row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
distance = double_distance;
|
||||||
|
row += 1;
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Self::LowLatency => {}
|
||||||
|
Self::WorkEfficient => {
|
||||||
|
distance /= 2;
|
||||||
|
while distance >= 1 {
|
||||||
|
let start = distance
|
||||||
|
.checked_mul(3)
|
||||||
|
.expect("prefix-sum item_count is too big")
|
||||||
|
- 1;
|
||||||
|
for rhs_and_dest_index in (start..item_count).step_by(distance * 2).rev() {
|
||||||
|
let Some(rhs_and_dest_index) = NonZeroUsize::new(rhs_and_dest_index) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
let lhs_index = rhs_and_dest_index.get() - distance;
|
||||||
|
retval.push(PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index,
|
||||||
|
row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
row += 1;
|
||||||
|
distance /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
pub fn ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
struct MyMemoize(PrefixSumAlgorithm);
|
||||||
|
impl Memoize for MyMemoize {
|
||||||
|
type Input = usize;
|
||||||
|
type InputOwned = usize;
|
||||||
|
type Output = Interned<[PrefixSumOp]>;
|
||||||
|
|
||||||
|
fn inner(self, item_count: &Self::Input) -> Self::Output {
|
||||||
|
Intern::intern_owned(self.0.ops_impl(*item_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MyMemoize(self).get_owned(item_count)
|
||||||
|
}
|
||||||
|
pub fn run<T>(self, items: impl IntoIterator<Item = T>, f: impl FnMut(&T, &T) -> T) -> Vec<T> {
|
||||||
|
let mut items = Vec::from_iter(items);
|
||||||
|
self.run_on_slice(&mut items, f);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
pub fn run_on_slice<T>(self, items: &mut [T], mut f: impl FnMut(&T, &T) -> T) -> &mut [T] {
|
||||||
|
self.ops(items.len()).into_iter().for_each(
|
||||||
|
|PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index,
|
||||||
|
row: _,
|
||||||
|
}| {
|
||||||
|
items[rhs_and_dest_index.get()] =
|
||||||
|
f(&items[lhs_index], &items[rhs_and_dest_index.get()]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
pub fn filtered_ops(
|
||||||
|
self,
|
||||||
|
item_live_out_flags: impl IntoIterator<Item = bool>,
|
||||||
|
) -> Vec<PrefixSumOp> {
|
||||||
|
let mut item_live_out_flags = Vec::from_iter(item_live_out_flags);
|
||||||
|
let prefix_sum_ops = self.ops(item_live_out_flags.len());
|
||||||
|
let mut ops_live_flags = vec![false; prefix_sum_ops.len()];
|
||||||
|
for (
|
||||||
|
op_index,
|
||||||
|
&PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index,
|
||||||
|
row: _,
|
||||||
|
},
|
||||||
|
) in prefix_sum_ops.iter().enumerate().rev()
|
||||||
|
{
|
||||||
|
let live = item_live_out_flags[rhs_and_dest_index.get()];
|
||||||
|
item_live_out_flags[lhs_index] |= live;
|
||||||
|
ops_live_flags[op_index] = live;
|
||||||
|
}
|
||||||
|
prefix_sum_ops
|
||||||
|
.into_iter()
|
||||||
|
.zip(ops_live_flags)
|
||||||
|
.filter_map(|(op, live)| live.then_some(op))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
pub fn reduce_ops(self, item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
struct MyMemoize(PrefixSumAlgorithm);
|
||||||
|
impl Memoize for MyMemoize {
|
||||||
|
type Input = usize;
|
||||||
|
type InputOwned = usize;
|
||||||
|
type Output = Interned<[PrefixSumOp]>;
|
||||||
|
|
||||||
|
fn inner(self, item_count: &Self::Input) -> Self::Output {
|
||||||
|
let mut item_live_out_flags = vec![false; *item_count];
|
||||||
|
let Some(last_item_live_out_flag) = item_live_out_flags.last_mut() else {
|
||||||
|
return Interned::default();
|
||||||
|
};
|
||||||
|
*last_item_live_out_flag = true;
|
||||||
|
Intern::intern_owned(self.0.filtered_ops(item_live_out_flags))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MyMemoize(self).get_owned(item_count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reduce_ops(item_count: usize) -> Interned<[PrefixSumOp]> {
|
||||||
|
PrefixSumAlgorithm::LowLatency.reduce_ops(item_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reduce<T>(items: impl IntoIterator<Item = T>, mut f: impl FnMut(T, T) -> T) -> Option<T> {
|
||||||
|
let mut items: Vec<_> = items.into_iter().map(Some).collect();
|
||||||
|
for op in reduce_ops(items.len()) {
|
||||||
|
let (Some(lhs), Some(rhs)) = (
|
||||||
|
items[op.lhs_index].take(),
|
||||||
|
items[op.rhs_and_dest_index.get()].take(),
|
||||||
|
) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
items[op.rhs_and_dest_index.get()] = Some(f(lhs, rhs));
|
||||||
|
}
|
||||||
|
items.last_mut().and_then(Option::take)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn input_strings() -> [String; 9] {
|
||||||
|
std::array::from_fn(|i| String::from_utf8(vec![b'a' + i as u8]).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prefix_sum_strings() {
|
||||||
|
let input = input_strings();
|
||||||
|
let expected: Vec<String> = input
|
||||||
|
.iter()
|
||||||
|
.scan(String::new(), |l, r| {
|
||||||
|
*l += r;
|
||||||
|
Some(l.clone())
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
println!("expected: {expected:?}");
|
||||||
|
assert_eq!(
|
||||||
|
*PrefixSumAlgorithm::WorkEfficient
|
||||||
|
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
|
||||||
|
*expected
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
*PrefixSumAlgorithm::LowLatency
|
||||||
|
.run_on_slice(&mut input.clone(), |l, r| l.to_string() + r),
|
||||||
|
*expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reduce_string() {
|
||||||
|
let input = input_strings();
|
||||||
|
let expected = input.clone().into_iter().reduce(|l, r| l + &r);
|
||||||
|
assert_eq!(reduce(input, |l, r| l + &r), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn op(lhs_index: usize, rhs_and_dest_index: usize, row: u32) -> PrefixSumOp {
|
||||||
|
PrefixSumOp {
|
||||||
|
lhs_index,
|
||||||
|
rhs_and_dest_index: NonZeroUsize::new(rhs_and_dest_index).expect("should be non-zero"),
|
||||||
|
row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reduce_ops_9() {
|
||||||
|
let expected = vec![
|
||||||
|
op(7, 8, 0),
|
||||||
|
op(5, 6, 0),
|
||||||
|
op(3, 4, 0),
|
||||||
|
op(1, 2, 0),
|
||||||
|
op(6, 8, 1),
|
||||||
|
op(2, 4, 1),
|
||||||
|
op(4, 8, 2),
|
||||||
|
op(0, 8, 3),
|
||||||
|
];
|
||||||
|
println!("expected: {expected:#?}");
|
||||||
|
let ops = reduce_ops(9);
|
||||||
|
println!("ops: {ops:#?}");
|
||||||
|
assert_eq!(*ops, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reduce_ops_8() {
|
||||||
|
let expected = vec![
|
||||||
|
op(6, 7, 0),
|
||||||
|
op(4, 5, 0),
|
||||||
|
op(2, 3, 0),
|
||||||
|
op(0, 1, 0),
|
||||||
|
op(5, 7, 1),
|
||||||
|
op(1, 3, 1),
|
||||||
|
op(3, 7, 2),
|
||||||
|
];
|
||||||
|
println!("expected: {expected:#?}");
|
||||||
|
let ops = reduce_ops(8);
|
||||||
|
println!("ops: {ops:#?}");
|
||||||
|
assert_eq!(*ops, *expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_count_ones() {
|
||||||
|
for width in 0..=10u32 {
|
||||||
|
for v in 0..1u32 << width {
|
||||||
|
let expected = v.count_ones();
|
||||||
|
assert_eq!(
|
||||||
|
reduce((0..width).map(|i| (v >> i) & 1), |l, r| l + r).unwrap_or(0),
|
||||||
|
expected,
|
||||||
|
"v={v:#X}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
|
fn test_diagram(ops: impl IntoIterator<Item = PrefixSumOp>, item_count: usize, expected: &str) {
|
||||||
|
let text = PrefixSumOp::diagram_with_config(
|
||||||
|
ops,
|
||||||
|
item_count,
|
||||||
|
DiagramConfig {
|
||||||
|
plus: Cow::Borrowed("@"),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
println!("text:\n{text}\n");
|
||||||
|
assert_eq!(text, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_work_efficient_diagram_16() {
|
||||||
|
let item_count = 16;
|
||||||
|
test_diagram(
|
||||||
|
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
● | ● | ● | ● | ● | ● | ● | ● |
|
||||||
|
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
|
||||||
|
| \| | \| | \| | \| | \| | \| | \| | \|
|
||||||
|
| @ | @ | @ | @ | @ | @ | @ | @
|
||||||
|
| |\ | | | |\ | | | |\ | | | |\ | |
|
||||||
|
| | \| | | | \| | | | \| | | | \| |
|
||||||
|
| | X | | | X | | | X | | | X |
|
||||||
|
| | |\ | | | |\ | | | |\ | | | |\ |
|
||||||
|
| | | \| | | | \| | | | \| | | | \|
|
||||||
|
| | | @ | | | @ | | | @ | | | @
|
||||||
|
| | | |\ | | | | | | | |\ | | | |
|
||||||
|
| | | | \| | | | | | | | \| | | |
|
||||||
|
| | | | X | | | | | | | X | | |
|
||||||
|
| | | | |\ | | | | | | | |\ | | |
|
||||||
|
| | | | | \| | | | | | | | \| | |
|
||||||
|
| | | | | X | | | | | | | X | |
|
||||||
|
| | | | | |\ | | | | | | | |\ | |
|
||||||
|
| | | | | | \| | | | | | | | \| |
|
||||||
|
| | | | | | X | | | | | | | X |
|
||||||
|
| | | | | | |\ | | | | | | | |\ |
|
||||||
|
| | | | | | | \| | | | | | | | \|
|
||||||
|
| | | | | | | @ | | | | | | | @
|
||||||
|
| | | | | | | |\ | | | | | | | |
|
||||||
|
| | | | | | | | \| | | | | | | |
|
||||||
|
| | | | | | | | X | | | | | | |
|
||||||
|
| | | | | | | | |\ | | | | | | |
|
||||||
|
| | | | | | | | | \| | | | | | |
|
||||||
|
| | | | | | | | | X | | | | | |
|
||||||
|
| | | | | | | | | |\ | | | | | |
|
||||||
|
| | | | | | | | | | \| | | | | |
|
||||||
|
| | | | | | | | | | X | | | | |
|
||||||
|
| | | | | | | | | | |\ | | | | |
|
||||||
|
| | | | | | | | | | | \| | | | |
|
||||||
|
| | | | | | | | | | | X | | | |
|
||||||
|
| | | | | | | | | | | |\ | | | |
|
||||||
|
| | | | | | | | | | | | \| | | |
|
||||||
|
| | | | | | | | | | | | X | | |
|
||||||
|
| | | | | | | | | | | | |\ | | |
|
||||||
|
| | | | | | | | | | | | | \| | |
|
||||||
|
| | | | | | | | | | | | | X | |
|
||||||
|
| | | | | | | | | | | | | |\ | |
|
||||||
|
| | | | | | | | | | | | | | \| |
|
||||||
|
| | | | | | | | | | | | | | X |
|
||||||
|
| | | | | | | | | | | | | | |\ |
|
||||||
|
| | | | | | | | | | | | | | | \|
|
||||||
|
| | | | | | | ● | | | | | | | @
|
||||||
|
| | | | | | | |\ | | | | | | | |
|
||||||
|
| | | | | | | | \| | | | | | | |
|
||||||
|
| | | | | | | | X | | | | | | |
|
||||||
|
| | | | | | | | |\ | | | | | | |
|
||||||
|
| | | | | | | | | \| | | | | | |
|
||||||
|
| | | | | | | | | X | | | | | |
|
||||||
|
| | | | | | | | | |\ | | | | | |
|
||||||
|
| | | | | | | | | | \| | | | | |
|
||||||
|
| | | | | | | | | | X | | | | |
|
||||||
|
| | | | | | | | | | |\ | | | | |
|
||||||
|
| | | | | | | | | | | \| | | | |
|
||||||
|
| | | ● | | | ● | | | @ | | | |
|
||||||
|
| | | |\ | | | |\ | | | |\ | | | |
|
||||||
|
| | | | \| | | | \| | | | \| | | |
|
||||||
|
| | | | X | | | X | | | X | | |
|
||||||
|
| | | | |\ | | | |\ | | | |\ | | |
|
||||||
|
| | | | | \| | | | \| | | | \| | |
|
||||||
|
| ● | ● | @ | ● | @ | ● | @ | |
|
||||||
|
| |\ | |\ | |\ | |\ | |\ | |\ | |\ | |
|
||||||
|
| | \| | \| | \| | \| | \| | \| | \| |
|
||||||
|
| | @ | @ | @ | @ | @ | @ | @ |
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_low_latency_diagram_16() {
|
||||||
|
let item_count = 16;
|
||||||
|
test_diagram(
|
||||||
|
PrefixSumAlgorithm::LowLatency.ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
● ● ● ● ● ● ● ● ● ● ● ● ● ● ● |
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| \|
|
||||||
|
● @ @ @ @ @ @ @ @ @ @ @ @ @ @ @
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||||
|
| \| \| \| \| \| \| \| \| \| \| \| \| \| \| |
|
||||||
|
| X X X X X X X X X X X X X X |
|
||||||
|
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| | \| \| \| \| \| \| \| \| \| \| \| \| \| \|
|
||||||
|
● ● @ @ @ @ @ @ @ @ @ @ @ @ @ @
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | | |
|
||||||
|
| \| \| \| \| \| \| \| \| \| \| \| \| | | |
|
||||||
|
| X X X X X X X X X X X X | | |
|
||||||
|
| |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | | |
|
||||||
|
| | \| \| \| \| \| \| \| \| \| \| \| \| | |
|
||||||
|
| | X X X X X X X X X X X X | |
|
||||||
|
| | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||||
|
| | | \| \| \| \| \| \| \| \| \| \| \| \| |
|
||||||
|
| | | X X X X X X X X X X X X |
|
||||||
|
| | | |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| | | | \| \| \| \| \| \| \| \| \| \| \| \|
|
||||||
|
● ● ● ● @ @ @ @ @ @ @ @ @ @ @ @
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ |\ | | | | | | | |
|
||||||
|
| \| \| \| \| \| \| \| \| | | | | | | |
|
||||||
|
| X X X X X X X X | | | | | | |
|
||||||
|
| |\ |\ |\ |\ |\ |\ |\ |\ | | | | | | |
|
||||||
|
| | \| \| \| \| \| \| \| \| | | | | | |
|
||||||
|
| | X X X X X X X X | | | | | |
|
||||||
|
| | |\ |\ |\ |\ |\ |\ |\ |\ | | | | | |
|
||||||
|
| | | \| \| \| \| \| \| \| \| | | | | |
|
||||||
|
| | | X X X X X X X X | | | | |
|
||||||
|
| | | |\ |\ |\ |\ |\ |\ |\ |\ | | | | |
|
||||||
|
| | | | \| \| \| \| \| \| \| \| | | | |
|
||||||
|
| | | | X X X X X X X X | | | |
|
||||||
|
| | | | |\ |\ |\ |\ |\ |\ |\ |\ | | | |
|
||||||
|
| | | | | \| \| \| \| \| \| \| \| | | |
|
||||||
|
| | | | | X X X X X X X X | | |
|
||||||
|
| | | | | |\ |\ |\ |\ |\ |\ |\ |\ | | |
|
||||||
|
| | | | | | \| \| \| \| \| \| \| \| | |
|
||||||
|
| | | | | | X X X X X X X X | |
|
||||||
|
| | | | | | |\ |\ |\ |\ |\ |\ |\ |\ | |
|
||||||
|
| | | | | | | \| \| \| \| \| \| \| \| |
|
||||||
|
| | | | | | | X X X X X X X X |
|
||||||
|
| | | | | | | |\ |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| | | | | | | | \| \| \| \| \| \| \| \|
|
||||||
|
| | | | | | | | @ @ @ @ @ @ @ @
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_work_efficient_diagram_9() {
|
||||||
|
let item_count = 9;
|
||||||
|
test_diagram(
|
||||||
|
PrefixSumAlgorithm::WorkEfficient.ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | |
|
||||||
|
● | ● | ● | ● | |
|
||||||
|
|\ | |\ | |\ | |\ | |
|
||||||
|
| \| | \| | \| | \| |
|
||||||
|
| @ | @ | @ | @ |
|
||||||
|
| |\ | | | |\ | | |
|
||||||
|
| | \| | | | \| | |
|
||||||
|
| | X | | | X | |
|
||||||
|
| | |\ | | | |\ | |
|
||||||
|
| | | \| | | | \| |
|
||||||
|
| | | @ | | | @ |
|
||||||
|
| | | |\ | | | | |
|
||||||
|
| | | | \| | | | |
|
||||||
|
| | | | X | | | |
|
||||||
|
| | | | |\ | | | |
|
||||||
|
| | | | | \| | | |
|
||||||
|
| | | | | X | | |
|
||||||
|
| | | | | |\ | | |
|
||||||
|
| | | | | | \| | |
|
||||||
|
| | | | | | X | |
|
||||||
|
| | | | | | |\ | |
|
||||||
|
| | | | | | | \| |
|
||||||
|
| | | ● | | | @ |
|
||||||
|
| | | |\ | | | | |
|
||||||
|
| | | | \| | | | |
|
||||||
|
| | | | X | | | |
|
||||||
|
| | | | |\ | | | |
|
||||||
|
| | | | | \| | | |
|
||||||
|
| ● | ● | @ | ● |
|
||||||
|
| |\ | |\ | |\ | |\ |
|
||||||
|
| | \| | \| | \| | \|
|
||||||
|
| | @ | @ | @ | @
|
||||||
|
| | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_low_latency_diagram_9() {
|
||||||
|
let item_count = 9;
|
||||||
|
test_diagram(
|
||||||
|
PrefixSumAlgorithm::LowLatency.ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | |
|
||||||
|
● ● ● ● ● ● ● ● |
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| \| \| \| \| \| \| \| \|
|
||||||
|
● @ @ @ @ @ @ @ @
|
||||||
|
|\ |\ |\ |\ |\ |\ |\ | |
|
||||||
|
| \| \| \| \| \| \| \| |
|
||||||
|
| X X X X X X X |
|
||||||
|
| |\ |\ |\ |\ |\ |\ |\ |
|
||||||
|
| | \| \| \| \| \| \| \|
|
||||||
|
● ● @ @ @ @ @ @ @
|
||||||
|
|\ |\ |\ |\ |\ | | | |
|
||||||
|
| \| \| \| \| \| | | |
|
||||||
|
| X X X X X | | |
|
||||||
|
| |\ |\ |\ |\ |\ | | |
|
||||||
|
| | \| \| \| \| \| | |
|
||||||
|
| | X X X X X | |
|
||||||
|
| | |\ |\ |\ |\ |\ | |
|
||||||
|
| | | \| \| \| \| \| |
|
||||||
|
| | | X X X X X |
|
||||||
|
| | | |\ |\ |\ |\ |\ |
|
||||||
|
| | | | \| \| \| \| \|
|
||||||
|
● | | | @ @ @ @ @
|
||||||
|
|\ | | | | | | | |
|
||||||
|
| \| | | | | | | |
|
||||||
|
| X | | | | | | |
|
||||||
|
| |\ | | | | | | |
|
||||||
|
| | \| | | | | | |
|
||||||
|
| | X | | | | | |
|
||||||
|
| | |\ | | | | | |
|
||||||
|
| | | \| | | | | |
|
||||||
|
| | | X | | | | |
|
||||||
|
| | | |\ | | | | |
|
||||||
|
| | | | \| | | | |
|
||||||
|
| | | | X | | | |
|
||||||
|
| | | | |\ | | | |
|
||||||
|
| | | | | \| | | |
|
||||||
|
| | | | | X | | |
|
||||||
|
| | | | | |\ | | |
|
||||||
|
| | | | | | \| | |
|
||||||
|
| | | | | | X | |
|
||||||
|
| | | | | | |\ | |
|
||||||
|
| | | | | | | \| |
|
||||||
|
| | | | | | | X |
|
||||||
|
| | | | | | | |\ |
|
||||||
|
| | | | | | | | \|
|
||||||
|
| | | | | | | | @
|
||||||
|
| | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reduce_diagram_16() {
|
||||||
|
let item_count = 16;
|
||||||
|
test_diagram(
|
||||||
|
reduce_ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
● | ● | ● | ● | ● | ● | ● | ● |
|
||||||
|
|\ | |\ | |\ | |\ | |\ | |\ | |\ | |\ |
|
||||||
|
| \| | \| | \| | \| | \| | \| | \| | \|
|
||||||
|
| @ | @ | @ | @ | @ | @ | @ | @
|
||||||
|
| |\ | | | |\ | | | |\ | | | |\ | |
|
||||||
|
| | \| | | | \| | | | \| | | | \| |
|
||||||
|
| | X | | | X | | | X | | | X |
|
||||||
|
| | |\ | | | |\ | | | |\ | | | |\ |
|
||||||
|
| | | \| | | | \| | | | \| | | | \|
|
||||||
|
| | | @ | | | @ | | | @ | | | @
|
||||||
|
| | | |\ | | | | | | | |\ | | | |
|
||||||
|
| | | | \| | | | | | | | \| | | |
|
||||||
|
| | | | X | | | | | | | X | | |
|
||||||
|
| | | | |\ | | | | | | | |\ | | |
|
||||||
|
| | | | | \| | | | | | | | \| | |
|
||||||
|
| | | | | X | | | | | | | X | |
|
||||||
|
| | | | | |\ | | | | | | | |\ | |
|
||||||
|
| | | | | | \| | | | | | | | \| |
|
||||||
|
| | | | | | X | | | | | | | X |
|
||||||
|
| | | | | | |\ | | | | | | | |\ |
|
||||||
|
| | | | | | | \| | | | | | | | \|
|
||||||
|
| | | | | | | @ | | | | | | | @
|
||||||
|
| | | | | | | |\ | | | | | | | |
|
||||||
|
| | | | | | | | \| | | | | | | |
|
||||||
|
| | | | | | | | X | | | | | | |
|
||||||
|
| | | | | | | | |\ | | | | | | |
|
||||||
|
| | | | | | | | | \| | | | | | |
|
||||||
|
| | | | | | | | | X | | | | | |
|
||||||
|
| | | | | | | | | |\ | | | | | |
|
||||||
|
| | | | | | | | | | \| | | | | |
|
||||||
|
| | | | | | | | | | X | | | | |
|
||||||
|
| | | | | | | | | | |\ | | | | |
|
||||||
|
| | | | | | | | | | | \| | | | |
|
||||||
|
| | | | | | | | | | | X | | | |
|
||||||
|
| | | | | | | | | | | |\ | | | |
|
||||||
|
| | | | | | | | | | | | \| | | |
|
||||||
|
| | | | | | | | | | | | X | | |
|
||||||
|
| | | | | | | | | | | | |\ | | |
|
||||||
|
| | | | | | | | | | | | | \| | |
|
||||||
|
| | | | | | | | | | | | | X | |
|
||||||
|
| | | | | | | | | | | | | |\ | |
|
||||||
|
| | | | | | | | | | | | | | \| |
|
||||||
|
| | | | | | | | | | | | | | X |
|
||||||
|
| | | | | | | | | | | | | | |\ |
|
||||||
|
| | | | | | | | | | | | | | | \|
|
||||||
|
| | | | | | | | | | | | | | | @
|
||||||
|
| | | | | | | | | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_reduce_diagram_9() {
|
||||||
|
let item_count = 9;
|
||||||
|
test_diagram(
|
||||||
|
reduce_ops(item_count),
|
||||||
|
item_count,
|
||||||
|
&r"
|
||||||
|
| | | | | | | | |
|
||||||
|
| ● | ● | ● | ● |
|
||||||
|
| |\ | |\ | |\ | |\ |
|
||||||
|
| | \| | \| | \| | \|
|
||||||
|
| | @ | @ | @ | @
|
||||||
|
| | |\ | | | |\ | |
|
||||||
|
| | | \| | | | \| |
|
||||||
|
| | | X | | | X |
|
||||||
|
| | | |\ | | | |\ |
|
||||||
|
| | | | \| | | | \|
|
||||||
|
| | | | @ | | | @
|
||||||
|
| | | | |\ | | | |
|
||||||
|
| | | | | \| | | |
|
||||||
|
| | | | | X | | |
|
||||||
|
| | | | | |\ | | |
|
||||||
|
| | | | | | \| | |
|
||||||
|
| | | | | | X | |
|
||||||
|
| | | | | | |\ | |
|
||||||
|
| | | | | | | \| |
|
||||||
|
| | | | | | | X |
|
||||||
|
| | | | | | | |\ |
|
||||||
|
| | | | | | | | \|
|
||||||
|
● | | | | | | | @
|
||||||
|
|\ | | | | | | | |
|
||||||
|
| \| | | | | | | |
|
||||||
|
| X | | | | | | |
|
||||||
|
| |\ | | | | | | |
|
||||||
|
| | \| | | | | | |
|
||||||
|
| | X | | | | | |
|
||||||
|
| | |\ | | | | | |
|
||||||
|
| | | \| | | | | |
|
||||||
|
| | | X | | | | |
|
||||||
|
| | | |\ | | | | |
|
||||||
|
| | | | \| | | | |
|
||||||
|
| | | | X | | | |
|
||||||
|
| | | | |\ | | | |
|
||||||
|
| | | | | \| | | |
|
||||||
|
| | | | | X | | |
|
||||||
|
| | | | | |\ | | |
|
||||||
|
| | | | | | \| | |
|
||||||
|
| | | | | | X | |
|
||||||
|
| | | | | | |\ | |
|
||||||
|
| | | | | | | \| |
|
||||||
|
| | | | | | | X |
|
||||||
|
| | | | | | | |\ |
|
||||||
|
| | | | | | | | \|
|
||||||
|
| | | | | | | | @
|
||||||
|
| | | | | | | | |
|
||||||
|
"[1..], // trim newline at start
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -191,10 +191,14 @@ circuit check_array_repeat:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait UnknownTrait {}
|
||||||
|
|
||||||
|
impl<T: ?Sized> UnknownTrait for T {}
|
||||||
|
|
||||||
#[hdl_module(outline_generated)]
|
#[hdl_module(outline_generated)]
|
||||||
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
|
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
|
||||||
where
|
where
|
||||||
T: StaticType,
|
T: StaticType + UnknownTrait,
|
||||||
ConstUsize<N>: KnownSize,
|
ConstUsize<N>: KnownSize,
|
||||||
U: std::fmt::Display,
|
U: std::fmt::Display,
|
||||||
{
|
{
|
||||||
|
@ -376,18 +380,18 @@ circuit check_written_inside_both_if_else:
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
#[hdl(outline_generated, cmp_eq)]
|
||||||
pub struct TestStruct<T> {
|
pub struct TestStruct<T> {
|
||||||
pub a: T,
|
pub a: T,
|
||||||
pub b: UInt<8>,
|
pub b: UInt<8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
#[hdl(outline_generated, cmp_eq)]
|
||||||
pub struct TestStruct2 {
|
pub struct TestStruct2 {
|
||||||
pub v: UInt<8>,
|
pub v: UInt<8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[hdl(outline_generated)]
|
#[hdl(outline_generated, cmp_eq)]
|
||||||
pub struct TestStruct3 {}
|
pub struct TestStruct3 {}
|
||||||
|
|
||||||
#[hdl_module(outline_generated)]
|
#[hdl_module(outline_generated)]
|
||||||
|
@ -4421,3 +4425,125 @@ circuit check_let_patterns:
|
||||||
",
|
",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[hdl_module(outline_generated)]
|
||||||
|
pub fn check_struct_cmp_eq() {
|
||||||
|
#[hdl]
|
||||||
|
let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let tuple_cmp_eq: Bool = m.output();
|
||||||
|
connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs));
|
||||||
|
#[hdl]
|
||||||
|
let tuple_cmp_ne: Bool = m.output();
|
||||||
|
connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs));
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_lhs: TestStruct<SInt<8>> = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_rhs: TestStruct<SInt<8>> = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_cmp_eq: Bool = m.output();
|
||||||
|
connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs));
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_cmp_ne: Bool = m.output();
|
||||||
|
connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs));
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_2_lhs: TestStruct2 = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_2_rhs: TestStruct2 = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_2_cmp_eq: Bool = m.output();
|
||||||
|
connect(
|
||||||
|
test_struct_2_cmp_eq,
|
||||||
|
test_struct_2_lhs.cmp_eq(test_struct_2_rhs),
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_2_cmp_ne: Bool = m.output();
|
||||||
|
connect(
|
||||||
|
test_struct_2_cmp_ne,
|
||||||
|
test_struct_2_lhs.cmp_ne(test_struct_2_rhs),
|
||||||
|
);
|
||||||
|
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_3_lhs: TestStruct3 = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_3_rhs: TestStruct3 = m.input();
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_3_cmp_eq: Bool = m.output();
|
||||||
|
connect(
|
||||||
|
test_struct_3_cmp_eq,
|
||||||
|
test_struct_3_lhs.cmp_eq(test_struct_3_rhs),
|
||||||
|
);
|
||||||
|
#[hdl]
|
||||||
|
let test_struct_3_cmp_ne: Bool = m.output();
|
||||||
|
connect(
|
||||||
|
test_struct_3_cmp_ne,
|
||||||
|
test_struct_3_lhs.cmp_ne(test_struct_3_rhs),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_struct_cmp_eq() {
|
||||||
|
let _n = SourceLocation::normalize_files_for_tests();
|
||||||
|
let m = check_struct_cmp_eq();
|
||||||
|
dbg!(m);
|
||||||
|
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
|
||||||
|
assert_export_firrtl! {
|
||||||
|
m =>
|
||||||
|
"/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
|
||||||
|
circuit check_struct_cmp_eq:
|
||||||
|
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
|
||||||
|
type Ty1 = {a: SInt<8>, b: UInt<8>}
|
||||||
|
type Ty2 = {v: UInt<8>}
|
||||||
|
type Ty3 = {}
|
||||||
|
module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
|
||||||
|
input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
|
||||||
|
input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
|
||||||
|
output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
|
||||||
|
output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
|
||||||
|
input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1]
|
||||||
|
input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1]
|
||||||
|
output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1]
|
||||||
|
output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1]
|
||||||
|
input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1]
|
||||||
|
input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1]
|
||||||
|
output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1]
|
||||||
|
output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1]
|
||||||
|
input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1]
|
||||||
|
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
|
||||||
|
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
|
||||||
|
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
|
||||||
|
wire _array_literal_expr: UInt<1>[3]
|
||||||
|
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||||
|
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||||
|
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||||
|
wire _cast_array_to_bits_expr: UInt<1>[3]
|
||||||
|
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
|
||||||
|
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
|
||||||
|
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
|
||||||
|
wire _cast_to_bits_expr: UInt<3>
|
||||||
|
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
|
||||||
|
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
|
||||||
|
wire _array_literal_expr_1: UInt<1>[3]
|
||||||
|
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
|
||||||
|
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
|
||||||
|
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
|
||||||
|
wire _cast_array_to_bits_expr_1: UInt<1>[3]
|
||||||
|
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
|
||||||
|
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
|
||||||
|
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
|
||||||
|
wire _cast_to_bits_expr_1: UInt<3>
|
||||||
|
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
|
||||||
|
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
|
||||||
|
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
|
||||||
|
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
|
||||||
|
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
|
||||||
|
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
|
||||||
|
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
|
||||||
|
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
|
||||||
|
",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -49,7 +49,8 @@
|
||||||
"AsyncReset": "Visible",
|
"AsyncReset": "Visible",
|
||||||
"SyncReset": "Visible",
|
"SyncReset": "Visible",
|
||||||
"Reset": "Visible",
|
"Reset": "Visible",
|
||||||
"Clock": "Visible"
|
"Clock": "Visible",
|
||||||
|
"PhantomConst": "Visible"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Bundle": {
|
"Bundle": {
|
||||||
|
@ -1262,6 +1263,12 @@
|
||||||
"ArrayElement": "Visible",
|
"ArrayElement": "Visible",
|
||||||
"DynArrayElement": "Visible"
|
"DynArrayElement": "Visible"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"PhantomConst": {
|
||||||
|
"data": {
|
||||||
|
"$kind": "Opaque"
|
||||||
|
},
|
||||||
|
"generics": "<T: ?Sized + crate::phantom_const::PhantomConstValue>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue