initial public commit

This commit is contained in:
Jacob Lifshay 2024-06-10 23:09:13 -07:00
commit 0b958e7852
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
56 changed files with 30235 additions and 0 deletions

View file

@ -0,0 +1,24 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
# See Notices.txt for copyright information
[package]
name = "fayalite"
version = "0.1.0"
edition = "2021"
workspace = "../.."
license = "LGPL-3.0-or-later"
[dependencies]
bitvec = { version = "1.0.1", features = ["serde"] }
hashbrown = "0.14.3"
num-bigint = "0.4.4"
num-traits = "0.2.16"
paste = "1.0.14"
fayalite-proc-macros = { version = "=0.1.0", path = "../fayalite-proc-macros" }
serde = { version = "1.0.202", features = ["derive"] }
serde_json = "1.0.117"
[dev-dependencies]
trybuild = "1.0"
[build-dependencies]
fayalite-visit-gen = { version = "=0.1.0", path = "../fayalite-visit-gen" }

15
crates/fayalite/build.rs Normal file
View file

@ -0,0 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite_visit_gen::parse_and_generate;
use std::{env, fs, path::Path};
fn main() {
let path = "visit_types.json";
println!("cargo::rerun-if-changed={path}");
println!("cargo::rerun-if-changed=build.rs");
let generated = parse_and_generate(path).map_err(|e| panic!("{e}")).unwrap();
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir).join("visit.rs");
fs::write(&out_path, generated).unwrap();
// println!("cargo::warning=generated {}", out_path.display());
}

View file

@ -0,0 +1,196 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::Target,
intern::{Intern, Interned},
};
use serde::{Deserialize, Serialize};
use std::{
fmt,
hash::{Hash, Hasher},
ops::Deref,
};
#[derive(Clone)]
struct CustomFirrtlAnnotationFieldsImpl {
value: serde_json::Map<String, serde_json::Value>,
serialized: Interned<str>,
}
impl Hash for CustomFirrtlAnnotationFieldsImpl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.serialized.hash(state);
}
}
impl Eq for CustomFirrtlAnnotationFieldsImpl {}
impl PartialEq for CustomFirrtlAnnotationFieldsImpl {
fn eq(&self, other: &Self) -> bool {
self.serialized == other.serialized
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct CustomFirrtlAnnotationFields(Interned<CustomFirrtlAnnotationFieldsImpl>);
impl fmt::Debug for CustomFirrtlAnnotationFields {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.value.fmt(f)
}
}
impl<'de> Deserialize<'de> for CustomFirrtlAnnotationFields {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
serde_json::Map::<String, serde_json::Value>::deserialize(deserializer).map(Self::from)
}
}
impl Serialize for CustomFirrtlAnnotationFields {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.value.serialize(serializer)
}
}
impl Deref for CustomFirrtlAnnotationFields {
type Target = serde_json::Map<String, serde_json::Value>;
fn deref(&self) -> &Self::Target {
&self.0.value
}
}
impl From<serde_json::Map<String, serde_json::Value>> for CustomFirrtlAnnotationFields {
fn from(value: serde_json::Map<String, serde_json::Value>) -> Self {
let serialized =
serde_json::to_string(&value).expect("serialization of JSON should succeed");
Self(Intern::intern_sized(CustomFirrtlAnnotationFieldsImpl {
value,
serialized: Intern::intern_owned(serialized),
}))
}
}
#[derive(Debug, Clone)]
pub struct NotAJsonObject(pub serde_json::Value);
impl fmt::Display for NotAJsonObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("not a JSON object")
}
}
impl std::error::Error for NotAJsonObject {}
impl TryFrom<serde_json::Value> for CustomFirrtlAnnotationFields {
type Error = NotAJsonObject;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
match value {
serde_json::Value::Object(value) => Ok(value.into()),
_ => Err(NotAJsonObject(value)),
}
}
}
impl From<CustomFirrtlAnnotationFields> for serde_json::Map<String, serde_json::Value> {
fn from(value: CustomFirrtlAnnotationFields) -> Self {
Self::clone(&value)
}
}
impl From<CustomFirrtlAnnotationFields> for serde_json::Value {
fn from(value: CustomFirrtlAnnotationFields) -> Self {
serde_json::Value::Object(value.into())
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub struct CustomFirrtlAnnotation {
pub class: Interned<str>,
#[serde(flatten)]
pub additional_fields: CustomFirrtlAnnotationFields,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Annotation {
DontTouch,
CustomFirrtl(CustomFirrtlAnnotation),
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct TargetedAnnotation {
target: Interned<Target>,
annotation: Annotation,
}
impl TargetedAnnotation {
#[track_caller]
pub fn new(target: Interned<Target>, annotation: Annotation) -> Self {
Self::assert_valid_target(target);
Self { target, annotation }
}
#[track_caller]
pub fn assert_valid_target(target: Interned<Target>) {
assert!(target.is_static(), "can't annotate non-static targets");
}
pub fn target(&self) -> Interned<Target> {
self.target
}
pub fn annotation(&self) -> &Annotation {
&self.annotation
}
}
pub trait IntoAnnotations {
type IntoAnnotations: IntoIterator<Item = Annotation>;
fn into_annotations(self) -> Self::IntoAnnotations;
}
impl IntoAnnotations for Annotation {
type IntoAnnotations = [Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[self]
}
}
impl IntoAnnotations for Box<Annotation> {
type IntoAnnotations = [Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[*self]
}
}
impl IntoAnnotations for &'_ Annotation {
type IntoAnnotations = [Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[self.clone()]
}
}
impl IntoAnnotations for &'_ mut Annotation {
type IntoAnnotations = [Annotation; 1];
fn into_annotations(self) -> Self::IntoAnnotations {
[self.clone()]
}
}
impl<T: IntoIterator<Item = Annotation>> IntoAnnotations for T {
type IntoAnnotations = Self;
fn into_annotations(self) -> Self::IntoAnnotations {
self
}
}

View file

@ -0,0 +1,729 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
bundle::{BundleType, BundleValue},
expr::{
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
Expr, ToExpr,
},
intern::{Intern, Interned, InternedCompare, Memoize},
module::{
transform::visit::{Fold, Folder, Visit, Visitor},
ModuleBuilder, NormalModule,
},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, DynValueTrait, FixedType, MatchVariantWithoutScope, Type,
TypeEnum, Value, ValueEnum,
},
util::{ConstBool, GenericConstBool, MakeMutSlice},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use std::{
any::Any,
borrow::{Borrow, BorrowMut},
fmt,
hash::Hash,
marker::PhantomData,
ops::IndexMut,
sync::Arc,
};
mod sealed {
pub trait Sealed {}
}
pub trait ValueArrayOrSlice:
sealed::Sealed
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
+ Hash
+ fmt::Debug
+ Eq
+ Send
+ Sync
+ 'static
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
+ ToOwned
+ InternedCompare
{
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
type Match: 'static
+ Clone
+ Eq
+ fmt::Debug
+ Hash
+ Send
+ Sync
+ BorrowMut<[Expr<Self::Element>]>;
type MaskVA: ValueArrayOrSlice<
Element = <Self::ElementType as Type>::MaskValue,
ElementType = <Self::ElementType as Type>::MaskType,
LenType = Self::LenType,
MaskVA = Self::MaskVA,
> + ?Sized;
type IsFixedLen: GenericConstBool;
const FIXED_LEN_TYPE: Option<Self::LenType>;
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
fn len_from_len_type(v: Self::LenType) -> usize;
#[allow(clippy::result_unit_err)]
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
fn len_type(&self) -> Self::LenType;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn iter(&self) -> std::slice::Iter<Self::Element> {
Borrow::<[_]>::borrow(self).iter()
}
fn clone_to_arc(&self) -> Arc<Self>;
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
}
impl<T> sealed::Sealed for [T] {}
impl<V: Value> ValueArrayOrSlice for [V]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = usize;
type Match = Box<[Expr<V>]>;
type MaskVA = [<Self::ElementType as Type>::MaskValue];
type IsFixedLen = ConstBool<false>;
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
(0..array.canonical_type().len())
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
.collect()
}
fn len_from_len_type(v: Self::LenType) -> usize {
v
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
Ok(v)
}
fn len_type(&self) -> Self::LenType {
self.len()
}
fn len(&self) -> usize {
<[_]>::len(self)
}
fn is_empty(&self) -> bool {
<[_]>::is_empty(self)
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::from(self)
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
MakeMutSlice::make_mut_slice(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
impl<T, const N: usize> sealed::Sealed for [T; N] {}
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = ();
type Match = [Expr<V>; N];
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
type IsFixedLen = ConstBool<true>;
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
std::array::from_fn(|index| {
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
})
}
fn len_from_len_type(_v: Self::LenType) -> usize {
N
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
if v == N {
Ok(())
} else {
Err(())
}
}
fn len_type(&self) -> Self::LenType {}
fn len(&self) -> usize {
N
}
fn is_empty(&self) -> bool {
N == 0
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::new(self.clone())
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
Arc::make_mut(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
element: VA::ElementType,
len: VA::LenType,
bit_width: usize,
}
pub trait ArrayTypeTrait:
Type<
CanonicalType = ArrayType<[DynCanonicalValue]>,
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
CanonicalValue = Array<[DynCanonicalValue]>,
MaskType = ArrayType<
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
>,
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ sealed::Sealed
+ Connect<Self>
{
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
+ ?Sized;
type Element: Value<Type = Self::ElementType>;
type ElementType: Type<Value = Self::Element>;
}
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
type ValueArrayOrSlice = VA;
type Element = VA::Element;
type ElementType = VA::ElementType;
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
fn clone(&self) -> Self {
Self {
element: self.element.clone(),
len: self.len,
bit_width: self.bit_width,
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
pub fn element(&self) -> &VA::ElementType {
&self.element
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.len)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn bit_width(&self) -> usize {
self.bit_width
}
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
ArrayType {
len: self.len(),
element: self.element,
bit_width: self.bit_width,
}
}
#[track_caller]
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
Self::new_with_len_type(
element,
VA::try_len_type_from_len(len).expect("length should match"),
)
}
#[track_caller]
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
panic!("array is too big: bit-width overflowed");
};
ArrayType {
element,
len,
bit_width,
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
where
VA::ElementType: Fold<State>,
{
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
state.fold_array_type(self)
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
}
}
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
where
VA::ElementType: Visit<State>,
{
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
state.visit_array_type(self)
}
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
self.element.visit(state)
}
}
impl<V: Value, const N: usize> ArrayType<[V; N]>
where
V::Type: Type<Value = V>,
{
pub fn new_array(element: V::Type) -> Self {
ArrayType::new_with_len_type(element, ())
}
}
impl<V: Value, const N: usize> FixedType for ArrayType<[V; N]>
where
V::Type: FixedType<Value = V>,
{
fn fixed_type() -> Self {
Self::new_array(FixedType::fixed_type())
}
}
impl<V: Value> ArrayType<[V]>
where
V::Type: Type<Value = V>,
{
pub fn new_slice(element: V::Type, len: usize) -> Self {
ArrayType::new_with_len_type(element, len)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
type CanonicalType = ArrayType<[DynCanonicalValue]>;
type Value = Array<VA>;
type CanonicalValue = Array<[DynCanonicalValue]>;
type MaskType = ArrayType<VA::MaskVA>;
type MaskValue = Array<VA::MaskVA>;
type MatchVariant = VA::Match;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
}
fn mask_type(&self) -> Self::MaskType {
#[derive(Clone, Hash, Eq, PartialEq)]
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
type Input = ArrayType<T::ValueArrayOrSlice>;
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
fn inner(self, input: &Self::Input) -> Self::Output {
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
}
}
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
}
fn canonical(&self) -> Self::CanonicalType {
ArrayType {
element: self.element.canonical_dyn(),
len: self.len(),
bit_width: self.bit_width,
}
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::ArrayType(self.canonical())
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
Self {
element: VA::ElementType::from_dyn_canonical_type(t.element),
len: VA::try_len_type_from_len(t.len).expect("length should match"),
bit_width: t.bit_width,
}
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
this,
)?)
}
}
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
for ArrayType<Lhs>
{
}
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
element_ty: VA::ElementType,
value: Arc<VA>,
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
fn clone(&self) -> Self {
Self {
element_ty: self.element_ty.clone(),
value: self.value.clone(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
type Type = ArrayType<VA>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
Array {
element_ty: self.element_ty.canonical_dyn(),
value: AsRef::<[_]>::as_ref(&*self.value)
.iter()
.map(|v| v.to_canonical_dyn())
.collect(),
}
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
fn clone(&self) -> Self {
*self
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
type Input = Array<VA>;
type InputOwned = Array<VA>;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let mut bits = BitVec::with_capacity(input.ty().bit_width());
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
bits.extend_from_bitslice(&element.to_bits());
}
Intern::intern_owned(bits)
}
}
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
}
}
impl CanonicalValue for Array<[DynCanonicalValue]> {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Array(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
Value::to_bits_impl(this)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
pub fn element_ty(&self) -> &VA::ElementType {
&self.element_ty
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.value.len_type())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn value(&self) -> &Arc<VA> {
&self.value
}
pub fn set_element(&mut self, index: usize, element: VA::Element) {
assert_eq!(self.element_ty, element.ty());
VA::arc_make_mut(&mut self.value)[index] = element;
}
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
for element in value.iter() {
assert_eq!(element_ty, element.ty());
}
Self { element_ty, value }
}
pub fn into_slice(self) -> Array<[VA::Element]> {
Array {
element_ty: self.element_ty,
value: self.value.arc_to_arc_slice(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
where
VA::ElementType: FixedType,
{
fn from(value: T) -> Self {
Self::new(FixedType::fixed_type(), value.into())
}
}
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for [E] {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(FixedType::fixed_type(), self.len())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for Vec<E> {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
<[E]>::ty(self)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
<[E]>::to_expr(self)
}
}
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for [E; 0] {
type Type = ArrayType<[T::Value; 0]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(FixedType::fixed_type(), ())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Array::new(FixedType::fixed_type(), Arc::new([])).to_expr()
}
}
macro_rules! impl_to_expr_for_non_empty_array {
($N:literal) => {
impl<E: ToExpr<Type = T>, T: Type> ToExpr for [E; $N] {
type Type = ArrayType<[T::Value; $N]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(self[0].ty(), ())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
};
}
impl_to_expr_for_non_empty_array!(1);
impl_to_expr_for_non_empty_array!(2);
impl_to_expr_for_non_empty_array!(3);
impl_to_expr_for_non_empty_array!(4);
impl_to_expr_for_non_empty_array!(5);
impl_to_expr_for_non_empty_array!(6);
impl_to_expr_for_non_empty_array!(7);
impl_to_expr_for_non_empty_array!(8);
impl_to_expr_for_non_empty_array!(9);
impl_to_expr_for_non_empty_array!(10);
impl_to_expr_for_non_empty_array!(11);
impl_to_expr_for_non_empty_array!(12);
impl_to_expr_for_non_empty_array!(13);
impl_to_expr_for_non_empty_array!(14);
impl_to_expr_for_non_empty_array!(15);
impl_to_expr_for_non_empty_array!(16);
impl_to_expr_for_non_empty_array!(17);
impl_to_expr_for_non_empty_array!(18);
impl_to_expr_for_non_empty_array!(19);
impl_to_expr_for_non_empty_array!(20);
impl_to_expr_for_non_empty_array!(21);
impl_to_expr_for_non_empty_array!(22);
impl_to_expr_for_non_empty_array!(23);
impl_to_expr_for_non_empty_array!(24);
impl_to_expr_for_non_empty_array!(25);
impl_to_expr_for_non_empty_array!(26);
impl_to_expr_for_non_empty_array!(27);
impl_to_expr_for_non_empty_array!(28);
impl_to_expr_for_non_empty_array!(29);
impl_to_expr_for_non_empty_array!(30);
impl_to_expr_for_non_empty_array!(31);
impl_to_expr_for_non_empty_array!(32);
#[derive(Clone, Debug)]
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
array: Arc<VA>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
type Item = VA::Element;
fn next(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next()?].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next_back()?].clone())
}
}
impl<VA: ValueArrayOrSlice> Array<VA> {
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
self.value.iter()
}
}
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
type Item = &'a VA::Element;
type IntoIter = std::slice::Iter<'a, VA::Element>;
fn into_iter(self) -> Self::IntoIter {
self.value.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
type Item = VA::Element;
type IntoIter = ArrayIntoIter<VA>;
fn into_iter(self) -> Self::IntoIter {
ArrayIntoIter {
indexes: 0..self.len(),
array: self.value,
}
}
}
#[derive(Clone, Debug)]
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
array: Expr<Array<VA>>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
type Item = Expr<VA::Element>;
fn next(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
pub fn len(self) -> usize {
self.canonical_type().len()
}
pub fn is_empty(self) -> bool {
self.canonical_type().is_empty()
}
pub fn iter(self) -> ArrayExprIter<VA> {
ArrayExprIter {
indexes: 0..self.len(),
array: self,
}
}
}

View file

@ -0,0 +1,796 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
},
module::{ModuleBuilder, NormalModule},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, FixedType, MatchVariantWithoutScope, Type, TypeEnum,
TypeWithDeref, Value, ValueEnum,
},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap;
use std::{
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
sync::Arc,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct FieldType<T> {
pub name: Interned<str>,
pub flipped: bool,
pub ty: T,
}
pub struct FmtDebugInStruct<'a, T> {
field: &'a FieldType<T>,
field_offset: usize,
}
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
field:
&FieldType {
name,
flipped,
ref ty,
},
field_offset,
} = *self;
if flipped {
write!(f, "#[hdl(flip)] ")?;
}
if f.alternate() {
writeln!(f, "/* offset = {field_offset} */")?;
}
write!(f, "{name}: ")?;
ty.fmt(f)
}
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> FieldType<T> {
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> FieldType<U> {
let Self { name, flipped, ty } = self;
FieldType {
name,
flipped,
ty: f(ty),
}
}
pub fn as_ref_ty(&self) -> FieldType<&T> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: &self.ty,
}
}
pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> {
FmtDebugInStruct {
field: self,
field_offset,
}
}
}
impl<T: Type> FieldType<T> {
pub fn canonical(&self) -> FieldType<T::CanonicalType> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical(),
}
}
pub fn to_dyn(&self) -> FieldType<Interned<dyn DynType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.to_dyn(),
}
}
pub fn canonical_dyn(&self) -> FieldType<Interned<dyn DynCanonicalType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical_dyn(),
}
}
}
impl FieldType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper<T: Type>(
self,
expected_name: &str,
expected_flipped: bool,
) -> T {
assert_eq!(&*self.name, expected_name, "field name doesn't match");
assert_eq!(
self.flipped, expected_flipped,
"field {expected_name} orientation (flipped or not) doesn't match"
);
let ty = &*self.ty;
if let Ok(ty) = <dyn DynCanonicalType>::downcast(ty) {
return T::from_canonical_type(ty);
}
let type_name = std::any::type_name::<T::CanonicalType>();
panic!("field {expected_name} type doesn't match, expected: {type_name:?}, got: {ty:?}");
}
}
#[derive(Clone, Eq)]
struct DynBundleTypeImpl {
fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>,
field_offsets: Interned<[usize]>,
is_passive: bool,
is_storable: bool,
is_castable_from_bits: bool,
bit_width: usize,
}
impl fmt::Debug for DynBundleTypeImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DynBundleType ")?;
f.debug_set()
.entries(
self.fields
.iter()
.enumerate()
.map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
)
.finish()
}
}
impl PartialEq for DynBundleTypeImpl {
fn eq(&self, other: &Self) -> bool {
self.fields == other.fields
}
}
impl Hash for DynBundleTypeImpl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.fields.hash(state);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynBundleType(Interned<DynBundleTypeImpl>);
impl fmt::Debug for DynBundleType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl DynBundleType {
pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self {
let is_passive = fields
.iter()
.all(|field| !field.flipped && field.ty.is_passive());
let is_storable = fields
.iter()
.all(|field| !field.flipped && field.ty.is_storable());
let is_castable_from_bits = fields
.iter()
.all(|field| !field.flipped && field.ty.is_castable_from_bits());
let mut name_indexes = HashMap::with_capacity(fields.len());
let mut field_offsets = Vec::with_capacity(fields.len());
let mut bit_width = 0usize;
for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate field name {name:?}: at both index {old_index} and {index}");
}
field_offsets.push(bit_width);
bit_width = bit_width
.checked_add(ty.bit_width())
.unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed"));
}
Self(
DynBundleTypeImpl {
fields,
name_indexes,
field_offsets: Intern::intern_owned(field_offsets),
is_passive,
is_storable,
is_castable_from_bits,
bit_width,
}
.intern_sized(),
)
}
pub fn is_passive(self) -> bool {
self.0.is_passive
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
}
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes
}
pub fn field_by_name(
&self,
name: Interned<str>,
) -> Option<FieldType<Interned<dyn DynCanonicalType>>> {
Some(self.0.fields[*self.0.name_indexes.get(&name)?])
}
pub fn field_offsets(self) -> Interned<[usize]> {
self.0.field_offsets
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynBundle {
ty: DynBundleType,
fields: Arc<[DynCanonicalValue]>,
}
impl DynBundle {
pub fn new(ty: DynBundleType, fields: Arc<[DynCanonicalValue]>) -> Self {
assert_eq!(
ty.fields().len(),
fields.len(),
"field values don't match type"
);
for (field_ty, field) in ty.fields().iter().zip(fields.iter()) {
assert_eq!(field_ty.ty, field.ty(), "field value doesn't match type");
}
DynBundle { ty, fields }
}
pub fn fields(&self) -> &Arc<[DynCanonicalValue]> {
&self.fields
}
}
pub trait TypeHintTrait: Send + Sync + fmt::Debug + SupportsPtrEqWithTypeId {
fn matches(&self, ty: &dyn DynType) -> Result<(), String>;
}
impl InternedCompare for dyn TypeHintTrait {
type InternedCompareKey = PtrEqWithTypeId;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(this)
}
fn interned_compare_key_weak(this: &std::sync::Weak<Self>) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap())
}
}
pub struct TypeHint<T: Type>(PhantomData<fn(T)>);
impl<T: Type> TypeHint<T> {
pub fn intern_dyn() -> Interned<dyn TypeHintTrait> {
Interned::cast_unchecked(
Self(PhantomData).intern_sized(),
|v| -> &dyn TypeHintTrait { v },
)
}
}
impl<T: Type> fmt::Debug for TypeHint<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeHint<{}>", std::any::type_name::<T>())
}
}
impl<T: Type + Hash> Hash for TypeHint<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: Type> Eq for TypeHint<T> {}
impl<T: Type> PartialEq for TypeHint<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: Type> Clone for TypeHint<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Type> Copy for TypeHint<T> {}
impl<T: Type> TypeHintTrait for TypeHint<T> {
fn matches(&self, ty: &dyn DynType) -> Result<(), String> {
match ty.downcast::<T>() {
Ok(_) => Ok(()),
Err(_) => Err(format!("can't cast {ty:?} to {self:?}")),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FieldsHint {
pub known_fields: Interned<[FieldType<Interned<dyn TypeHintTrait>>]>,
pub more_fields: bool,
}
impl FieldsHint {
pub fn new(
known_fields: impl IntoIterator<Item = FieldType<Interned<dyn TypeHintTrait>>>,
more_fields: bool,
) -> Self {
let known_fields = Intern::intern_owned(Vec::from_iter(known_fields));
Self {
known_fields,
more_fields,
}
}
pub fn check_field(self, index: usize, field: FieldType<&dyn DynType>) -> Result<(), String> {
let Some(&known_field) = self.known_fields.get(index) else {
return if self.more_fields {
Ok(())
} else {
Err(format!(
"too many fields: name={:?} index={index}",
field.name
))
};
};
let FieldType {
name: known_name,
flipped: known_flipped,
ty: type_hint,
} = known_field;
let FieldType { name, flipped, ty } = field;
if name != known_name {
Err(format!(
"wrong field name {name:?}, expected {known_name:?}"
))
} else if flipped != known_flipped {
Err(format!(
"wrong field direction: flipped={flipped:?}, expected flipped={known_flipped}"
))
} else {
type_hint.matches(ty)
}
}
}
pub trait BundleType:
Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref + Connect<Self>
where
Self::Value: BundleValue + ToExpr<Type = Self>,
{
type Builder;
fn builder() -> Self::Builder;
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]>;
fn fields_hint() -> FieldsHint;
}
pub trait BundleValue: Value
where
<Self as ToExpr>::Type: BundleType<Value = Self>,
{
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ToBitsMemoize<T> {}
impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
type Input = T;
type InputOwned = T;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let input = input.to_canonical();
let mut bits = BitVec::with_capacity(input.ty.bit_width());
for field in input.fields.iter() {
bits.extend_from_bitslice(&field.to_bits());
}
Intern::intern_owned(bits)
}
}
ToBitsMemoize::<Self>(PhantomData).get(this)
}
}
pub struct DynBundleMatch;
impl Type for DynBundleType {
type CanonicalType = DynBundleType;
type Value = DynBundle;
type CanonicalValue = DynBundle;
type MaskType = DynBundleType;
type MaskValue = DynBundle;
type MatchVariant = DynBundleMatch;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = this;
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(DynBundleMatch))
}
fn mask_type(&self) -> Self::MaskType {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct Impl;
impl Memoize for Impl {
type Input = DynBundleType;
type InputOwned = DynBundleType;
type Output = DynBundleType;
fn inner(self, input: &Self::Input) -> Self::Output {
DynBundleType::new(Intern::intern_owned(Vec::from_iter(
input
.fields()
.iter()
.map(|&FieldType { name, flipped, ty }| FieldType {
name,
flipped,
ty: ty.mask_type().canonical(),
}),
)))
}
}
Impl.get(self)
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
pub struct NoBuilder;
impl TypeWithDeref for DynBundleType {
fn expr_deref(this: &Expr<Self::Value>) -> &Self::MatchVariant {
let _ = this;
&DynBundleMatch
}
}
impl Connect<Self> for DynBundleType {}
impl BundleType for DynBundleType {
type Builder = NoBuilder;
fn builder() -> Self::Builder {
NoBuilder
}
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
self.0.fields
}
fn fields_hint() -> FieldsHint {
FieldsHint {
known_fields: [][..].intern(),
more_fields: true,
}
}
}
impl CanonicalType for DynBundleType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::BundleType;
}
impl ToExpr for DynBundle {
type Type = DynBundleType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynBundle {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl BundleValue for DynBundle {}
impl CanonicalValue for DynBundle {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Bundle(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
macro_rules! impl_tuple_builder {
($builder:ident, [
$(($before_Ts:ident $before_fields:ident $before_members:literal))*
] [
($T:ident $field:ident $m:literal)
$(($after_Ts:ident $after_fields:ident $after_members:literal))*
]) => {
impl_tuple_builder!($builder, [
$(($before_Ts $before_fields $before_members))*
($T $field $m)
] [
$(($after_Ts $after_fields $after_members))*
]);
impl<Phantom, $($before_Ts,)* $($after_Ts,)*> $builder<Phantom, $($before_Ts,)* () $(, $after_Ts)*> {
pub fn $field<$T: ToExpr>(self, $field: $T) -> $builder<Phantom, $($before_Ts,)* Expr<<$T::Type as Type>::Value> $(, $after_Ts)*> {
let Self {
$($before_fields,)*
$field: _,
$($after_fields, )*
_phantom: _,
} = self;
let $field = $field.to_expr();
$builder {
$($before_fields,)*
$field,
$($after_fields,)*
_phantom: PhantomData,
}
}
}
};
($builder:ident, [$($before:tt)*] []) => {};
}
macro_rules! into_unit {
($($tt:tt)*) => {
()
};
}
macro_rules! impl_tuple {
($builder:ident, $(($T:ident $T2:ident $field:ident $m:tt)),*) => {
pub struct $builder<Phantom, $($T),*> {
$($field: $T,)*
_phantom: PhantomData<Phantom>,
}
impl_tuple_builder!($builder, [] [$(($T $field $m))*]);
impl<$($T: Value),*> $builder<($($T,)*), $(Expr<$T>,)*>
where
$($T::Type: Type<Value = $T>,)*
{
pub fn build(self) -> Expr<($($T,)*)> {
let Self {
$($field,)*
_phantom: _,
} = self;
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
}
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
type Type = ($($T::Type,)*);
#[allow(clippy::unused_unit)]
fn ty(&self) -> Self::Type {
let ($($field,)*) = self;
($($field.ty(),)*)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let ($($field,)*) = self;
$(let $field = $field.to_expr();)*
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
}
}
impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*)
where
$($T: Connect<$T2>,)*
{
}
impl<$($T: Type,)*> Type for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type CanonicalType = DynBundleType;
type Value = ($($T::Value,)*);
type CanonicalValue = DynBundle;
type MaskType = ($($T::MaskType,)*);
type MaskValue = ($($T::MaskValue,)*);
type MatchVariant = ($(Expr<$T::Value>,)*);
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = this;
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(($(this.field(stringify!($m)),)*)))
}
#[allow(clippy::unused_unit)]
fn mask_type(&self) -> Self::MaskType {
let ($($field,)*) = self;
($($field.mask_type(),)*)
}
fn canonical(&self) -> Self::CanonicalType {
DynBundleType::new(self.fields())
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(self.canonical())
}
#[allow(clippy::unused_unit)]
fn from_canonical_type(t: Self::CanonicalType) -> Self {
let [$($field),*] = *t.fields() else {
panic!("wrong number of fields");
};
($($field.from_canonical_type_helper(stringify!($m), false),)*)
}
}
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
fn expr_deref(
this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
let _ = this;
Interned::<_>::into_inner(
Intern::intern_sized((
$(this.field(stringify!($m)),)*
)),
)
}
}
impl<$($T: Type,)*> BundleType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type Builder = $builder<($($T::Value,)*), $(into_unit!($T),)*>;
fn builder() -> Self::Builder {
$builder {
$($field: (),)*
_phantom: PhantomData,
}
}
fn fields(
&self,
) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
[
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: self.$m.canonical_dyn(),
},)*
][..].intern()
}
fn fields_hint() -> FieldsHint {
FieldsHint::new([
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: TypeHint::<$T>::intern_dyn(),
},)*
], false)
}
}
impl<$($T: FixedType,)*> FixedType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
#[allow(clippy::unused_unit)]
fn fixed_type() -> Self {
($($T::fixed_type(),)*)
}
}
impl<$($T: Value,)*> Value for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
let ty = self.ty().canonical();
DynBundle::new(
ty,
Arc::new([
$(self.$m.to_canonical_dyn(),)*
]),
)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl<$($T: Value,)*> BundleValue for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
}
};
}
impl_tuple!(TupleBuilder0,);
impl_tuple!(TupleBuilder1, (A A2 field_0 0));
impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1));
impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2));
impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3));
impl_tuple!(TupleBuilder5, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4));
impl_tuple!(TupleBuilder6, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5));
impl_tuple!(TupleBuilder7, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6));
impl_tuple!(TupleBuilder8, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7));
impl_tuple!(TupleBuilder9, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8));
impl_tuple!(TupleBuilder10, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9));
impl_tuple!(TupleBuilder11, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10));
impl_tuple!(TupleBuilder12, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10), (L L2 field_11 11));

View file

@ -0,0 +1,156 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{Expr, ToExpr},
int::{UInt, UIntType},
intern::Interned,
reset::Reset,
source_location::SourceLocation,
ty::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, FixedType, Type, TypeEnum, Value, ValueEnum,
},
util::interned_bit,
};
use bitvec::slice::BitSlice;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct ClockType;
impl ClockType {
pub const fn new() -> Self {
Self
}
}
impl Type for ClockType {
type Value = Clock;
type CanonicalType = ClockType;
type CanonicalValue = Clock;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::Clock(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for ClockType {}
impl CanonicalType for ClockType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock;
}
impl FixedType for ClockType {
fn fixed_type() -> Self {
Self
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Clock(pub bool);
impl ToExpr for Clock {
type Type = ClockType;
fn ty(&self) -> Self::Type {
ClockType
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for Clock {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for Clock {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Clock(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, crate::Value)]
#[hdl(fixed_type, outline_generated)]
pub struct ClockDomain {
pub clock: Clock,
pub reset: Reset,
}
pub trait ToClock {
fn to_clock(&self) -> Expr<Clock>;
}
impl<T: ?Sized + ToClock> ToClock for &'_ T {
fn to_clock(&self) -> Expr<Clock> {
(**self).to_clock()
}
}
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
fn to_clock(&self) -> Expr<Clock> {
(**self).to_clock()
}
}
impl<T: ?Sized + ToClock> ToClock for Box<T> {
fn to_clock(&self) -> Expr<Clock> {
(**self).to_clock()
}
}
impl ToClock for Expr<Clock> {
fn to_clock(&self) -> Expr<Clock> {
*self
}
}
impl ToClock for Clock {
fn to_clock(&self) -> Expr<Clock> {
self.to_expr()
}
}
impl ToClock for bool {
fn to_clock(&self) -> Expr<Clock> {
self.to_expr().to_clock()
}
}
impl ToClock for UInt<1> {
fn to_clock(&self) -> Expr<Clock> {
self.to_expr().to_clock()
}
}

View file

@ -0,0 +1,620 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{
bundle::{BundleValue, TypeHintTrait},
expr::{ops::VariantAccess, Expr, ToExpr},
int::{UInt, UIntType},
intern::{Intern, Interned, MemoizeGeneric},
module::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
NormalModule, Scope,
},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
},
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap;
use std::{
borrow::Cow,
fmt,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct VariantType<T> {
pub name: Interned<str>,
pub ty: Option<T>,
}
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let VariantType { name, ref ty } = *self.0;
if let Some(ty) = ty {
write!(f, "{name}({ty:?})")
} else {
write!(f, "{name}")
}
}
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> VariantType<T> {
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType { name, ty: f(ty) }
}
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType {
name,
ty: ty.map(f),
}
}
pub fn as_ref_ty(&self) -> VariantType<&T> {
VariantType {
name: self.name,
ty: self.ty.as_ref(),
}
}
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
FmtDebugInEnum(self)
}
}
impl<T: Type> VariantType<T> {
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
self.as_ref_ty().map_ty(T::canonical)
}
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
self.as_ref_ty().map_ty(T::to_dyn)
}
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.as_ref_ty().map_ty(T::canonical_dyn)
}
}
impl VariantType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
let Some(ty) = self.ty else {
panic!("variant {expected_name} has no value but a value is expected");
};
T::from_dyn_canonical_type(ty)
}
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
assert!(
self.ty.is_none(),
"variant {expected_name} has a value but is expected to have no value"
);
}
}
#[derive(Clone, Eq)]
struct DynEnumTypeImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize,
is_storable: bool,
is_castable_from_bits: bool,
}
impl fmt::Debug for DynEnumTypeImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DynEnumType ")?;
f.debug_set()
.entries(
self.variants
.iter()
.map(|variant| variant.fmt_debug_in_enum()),
)
.finish()
}
}
impl PartialEq for DynEnumTypeImpl {
fn eq(&self, other: &Self) -> bool {
self.variants == other.variants
}
}
impl Hash for DynEnumTypeImpl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.variants.hash(state);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
impl fmt::Debug for DynEnumType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn discriminant_bit_width_impl(variant_count: usize) -> usize {
variant_count
.next_power_of_two()
.checked_ilog2()
.unwrap_or(0) as usize
}
impl DynEnumType {
#[track_caller]
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize;
let mut is_storable = true;
let mut is_castable_from_bits = true;
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
}
if let Some(ty) = ty {
assert!(
ty.is_passive(),
"variant type must be a passive type: {ty:?}"
);
body_bit_width = body_bit_width.max(ty.bit_width());
is_storable &= ty.is_storable();
is_castable_from_bits &= ty.is_castable_from_bits();
}
}
let bit_width = body_bit_width
.checked_add(discriminant_bit_width_impl(variants.len()))
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
Self(
DynEnumTypeImpl {
variants,
name_indexes,
bit_width,
is_storable,
is_castable_from_bits,
}
.intern_sized(),
)
}
pub fn discriminant_bit_width(self) -> usize {
discriminant_bit_width_impl(self.variants().len())
}
pub fn is_passive(self) -> bool {
true
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
}
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynEnum {
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
}
impl DynEnum {
#[track_caller]
pub fn new_by_index(
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant = ty.variants()[variant_index];
assert_eq!(
variant_value.as_ref().map(|v| v.ty()),
variant.ty,
"variant value doesn't match type"
);
Self {
ty,
variant_index,
variant_value,
}
}
#[track_caller]
pub fn new_by_name(
ty: DynEnumType,
variant_name: Interned<str>,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant_index = ty.name_indexes()[&variant_name];
Self::new_by_index(ty, variant_index, variant_value)
}
pub fn variant_index(&self) -> usize {
self.variant_index
}
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
&self.variant_value
}
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.ty.variants()[self.variant_index]
}
pub fn variant_name(&self) -> Interned<str> {
self.variant_with_type().name
}
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
self.variant_with_type().ty
}
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
self.variant_with_type()
.map_opt_ty(|_| self.variant_value.as_ref())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct VariantsHint {
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
pub more_variants: bool,
}
impl VariantsHint {
pub fn new(
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
more_variants: bool,
) -> Self {
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
Self {
known_variants,
more_variants,
}
}
pub fn check_variant(
self,
index: usize,
variant: VariantType<&dyn DynType>,
) -> Result<(), String> {
let Some(&known_variant) = self.known_variants.get(index) else {
return if self.more_variants {
Ok(())
} else {
Err(format!(
"too many variants: name={:?} index={index}",
variant.name
))
};
};
let VariantType {
name: known_name,
ty: type_hint,
} = known_variant;
let VariantType { name, ty } = variant;
if name != known_name {
Err(format!(
"wrong variant name {name:?}, expected {known_name:?}"
))
} else {
match (ty, type_hint) {
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
(None, None) => Ok(()),
(None, Some(_)) => Err(format!(
"expected variant {name:?} to have type, no type provided"
)),
(Some(_), None) => Err(format!(
"expected variant {name:?} to have no type, but a type was provided"
)),
}
}
}
}
pub trait EnumType:
Type<
CanonicalType = DynEnumType,
CanonicalValue = DynEnum,
MaskType = UIntType<1>,
MaskValue = UInt<1>,
MatchActiveScope = Scope,
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MatchVariantsIter = EnumMatchVariantsIter<Self>,
> + Connect<Self>
where
Self::Value: EnumValue + ToExpr<Type = Self>,
{
type Builder;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope);
fn builder() -> Self::Builder;
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
fn variants_hint() -> VariantsHint;
#[allow(clippy::result_unit_err)]
fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
&self,
variant_index: usize,
variant_value: Option<&VariantValue>,
) -> Result<Interned<BitSlice>, ()> {
#[derive(Hash, Eq, PartialEq)]
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
impl<E, V> Clone for VariantToBitsMemoize<E, V> {
fn clone(&self) -> Self {
*self
}
}
impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
impl<
E: EnumType<Value: EnumValue<Type = E>>,
V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
> MemoizeGeneric for VariantToBitsMemoize<E, V>
{
type InputRef<'a> = (&'a E, usize, Option<&'a V>);
type InputOwned = (E, usize, Option<V>);
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
type Output = Result<Interned<BitSlice>, ()>;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
(&input.0, input.1, input.2.as_ref())
}
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
(&input.0, input.1, input.2.as_deref())
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
(Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
let (ty, variant_index, variant_value) = input;
let ty = ty.canonical();
let mut bits = BitVec::with_capacity(ty.bit_width());
bits.extend_from_bitslice(
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
);
if let Some(variant_value) = variant_value {
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
}
bits.resize(ty.bit_width(), false);
Ok(Intern::intern_owned(bits))
}
}
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
self,
variant_index,
variant_value,
))
}
}
pub trait EnumValue: Value
where
<Self as ToExpr>::Type: EnumType<Value = Self>,
{
}
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
where
T::Value: EnumValue<Type = T>;
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
type MatchVariant = T::MatchVariant;
type MatchActiveScope = Scope;
fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope) {
T::match_activate_scope(self)
}
}
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
self.0.variant_access()
}
pub fn activate(
self,
) -> (
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
Scope,
) {
self.0.activate()
}
}
#[derive(Clone)]
pub struct EnumMatchVariantsIter<T: EnumType>
where
T::Value: EnumValue<Type = T>,
{
pub(crate) inner: EnumMatchVariantsIterImpl<T>,
pub(crate) variant_index: std::ops::Range<usize>,
}
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
type Item = EnumMatchVariantAndInactiveScope<T>;
fn next(&mut self) -> Option<Self::Item> {
self.variant_index.next().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.variant_index.size_hint()
}
}
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
fn len(&self) -> usize {
self.variant_index.len()
}
}
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {}
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.variant_index.next_back().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
})
}
}
impl Type for DynEnumType {
type CanonicalType = DynEnumType;
type Value = DynEnum;
type CanonicalValue = DynEnum;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
type MatchVariant = Option<Expr<DynCanonicalValue>>;
type MatchActiveScope = Scope;
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: crate::bundle::BundleType<Value = IO>,
{
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::EnumType(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for DynEnumType {}
pub struct NoBuilder;
impl EnumType for DynEnumType {
type Builder = NoBuilder;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope) {
let (expr, scope) = v.0.activate();
(expr.variant_type().ty.map(|_| expr.to_expr()), scope)
}
fn builder() -> Self::Builder {
NoBuilder
}
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
self.0.variants
}
fn variants_hint() -> VariantsHint {
VariantsHint {
known_variants: [][..].intern(),
more_variants: true,
}
}
}
impl CanonicalType for DynEnumType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType;
}
impl ToExpr for DynEnum {
type Type = DynEnumType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynEnum {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
impl EnumValue for DynEnum {}
impl CanonicalValue for DynEnum {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Enum(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
mod impl_option {
#[allow(dead_code)]
#[derive(crate::Value)]
#[hdl(target(std::option::Option), connect_inexact, outline_generated)]
pub enum Option<T> {
None,
Some(T),
}
}

1090
crates/fayalite/src/expr.rs Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1176
crates/fayalite/src/int.rs Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,138 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use hashbrown::HashMap;
use std::{
any::{Any, TypeId},
hash::{BuildHasher, Hasher},
ptr::NonNull,
sync::RwLock,
};
struct TypeIdHasher(u64);
// assumes TypeId has at least 64 bits that is a good hash
impl Hasher for TypeIdHasher {
#[inline(always)]
fn finish(&self) -> u64 {
self.0
}
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.write_u8(byte);
}
}
#[inline(always)]
fn write_u8(&mut self, i: u8) {
self.0 = self.0.rotate_left(8);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u16(&mut self, i: u16) {
self.0 = self.0.rotate_left(16);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u32(&mut self, i: u32) {
self.0 = self.0.rotate_left(32);
self.0 ^= i as u64;
}
#[inline(always)]
fn write_u64(&mut self, i: u64) {
self.0 ^= i;
}
#[inline(always)]
fn write_u128(&mut self, i: u128) {
self.write_u64(i as u64);
self.write_u64((i >> 64) as u64);
}
#[inline(always)]
fn write_usize(&mut self, i: usize) {
match usize::BITS {
128 => self.write_u128(i as u128),
64 => self.write_u64(i as u64),
32 => self.write_u32(i as u32),
16 => self.write_u16(i as u16),
_ => self.write(&i.to_ne_bytes()),
}
}
}
struct TypeIdBuildHasher;
impl BuildHasher for TypeIdBuildHasher {
type Hasher = TypeIdHasher;
fn build_hasher(&self) -> Self::Hasher {
TypeIdHasher(0)
}
}
struct Value(NonNull<dyn Any + Send + Sync>);
impl Value {
unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) {
unsafe { &*self.0.as_ptr() }
}
fn new(v: Box<dyn Any + Send + Sync>) -> Self {
unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) }
}
}
unsafe impl Send for Value {}
unsafe impl Sync for Value {}
impl Drop for Value {
fn drop(&mut self) {
unsafe { std::ptr::drop_in_place(self.0.as_ptr()) }
}
}
pub struct TypeIdMap(RwLock<HashMap<TypeId, Value, TypeIdBuildHasher>>);
impl TypeIdMap {
pub const fn new() -> Self {
Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher)))
}
#[cold]
unsafe fn insert_slow(
&self,
type_id: TypeId,
make: fn() -> Box<dyn Any + Sync + Send>,
) -> &(dyn Any + Sync + Send) {
let value = Value::new(make());
let mut write_guard = self.0.write().unwrap();
unsafe {
write_guard
.entry(type_id)
.or_insert(value)
.get_transmute_lifetime()
}
}
pub fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
let type_id = TypeId::of::<T>();
let read_guard = self.0.read().unwrap();
let retval = read_guard
.get(&type_id)
.map(|v| unsafe { Value::get_transmute_lifetime(v) });
drop(read_guard);
let retval = match retval {
Some(retval) => retval,
None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) },
};
unsafe { &*(retval as *const dyn Any as *const T) }
}
}
impl Default for TypeIdMap {
fn default() -> Self {
Self::new()
}
}

View file

@ -0,0 +1,27 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
extern crate self as fayalite;
#[doc(hidden)]
pub use std as __std;
pub use fayalite_proc_macros::{hdl_module, Value};
pub mod annotations;
pub mod array;
pub mod bundle;
pub mod clock;
pub mod enum_;
pub mod expr;
pub mod firrtl;
pub mod int;
pub mod intern;
pub mod memory;
pub mod module;
pub mod reg;
pub mod reset;
pub mod source_location;
pub mod ty;
pub mod util;
pub mod valueless;
pub mod wire;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
pub mod simplify_enums;
pub mod simplify_memories;
pub mod visit;

View file

@ -0,0 +1,654 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
array::{Array, ArrayType},
bundle::{BundleType, BundleValue, DynBundle, DynBundleType},
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
expr::{ops, Expr, ExprEnum, ToExpr},
int::{DynUInt, DynUIntType, IntCmp},
intern::{Intern, Interned},
memory::{DynPortType, Mem, MemPort},
module::{
transform::visit::{Fold, Folder},
Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
},
source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum},
wire::Wire,
};
use core::fmt;
use hashbrown::HashMap;
#[derive(Debug)]
pub enum SimplifyEnumsError {
EnumIsNotCastableFromBits { enum_type: DynEnumType },
}
impl fmt::Display for SimplifyEnumsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type } => write!(
f,
"simplify_enums failed: enum type is not castable from bits: {enum_type:?}"
),
}
}
}
impl std::error::Error for SimplifyEnumsError {}
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)]
struct TagAndBody<T> {
tag: T,
body: DynUInt,
}
type TagAndBodyType<T> = <TagAndBody<T> as ToExpr>::Type;
#[derive(Clone, Debug)]
enum EnumTypeState {
TagEnumAndBody(TagAndBodyType<DynEnum>),
TagUIntAndBody(TagAndBodyType<DynUInt>),
UInt(DynUIntType),
Unchanged,
}
struct State {
enum_types: HashMap<DynEnumType, EnumTypeState>,
replacement_mem_ports:
HashMap<Interned<MemPort<DynPortType>>, Interned<Wire<Interned<dyn DynCanonicalType>>>>,
kind: SimplifyEnumsKind,
name_id_gen: NameIdGen,
}
impl State {
fn get_or_make_enum_type_state(
&mut self,
enum_type: DynEnumType,
) -> Result<EnumTypeState, SimplifyEnumsError> {
if let Some(retval) = self.enum_types.get(&enum_type) {
return Ok(retval.clone());
}
if !enum_type.is_castable_from_bits() {
return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
}
let has_body = enum_type
.variants()
.iter()
.any(|variant| variant.ty.is_some());
let retval = match (self.kind, has_body) {
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
tag: DynEnumType::new(Interned::from_iter(enum_type.variants().iter().map(
|v| VariantType {
name: v.name,
ty: None,
},
))),
body: DynUIntType::new(
enum_type.bit_width() - enum_type.discriminant_bit_width(),
),
})
}
(SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged,
(SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => {
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
tag: DynUIntType::new(enum_type.discriminant_bit_width()),
body: DynUIntType::new(
enum_type.bit_width() - enum_type.discriminant_bit_width(),
),
})
}
(SimplifyEnumsKind::ReplaceWithUInt, _) => {
EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width()))
}
};
self.enum_types.insert(enum_type, retval.clone());
Ok(retval)
}
}
fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt {
let Some(value) = value else {
return DynUInt::with_type(target_ty, 0u8);
};
DynUInt::from_bit_slice(&value.to_bits())
}
fn connect_port(
stmts: &mut Vec<Stmt>,
lhs: Expr<DynCanonicalValue>,
rhs: Expr<DynCanonicalValue>,
source_location: SourceLocation,
) {
println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
if lhs.canonical_type() == rhs.canonical_type() {
stmts.push(
dbg!(StmtConnect {
lhs,
rhs,
source_location,
})
.into(),
);
return;
}
match (
lhs.canonical_type().type_enum(),
rhs.canonical_type().type_enum(),
) {
(TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
let lhs = lhs.with_type::<DynBundle>();
for field in lhs_type.fields() {
assert!(!field.flipped);
connect_port(stmts, lhs.field(&field.name), rhs, source_location);
}
}
(TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => {
let rhs = rhs.with_type::<DynBundle>();
for field in rhs_type.fields() {
assert!(!field.flipped);
connect_port(stmts, lhs, rhs.field(&field.name), source_location);
}
}
(TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => {
let lhs = lhs.with_type::<DynBundle>();
let rhs = rhs.with_type::<DynBundle>();
for field in lhs_type.fields() {
let (lhs_field, rhs_field) = if field.flipped {
(rhs.field(&field.name), lhs.field(&field.name))
} else {
(lhs.field(&field.name), rhs.field(&field.name))
};
connect_port(stmts, lhs_field, rhs_field, source_location);
}
}
(TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => {
let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>();
let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>();
for index in 0..lhs_type.len() {
connect_port(stmts, lhs[index], rhs[index], source_location);
}
}
(TypeEnum::BundleType(_), _)
| (TypeEnum::EnumType(_), _)
| (TypeEnum::ArrayType(_), _)
| (TypeEnum::UInt(_), _)
| (TypeEnum::SInt(_), _)
| (TypeEnum::Clock(_), _)
| (TypeEnum::AsyncReset(_), _)
| (TypeEnum::SyncReset(_), _)
| (TypeEnum::Reset(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}",
lhs.canonical_type().type_enum(),
rhs.canonical_type().type_enum(),
),
}
}
impl Folder for State {
type Error = SimplifyEnumsError;
fn fold_dyn_enum(&mut self, _v: DynEnum) -> Result<DynEnum, Self::Error> {
unreachable!()
}
fn fold_dyn_enum_type(&mut self, _v: DynEnumType) -> Result<DynEnumType, Self::Error> {
unreachable!()
}
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
where
T::Type: BundleType<Value = T>,
{
let old_name_id_gen =
std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical()));
let retval = Fold::default_fold(v, self);
self.name_id_gen = old_name_id_gen;
retval
}
fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
match op {
ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { tag, body }) => {
TagAndBodyType::<DynEnum>::builder()
.field_tag(DynEnum::new_by_index(tag, op.variant_index(), None))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(body, 0u8).to_expr(),
})
.build()
.expr_enum()
}
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { tag, body }) => {
TagAndBodyType::<DynUInt>::builder()
.field_tag(DynUInt::with_type(tag, op.variant_index()))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(body, 0u8).to_expr(),
})
.build()
.expr_enum()
}
EnumTypeState::UInt(_) => TagAndBodyType::<DynUInt>::builder()
.field_tag(DynUInt::with_type(
DynUIntType::new(op.ty().discriminant_bit_width()),
op.variant_index(),
))
.field_body(match op.variant_value() {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(
DynUIntType::new(
op.ty().bit_width() - op.ty().discriminant_bit_width(),
),
0u8,
)
.to_expr(),
})
.build()
.cast_to_bits()
.expr_enum(),
EnumTypeState::Unchanged => ExprEnum::EnumLiteral(
ops::EnumLiteral::new_unchecked(
op.variant_value().map(|v| v.fold(self)).transpose()?,
op.variant_index(),
op.ty(),
)
.intern_sized(),
),
}),
ExprEnum::VariantAccess(op) => {
Ok(match self.get_or_make_enum_type_state(op.base().ty())? {
EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
match op.variant_type().ty {
Some(_) => op
.base()
.expr_enum()
.fold(self)?
.to_expr()
.with_type::<TagAndBody<DynCanonicalValue>>()
.body[..op.ty().bit_width()]
.cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum(),
None => ().to_expr().expr_enum(),
}
}
EnumTypeState::UInt(_) => match op.variant_type().ty {
Some(_) => {
let base_int = op
.base()
.expr_enum()
.fold(self)?
.to_expr()
.with_type::<DynUInt>();
dbg!(base_int);
let base_ty = op.base().ty();
let ty_bit_width = op.ty().bit_width();
base_int[base_ty.discriminant_bit_width()..][..ty_bit_width]
.cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum()
}
None => ().to_expr().expr_enum(),
},
EnumTypeState::Unchanged => match op.variant_type().ty {
Some(_) => ExprEnum::VariantAccess(
ops::VariantAccess::new_unchecked(
op.base().fold(self)?,
op.variant_index(),
)
.intern_sized(),
),
None => ().to_expr().expr_enum(),
},
})
}
ExprEnum::MemPort(mem_port) => Ok(
if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
ExprEnum::Wire(wire)
} else {
ExprEnum::MemPort(mem_port.fold(self)?)
},
),
ExprEnum::Literal(_)
| ExprEnum::ArrayLiteral(_)
| ExprEnum::BundleLiteral(_)
| ExprEnum::NotU(_)
| ExprEnum::NotS(_)
| ExprEnum::Neg(_)
| ExprEnum::BitAndU(_)
| ExprEnum::BitAndS(_)
| ExprEnum::BitOrU(_)
| ExprEnum::BitOrS(_)
| ExprEnum::BitXorU(_)
| ExprEnum::BitXorS(_)
| ExprEnum::AddU(_)
| ExprEnum::AddS(_)
| ExprEnum::SubU(_)
| ExprEnum::SubS(_)
| ExprEnum::MulU(_)
| ExprEnum::MulS(_)
| ExprEnum::DynShlU(_)
| ExprEnum::DynShlS(_)
| ExprEnum::DynShrU(_)
| ExprEnum::DynShrS(_)
| ExprEnum::FixedShlU(_)
| ExprEnum::FixedShlS(_)
| ExprEnum::FixedShrU(_)
| ExprEnum::FixedShrS(_)
| ExprEnum::CmpLtU(_)
| ExprEnum::CmpLtS(_)
| ExprEnum::CmpLeU(_)
| ExprEnum::CmpLeS(_)
| ExprEnum::CmpGtU(_)
| ExprEnum::CmpGtS(_)
| ExprEnum::CmpGeU(_)
| ExprEnum::CmpGeS(_)
| ExprEnum::CmpEqU(_)
| ExprEnum::CmpEqS(_)
| ExprEnum::CmpNeU(_)
| ExprEnum::CmpNeS(_)
| ExprEnum::CastUIntToUInt(_)
| ExprEnum::CastUIntToSInt(_)
| ExprEnum::CastSIntToUInt(_)
| ExprEnum::CastSIntToSInt(_)
| ExprEnum::SliceUInt(_)
| ExprEnum::SliceSInt(_)
| ExprEnum::ReduceBitAnd(_)
| ExprEnum::ReduceBitOr(_)
| ExprEnum::ReduceBitXor(_)
| ExprEnum::FieldAccess(_)
| ExprEnum::ArrayIndex(_)
| ExprEnum::DynArrayIndex(_)
| ExprEnum::CastToBits(_)
| ExprEnum::CastBitsTo(_)
| ExprEnum::CastBitToClock(_)
| ExprEnum::CastBitToSyncReset(_)
| ExprEnum::CastBitToAsyncReset(_)
| ExprEnum::CastSyncResetToReset(_)
| ExprEnum::CastAsyncResetToReset(_)
| ExprEnum::CastClockToBit(_)
| ExprEnum::CastSyncResetToBit(_)
| ExprEnum::CastAsyncResetToBit(_)
| ExprEnum::CastResetToBit(_)
| ExprEnum::ModuleIO(_)
| ExprEnum::Instance(_)
| ExprEnum::Wire(_)
| ExprEnum::Reg(_) => op.default_fold(self),
}
}
fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> {
let mut memories = vec![];
let mut stmts = vec![];
for memory in block.memories {
let old_element_ty = *memory.array_type().element();
let new_element_ty = memory.array_type().element().fold(self)?;
if new_element_ty != old_element_ty {
let mut new_ports = vec![];
for port in memory.ports() {
let new_port = MemPort::<DynPortType>::new_unchecked(
port.mem_name(),
port.source_location(),
port.port_name(),
port.addr_type(),
new_element_ty,
);
new_ports.push(new_port.intern());
let new_port_ty = new_port.ty();
let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields());
if let Some(wmask_name) = new_port.port_kind().wmask_name() {
let index = *new_port_ty
.name_indexes()
.get(&wmask_name.intern())
.unwrap();
wire_ty_fields[index].ty = port.ty().fields()[index].ty;
}
let wire_ty = DynBundleType::new(Intern::intern_owned(wire_ty_fields));
if wire_ty == new_port_ty {
continue;
}
let wire_name = self.name_id_gen.gen(
(*format!("{}_{}", memory.scoped_name().1 .0, port.port_name())).intern(),
);
let wire = Wire::new_unchecked(
ScopedNameId(memory.scoped_name().0, wire_name),
port.source_location(),
wire_ty,
);
stmts.push(
StmtWire {
annotations: Default::default(),
wire: wire.to_dyn_canonical_wire(),
}
.into(),
);
connect_port(
&mut stmts,
new_port.to_expr().to_canonical_dyn(),
wire.to_expr().to_canonical_dyn(),
port.source_location(),
);
self.replacement_mem_ports
.insert(port, wire.to_dyn_canonical_wire().intern_sized());
}
memories.push(Mem::new_unchecked(
memory.scoped_name(),
memory.source_location(),
ArrayType::new_slice(new_element_ty, memory.array_type().len()),
memory.initial_value(),
Intern::intern_owned(new_ports),
memory.read_latency(),
memory.write_latency(),
memory.read_under_write(),
memory.port_annotations(),
memory.mem_annotations(),
));
} else {
memories.push(memory.fold(self)?);
}
}
stmts.extend_from_slice(&block.stmts.fold(self)?);
Ok(Block {
memories: Intern::intern_owned(memories),
stmts: Intern::intern_owned(stmts),
})
}
fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> {
fn match_int_tag(
state: &mut State,
int_tag_expr: Expr<DynUInt>,
source_location: SourceLocation,
blocks: Interned<[Block]>,
) -> Result<StmtIf, SimplifyEnumsError> {
let mut blocks_iter = blocks.iter().copied().enumerate();
let (_, last_block) = blocks_iter.next_back().unwrap_or_default();
let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back()
else {
return Ok(StmtIf {
cond: true.to_expr(),
source_location,
blocks: [last_block.fold(state)?, Block::default()],
});
};
let mut retval = StmtIf {
cond: int_tag_expr.cmp_eq(DynUInt::with_type(
int_tag_expr.ty(),
next_to_last_variant_index,
)),
source_location,
blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?],
};
for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf {
cond: int_tag_expr.cmp_eq(DynUInt::with_type(int_tag_expr.ty(), variant_index)),
source_location,
blocks: [
block.fold(state)?,
Block {
memories: Default::default(),
stmts: [Stmt::from(retval)][..].intern(),
},
],
};
}
Ok(retval)
}
match stmt {
Stmt::Match(StmtMatch {
expr,
source_location,
blocks,
}) => match self.get_or_make_enum_type_state(expr.ty())? {
EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
expr: expr
.expr_enum()
.fold(self)?
.to_expr()
.with_type::<TagAndBody<DynEnum>>()
.tag,
source_location,
blocks: blocks.fold(self)?,
}
.into()),
EnumTypeState::TagUIntAndBody(_) => {
let int_tag_expr = expr
.expr_enum()
.fold(self)?
.to_expr()
.with_type::<TagAndBody<DynUInt>>()
.tag;
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
}
EnumTypeState::UInt(_) => {
let int_tag_expr = expr
.expr_enum()
.fold(self)?
.to_expr()
.with_type::<DynUInt>()[..expr.ty().discriminant_bit_width()];
Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
}
EnumTypeState::Unchanged => Ok(StmtMatch {
expr: expr.fold(self)?,
source_location,
blocks: blocks.fold(self)?,
}
.into()),
},
Stmt::Connect(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self),
}
}
fn fold_stmt_match(&mut self, _v: StmtMatch) -> Result<StmtMatch, Self::Error> {
unreachable!()
}
fn fold_type_enum(&mut self, type_enum: TypeEnum) -> Result<TypeEnum, Self::Error> {
match type_enum {
TypeEnum::EnumType(enum_type) => {
Ok(match self.get_or_make_enum_type_state(enum_type)? {
EnumTypeState::TagEnumAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
EnumTypeState::TagUIntAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
EnumTypeState::UInt(ty) => TypeEnum::UInt(ty),
EnumTypeState::Unchanged => TypeEnum::EnumType(enum_type),
})
}
TypeEnum::BundleType(_)
| TypeEnum::ArrayType(_)
| TypeEnum::UInt(_)
| TypeEnum::SInt(_)
| TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => type_enum.default_fold(self),
}
}
fn fold_value_enum(&mut self, value_enum: ValueEnum) -> Result<ValueEnum, Self::Error> {
match value_enum {
ValueEnum::Enum(enum_value) => {
Ok(match self.get_or_make_enum_type_state(enum_value.ty())? {
EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
tag: tag_ty,
body: body_ty,
}) => ValueEnum::Bundle(
TagAndBody {
tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
}
.to_canonical(),
),
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
tag: tag_ty,
body: body_ty,
}) => ValueEnum::Bundle(
TagAndBody {
tag: DynUInt::with_type(tag_ty, enum_value.variant_index()),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
}
.to_canonical(),
),
EnumTypeState::UInt(target_ty) => {
ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty))
}
EnumTypeState::Unchanged => ValueEnum::Enum(enum_value),
})
}
ValueEnum::Bundle(_)
| ValueEnum::Array(_)
| ValueEnum::UInt(_)
| ValueEnum::SInt(_)
| ValueEnum::Clock(_)
| ValueEnum::AsyncReset(_)
| ValueEnum::SyncReset(_)
| ValueEnum::Reset(_) => value_enum.default_fold(self),
}
}
fn fold_variant_type<T>(&mut self, _v: VariantType<T>) -> Result<VariantType<T>, Self::Error> {
unreachable!()
}
fn fold_enum_literal<EnumTy>(
&mut self,
_v: ops::EnumLiteral<EnumTy>,
) -> Result<ops::EnumLiteral<EnumTy>, Self::Error>
where
EnumTy: EnumType,
EnumTy::Value: EnumValue<Type = EnumTy>,
{
unreachable!()
}
fn fold_variant_access<T, VariantTy>(
&mut self,
_v: ops::VariantAccess<T, VariantTy>,
) -> Result<ops::VariantAccess<T, VariantTy>, Self::Error>
where
T: EnumType,
T::Value: EnumValue,
VariantTy: Type,
{
unreachable!()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum SimplifyEnumsKind {
SimplifyToEnumsWithNoBody,
ReplaceWithBundleOfUInts,
ReplaceWithUInt,
}
pub fn simplify_enums(
module: Interned<Module<DynBundle>>,
kind: SimplifyEnumsKind,
) -> Result<Interned<Module<DynBundle>>, SimplifyEnumsError> {
module.fold(&mut State {
enum_types: HashMap::new(),
replacement_mem_ports: HashMap::new(),
kind,
name_id_gen: NameIdGen::default(),
})
}

View file

@ -0,0 +1,940 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
annotations::TargetedAnnotation,
array::{Array, ArrayType, ValueArrayOrSlice},
bundle::{BundleType, BundleValue, DynBundle},
expr::{Expr, ExprEnum, ToExpr},
int::{DynSInt, DynSIntType, DynUInt, DynUIntType},
intern::{Intern, Interned},
memory::{Mem, MemPort, PortType},
module::{
transform::visit::{Fold, Folder},
Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire,
},
source_location::SourceLocation,
ty::{DynCanonicalValue, DynType, Type, TypeEnum},
util::MakeMutSlice,
wire::Wire,
};
use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap;
use std::{
convert::Infallible,
fmt::Write,
ops::{Deref, DerefMut},
rc::Rc,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum SingleType {
UInt(DynUIntType),
SInt(DynSIntType),
UIntArray(ArrayType<[DynUInt]>),
SIntArray(ArrayType<[DynSInt]>),
}
impl SingleType {
fn is_array_type(self, array_type: ArrayType<[DynCanonicalValue]>) -> bool {
match self {
SingleType::UInt(_) | SingleType::SInt(_) => false,
SingleType::UIntArray(ty) => ty.canonical() == array_type,
SingleType::SIntArray(ty) => ty.canonical() == array_type,
}
}
fn array_len(self) -> usize {
match self {
SingleType::UInt(_ty) => 1,
SingleType::SInt(_ty) => 1,
SingleType::UIntArray(ty) => ty.len(),
SingleType::SIntArray(ty) => ty.len(),
}
}
}
#[derive(Clone, Debug)]
enum MemSplit {
Bundle {
fields: Rc<[MemSplit]>,
},
Single {
output_mem: Option<Mem<[DynCanonicalValue]>>,
element_type: SingleType,
unchanged_element_type: bool,
},
Array {
elements: Rc<[MemSplit]>,
},
}
impl MemSplit {
fn mark_changed_element_type(self) -> Self {
match self {
MemSplit::Bundle { fields: _ } => self,
MemSplit::Single {
output_mem,
element_type,
unchanged_element_type: _,
} => MemSplit::Single {
output_mem,
element_type,
unchanged_element_type: false,
},
MemSplit::Array { elements: _ } => self,
}
}
fn new(element_type: TypeEnum) -> Self {
match element_type {
TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle {
fields: bundle_ty
.fields()
.into_iter()
.map(|field| Self::new(field.ty.type_enum()).mark_changed_element_type())
.collect(),
},
TypeEnum::ArrayType(ty) => {
let element = MemSplit::new(ty.element().type_enum());
if let Self::Single {
output_mem: _,
element_type,
unchanged_element_type,
} = element
{
match element_type {
SingleType::UInt(element_type) => Self::Single {
output_mem: None,
element_type: SingleType::UIntArray(ArrayType::new_slice(
element_type,
ty.len(),
)),
unchanged_element_type,
},
SingleType::SInt(element_type) => Self::Single {
output_mem: None,
element_type: SingleType::SIntArray(ArrayType::new_slice(
element_type,
ty.len(),
)),
unchanged_element_type,
},
SingleType::UIntArray(element_type) => Self::Single {
output_mem: None,
element_type: SingleType::UIntArray(ArrayType::new_slice(
*element_type.element(),
ty.len()
.checked_mul(element_type.len())
.expect("memory element type can't be too big"),
)),
unchanged_element_type: false,
},
SingleType::SIntArray(element_type) => Self::Single {
output_mem: None,
element_type: SingleType::SIntArray(ArrayType::new_slice(
*element_type.element(),
ty.len()
.checked_mul(element_type.len())
.expect("memory element type can't be too big"),
)),
unchanged_element_type: false,
},
}
} else {
let element = element.mark_changed_element_type();
Self::Array {
elements: (0..ty.len()).map(|_| element.clone()).collect(),
}
}
}
TypeEnum::UInt(ty) => Self::Single {
output_mem: None,
element_type: SingleType::UInt(ty),
unchanged_element_type: true,
},
TypeEnum::SInt(ty) => Self::Single {
output_mem: None,
element_type: SingleType::SInt(ty),
unchanged_element_type: true,
},
TypeEnum::EnumType(ty) => Self::Single {
output_mem: None,
element_type: SingleType::UInt(DynUIntType::new(ty.bit_width())),
unchanged_element_type: false,
},
TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
}
}
}
struct MemState {
replacement_ports: Box<[ExprEnum]>,
}
struct SplitState<'a> {
wire_rdata: Box<[Option<Expr<DynCanonicalValue>>]>,
wire_wdata: Box<[Option<Expr<DynCanonicalValue>>]>,
wire_wmask: Box<[Option<Expr<DynCanonicalValue>>]>,
initial_value: Option<Box<[&'a BitSlice]>>,
}
impl<'a> SplitState<'a> {
fn placeholder() -> Self {
Self {
wire_rdata: Box::new([]),
wire_wdata: Box::new([]),
wire_wmask: Box::new([]),
initial_value: None,
}
}
fn new_empty(ports_len: usize) -> Self {
Self {
wire_rdata: (0..ports_len).map(|_| None).collect(),
wire_wdata: (0..ports_len).map(|_| None).collect(),
wire_wmask: (0..ports_len).map(|_| None).collect(),
initial_value: None,
}
}
}
struct SplitStateStack<'a> {
ports_len: usize,
values: Vec<SplitState<'a>>,
top_index: usize,
}
impl<'a> SplitStateStack<'a> {
fn new(ports_len: usize, value: SplitState<'a>) -> Self {
Self {
ports_len,
values: vec![value],
top_index: 0,
}
}
fn top(&mut self) -> &mut SplitState<'a> {
&mut self.values[self.top_index]
}
fn pop(&mut self) {
self.top_index = self
.top_index
.checked_sub(1)
.expect("there's always at least one entry in the stack");
}
fn push_map(
&mut self,
mut wire_map: impl FnMut(Expr<DynCanonicalValue>) -> Expr<DynCanonicalValue>,
mut initial_value_element_map: impl FnMut(&BitSlice) -> &BitSlice,
) {
let top_index = self.top_index + 1;
let mut top = match self.values.get_mut(top_index) {
Some(top) => std::mem::replace(top, SplitState::placeholder()),
None => SplitState::new_empty(self.ports_len),
};
for (l, &r) in top.wire_rdata.iter_mut().zip(self.top().wire_rdata.iter()) {
*l = r.map(&mut wire_map);
}
for (l, &r) in top.wire_wdata.iter_mut().zip(self.top().wire_wdata.iter()) {
*l = r.map(&mut wire_map);
}
for (l, &r) in top.wire_wmask.iter_mut().zip(self.top().wire_wmask.iter()) {
*l = r.map(&mut wire_map);
}
if let Some(initial_value) = &self.top().initial_value {
let new_initial_value = top.initial_value.get_or_insert_with(|| {
Box::from_iter((0..initial_value.len()).map(|_| Default::default()))
});
for (l, &r) in new_initial_value.iter_mut().zip(initial_value.iter()) {
*l = initial_value_element_map(r);
}
}
self.top_index = top_index;
if let Some(v) = self.values.get_mut(top_index) {
*v = top;
} else {
assert_eq!(top_index, self.values.len());
self.values.push(top);
}
}
}
struct SplitMemState<'a, 'b> {
module_state: &'a mut ModuleState,
input_mem: Mem<[DynCanonicalValue]>,
output_mems: &'a mut Vec<Mem<[DynCanonicalValue]>>,
output_stmts: &'a mut Vec<Stmt>,
element_type: TypeEnum,
split: &'a mut MemSplit,
mem_name_path: &'a mut String,
split_state_stack: &'a mut SplitStateStack<'b>,
mem_state: &'a MemState,
}
impl SplitMemState<'_, '_> {
fn split_mem(self) {
let outer_mem_name_path_len = self.mem_name_path.len();
match self.split {
MemSplit::Bundle { fields } => {
let TypeEnum::BundleType(bundle_type) = self.element_type else {
unreachable!();
};
for ((field, field_offset), split) in bundle_type
.fields()
.into_iter()
.zip(bundle_type.field_offsets())
.zip(fields.make_mut_slice())
{
self.mem_name_path.truncate(outer_mem_name_path_len);
self.mem_name_path.push('_');
self.mem_name_path.push_str(&field.name);
let field_ty_bit_width = field.ty.bit_width();
self.split_state_stack.push_map(
|e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name),
|initial_value_element| {
&initial_value_element[field_offset..][..field_ty_bit_width]
},
);
SplitMemState {
module_state: self.module_state,
input_mem: self.input_mem,
output_mems: self.output_mems,
output_stmts: self.output_stmts,
element_type: field.ty.type_enum(),
split,
mem_name_path: self.mem_name_path,
split_state_stack: self.split_state_stack,
mem_state: self.mem_state,
}
.split_mem();
self.split_state_stack.pop();
}
}
MemSplit::Single {
output_mem,
element_type: single_type,
unchanged_element_type: _,
} => {
let new_mem = self.module_state.create_split_mem(
self.input_mem,
self.output_stmts,
self.element_type,
*single_type,
self.mem_name_path,
self.split_state_stack.top(),
);
for (port, wire) in new_mem
.ports()
.into_iter()
.zip(self.mem_state.replacement_ports.iter())
{
let port_expr = port.to_expr();
let wire_expr = Expr::<DynBundle>::new_unchecked(*wire);
for name in [
Some("addr"),
Some("clk"),
Some("en"),
port.port_kind().wmode_name(),
] {
let Some(name) = name else {
continue;
};
self.output_stmts.push(
StmtConnect {
lhs: port_expr.field(name),
rhs: wire_expr.field(name),
source_location: port.source_location(),
}
.into(),
);
}
}
*output_mem = Some(new_mem);
self.output_mems.push(new_mem);
}
MemSplit::Array { elements } => {
let TypeEnum::ArrayType(array_type) = self.element_type else {
unreachable!();
};
let element_type = array_type.element().type_enum();
let element_bit_width = array_type.element().bit_width();
for (index, split) in elements.make_mut_slice().iter_mut().enumerate() {
self.mem_name_path.truncate(outer_mem_name_path_len);
write!(self.mem_name_path, "_{index}").unwrap();
self.split_state_stack.push_map(
|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()[index]
},
|initial_value_element| {
&initial_value_element[index * element_bit_width..][..element_bit_width]
},
);
SplitMemState {
module_state: self.module_state,
input_mem: self.input_mem,
output_mems: self.output_mems,
output_stmts: self.output_stmts,
element_type,
split,
mem_name_path: self.mem_name_path,
split_state_stack: self.split_state_stack,
mem_state: self.mem_state,
}
.split_mem();
self.split_state_stack.pop();
}
}
}
}
}
struct ModuleState {
output_module: Option<Interned<Module<DynBundle>>>,
name_id_gen: NameIdGen,
memories: HashMap<ScopedNameId, MemState>,
}
impl ModuleState {
#[allow(clippy::too_many_arguments)]
fn connect_split_mem_port_arrays(
output_stmts: &mut Vec<Stmt>,
input_array_types: &[ArrayType<[DynCanonicalValue]>],
memory_element_array_range_start: usize,
memory_element_array_range_len: usize,
wire_rdata: Option<Expr<DynCanonicalValue>>,
wire_wdata: Option<Expr<DynCanonicalValue>>,
wire_wmask: Option<Expr<DynCanonicalValue>>,
port_rdata: Option<Expr<Array<[DynCanonicalValue]>>>,
port_wdata: Option<Expr<Array<[DynCanonicalValue]>>>,
port_wmask: Option<Expr<Array<[DynCanonicalValue]>>>,
connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
) {
let Some((input_array_type, input_array_types_rest)) = input_array_types.split_first()
else {
assert_eq!(memory_element_array_range_len, 1);
if let (Some(wire), Some(port)) = (wire_rdata, port_rdata) {
connect_rdata(output_stmts, wire, port[memory_element_array_range_start]);
}
if let (Some(wire), Some(port)) = (wire_wdata, port_wdata) {
connect_wdata(output_stmts, wire, port[memory_element_array_range_start]);
}
if let (Some(wire), Some(port)) = (wire_wmask, port_wmask) {
connect_wmask(output_stmts, wire, port[memory_element_array_range_start]);
}
return;
};
if input_array_type.is_empty() {
return; // no need to connect zero-length arrays, also avoids division by zero
}
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
let chunk_size = memory_element_array_range_len / input_array_type.len();
for index in 0..input_array_type.len() {
let map =
|e: Expr<DynCanonicalValue>| e.with_type::<Array<[DynCanonicalValue]>>()[index];
let wire_rdata = wire_rdata.map(map);
let wire_wdata = wire_wdata.map(map);
let wire_wmask = wire_wmask.map(map);
Self::connect_split_mem_port_arrays(
output_stmts,
input_array_types_rest,
memory_element_array_range_start + chunk_size * index,
chunk_size,
wire_rdata,
wire_wdata,
wire_wmask,
port_rdata,
port_wdata,
port_wmask,
connect_rdata,
connect_wdata,
connect_wmask,
);
}
}
#[allow(clippy::too_many_arguments)]
fn connect_split_mem_port(
&mut self,
output_stmts: &mut Vec<Stmt>,
mut input_element_type: TypeEnum,
single_type: SingleType,
source_location: SourceLocation,
wire_rdata: Option<Expr<DynCanonicalValue>>,
wire_wdata: Option<Expr<DynCanonicalValue>>,
wire_wmask: Option<Expr<DynCanonicalValue>>,
port_rdata: Option<Expr<DynCanonicalValue>>,
port_wdata: Option<Expr<DynCanonicalValue>>,
port_wmask: Option<Expr<DynCanonicalValue>>,
) {
let mut input_array_types = vec![];
let connect_read = |output_stmts: &mut Vec<Stmt>,
wire_read: Expr<DynCanonicalValue>,
port_read: Expr<DynCanonicalValue>| {
output_stmts.push(
StmtConnect {
lhs: wire_read,
rhs: port_read,
source_location,
}
.into(),
);
};
let connect_write = |output_stmts: &mut Vec<Stmt>,
wire_write: Expr<DynCanonicalValue>,
port_write: Expr<DynCanonicalValue>| {
output_stmts.push(
StmtConnect {
lhs: port_write,
rhs: wire_write,
source_location,
}
.into(),
);
};
let connect_read_enum =
|output_stmts: &mut Vec<Stmt>,
wire_read: Expr<DynCanonicalValue>,
port_read: Expr<DynCanonicalValue>| {
connect_read(
output_stmts,
wire_read,
port_read
.with_type::<DynUInt>()
.cast_bits_to(wire_read.ty()),
);
};
let connect_write_enum =
|output_stmts: &mut Vec<Stmt>,
wire_write: Expr<DynCanonicalValue>,
port_write: Expr<DynCanonicalValue>| {
connect_write(
output_stmts,
wire_write.cast_to_bits().to_canonical_dyn(),
port_write,
);
};
loop {
match input_element_type {
TypeEnum::BundleType(_) => unreachable!("bundle types are always split"),
TypeEnum::EnumType(_)
if input_array_types
.first()
.map(|&v| single_type.is_array_type(v))
.unwrap_or(true) =>
{
if let (Some(wire_rdata), Some(port_rdata)) = (wire_rdata, port_rdata) {
connect_read_enum(output_stmts, wire_rdata, port_rdata);
}
if let (Some(wire_wdata), Some(port_wdata)) = (wire_wdata, port_wdata) {
connect_write_enum(output_stmts, wire_wdata, port_wdata);
}
if let (Some(wire_wmask), Some(port_wmask)) = (wire_wmask, port_wmask) {
connect_write(output_stmts, wire_wmask, port_wmask);
}
}
TypeEnum::EnumType(_) => Self::connect_split_mem_port_arrays(
output_stmts,
&input_array_types,
0,
single_type.array_len(),
wire_rdata,
wire_wdata,
wire_wmask,
port_rdata.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
port_wdata.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
port_wmask.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
connect_read_enum,
connect_write_enum,
connect_write_enum,
),
TypeEnum::ArrayType(array_type) => {
input_array_types.push(array_type);
input_element_type = array_type.element().type_enum();
continue;
}
TypeEnum::UInt(_) | TypeEnum::SInt(_)
if input_array_types
.first()
.map(|&v| single_type.is_array_type(v))
.unwrap_or(true) =>
{
if let (Some(wire_rdata), Some(port_rdata)) = (wire_rdata, port_rdata) {
connect_read(output_stmts, wire_rdata, port_rdata);
}
if let (Some(wire_wdata), Some(port_wdata)) = (wire_wdata, port_wdata) {
connect_write(output_stmts, wire_wdata, port_wdata);
}
if let (Some(wire_wmask), Some(port_wmask)) = (wire_wmask, port_wmask) {
connect_write(output_stmts, wire_wmask, port_wmask);
}
}
TypeEnum::UInt(_) | TypeEnum::SInt(_) => Self::connect_split_mem_port_arrays(
output_stmts,
&input_array_types,
0,
single_type.array_len(),
wire_rdata,
wire_wdata,
wire_wmask,
port_rdata.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
port_wdata.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
port_wmask.map(|e: Expr<DynCanonicalValue>| {
e.with_type::<Array<[DynCanonicalValue]>>()
}),
connect_read,
connect_write,
connect_write,
),
TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
}
break;
}
}
fn create_split_mem(
&mut self,
input_mem: Mem<[DynCanonicalValue]>,
output_stmts: &mut Vec<Stmt>,
input_element_type: TypeEnum,
single_type: SingleType,
mem_name_path: &str,
split_state: &SplitState<'_>,
) -> Mem<[DynCanonicalValue]> {
let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
"{}{mem_name_path}",
input_mem.scoped_name().1 .0
)));
let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name);
let output_element_type = match single_type {
SingleType::UInt(ty) => ty.canonical_dyn(),
SingleType::SInt(ty) => ty.canonical_dyn(),
SingleType::UIntArray(ty) => ty.canonical_dyn(),
SingleType::SIntArray(ty) => ty.canonical_dyn(),
};
let output_array_type =
ArrayType::new_slice(output_element_type, input_mem.array_type().len());
let initial_value = split_state.initial_value.as_ref().map(|initial_value| {
let mut bits = BitVec::with_capacity(output_array_type.bit_width());
for element in initial_value.iter() {
bits.extend_from_bitslice(element);
}
Intern::intern_owned(bits)
});
let ports = input_mem
.ports()
.into_iter()
.map(|port| {
MemPort::new_unchecked(
mem_name,
port.source_location(),
port.port_name(),
port.addr_type(),
output_element_type,
)
.intern_sized()
})
.collect();
let output_mem = Mem::new_unchecked(
mem_name,
input_mem.source_location(),
output_array_type,
initial_value,
ports,
input_mem.read_latency(),
input_mem.write_latency(),
input_mem.read_under_write(),
input_mem
.port_annotations()
.iter()
.flat_map(|_v| -> Option<TargetedAnnotation> {
// TODO: map annotation target for memory port
None
})
.collect(),
input_mem.mem_annotations(),
);
for (index, port) in ports.into_iter().enumerate() {
let SplitState {
wire_rdata,
wire_wdata,
wire_wmask,
initial_value: _,
} = split_state;
let port_expr = port.to_expr();
let port_rdata = port
.port_kind()
.rdata_name()
.map(|name| port_expr.field(name));
let port_wdata = port
.port_kind()
.wdata_name()
.map(|name| port_expr.field(name));
let port_wmask = port
.port_kind()
.wmask_name()
.map(|name| port_expr.field(name));
self.connect_split_mem_port(
output_stmts,
input_element_type,
single_type,
port.source_location(),
wire_rdata[index],
wire_wdata[index],
wire_wmask[index],
port_rdata,
port_wdata,
port_wmask,
);
}
output_mem
}
fn process_mem(
&mut self,
input_mem: Mem<[DynCanonicalValue]>,
output_mems: &mut Vec<Mem<[DynCanonicalValue]>>,
output_stmts: &mut Vec<Stmt>,
) {
let element_type = input_mem.array_type().element().type_enum();
let mut split = MemSplit::new(element_type);
let mem_state = match split {
MemSplit::Single {
ref mut output_mem,
element_type: _,
unchanged_element_type: true,
} => {
// no change necessary
*output_mem = Some(input_mem);
output_mems.push(input_mem);
MemState {
replacement_ports: input_mem
.ports()
.into_iter()
.map(ExprEnum::MemPort)
.collect(),
}
}
MemSplit::Single {
unchanged_element_type: false,
..
}
| MemSplit::Bundle { .. }
| MemSplit::Array { .. } => {
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_wdata = Vec::with_capacity(input_mem.ports().len());
let mut wire_port_wmask = Vec::with_capacity(input_mem.ports().len());
for port in input_mem.ports() {
let port_ty = port.ty();
let NameId(mem_name, _) = input_mem.scoped_name().1;
let port_name = port.port_name();
let wire_name = self
.name_id_gen
.gen(Intern::intern_owned(format!("{mem_name}_{port_name}")));
let wire = Wire::new_unchecked(
ScopedNameId(input_mem.scoped_name().0, wire_name),
port.source_location(),
port_ty,
);
let wire_expr = wire.to_expr();
let canonical_wire = wire.to_dyn_canonical_wire();
output_stmts.push(
StmtWire {
annotations: Default::default(),
wire: canonical_wire.clone(),
}
.into(),
);
replacement_ports.push(ExprEnum::Wire(canonical_wire.intern_sized()));
wire_port_rdata.push(
port.port_kind()
.rdata_name()
.map(|name| wire_expr.field(name)),
);
wire_port_wdata.push(
port.port_kind()
.wdata_name()
.map(|name| wire_expr.field(name)),
);
wire_port_wmask.push(
port.port_kind()
.wmask_name()
.map(|name| wire_expr.field(name)),
);
}
let mem_state = MemState {
replacement_ports: replacement_ports.into_boxed_slice(),
};
SplitMemState {
module_state: self,
input_mem,
output_mems,
output_stmts,
element_type,
split: &mut split,
mem_name_path: &mut String::with_capacity(32),
split_state_stack: &mut SplitStateStack::new(
input_mem.ports().len(),
SplitState {
wire_rdata: wire_port_rdata.into_boxed_slice(),
wire_wdata: wire_port_wdata.into_boxed_slice(),
wire_wmask: wire_port_wmask.into_boxed_slice(),
initial_value: input_mem.initial_value().as_ref().map(
|initial_value| {
if initial_value.len() == 0 {
Box::new([])
} else {
Box::from_iter(initial_value.chunks(
initial_value.len() / input_mem.array_type().len(),
))
}
},
),
},
),
mem_state: &mem_state,
}
.split_mem();
mem_state
}
};
self.memories.insert(input_mem.scoped_name(), mem_state);
}
}
#[derive(Default)]
struct State {
modules: HashMap<Interned<Module<DynBundle>>, ModuleState>,
current_module: Option<Interned<Module<DynBundle>>>,
}
impl State {
fn module_state(&mut self) -> &mut ModuleState {
let current_module = self.current_module.unwrap();
self.modules.get_mut(&current_module).unwrap()
}
}
struct PushedState<'a> {
state: &'a mut State,
old_module: Option<Interned<Module<DynBundle>>>,
}
impl<'a> PushedState<'a> {
fn push_module(state: &'a mut State, module: Interned<Module<DynBundle>>) -> Self {
let old_module = state.current_module.replace(module);
Self { state, old_module }
}
}
impl Drop for PushedState<'_> {
fn drop(&mut self) {
self.state.current_module = self.old_module.take();
}
}
impl Deref for PushedState<'_> {
type Target = State;
fn deref(&self) -> &Self::Target {
self.state
}
}
impl DerefMut for PushedState<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.state
}
}
impl Folder for State {
type Error = Infallible;
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
where
T::Type: BundleType<Value = T>,
{
let module: Interned<_> = v.canonical().intern_sized();
if let Some(module_state) = self.modules.get(&module) {
return Ok(Module::from_canonical(
*module_state
.output_module
.expect("modules can't be mutually recursive"),
));
}
self.modules.insert(
module,
ModuleState {
output_module: None,
name_id_gen: NameIdGen::for_module(*module),
memories: HashMap::new(),
},
);
let mut this = PushedState::push_module(self, module);
let module = module.default_fold(&mut *this)?;
this.module_state().output_module = Some(module);
Ok(Module::from_canonical(*module))
}
fn fold_mem<VA: ValueArrayOrSlice + ?Sized>(
&mut self,
_v: Mem<VA>,
) -> Result<Mem<VA>, Self::Error> {
unreachable!()
}
fn fold_block(&mut self, v: Block) -> Result<Block, Self::Error> {
let Block {
memories: input_mems,
stmts: input_stmts,
} = v;
let mut output_mems = vec![];
let mut output_stmts = vec![];
let module_state = self.module_state();
for input_mem in input_mems {
module_state.process_mem(input_mem, &mut output_mems, &mut output_stmts);
}
output_stmts.extend(
input_stmts
.into_iter()
.map(|stmt| stmt.fold(self).unwrap_or_else(|v| match v {})),
);
Ok(Block {
memories: Intern::intern_owned(output_mems),
stmts: Intern::intern_owned(output_stmts),
})
}
fn fold_expr_enum(&mut self, v: ExprEnum) -> Result<ExprEnum, Self::Error> {
if let ExprEnum::MemPort(mem_port) = v {
Ok(self
.module_state()
.memories
.get(&mem_port.mem_name())
.expect("all uses of a memory must come after the memory is declared")
.replacement_ports[mem_port.port_index()])
} else {
v.default_fold(self)
}
}
fn fold_mem_port<T: PortType>(&mut self, _v: MemPort<T>) -> Result<MemPort<T>, Self::Error> {
unreachable!()
}
}
pub fn simplify_memories(module: Interned<Module<DynBundle>>) -> Interned<Module<DynBundle>> {
module
.fold(&mut State::default())
.unwrap_or_else(|v| match v {})
}

View file

@ -0,0 +1,462 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(clippy::multiple_bound_locations)]
use crate::{
annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType},
clock::{Clock, ClockType},
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
expr::{
ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement,
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
},
int::{DynInt, DynIntType, FixedOrDynIntType, IntType, IntTypeTrait},
intern::{Intern, Interned},
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
module::{
AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance,
StmtMatch, StmtReg, StmtWire,
},
reg::Reg,
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum},
util::{ConstBool, GenericConstBool},
wire::Wire,
};
use num_bigint::{BigInt, BigUint};
use std::{rc::Rc, sync::Arc};
pub trait Fold<State: ?Sized + Folder>: Sized {
fn fold(self, state: &mut State) -> Result<Self, State::Error>;
fn default_fold(self, state: &mut State) -> Result<Self, State::Error>;
}
pub trait Visit<State: ?Sized + Visitor> {
fn visit(&self, state: &mut State) -> Result<(), State::Error>;
fn default_visit(&self, state: &mut State) -> Result<(), State::Error>;
}
macro_rules! impl_visit_fold {
(
impl<$T:ident, $State:ident $(, const $Const:ident: $const_ty:ty)*> _ for $ty:ty $(where ($($where_tt:tt)*))? {
fn fold($($fold_args:tt)*) -> $fold_ret_ty:ty $fold_block:block
fn default_fold($($default_fold_args:tt)*) -> $default_fold_ret_ty:ty $default_fold_block:block
fn visit($($visit_args:tt)*) -> $visit_ret_ty:ty $visit_block:block
fn default_visit($($default_visit_args:tt)*) -> $default_visit_ret_ty:ty $default_visit_block:block
}
) => {
impl<$T: Fold<$State>, $State: ?Sized + Folder $(, const $Const: $const_ty)*> Fold<$State> for $ty
$(where $($where_tt)*)?
{
fn fold($($fold_args)*) -> $fold_ret_ty $fold_block
fn default_fold($($default_fold_args)*) -> $default_fold_ret_ty $default_fold_block
}
impl<$T: Visit<$State>, $State: ?Sized + Visitor $(, const $Const: $const_ty)*> Visit<$State> for $ty
$(where $($where_tt)*)?
{
fn visit($($visit_args)*) -> $visit_ret_ty $visit_block
fn default_visit($($default_visit_args)*) -> $default_visit_ret_ty $default_visit_block
}
};
}
macro_rules! no_op_visit_fold {
($ty:ty) => {
impl<State: ?Sized + Folder> Fold<State> for $ty {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
let _ = state;
Ok(self)
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
let _ = state;
Ok(self)
}
}
impl<State: ?Sized + Visitor> Visit<State> for $ty {
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
let _ = state;
Ok(())
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
let _ = state;
Ok(())
}
}
};
}
no_op_visit_fold!(());
no_op_visit_fold!(char);
no_op_visit_fold!(u8);
no_op_visit_fold!(u16);
no_op_visit_fold!(u32);
no_op_visit_fold!(u64);
no_op_visit_fold!(u128);
no_op_visit_fold!(usize);
no_op_visit_fold!(i8);
no_op_visit_fold!(i16);
no_op_visit_fold!(i32);
no_op_visit_fold!(i64);
no_op_visit_fold!(i128);
no_op_visit_fold!(isize);
no_op_visit_fold!(std::num::NonZeroU8);
no_op_visit_fold!(std::num::NonZeroU16);
no_op_visit_fold!(std::num::NonZeroU32);
no_op_visit_fold!(std::num::NonZeroU64);
no_op_visit_fold!(std::num::NonZeroU128);
no_op_visit_fold!(std::num::NonZeroUsize);
no_op_visit_fold!(std::num::NonZeroI8);
no_op_visit_fold!(std::num::NonZeroI16);
no_op_visit_fold!(std::num::NonZeroI32);
no_op_visit_fold!(std::num::NonZeroI64);
no_op_visit_fold!(std::num::NonZeroI128);
no_op_visit_fold!(std::num::NonZeroIsize);
no_op_visit_fold!(bool);
no_op_visit_fold!(String);
no_op_visit_fold!(Interned<str>);
no_op_visit_fold!(Box<str>);
no_op_visit_fold!(Arc<str>);
no_op_visit_fold!(Rc<str>);
no_op_visit_fold!(BigInt);
no_op_visit_fold!(BigUint);
impl_visit_fold! {
impl<T, State> _ for Box<T> {
fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
*self = T::fold(*self, state)?;
Ok(self)
}
fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
*self = T::default_fold(*self, state)?;
Ok(self)
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
}
impl_visit_fold! {
impl<T, State> _ for Arc<T>
where (
T: Clone,
)
{
fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Arc::get_mut(&mut self) {
*v = T::fold(v.clone(), state)?;
Ok(self)
} else {
T::fold(T::clone(&*self), state).map(Arc::new)
}
}
fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Arc::get_mut(&mut self) {
*v = T::default_fold(v.clone(), state)?;
Ok(self)
} else {
T::default_fold(T::clone(&*self), state).map(Arc::new)
}
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
}
impl_visit_fold! {
impl<T, State> _ for Rc<T>
where (
T: Clone,
)
{
fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Rc::get_mut(&mut self) {
*v = T::fold(v.clone(), state)?;
Ok(self)
} else {
T::fold(T::clone(&*self), state).map(Rc::new)
}
}
fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Rc::get_mut(&mut self) {
*v = T::default_fold(v.clone(), state)?;
Ok(self)
} else {
T::default_fold(T::clone(&*self), state).map(Rc::new)
}
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
}
impl_visit_fold! {
impl<T, State> _ for Arc<[T]>
where (
T: Clone,
) {
fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Arc::get_mut(&mut self) {
v.iter_mut().try_for_each(|v| {
*v = v.clone().fold(state)?;
Ok(())
})?;
Ok(self)
} else {
FromIterator::from_iter(self.iter().map(|v| v.clone().fold(state)))
}
}
fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Arc::get_mut(&mut self) {
v.iter_mut().try_for_each(|v| {
*v = v.clone().default_fold(state)?;
Ok(())
})?;
Ok(self)
} else {
FromIterator::from_iter(self.iter().map(|v| v.clone().default_fold(state)))
}
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State> _ for Rc<[T]>
where (
T: Clone,
) {
fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Rc::get_mut(&mut self) {
v.iter_mut().try_for_each(|v| {
*v = v.clone().fold(state)?;
Ok(())
})?;
Ok(self)
} else {
FromIterator::from_iter(self.iter().map(|v| v.clone().fold(state)))
}
}
fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
if let Some(v) = Rc::get_mut(&mut self) {
v.iter_mut().try_for_each(|v| {
*v = v.clone().default_fold(state)?;
Ok(())
})?;
Ok(self)
} else {
FromIterator::from_iter(self.iter().map(|v| v.clone().default_fold(state)))
}
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State> _ for Interned<T>
where (
T: Clone + Intern
)
{
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
T::fold(T::clone(&self), state).map(Intern::intern_sized)
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
T::default_fold(T::clone(&self), state).map(Intern::intern_sized)
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
}
impl_visit_fold! {
impl<T, State> _ for Interned<[T]>
where (
T: Clone + 'static + Send + Sync,
[T]: Intern,
)
{
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_iter().map(|v| v.fold(state)))
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_iter().map(|v| v.default_fold(state)))
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State> _ for Box<[T]> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_vec().into_iter().map(|v| v.fold(state)))
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_vec().into_iter().map(|v| v.default_fold(state)))
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State, const N: usize> _ for [T; N] {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
let mut retval = std::array::from_fn(|_| None);
for (retval, v) in retval.iter_mut().zip(self) {
*retval = Some(v.fold(state)?);
}
Ok(retval.map(Option::unwrap))
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
let mut retval = std::array::from_fn(|_| None);
for (retval, v) in retval.iter_mut().zip(self) {
*retval = Some(v.default_fold(state)?);
}
Ok(retval.map(Option::unwrap))
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State> _ for Vec<T> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_iter().map(|v| v.fold(state)))
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
FromIterator::from_iter(self.into_iter().map(|v| v.default_fold(state)))
}
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::visit(v, state))
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
self.iter().try_for_each(|v| T::default_visit(v, state))
}
}
}
impl_visit_fold! {
impl<T, State> _ for Option<T> {
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
self.map(|v| v.fold(state)).transpose()
}
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
self.map(|v| v.default_fold(state)).transpose()
}
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
let Some(v) = self else {
return Ok(());
};
T::visit(v, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
let Some(v) = self else {
return Ok(());
};
T::default_visit(v, state)
}
}
}
impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ T {
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut T {
fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::visit(self, state)
}
fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
T::default_visit(self, state)
}
}
type InternedDynType = Interned<dyn DynType>;
type InternedDynCanonicalType = Interned<dyn DynCanonicalType>;
include!(concat!(env!("OUT_DIR"), "/visit.rs"));

151
crates/fayalite/src/reg.rs Normal file
View file

@ -0,0 +1,151 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
clock::ClockDomain,
expr::{Expr, ExprTrait, Flow, ToExpr},
intern::Interned,
module::{NameId, ScopedNameId},
source_location::SourceLocation,
ty::{DynCanonicalType, DynType, Type},
};
use std::fmt;
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Reg<T: Type> {
name: ScopedNameId,
source_location: SourceLocation,
ty: T,
clock_domain: Expr<ClockDomain>,
init: Option<Expr<T::Value>>,
}
impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
name,
source_location: _,
ty,
clock_domain,
init,
} = self;
f.debug_struct("Reg")
.field("name", name)
.field("ty", ty)
.field("clock_domain", clock_domain)
.field("init", init)
.finish_non_exhaustive()
}
}
impl<T: Type> ToExpr for Reg<T> {
type Type = T;
fn ty(&self) -> Self::Type {
self.ty.clone()
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::new_unchecked(self.expr_enum())
}
}
impl<T: Type> Reg<T> {
pub fn canonical(&self) -> Reg<T::CanonicalType> {
let Self {
name,
source_location,
ref ty,
clock_domain,
init,
} = *self;
Reg {
name,
source_location,
ty: ty.canonical(),
clock_domain,
init: init.map(Expr::canonical),
}
}
pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> {
let Self {
name,
source_location,
ref ty,
clock_domain,
init,
} = *self;
Reg {
name,
source_location,
ty: ty.to_dyn(),
clock_domain,
init: init.map(Expr::to_dyn),
}
}
pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> {
let Self {
name,
source_location,
ref ty,
clock_domain,
init,
} = *self;
Reg {
name,
source_location,
ty: ty.canonical_dyn(),
clock_domain,
init: init.map(Expr::to_canonical_dyn),
}
}
#[track_caller]
pub fn new_unchecked(
scoped_name: ScopedNameId,
source_location: SourceLocation,
ty: T,
clock_domain: Expr<ClockDomain>,
init: Option<Expr<T::Value>>,
) -> Self {
assert!(ty.is_storable(), "register type must be a storable type");
if let Some(init) = init {
assert_eq!(ty, init.ty(), "register's type must match init type");
}
Self {
name: scoped_name,
source_location,
ty,
clock_domain,
init,
}
}
pub fn source_location(&self) -> SourceLocation {
self.source_location
}
pub fn containing_module_name(&self) -> Interned<str> {
self.containing_module_name_id().0
}
pub fn containing_module_name_id(&self) -> NameId {
self.name.0
}
pub fn name(&self) -> Interned<str> {
self.name_id().0
}
pub fn name_id(&self) -> NameId {
self.name.1
}
pub fn scoped_name(&self) -> ScopedNameId {
self.name
}
pub fn clock_domain(&self) -> Expr<ClockDomain> {
self.clock_domain
}
pub fn init(&self) -> Option<Expr<T::Value>> {
self.init
}
pub fn flow(&self) -> Flow {
Flow::Duplex
}
pub fn must_connect_to(&self) -> bool {
false
}
}

View file

@ -0,0 +1,359 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{Expr, ToExpr},
int::{UInt, UIntType},
intern::Interned,
source_location::SourceLocation,
ty::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, FixedType, Type, TypeEnum, Value, ValueEnum,
},
util::interned_bit,
};
use bitvec::slice::BitSlice;
pub trait ResetTypeTrait: CanonicalType + FixedType {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct AsyncResetType;
impl AsyncResetType {
pub const fn new() -> Self {
Self
}
}
impl Type for AsyncResetType {
type Value = AsyncReset;
type CanonicalType = AsyncResetType;
type CanonicalValue = AsyncReset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::AsyncReset(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for AsyncResetType {}
impl CanonicalType for AsyncResetType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset;
}
impl FixedType for AsyncResetType {
fn fixed_type() -> Self {
Self
}
}
impl ResetTypeTrait for AsyncResetType {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct AsyncReset(pub bool);
impl ToExpr for AsyncReset {
type Type = AsyncResetType;
fn ty(&self) -> Self::Type {
AsyncResetType
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for AsyncReset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for AsyncReset {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::AsyncReset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct SyncResetType;
impl SyncResetType {
pub const fn new() -> Self {
Self
}
}
impl Type for SyncResetType {
type CanonicalType = SyncResetType;
type Value = SyncReset;
type CanonicalValue = SyncReset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::SyncReset(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for SyncResetType {}
impl CanonicalType for SyncResetType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset;
}
impl FixedType for SyncResetType {
fn fixed_type() -> Self {
Self
}
}
impl ResetTypeTrait for SyncResetType {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
pub struct SyncReset(pub bool);
impl ToExpr for SyncReset {
type Type = SyncResetType;
fn ty(&self) -> Self::Type {
SyncResetType
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for SyncReset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
impl CanonicalValue for SyncReset {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::SyncReset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
interned_bit(this.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub struct ResetType;
impl ResetType {
pub const fn new() -> Self {
Self
}
}
impl Type for ResetType {
type Value = Reset;
type CanonicalType = ResetType;
type CanonicalValue = Reset;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::Reset(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for ResetType {}
impl CanonicalType for ResetType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset;
}
impl FixedType for ResetType {
fn fixed_type() -> Self {
Self
}
}
impl ResetTypeTrait for ResetType {}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Reset {}
impl ToExpr for Reset {
type Type = ResetType;
fn ty(&self) -> Self::Type {
match *self {}
}
fn to_expr(&self) -> Expr<Self> {
Expr::from_value(self)
}
}
impl Value for Reset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
match *this {}
}
}
impl CanonicalValue for Reset {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Reset(*this)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
match *this {}
}
}
macro_rules! make_to_reset {
(
$(#[from_value($from_value_ty:ty)])*
$vis:vis trait $Trait:ident {
fn $fn:ident(&self) -> Expr<$T:ty>;
}
) => {
$vis trait $Trait {
fn $fn(&self) -> Expr<$T>;
}
impl<T: ?Sized + $Trait> $Trait for &'_ T {
fn $fn(&self) -> Expr<$T> {
(**self).$fn()
}
}
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
fn $fn(&self) -> Expr<$T> {
(**self).$fn()
}
}
impl<T: ?Sized + $Trait> $Trait for Box<T> {
fn $fn(&self) -> Expr<$T> {
(**self).$fn()
}
}
impl $Trait for Expr<$T> {
fn $fn(&self) -> Expr<$T> {
*self
}
}
impl $Trait for $T {
fn $fn(&self) -> Expr<$T> {
self.to_expr()
}
}
$(impl $Trait for $from_value_ty {
fn $fn(&self) -> Expr<$T> {
self.to_expr().$fn()
}
})*
};
}
make_to_reset! {
#[from_value(SyncReset)]
#[from_value(AsyncReset)]
pub trait ToReset {
fn to_reset(&self) -> Expr<Reset>;
}
}
make_to_reset! {
#[from_value(bool)]
#[from_value(UInt<1>)]
pub trait ToAsyncReset {
fn to_async_reset(&self) -> Expr<AsyncReset>;
}
}
make_to_reset! {
#[from_value(bool)]
#[from_value(UInt<1>)]
pub trait ToSyncReset {
fn to_sync_reset(&self) -> Expr<SyncReset>;
}
}

View file

@ -0,0 +1,203 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
intern::{Intern, Interned},
util::DebugAsDisplay,
};
use hashbrown::HashMap;
use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SourceLocation {
file: Interned<str>,
line: u32,
column: u32,
}
impl fmt::Debug for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("SourceLocation")
.field(&DebugAsDisplay(self))
.finish()
}
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { file, line, column } = *self;
write!(f, "{file}:{line}:{column}")
}
}
#[derive(Copy, Clone, Debug)]
struct FilePattern(&'static str);
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct FilePatternMatch {
prefix: String,
suffix: String,
}
impl From<&'_ FilePatternMatch> for FilePatternMatch {
fn from(value: &'_ FilePatternMatch) -> Self {
value.clone()
}
}
impl FilePatternMatch {
fn generate_file_name(&self, file_name_id: NonZeroUsize) -> String {
if file_name_id.get() == 1 {
self.prefix.clone() + &self.suffix
} else {
format!("{}-{file_name_id}{}", self.prefix, self.suffix)
}
}
}
impl FilePattern {
const TEMPLATE_CHAR: char = 'X';
fn matches(self, file_name: &str) -> Option<FilePatternMatch> {
if self.0.len() != file_name.len() {
return None;
}
let mut prefix = String::with_capacity(file_name.len());
for (pattern_ch, ch) in self.0.chars().zip(file_name.chars()) {
if pattern_ch == Self::TEMPLATE_CHAR {
if !ch.is_ascii_hexdigit() {
return None;
} else {
prefix.push(Self::TEMPLATE_CHAR);
}
} else if pattern_ch != ch {
return None;
} else {
prefix.push(ch);
}
}
let split_point = self.0.rfind('.').unwrap_or(self.0.len());
Some(FilePatternMatch {
suffix: prefix.split_off(split_point),
prefix,
})
}
}
struct NormalizedFileForTestState {
file_name_id: NonZeroUsize,
positions_map: HashMap<(u32, u32), u32>,
}
struct NormalizeFilesForTestsState {
test_position: &'static panic::Location<'static>,
file_pattern_matches: HashMap<FilePatternMatch, HashMap<String, NormalizedFileForTestState>>,
}
impl NormalizeFilesForTestsState {
#[track_caller]
fn new() -> Self {
Self {
test_position: panic::Location::caller(),
file_pattern_matches: HashMap::new(),
}
}
}
const FILE_PATTERNS: &[FilePattern] = &[
FilePattern("module-XXXXXXXXXX.rs"),
FilePattern("value-struct-XXXXXXXXXX.rs"),
FilePattern("value-enum-XXXXXXXXXX.rs"),
];
thread_local! {
static NORMALIZE_FILES_FOR_TESTS: RefCell<Option<NormalizeFilesForTestsState>> = const { RefCell::new(None) };
}
impl From<&'_ panic::Location<'_>> for SourceLocation {
fn from(value: &'_ panic::Location<'_>) -> Self {
let mut file = value.file();
let mut line = value.line();
let mut column = value.column();
let mut file_str = String::new();
NORMALIZE_FILES_FOR_TESTS.with_borrow_mut(|state| {
let Some(state) = state else {
return;
};
if file == state.test_position.file() {
file = "the_test_file.rs";
line = line
.saturating_add(10000)
.saturating_sub(state.test_position.line());
return;
}
file = Path::new(file)
.file_name()
.and_then(|v| v.to_str())
.unwrap_or("<no-file-name>");
for p in FILE_PATTERNS {
if let Some(m) = p.matches(file) {
let map = state.file_pattern_matches.entry_ref(&m).or_default();
let len = map.len();
let file_state =
map.entry_ref(file)
.or_insert_with(|| NormalizedFileForTestState {
file_name_id: NonZeroUsize::new(len + 1).unwrap(),
positions_map: HashMap::new(),
});
file_str = m.generate_file_name(file_state.file_name_id);
file = &file_str;
let positions_len = file_state.positions_map.len();
line = *file_state
.positions_map
.entry((line, column))
.or_insert((positions_len + 1).try_into().unwrap());
column = 1;
break;
}
}
});
Self {
file: file.intern(),
line,
column,
}
}
}
#[must_use = "resets value when dropped"]
pub struct NormalizeFilesForTestsScope {
old_state: Option<NormalizeFilesForTestsState>,
}
impl Drop for NormalizeFilesForTestsScope {
fn drop(&mut self) {
NORMALIZE_FILES_FOR_TESTS.set(self.old_state.take());
}
}
impl SourceLocation {
#[track_caller]
pub fn normalize_files_for_tests() -> NormalizeFilesForTestsScope {
NormalizeFilesForTestsScope {
old_state: NORMALIZE_FILES_FOR_TESTS.replace(Some(NormalizeFilesForTestsState::new())),
}
}
#[track_caller]
pub fn caller() -> Self {
panic::Location::caller().into()
}
pub(crate) fn builtin() -> Self {
Self::new("builtin".intern(), 1, 1)
}
pub const fn new(file: Interned<str>, line: u32, column: u32) -> Self {
Self { file, line, column }
}
pub const fn file(self) -> Interned<str> {
self.file
}
pub const fn line(self) -> u32 {
self.line
}
pub const fn column(self) -> u32 {
self.column
}
}

1007
crates/fayalite/src/ty.rs Normal file

File diff suppressed because it is too large Load diff

267
crates/fayalite/src/util.rs Normal file
View file

@ -0,0 +1,267 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::intern::{Intern, Interned};
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
use std::{
cmp::Ordering,
fmt::{self, Debug, Write},
hash::Hash,
mem::ManuallyDrop,
ptr,
rc::Rc,
sync::{Arc, OnceLock},
};
mod sealed {
pub trait Sealed {}
}
/// # Safety
/// the only implementation is `ConstBool<Self::VALUE>`
pub unsafe trait GenericConstBool:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: bool;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstBool<const VALUE: bool>;
impl<const VALUE: bool> Debug for ConstBool<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstBool").field(&Self::VALUE).finish()
}
}
impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {}
/// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> {
const VALUE: bool = VALUE;
}
pub trait ConstBoolDispatchTag {
type Type<Select: GenericConstBool>;
}
pub enum ConstBoolDispatch<FalseT, TrueT> {
False(FalseT),
True(TrueT),
}
impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> {
pub fn new<
Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT>
+ ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>,
Select: GenericConstBool,
>(
v: Tag::Type<Select>,
) -> Self {
let v = ManuallyDrop::new(v);
let v_ptr: *const Tag::Type<Select> = &*v;
// SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
unsafe {
if Select::VALUE {
ConstBoolDispatch::True(ptr::read(v_ptr.cast()))
} else {
ConstBoolDispatch::False(ptr::read(v_ptr.cast()))
}
}
}
}
/// replacement for Iterator::eq_by which isn't stable yet
pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>(
l: L,
r: R,
mut f: F,
) -> bool {
let mut l = l.into_iter();
let mut r = r.into_iter();
l.try_for_each(|l| {
if let Some(r) = r.next() {
f(l, r).then_some(())
} else {
None
}
})
.is_some()
&& r.next().is_none()
}
pub struct DebugAsDisplay<T>(pub T);
impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering {
if a < b {
Ordering::Less
} else if a > b {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering {
let mut i = 0;
while i < a.len() && i < b.len() {
match const_u8_cmp(a[i], b[i]) {
Ordering::Equal => {}
retval => return retval,
}
i += 1;
}
const_usize_cmp(a.len(), b.len())
}
pub const fn const_str_cmp(a: &str, b: &str) -> Ordering {
const_bytes_cmp(a.as_bytes(), b.as_bytes())
}
pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool {
let mut i = 1;
while i < a.len() {
match const_str_cmp(a[i - 1], a[i]) {
Ordering::Less => {}
_ => return false,
}
i += 1;
}
true
}
pub trait MakeMutSlice {
type Element: Clone;
fn make_mut_slice(&mut self) -> &mut [Self::Element];
}
impl<T: Clone> MakeMutSlice for Arc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Arc::get_mut(self).is_none() {
*self = Arc::<[T]>::from(&**self);
}
Arc::get_mut(self).unwrap()
}
}
impl<T: Clone> MakeMutSlice for Rc<[T]> {
type Element = T;
fn make_mut_slice(&mut self) -> &mut [Self::Element] {
if Rc::get_mut(self).is_none() {
*self = Rc::<[T]>::from(&**self);
}
Rc::get_mut(self).unwrap()
}
}
pub struct DebugAsRawString<'a>(pub &'a str);
impl fmt::Debug for DebugAsRawString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pounds = self
.0
.split_inclusive('"')
.skip(1)
.map(|v| v.len() - v.trim_start_matches('#').len())
.max()
.map(|v| v + 1)
.unwrap_or(0);
f.write_char('r')?;
for _ in 0..pounds {
f.write_char('#')?;
}
f.write_char('"')?;
f.write_str(self.0)?;
f.write_char('"')?;
for _ in 0..pounds {
f.write_char('#')?;
}
Ok(())
}
}
pub fn interned_bit(v: bool) -> Interned<BitSlice> {
static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new();
// use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
}
#[derive(Copy, Clone, Debug)]
pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice);
impl BitSliceWriteWithBase<'_> {
fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1);
// TODO: optimize
let mut buf = String::with_capacity(digit_count);
for digit_index in (0..digit_count).rev() {
let mut digit = 0;
let src = self
.0
.get(digit_index * BITS_PER_DIGIT..)
.unwrap_or_default();
let src = src.get(..BITS_PER_DIGIT).unwrap_or(src);
digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src);
let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap();
if UPPER_CASE {
ch = ch.to_ascii_uppercase();
}
buf.push(ch);
}
f.pad_integral(
true,
match BITS_PER_DIGIT {
1 => "0b",
3 => "0o",
4 => "0x",
_ => "",
},
&buf,
)
}
}
impl fmt::Binary for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<1, false>(f)
}
}
impl fmt::Octal for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<3, false>(f)
}
}
impl fmt::LowerHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, false>(f)
}
}
impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_with_base::<4, true>(f)
}
}

View file

@ -0,0 +1,89 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
int::{DynIntType, DynSIntType, DynUIntType, IntTypeTrait, SIntType},
ty::{Type, Value},
};
use std::ops::RangeBounds;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
pub struct Valueless<T> {
pub ty: T,
}
impl<T: Type> Valueless<T> {
pub fn to_canonical(&self) -> Valueless<T::CanonicalType> {
Valueless {
ty: self.ty.canonical(),
}
}
pub fn from_canonical(v: Valueless<T::CanonicalType>) -> Self {
Valueless {
ty: T::from_canonical_type(v.ty),
}
}
}
mod sealed {
pub trait Sealed {}
}
pub trait ValuelessTr: sealed::Sealed {
type Type: Type<Value = Self::Value>;
type Value: Value<Type = Self::Type>;
}
impl<T> sealed::Sealed for Valueless<T> {}
impl<T: Type> ValuelessTr for Valueless<T> {
type Type = T;
type Value = T::Value;
}
impl<T: IntTypeTrait> Valueless<T> {
pub fn signum(&self) -> Valueless<SIntType<2>> {
Valueless::default()
}
pub fn as_same_width_uint(self) -> Valueless<T::SameWidthUInt> {
Valueless {
ty: self.ty.as_same_width_uint(),
}
}
pub fn as_same_width_sint(self) -> Valueless<T::SameWidthSInt> {
Valueless {
ty: self.ty.as_same_width_sint(),
}
}
pub fn as_same_value_uint(self) -> Valueless<DynUIntType> {
Valueless {
ty: self.ty.as_same_value_uint(),
}
}
pub fn as_same_value_sint(self) -> Valueless<DynSIntType> {
Valueless {
ty: self.ty.as_same_value_sint(),
}
}
pub fn concat<HighType: IntTypeTrait>(
&self,
high_part: Valueless<HighType>,
) -> Valueless<DynIntType<HighType::Signed>> {
let self_type = self.ty.canonical();
let ty = DynIntType::new(
self_type
.width
.checked_add(high_part.ty.width())
.expect("result has too many bits"),
);
Valueless { ty }
}
pub fn repeat(&self, count: usize) -> Valueless<DynIntType<T::Signed>> {
let width = self.ty.width();
let ty = DynIntType::new(width.checked_mul(count).expect("result has too many bits"));
Valueless { ty }
}
pub fn slice<I: RangeBounds<usize>>(&self, index: I) -> Valueless<DynUIntType> {
let ty = self.ty.slice(index);
Valueless { ty }
}
}

112
crates/fayalite/src/wire.rs Normal file
View file

@ -0,0 +1,112 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{Expr, ExprTrait, Flow, ToExpr},
intern::Interned,
module::{NameId, ScopedNameId},
source_location::SourceLocation,
ty::{DynCanonicalType, DynType, Type},
};
use std::fmt;
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Wire<T: Type> {
name: ScopedNameId,
source_location: SourceLocation,
ty: T,
}
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Wire")
.field("name", &self.name)
.field("ty", &self.ty)
.finish_non_exhaustive()
}
}
impl<T: Type> ToExpr for Wire<T> {
type Type = T;
fn ty(&self) -> Self::Type {
self.ty.clone()
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::new_unchecked(self.expr_enum())
}
}
impl<T: Type> Wire<T> {
pub fn canonical(&self) -> Wire<T::CanonicalType> {
let Self {
name,
source_location,
ref ty,
} = *self;
Wire {
name,
source_location,
ty: ty.canonical(),
}
}
pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> {
let Self {
name,
source_location,
ref ty,
} = *self;
Wire {
name,
source_location,
ty: ty.to_dyn(),
}
}
pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> {
let Self {
name,
source_location,
ref ty,
} = *self;
Wire {
name,
source_location,
ty: ty.canonical_dyn(),
}
}
pub fn new_unchecked(
scoped_name: ScopedNameId,
source_location: SourceLocation,
ty: T,
) -> Self {
Self {
name: scoped_name,
source_location,
ty,
}
}
pub fn source_location(&self) -> SourceLocation {
self.source_location
}
pub fn containing_module_name(&self) -> Interned<str> {
self.containing_module_name_id().0
}
pub fn containing_module_name_id(&self) -> NameId {
self.name.0
}
pub fn name(&self) -> Interned<str> {
self.name_id().0
}
pub fn name_id(&self) -> NameId {
self.name.1
}
pub fn scoped_name(&self) -> ScopedNameId {
self.name
}
pub fn flow(&self) -> Flow {
Flow::Duplex
}
pub fn must_connect_to(&self) -> bool {
true
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#[test]
fn ui() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/*.rs");
}

View file

@ -0,0 +1,14 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#[allow(unused_imports)]
use fayalite::{hdl_module, int::UInt};
#[hdl_module]
pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
#[hdl]
let m: UInt<8> = m.input();
#[hdl]
let o: UInt<8> = m.output();
}
fn main() {}

View file

@ -0,0 +1,17 @@
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
--> tests/ui/module.rs:7:26
|
7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
| ^
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
--> tests/ui/module.rs:7:35
|
7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
| ^
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
--> tests/ui/module.rs:9:9
|
9 | let m: UInt<8> = m.input();
| ^

View file

@ -0,0 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::Value;
#[derive(Value)]
union U {
a: (),
}
fn main() {}

View file

@ -0,0 +1,7 @@
error: derive(Value) can only be used on structs or enums
--> tests/ui/value_derive.rs:5:10
|
5 | #[derive(Value)]
| ^^^^^
|
= note: this error originates in the derive macro `Value` (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -0,0 +1,23 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use fayalite::{int::UInt, Value};
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)]
pub struct S<T> {
pub a: T,
b: UInt<3>,
}
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)]
pub enum E<T> {
A,
B {},
C(),
D(UInt<3>),
E { a: UInt<3> },
F(UInt<3>, UInt<3>),
G(T),
H(T, UInt<1>),
}

View file

@ -0,0 +1,914 @@
{
"types": {
"Module": {
"data": {
"$kind": "Struct",
"$constructor": "Module::new_unchecked",
"name_id()": "Visible",
"source_location()": "Visible",
"body()": "Visible",
"module_io()": "Visible",
"module_annotations()": "Visible"
},
"generics": {
"generics": "<T: BundleValue>",
"where": "T::Type: BundleType<Value = T>"
}
},
"ModuleBody": {
"data": {
"$kind": "Enum",
"Normal": "Visible",
"Extern": "Visible"
}
},
"ModuleIO": {
"data": {
"$kind": "Struct",
"$constructor": "ModuleIO::new_unchecked",
"containing_module_name_id()": "Visible",
"name()": "Visible",
"source_location()": "Visible",
"is_input()": "Visible",
"ty()": "Visible"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"InternedDynCanonicalType": {
"data": {
"$kind": "ManualImpl"
}
},
"InternedDynType": {
"data": {
"$kind": "ManualImpl"
}
},
"TypeEnum": {
"data": {
"$kind": "Enum",
"BundleType": "Visible",
"EnumType": "Visible",
"ArrayType": "Visible",
"UInt": "Visible",
"SInt": "Visible",
"Clock": "Visible",
"AsyncReset": "Visible",
"SyncReset": "Visible",
"Reset": "Visible"
}
},
"DynBundleType": {
"data": {
"$kind": "Struct",
"$constructor": "DynBundleType::new",
"fields()": "Visible"
}
},
"FieldType": {
"data": {
"$kind": "Struct",
"name": "Visible",
"flipped": "Visible",
"ty": "Visible"
},
"generics": "<T>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"DynEnumType": {
"data": {
"$kind": "Struct",
"$constructor": "DynEnumType::new",
"variants()": "Visible"
}
},
"VariantType": {
"data": {
"$kind": "Struct",
"name": "Visible",
"ty": "Visible"
},
"generics": "<T>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"ArrayType": {
"data": {
"$kind": "ManualImpl"
},
"generics": "<VA: ValueArrayOrSlice + ?Sized>",
"fold_where": "VA::ElementType: Fold<State>",
"visit_where": "VA::ElementType: Visit<State>"
},
"DynIntType": {
"data": {
"$kind": "Opaque"
},
"generics": "<Signed: GenericConstBool>"
},
"IntType": {
"data": {
"$kind": "Opaque"
},
"generics": "<Signed: GenericConstBool, const WIDTH: usize>"
},
"ClockType": {
"data": {
"$kind": "Struct"
}
},
"AsyncResetType": {
"data": {
"$kind": "Struct"
}
},
"SyncResetType": {
"data": {
"$kind": "Struct"
}
},
"ResetType": {
"data": {
"$kind": "Struct"
}
},
"SourceLocation": {
"data": {
"$kind": "Opaque"
}
},
"NameId": {
"data": {
"$kind": "Opaque"
}
},
"ScopedNameId": {
"data": {
"$kind": "Struct",
"0": "Visible",
"1": "Visible"
}
},
"ExternModuleBody": {
"data": {
"$kind": "Struct",
"verilog_name": "Visible",
"parameters": "Visible"
}
},
"ExternModuleParameter": {
"data": {
"$kind": "Struct",
"name": "Visible",
"value": "Visible"
}
},
"ExternModuleParameterValue": {
"data": {
"$kind": "Enum",
"Integer": "Visible",
"String": "Visible",
"RawVerilog": "Visible"
}
},
"NormalModuleBody": {
"data": {
"$kind": "Struct",
"body": "Visible"
}
},
"Block": {
"data": {
"$kind": "Struct",
"memories": "Visible",
"stmts": "Visible"
}
},
"Mem": {
"data": {
"$kind": "Struct",
"$constructor": "Mem::new_unchecked",
"scoped_name()": "Visible",
"source_location()": "Visible",
"array_type()": "Visible",
"initial_value()": "Opaque",
"ports()": "Visible",
"read_latency()": "Visible",
"write_latency()": "Visible",
"read_under_write()": "Visible",
"port_annotations()": "Visible",
"mem_annotations()": "Visible"
},
"generics": "<VA: ValueArrayOrSlice + ?Sized>",
"fold_where": "VA::ElementType: Fold<State>",
"visit_where": "VA::ElementType: Visit<State>"
},
"MemPort": {
"data": {
"$kind": "Struct",
"$constructor": "MemPort::new_unchecked",
"mem_name()": "Visible",
"source_location()": "Visible",
"port_name()": "Visible",
"addr_type()": "Visible",
"mem_element_type()": "Visible"
},
"generics": "<T: PortType>",
"fold_where": "T::PortType: Fold<State>",
"visit_where": "T::PortType: Visit<State>"
},
"PortName": {
"data": {
"$kind": "Struct",
"kind": "Visible",
"index": "Visible"
}
},
"PortKind": {
"data": {
"$kind": "Enum",
"ReadOnly": null,
"WriteOnly": null,
"ReadWrite": null
}
},
"Expr": {
"data": {
"$kind": "ManualImpl"
},
"generics": {
"generics": "<T: Value>",
"where": "T::Type: Type<Value = T>"
}
},
"ExprEnum": {
"data": {
"$kind": "ManualImpl"
}
},
"Literal": {
"data": {
"$kind": "ManualImpl"
},
"generics": "<T: Type>",
"fold_where": "T::CanonicalValue: Fold<State>",
"visit_where": "T::CanonicalValue: Visit<State>"
},
"DynCanonicalValue": {
"data": {
"$kind": "ManualImpl"
}
},
"ValueEnum": {
"data": {
"$kind": "Enum",
"Bundle": "Visible",
"Enum": "Visible",
"Array": "Visible",
"UInt": "Visible",
"SInt": "Visible",
"Clock": "Visible",
"AsyncReset": "Visible",
"SyncReset": "Visible",
"Reset": "Visible"
}
},
"DynBundle": {
"data": {
"$kind": "Struct",
"$constructor": "DynBundle::new",
"ty()": "Visible",
"fields()": "RefVisible"
}
},
"DynEnum": {
"data": {
"$kind": "Struct",
"$constructor": "DynEnum::new_by_index",
"ty()": "Visible",
"variant_index()": "Visible",
"variant_value()": "RefVisible"
}
},
"Array": {
"data": {
"$kind": "Struct",
"$constructor": "Array::new",
"element_ty()": "RefVisible",
"value()": "RefVisible"
},
"generics": "<VA: ValueArrayOrSlice + ?Sized>",
"fold_where": "Arc<VA>: Fold<State>, VA::ElementType: Fold<State>",
"visit_where": "Arc<VA>: Visit<State>, VA::ElementType: Visit<State>"
},
"DynInt": {
"data": {
"$kind": "Opaque"
},
"generics": "<Signed: GenericConstBool>"
},
"Clock": {
"data": {
"$kind": "Opaque"
}
},
"AsyncReset": {
"data": {
"$kind": "Opaque"
}
},
"SyncReset": {
"data": {
"$kind": "Opaque"
}
},
"Reset": {
"data": {
"$kind": "Opaque"
}
},
"ops::ArrayLiteral": {
"data": {
"$kind": "Struct",
"$constructor": "ops::ArrayLiteral::new_unchecked",
"elements()": "Visible",
"ty()": "Visible"
},
"generics": "<ArrayTy: ArrayTypeTrait>",
"fold_where": "ArrayTy: Fold<State>",
"visit_where": "ArrayTy: Visit<State>"
},
"ops::BundleLiteral": {
"data": {
"$kind": "Struct",
"$constructor": "ops::BundleLiteral::new_unchecked",
"fields()": "Visible",
"ty()": "Visible"
},
"generics": {
"generics": "<BundleTy>",
"where": "BundleTy: BundleType, BundleTy::Value: BundleValue<Type = BundleTy>"
},
"fold_where": "BundleTy: Fold<State>",
"visit_where": "BundleTy: Visit<State>"
},
"ops::EnumLiteral": {
"data": {
"$kind": "Struct",
"$constructor": "ops::EnumLiteral::new_unchecked",
"variant_value()": "Visible",
"variant_index()": "Visible",
"ty()": "Visible"
},
"generics": {
"generics": "<EnumTy>",
"where": "EnumTy: EnumType, EnumTy::Value: EnumValue<Type = EnumTy>"
},
"fold_where": "EnumTy: Fold<State>",
"visit_where": "EnumTy: Visit<State>"
},
"ops::Not": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Not::new_unchecked",
"arg": "Visible"
},
"generics": "<T: IntTypeTrait>"
},
"ops::Neg": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Neg::new_unchecked",
"arg": "Visible"
},
"generics": "<T: IntTypeTrait<Signed = ConstBool<true>>>"
},
"ops::BitAnd": {
"data": {
"$kind": "Struct",
"$constructor": "ops::BitAnd::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::BitOr": {
"data": {
"$kind": "Struct",
"$constructor": "ops::BitOr::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::BitXor": {
"data": {
"$kind": "Struct",
"$constructor": "ops::BitXor::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::Add": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Add::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::Sub": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Sub::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::Mul": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Mul::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": {
"generics": "<L, R>",
"where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
}
},
"ops::DynShl": {
"data": {
"$kind": "Struct",
"$constructor": "ops::DynShl::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait>"
},
"ops::DynShr": {
"data": {
"$kind": "Struct",
"$constructor": "ops::DynShr::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait>"
},
"ops::FixedShl": {
"data": {
"$kind": "Struct",
"$constructor": "ops::FixedShl::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait>"
},
"ops::FixedShr": {
"data": {
"$kind": "Struct",
"$constructor": "ops::FixedShr::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait>"
},
"ops::CmpLt": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpLt::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CmpLe": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpLe::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CmpGt": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpGt::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CmpGe": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpGe::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CmpEq": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpEq::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CmpNe": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CmpNe::new_unchecked",
"lhs": "Visible",
"rhs": "Visible"
},
"generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::CastInt": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastInt::new_unchecked",
"value": "Visible",
"ty()": "Visible"
},
"generics": "<FromType: IntTypeTrait, ToType: IntTypeTrait>",
"fold_where": "ToType: Fold<State>",
"visit_where": "ToType: Visit<State>"
},
"ops::Slice": {
"data": {
"$kind": "Struct",
"$constructor": "ops::Slice::new_unchecked",
"base": "Visible",
"range": "Opaque"
},
"generics": "<Base: IntTypeTrait>"
},
"ops::ReduceBitAnd": {
"data": {
"$kind": "Struct",
"$constructor": "ops::ReduceBitAnd::new_unchecked",
"arg": "Visible"
},
"generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::ReduceBitOr": {
"data": {
"$kind": "Struct",
"$constructor": "ops::ReduceBitOr::new_unchecked",
"arg": "Visible"
},
"generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::ReduceBitXor": {
"data": {
"$kind": "Struct",
"$constructor": "ops::ReduceBitXor::new_unchecked",
"arg": "Visible"
},
"generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
},
"ops::FieldAccess": {
"data": {
"$kind": "Struct",
"$constructor": "ops::FieldAccess::new_unchecked",
"base()": "Visible",
"name()": "Visible"
},
"generics": {
"generics": "<Base, Field>",
"where": "Base: BundleType, Base::Value: BundleValue, Field: Type"
}
},
"ops::VariantAccess": {
"data": {
"$kind": "Struct",
"$constructor": "ops::VariantAccess::new_unchecked",
"base()": "Visible",
"variant_index()": "Visible"
},
"generics": {
"generics": "<T, VariantTy>",
"where": "T: EnumType, T::Value: EnumValue, VariantTy: Type"
}
},
"ops::ArrayIndex": {
"data": {
"$kind": "Struct",
"$constructor": "ops::ArrayIndex::new_unchecked",
"base()": "Visible",
"index()": "Visible"
},
"generics": "<ElementType: Type>"
},
"ops::DynArrayIndex": {
"data": {
"$kind": "Struct",
"$constructor": "ops::DynArrayIndex::new_unchecked",
"base()": "Visible",
"index()": "Visible"
},
"generics": "<ElementType: Type>"
},
"ops::CastToBits": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastToBits::new_unchecked",
"value()": "Visible"
}
},
"ops::CastBitsTo": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastBitsTo::new_unchecked",
"value()": "Visible",
"ty()": "Visible"
},
"generics": {
"generics": "<T>",
"where": "T: Type, T::Value: Value<Type = T>"
},
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"ops::CastBitToClock": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastBitToClock::new_unchecked",
"value": "Visible"
}
},
"ops::CastBitToSyncReset": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastBitToSyncReset::new_unchecked",
"value": "Visible"
}
},
"ops::CastBitToAsyncReset": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastBitToAsyncReset::new_unchecked",
"value": "Visible"
}
},
"ops::CastSyncResetToReset": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastSyncResetToReset::new_unchecked",
"value": "Visible"
}
},
"ops::CastAsyncResetToReset": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastAsyncResetToReset::new_unchecked",
"value": "Visible"
}
},
"ops::CastClockToBit": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastClockToBit::new_unchecked",
"value": "Visible"
},
"generics": "<ToType: FixedOrDynIntType<1>>"
},
"ops::CastSyncResetToBit": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastSyncResetToBit::new_unchecked",
"value": "Visible"
},
"generics": "<ToType: FixedOrDynIntType<1>>"
},
"ops::CastAsyncResetToBit": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastAsyncResetToBit::new_unchecked",
"value": "Visible"
},
"generics": "<ToType: FixedOrDynIntType<1>>"
},
"ops::CastResetToBit": {
"data": {
"$kind": "Struct",
"$constructor": "ops::CastResetToBit::new_unchecked",
"value": "Visible"
},
"generics": "<ToType: FixedOrDynIntType<1>>"
},
"BlockId": {
"data": {
"$kind": "Opaque"
}
},
"ReadUnderWrite": {
"data": {
"$kind": "Opaque"
}
},
"Instance": {
"data": {
"$kind": "Struct",
"$constructor": "Instance::new_unchecked",
"scoped_name()": "Visible",
"instantiated()": "Visible",
"source_location()": "Visible"
},
"generics": {
"generics": "<T: BundleValue>",
"where": "T::Type: BundleType<Value = T>"
}
},
"Reg": {
"data": {
"$kind": "Struct",
"$constructor": "Reg::new_unchecked",
"scoped_name()": "Visible",
"source_location()": "Visible",
"ty()": "Visible",
"clock_domain()": "Visible",
"init()": "Visible"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"Wire": {
"data": {
"$kind": "Struct",
"$constructor": "Wire::new_unchecked",
"scoped_name()": "Visible",
"source_location()": "Visible",
"ty()": "Visible"
},
"generics": "<T: Type>",
"fold_where": "T: Fold<State>",
"visit_where": "T: Visit<State>"
},
"Stmt": {
"data": {
"$kind": "Enum",
"Connect": "Visible",
"If": "Visible",
"Match": "Visible",
"Declaration": "Visible"
}
},
"StmtDeclaration": {
"data": {
"$kind": "Enum",
"Wire": "Visible",
"Reg": "Visible",
"Instance": "Visible"
}
},
"StmtConnect": {
"data": {
"$kind": "Struct",
"lhs": "Visible",
"rhs": "Visible",
"source_location": "Visible"
}
},
"StmtIf": {
"data": {
"$kind": "Struct",
"cond": "Visible",
"source_location": "Visible",
"blocks": "Visible"
}
},
"StmtInstance": {
"data": {
"$kind": "Struct",
"annotations": "Visible",
"instance": "Visible"
}
},
"StmtMatch": {
"data": {
"$kind": "Struct",
"expr": "Visible",
"source_location": "Visible",
"blocks": "Visible"
}
},
"StmtReg": {
"data": {
"$kind": "Struct",
"annotations": "Visible",
"reg": "Visible"
}
},
"StmtWire": {
"data": {
"$kind": "Struct",
"annotations": "Visible",
"wire": "Visible"
}
},
"AnnotatedModuleIO": {
"data": {
"$kind": "Struct",
"annotations": "Visible",
"module_io": "Visible"
}
},
"TargetedAnnotation": {
"data": {
"$kind": "Struct",
"$constructor": "TargetedAnnotation::new",
"target()": "Visible",
"annotation()": "RefVisible"
}
},
"Annotation": {
"data": {
"$kind": "Enum",
"DontTouch": null,
"CustomFirrtl": "Visible"
}
},
"CustomFirrtlAnnotation": {
"data": {
"$kind": "Opaque"
}
},
"Target": {
"data": {
"$kind": "Enum",
"Base": "Visible",
"Child": "Visible"
}
},
"TargetBase": {
"data": {
"$kind": "Enum",
"ModuleIO": "Visible",
"MemPort": "Visible",
"Reg": "Visible",
"Wire": "Visible",
"Instance": "Visible"
}
},
"TargetChild": {
"data": {
"$kind": "Struct",
"$constructor": "TargetChild::new",
"parent()": "Visible",
"path_element()": "Visible"
}
},
"TargetPathBundleField": {
"data": {
"$kind": "Struct",
"name": "Visible"
}
},
"TargetPathArrayElement": {
"data": {
"$kind": "Struct",
"index": "Visible"
}
},
"TargetPathDynArrayElement": {
"data": {
"$kind": "Struct"
}
},
"TargetPathElement": {
"data": {
"$kind": "Enum",
"BundleField": "Visible",
"ArrayElement": "Visible",
"DynArrayElement": "Visible"
}
}
}
}