forked from libre-chip/fayalite
initial public commit
This commit is contained in:
commit
0b958e7852
56 changed files with 30235 additions and 0 deletions
24
crates/fayalite/Cargo.toml
Normal file
24
crates/fayalite/Cargo.toml
Normal 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
15
crates/fayalite/build.rs
Normal 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());
|
||||
}
|
196
crates/fayalite/src/annotations.rs
Normal file
196
crates/fayalite/src/annotations.rs
Normal 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
|
||||
}
|
||||
}
|
729
crates/fayalite/src/array.rs
Normal file
729
crates/fayalite/src/array.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
796
crates/fayalite/src/bundle.rs
Normal file
796
crates/fayalite/src/bundle.rs
Normal 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));
|
156
crates/fayalite/src/clock.rs
Normal file
156
crates/fayalite/src/clock.rs
Normal 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()
|
||||
}
|
||||
}
|
620
crates/fayalite/src/enum_.rs
Normal file
620
crates/fayalite/src/enum_.rs
Normal 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
1090
crates/fayalite/src/expr.rs
Normal file
File diff suppressed because it is too large
Load diff
1590
crates/fayalite/src/expr/ops.rs
Normal file
1590
crates/fayalite/src/expr/ops.rs
Normal file
File diff suppressed because it is too large
Load diff
2470
crates/fayalite/src/firrtl.rs
Normal file
2470
crates/fayalite/src/firrtl.rs
Normal file
File diff suppressed because it is too large
Load diff
1176
crates/fayalite/src/int.rs
Normal file
1176
crates/fayalite/src/int.rs
Normal file
File diff suppressed because it is too large
Load diff
1076
crates/fayalite/src/intern.rs
Normal file
1076
crates/fayalite/src/intern.rs
Normal file
File diff suppressed because it is too large
Load diff
138
crates/fayalite/src/intern/type_map.rs
Normal file
138
crates/fayalite/src/intern/type_map.rs
Normal 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()
|
||||
}
|
||||
}
|
27
crates/fayalite/src/lib.rs
Normal file
27
crates/fayalite/src/lib.rs
Normal 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;
|
1094
crates/fayalite/src/memory.rs
Normal file
1094
crates/fayalite/src/memory.rs
Normal file
File diff suppressed because it is too large
Load diff
2615
crates/fayalite/src/module.rs
Normal file
2615
crates/fayalite/src/module.rs
Normal file
File diff suppressed because it is too large
Load diff
5
crates/fayalite/src/module/transform.rs
Normal file
5
crates/fayalite/src/module/transform.rs
Normal 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;
|
654
crates/fayalite/src/module/transform/simplify_enums.rs
Normal file
654
crates/fayalite/src/module/transform/simplify_enums.rs
Normal 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(),
|
||||
})
|
||||
}
|
940
crates/fayalite/src/module/transform/simplify_memories.rs
Normal file
940
crates/fayalite/src/module/transform/simplify_memories.rs
Normal 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(¤t_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 {})
|
||||
}
|
462
crates/fayalite/src/module/transform/visit.rs
Normal file
462
crates/fayalite/src/module/transform/visit.rs
Normal 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
151
crates/fayalite/src/reg.rs
Normal 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
|
||||
}
|
||||
}
|
359
crates/fayalite/src/reset.rs
Normal file
359
crates/fayalite/src/reset.rs
Normal 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>;
|
||||
}
|
||||
}
|
203
crates/fayalite/src/source_location.rs
Normal file
203
crates/fayalite/src/source_location.rs
Normal 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
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
267
crates/fayalite/src/util.rs
Normal 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)
|
||||
}
|
||||
}
|
89
crates/fayalite/src/valueless.rs
Normal file
89
crates/fayalite/src/valueless.rs
Normal 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
112
crates/fayalite/src/wire.rs
Normal 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
|
||||
}
|
||||
}
|
3065
crates/fayalite/tests/module.rs
Normal file
3065
crates/fayalite/tests/module.rs
Normal file
File diff suppressed because it is too large
Load diff
7
crates/fayalite/tests/ui.rs
Normal file
7
crates/fayalite/tests/ui.rs
Normal 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");
|
||||
}
|
14
crates/fayalite/tests/ui/module.rs
Normal file
14
crates/fayalite/tests/ui/module.rs
Normal 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() {}
|
17
crates/fayalite/tests/ui/module.stderr
Normal file
17
crates/fayalite/tests/ui/module.stderr
Normal 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();
|
||||
| ^
|
10
crates/fayalite/tests/ui/value_derive.rs
Normal file
10
crates/fayalite/tests/ui/value_derive.rs
Normal 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() {}
|
7
crates/fayalite/tests/ui/value_derive.stderr
Normal file
7
crates/fayalite/tests/ui/value_derive.stderr
Normal 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)
|
23
crates/fayalite/tests/value_derive.rs
Normal file
23
crates/fayalite/tests/value_derive.rs
Normal 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>),
|
||||
}
|
914
crates/fayalite/visit_types.json
Normal file
914
crates/fayalite/visit_types.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue