forked from libre-chip/fayalite
919 lines
32 KiB
Rust
919 lines
32 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
|
|
use crate::{
|
|
expr::{
|
|
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType,
|
|
Valueless,
|
|
ops::{ArrayLiteral, BundleLiteral},
|
|
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
|
|
},
|
|
int::{Bool, DynSize},
|
|
intern::{Intern, InternSlice, Interned},
|
|
sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType},
|
|
source_location::SourceLocation,
|
|
ty::{
|
|
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
|
|
OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type,
|
|
TypeProperties, TypeWithDeref, impl_match_variant_as_self,
|
|
},
|
|
util::HashMap,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{borrow::Cow, fmt, marker::PhantomData};
|
|
|
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct BundleField {
|
|
pub name: Interned<str>,
|
|
pub flipped: bool,
|
|
pub ty: CanonicalType,
|
|
}
|
|
|
|
impl BundleField {
|
|
pub fn fmt_debug_in_struct(self, field_offset: usize) -> FmtDebugInStruct {
|
|
FmtDebugInStruct {
|
|
field: self,
|
|
field_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct FmtDebugInStruct {
|
|
field: BundleField,
|
|
field_offset: usize,
|
|
}
|
|
|
|
impl fmt::Debug for FmtDebugInStruct {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
field: BundleField { name, flipped, 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 fmt::Display for FmtDebugInStruct {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Eq)]
|
|
struct BundleImpl {
|
|
fields: Interned<[BundleField]>,
|
|
name_indexes: HashMap<Interned<str>, usize>,
|
|
field_offsets: Interned<[OpaqueSimValueSize]>,
|
|
type_properties: TypeProperties,
|
|
}
|
|
|
|
impl std::hash::Hash for BundleImpl {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
self.fields.hash(state);
|
|
}
|
|
}
|
|
|
|
impl PartialEq for BundleImpl {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.fields == other.fields
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Debug for BundleImpl {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str("Bundle ")?;
|
|
f.debug_set()
|
|
.entries(self.fields.iter().enumerate().map(|(index, field)| {
|
|
field.fmt_debug_in_struct(self.field_offsets[index].bit_width)
|
|
}))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub struct Bundle(Interned<BundleImpl>);
|
|
|
|
impl std::fmt::Debug for Bundle {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct BundleTypePropertiesBuilder(TypeProperties);
|
|
|
|
impl BundleTypePropertiesBuilder {
|
|
#[must_use]
|
|
pub const fn new() -> Self {
|
|
Self(TypeProperties {
|
|
is_passive: true,
|
|
is_storable: true,
|
|
is_castable_from_bits: true,
|
|
bit_width: 0,
|
|
sim_only_values_len: 0,
|
|
})
|
|
}
|
|
pub const fn clone(&self) -> Self {
|
|
Self(self.0)
|
|
}
|
|
#[must_use]
|
|
pub const fn field(self, flipped: bool, field_props: TypeProperties) -> Self {
|
|
let Some(OpaqueSimValueSize {
|
|
bit_width,
|
|
sim_only_values_len,
|
|
}) = self.0.size().checked_add(field_props.size())
|
|
else {
|
|
panic!("bundle is too big: size overflowed");
|
|
};
|
|
if flipped {
|
|
Self(TypeProperties {
|
|
is_passive: false,
|
|
is_storable: false,
|
|
is_castable_from_bits: false,
|
|
bit_width,
|
|
sim_only_values_len,
|
|
})
|
|
} else {
|
|
Self(TypeProperties {
|
|
is_passive: self.0.is_passive & field_props.is_passive,
|
|
is_storable: self.0.is_storable & field_props.is_storable,
|
|
is_castable_from_bits: self.0.is_castable_from_bits
|
|
& field_props.is_castable_from_bits,
|
|
bit_width,
|
|
sim_only_values_len,
|
|
})
|
|
}
|
|
}
|
|
pub const fn finish(self) -> TypeProperties {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
impl Default for BundleTypePropertiesBuilder {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Bundle {
|
|
#[track_caller]
|
|
pub fn new(fields: Interned<[BundleField]>) -> Self {
|
|
let mut name_indexes = HashMap::with_capacity_and_hasher(fields.len(), Default::default());
|
|
let mut field_offsets = Vec::with_capacity(fields.len());
|
|
let mut type_props_builder = BundleTypePropertiesBuilder::new();
|
|
for (index, &BundleField { name, flipped, 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(type_props_builder.0.size());
|
|
type_props_builder = type_props_builder.field(flipped, ty.type_properties());
|
|
}
|
|
Self(Intern::intern_sized(BundleImpl {
|
|
fields,
|
|
name_indexes,
|
|
field_offsets: Intern::intern_owned(field_offsets),
|
|
type_properties: type_props_builder.finish(),
|
|
}))
|
|
}
|
|
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
|
|
&self.0.name_indexes
|
|
}
|
|
pub fn field_by_name(&self, name: Interned<str>) -> Option<BundleField> {
|
|
Some(self.0.fields[*self.0.name_indexes.get(&name)?])
|
|
}
|
|
pub fn field_offsets(self) -> Interned<[OpaqueSimValueSize]> {
|
|
self.0.field_offsets
|
|
}
|
|
pub fn type_properties(self) -> TypeProperties {
|
|
self.0.type_properties
|
|
}
|
|
pub fn can_connect(self, rhs: Self) -> bool {
|
|
if self.0.fields.len() != rhs.0.fields.len() {
|
|
return false;
|
|
}
|
|
for (
|
|
&BundleField {
|
|
name: lhs_name,
|
|
flipped: lhs_flipped,
|
|
ty: lhs_ty,
|
|
},
|
|
&BundleField {
|
|
name: rhs_name,
|
|
flipped: rhs_flipped,
|
|
ty: rhs_ty,
|
|
},
|
|
) in self.0.fields.iter().zip(rhs.0.fields.iter())
|
|
{
|
|
if lhs_name != rhs_name || lhs_flipped != rhs_flipped || !lhs_ty.can_connect(rhs_ty) {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Type for Bundle {
|
|
type BaseType = Bundle;
|
|
type MaskType = Bundle;
|
|
type SimValue = OpaqueSimValue;
|
|
impl_match_variant_as_self!();
|
|
fn mask_type(&self) -> Self::MaskType {
|
|
Self::new(Interned::from_iter(self.0.fields.into_iter().map(
|
|
|BundleField { name, flipped, ty }| BundleField {
|
|
name,
|
|
flipped,
|
|
ty: ty.mask_type(),
|
|
},
|
|
)))
|
|
}
|
|
fn canonical(&self) -> CanonicalType {
|
|
CanonicalType::Bundle(*self)
|
|
}
|
|
#[track_caller]
|
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
|
let CanonicalType::Bundle(bundle) = canonical_type else {
|
|
panic!("expected bundle");
|
|
};
|
|
bundle
|
|
}
|
|
fn source_location() -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue {
|
|
assert_eq!(self.type_properties().size(), opaque.size());
|
|
opaque.to_owned()
|
|
}
|
|
fn sim_value_clone_from_opaque(
|
|
&self,
|
|
value: &mut Self::SimValue,
|
|
opaque: OpaqueSimValueSlice<'_>,
|
|
) {
|
|
assert_eq!(self.type_properties().size(), opaque.size());
|
|
assert_eq!(value.size(), opaque.size());
|
|
value.clone_from_slice(opaque);
|
|
}
|
|
fn sim_value_to_opaque<'w>(
|
|
&self,
|
|
value: &Self::SimValue,
|
|
writer: OpaqueSimValueWriter<'w>,
|
|
) -> OpaqueSimValueWritten<'w> {
|
|
assert_eq!(self.type_properties().size(), writer.size());
|
|
assert_eq!(value.size(), writer.size());
|
|
writer.fill_cloned_from_slice(value.as_slice())
|
|
}
|
|
}
|
|
|
|
pub trait BundleType: Type<BaseType = Bundle> {
|
|
type Builder: Default;
|
|
fn fields(&self) -> Interned<[BundleField]>;
|
|
}
|
|
|
|
pub struct BundleSimValueFromOpaque<'a> {
|
|
fields: std::slice::Iter<'static, BundleField>,
|
|
opaque: OpaqueSimValueSlice<'a>,
|
|
}
|
|
|
|
impl<'a> BundleSimValueFromOpaque<'a> {
|
|
#[track_caller]
|
|
pub fn new<T: BundleType>(bundle_ty: T, opaque: OpaqueSimValueSlice<'a>) -> Self {
|
|
let fields = bundle_ty.fields();
|
|
assert_eq!(
|
|
opaque.size(),
|
|
fields
|
|
.iter()
|
|
.map(|BundleField { ty, .. }| ty.size())
|
|
.sum::<OpaqueSimValueSize>()
|
|
);
|
|
Self {
|
|
fields: Interned::into_inner(fields).iter(),
|
|
opaque,
|
|
}
|
|
}
|
|
#[track_caller]
|
|
fn field_ty_and_opaque<T: Type>(&mut self) -> (T, OpaqueSimValueSlice<'a>) {
|
|
let Some(&BundleField {
|
|
name: _,
|
|
flipped: _,
|
|
ty,
|
|
}) = self.fields.next()
|
|
else {
|
|
panic!("tried to read too many fields from BundleSimValueFromBits");
|
|
};
|
|
let (field_opaque, rest) = self.opaque.split_at(ty.size());
|
|
self.opaque = rest;
|
|
(T::from_canonical(ty), field_opaque)
|
|
}
|
|
#[track_caller]
|
|
pub fn field_from_opaque<T: Type>(&mut self) -> SimValue<T> {
|
|
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
|
|
SimValue::from_opaque(field_ty, field_opaque.to_owned())
|
|
}
|
|
#[track_caller]
|
|
pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) {
|
|
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
|
|
assert_eq!(field_ty, field_value.ty());
|
|
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
|
|
}
|
|
}
|
|
|
|
pub struct BundleSimValueToOpaque<'a> {
|
|
fields: std::slice::Iter<'static, BundleField>,
|
|
writer: OpaqueSimValueWriter<'a>,
|
|
}
|
|
|
|
impl<'a> BundleSimValueToOpaque<'a> {
|
|
#[track_caller]
|
|
pub fn new<T: BundleType>(bundle_ty: T, writer: OpaqueSimValueWriter<'a>) -> Self {
|
|
let fields = bundle_ty.fields();
|
|
assert_eq!(
|
|
writer.size(),
|
|
fields
|
|
.iter()
|
|
.map(|BundleField { ty, .. }| ty.size())
|
|
.sum::<OpaqueSimValueSize>()
|
|
);
|
|
Self {
|
|
fields: Interned::into_inner(fields).iter(),
|
|
writer,
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub fn field<T: Type>(&mut self, field_value: &SimValue<T>) {
|
|
let Some(&BundleField {
|
|
name: _,
|
|
flipped: _,
|
|
ty,
|
|
}) = self.fields.next()
|
|
else {
|
|
panic!("tried to write too many fields with BundleSimValueToOpaque");
|
|
};
|
|
assert_eq!(T::from_canonical(ty), field_value.ty());
|
|
self.writer.fill_prefix_with(ty.size(), |writer| {
|
|
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
|
|
});
|
|
}
|
|
#[track_caller]
|
|
pub fn finish(mut self) -> OpaqueSimValueWritten<'a> {
|
|
assert_eq!(
|
|
self.fields.next(),
|
|
None,
|
|
"wrote too few fields with BundleSimValueToOpaque"
|
|
);
|
|
self.writer
|
|
.fill_cloned_from_slice(OpaqueSimValueSlice::empty())
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct NoBuilder;
|
|
|
|
impl BundleType for Bundle {
|
|
type Builder = NoBuilder;
|
|
fn fields(&self) -> Interned<[BundleField]> {
|
|
self.0.fields
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct TupleBuilder<T>(T);
|
|
|
|
macro_rules! impl_tuple_builder_fields {
|
|
(
|
|
@impl
|
|
{
|
|
}
|
|
[
|
|
$({
|
|
#[type_var($head_type_var:ident)]
|
|
#[field($head_field:ident)]
|
|
#[var($head_var:ident)]
|
|
})*
|
|
]
|
|
{
|
|
#[type_var($cur_type_var:ident)]
|
|
#[field($cur_field:ident)]
|
|
#[var($cur_var:ident)]
|
|
}
|
|
[
|
|
$({
|
|
#[type_var($tail_type_var:ident)]
|
|
#[field($tail_field:ident)]
|
|
#[var($tail_var:ident)]
|
|
})*
|
|
]
|
|
) => {
|
|
impl<
|
|
$($head_type_var,)*
|
|
$($tail_type_var,)*
|
|
> TupleBuilder<(
|
|
$($head_type_var,)*
|
|
(),
|
|
$($tail_type_var,)*
|
|
)>
|
|
{
|
|
pub fn $cur_field<$cur_type_var: Type>(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
|
|
$($head_type_var,)*
|
|
Expr<$cur_type_var>,
|
|
$($tail_type_var,)*
|
|
)>
|
|
{
|
|
let ($($head_var,)* _, $($tail_var,)*) = self.0;
|
|
TupleBuilder(($($head_var,)* $cur_var.to_expr(), $($tail_var,)*))
|
|
}
|
|
}
|
|
};
|
|
($global:tt [$($head:tt)*] $cur:tt [$next:tt $($tail:tt)*]) => {
|
|
impl_tuple_builder_fields!(@impl $global [$($head)*] $cur [$next $($tail)*]);
|
|
impl_tuple_builder_fields!($global [$($head)* $cur] $next [$($tail)*]);
|
|
};
|
|
($global:tt [$($head:tt)*] $cur:tt []) => {
|
|
impl_tuple_builder_fields!(@impl $global [$($head)*] $cur []);
|
|
};
|
|
($global:tt [$cur:tt $($tail:tt)*]) => {
|
|
impl_tuple_builder_fields!($global [] $cur [$($tail)*]);
|
|
};
|
|
($global:tt []) => {};
|
|
}
|
|
|
|
macro_rules! get_unit_ty {
|
|
($($tt:tt)*) => {
|
|
()
|
|
};
|
|
}
|
|
|
|
macro_rules! impl_tuples {
|
|
(
|
|
[$({
|
|
#[
|
|
num = $num:tt,
|
|
field = $field:ident,
|
|
ty = $ty_var:ident: $Ty:ident,
|
|
lhs = $lhs_var:ident: $Lhs:ident,
|
|
rhs = $rhs_var:ident: $Rhs:ident
|
|
]
|
|
$var:ident: $T:ident
|
|
})*]
|
|
[]
|
|
) => {
|
|
impl_tuple_builder_fields! {
|
|
{}
|
|
[$({
|
|
#[type_var($T)]
|
|
#[field($field)]
|
|
#[var($var)]
|
|
})*]
|
|
}
|
|
impl<$($T: Type,)*> Type for ($($T,)*) {
|
|
type BaseType = Bundle;
|
|
type MaskType = ($($T::MaskType,)*);
|
|
type SimValue = ($(SimValue<$T>,)*);
|
|
type MatchVariant = ($(Expr<$T>,)*);
|
|
type MatchActiveScope = ();
|
|
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
|
|
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
|
|
fn match_variants(
|
|
this: Expr<Self>,
|
|
source_location: SourceLocation,
|
|
) -> Self::MatchVariantsIter {
|
|
let _ = this;
|
|
let _ = source_location;
|
|
std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*)))
|
|
}
|
|
fn mask_type(&self) -> Self::MaskType {
|
|
#![allow(clippy::unused_unit)]
|
|
let ($($var,)*) = self;
|
|
($($var.mask_type(),)*)
|
|
}
|
|
fn canonical(&self) -> CanonicalType {
|
|
Bundle::new(self.fields()).canonical()
|
|
}
|
|
#[track_caller]
|
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
|
#![allow(clippy::unused_unit)]
|
|
let CanonicalType::Bundle(bundle) = canonical_type else {
|
|
panic!("expected bundle");
|
|
};
|
|
let [$($var,)*] = *bundle.fields() else {
|
|
panic!("bundle has wrong number of fields");
|
|
};
|
|
$(let BundleField { name, flipped, ty } = $var;
|
|
assert_eq!(&*name, stringify!($num));
|
|
assert!(!flipped);
|
|
let $var = $T::from_canonical(ty);)*
|
|
($($var,)*)
|
|
}
|
|
fn source_location() -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue {
|
|
#![allow(unused_mut, unused_variables)]
|
|
let mut v = BundleSimValueFromOpaque::new(*self, opaque);
|
|
$(let $var = v.field_from_opaque();)*
|
|
($($var,)*)
|
|
}
|
|
fn sim_value_clone_from_opaque(
|
|
&self,
|
|
value: &mut Self::SimValue,
|
|
opaque: OpaqueSimValueSlice<'_>,
|
|
) {
|
|
#![allow(unused_mut, unused_variables)]
|
|
let mut v = BundleSimValueFromOpaque::new(*self, opaque);
|
|
let ($($var,)*) = value;
|
|
$(v.field_clone_from_opaque($var);)*
|
|
}
|
|
fn sim_value_to_opaque<'w>(
|
|
&self,
|
|
value: &Self::SimValue,
|
|
writer: OpaqueSimValueWriter<'w>,
|
|
) -> OpaqueSimValueWritten<'w> {
|
|
#![allow(unused_mut, unused_variables)]
|
|
let mut v = BundleSimValueToOpaque::new(*self, writer);
|
|
let ($($var,)*) = value;
|
|
$(v.field($var);)*
|
|
v.finish()
|
|
}
|
|
}
|
|
impl<$($T: Type,)*> BundleType for ($($T,)*) {
|
|
type Builder = TupleBuilder<($(get_unit_ty!($T),)*)>;
|
|
fn fields(&self) -> Interned<[BundleField]> {
|
|
let ($($var,)*) = self;
|
|
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*].intern_slice()
|
|
}
|
|
}
|
|
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
|
|
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
|
let _ = this;
|
|
Interned::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized())
|
|
}
|
|
}
|
|
impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
|
|
const TYPE: Self = ($($T::TYPE,)*);
|
|
const MASK_TYPE: Self::MaskType = ($($T::MASK_TYPE,)*);
|
|
const TYPE_PROPERTIES: TypeProperties = {
|
|
let builder = BundleTypePropertiesBuilder::new();
|
|
$(let builder = builder.field(false, $T::TYPE_PROPERTIES);)*
|
|
builder.finish()
|
|
};
|
|
const MASK_TYPE_PROPERTIES: TypeProperties = {
|
|
let builder = BundleTypePropertiesBuilder::new();
|
|
$(let builder = builder.field(false, $T::MASK_TYPE_PROPERTIES);)*
|
|
builder.finish()
|
|
};
|
|
}
|
|
impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*)
|
|
where
|
|
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
{
|
|
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
|
|
let ($($var,)*) = this;
|
|
Cow::Owned(($($var.to_sim_value(),)*))
|
|
}
|
|
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
|
|
let ($($var,)*) = this;
|
|
Cow::Owned(($($var.into_sim_value(),)*))
|
|
}
|
|
}
|
|
impl<$($T: ValueType,)*> ValueType for ($($T,)*)
|
|
where
|
|
ValueCategoryValue: ValueCategoryCommon<($($T::ValueCategory,)*)>,
|
|
{
|
|
type Type = ($($T::Type,)*);
|
|
type ValueCategory = <ValueCategoryValue as ValueCategoryCommon<($($T::ValueCategory,)*)>>::Common;
|
|
fn ty(&self) -> Self::Type {
|
|
let ($($var,)*) = self;
|
|
($($var.ty(),)*)
|
|
}
|
|
}
|
|
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*)
|
|
where
|
|
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
{
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
let ($($var,)*) = self;
|
|
$(let $var = $var.to_expr();)*
|
|
let ty = ($($var.ty(),)*);
|
|
let field_values = [$(Expr::canonical($var)),*];
|
|
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
|
}
|
|
}
|
|
impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> {
|
|
type Type = ($($T,)*);
|
|
type ValueCategory = ValueCategoryExpr;
|
|
fn ty(&self) -> Self::Type {
|
|
let ($($var,)*) = self.0;
|
|
($($var.ty(),)*)
|
|
}
|
|
}
|
|
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
let ($($var,)*) = self.0;
|
|
let ty = ($($var.ty(),)*);
|
|
let field_values = [$(Expr::canonical($var)),*];
|
|
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
|
|
}
|
|
}
|
|
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<CanonicalType> for ($($T,)*) {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
|
|
SimValue::into_canonical(ToSimValueWithType::<Bundle>::to_sim_value_with_type(self, Bundle::from_canonical(ty)))
|
|
}
|
|
#[track_caller]
|
|
fn into_sim_value_with_type(self, ty: CanonicalType) -> SimValue<CanonicalType>
|
|
{
|
|
SimValue::into_canonical(ToSimValueWithType::<Bundle>::into_sim_value_with_type(self, Bundle::from_canonical(ty)))
|
|
}
|
|
}
|
|
impl<$($T: ToSimValueWithType<CanonicalType>,)*> ToSimValueWithType<Bundle> for ($($T,)*) {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue<Bundle> {
|
|
let ($($var,)*) = self;
|
|
let [$($ty_var,)*] = *ty.fields() else {
|
|
panic!("bundle has wrong number of fields");
|
|
};
|
|
$(let $var = $var.to_sim_value_with_type($ty_var.ty);)*
|
|
ToSimValueWithType::into_sim_value_with_type(($($var,)*), ty)
|
|
}
|
|
#[track_caller]
|
|
fn into_sim_value_with_type(self, ty: Bundle) -> SimValue<Bundle> {
|
|
#![allow(unused_mut)]
|
|
#![allow(clippy::unused_unit)]
|
|
let ($($var,)*) = self;
|
|
let [$($ty_var,)*] = *ty.fields() else {
|
|
panic!("bundle has wrong number of fields");
|
|
};
|
|
let mut opaque = OpaqueSimValue::empty();
|
|
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
|
|
assert_eq!($var.ty(), $ty_var.ty);
|
|
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
|
|
)*
|
|
SimValue::from_opaque(ty, opaque)
|
|
}
|
|
}
|
|
impl<$($T: ToSimValueWithType<$Ty>, $Ty: Type,)*> ToSimValueWithType<($($Ty,)*)> for ($($T,)*) {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
|
|
let ($($var,)*) = self;
|
|
let ($($ty_var,)*) = ty;
|
|
$(let $var = $var.to_sim_value_with_type($ty_var);)*
|
|
SimValue::from_value(ty, ($($var,)*))
|
|
}
|
|
#[track_caller]
|
|
fn into_sim_value_with_type(self, ty: ($($Ty,)*)) -> SimValue<($($Ty,)*)> {
|
|
let ($($var,)*) = self;
|
|
let ($($ty_var,)*) = ty;
|
|
$(let $var = $var.into_sim_value_with_type($ty_var);)*
|
|
SimValue::from_value(ty, ($($var,)*))
|
|
}
|
|
}
|
|
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*)
|
|
where
|
|
Self: ValueType<Type = ($($T::Type,)*)>,
|
|
{
|
|
#[track_caller]
|
|
fn to_sim_value(&self) -> SimValue<Self::Type> {
|
|
let ($($var,)*) = self;
|
|
$(let $var = $var.to_sim_value();)*
|
|
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
|
}
|
|
#[track_caller]
|
|
fn into_sim_value(self) -> SimValue<Self::Type> {
|
|
let ($($var,)*) = self;
|
|
$(let $var = $var.to_sim_value();)*
|
|
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
|
|
}
|
|
}
|
|
impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) {
|
|
#[track_caller]
|
|
fn cmp_value_eq(
|
|
lhs: Self,
|
|
lhs_value: Cow<'_, Self::SimValue>,
|
|
rhs: ($($Rhs,)*),
|
|
rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>,
|
|
) -> bool {
|
|
#![allow(unused_variables)]
|
|
let ($($lhs_var,)*) = &*lhs_value;
|
|
let ($($rhs_var,)*) = &*rhs_value;
|
|
let retval = true;
|
|
$(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)*
|
|
retval
|
|
}
|
|
|
|
#[track_caller]
|
|
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
|
let ($($lhs_var,)*) = *lhs;
|
|
let ($($rhs_var,)*) = *rhs;
|
|
ArrayLiteral::<Bool, DynSize>::new(
|
|
Bool,
|
|
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]),
|
|
)
|
|
.cast_to_bits()
|
|
.all_one_bits()
|
|
}
|
|
|
|
#[track_caller]
|
|
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
|
|
let ($($lhs_var,)*) = *lhs;
|
|
let ($($rhs_var,)*) = *rhs;
|
|
ArrayLiteral::<Bool, DynSize>::new(
|
|
Bool,
|
|
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]),
|
|
)
|
|
.cast_to_bits()
|
|
.any_one_bits()
|
|
}
|
|
|
|
#[track_caller]
|
|
fn cmp_valueless_eq(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
|
|
let ($($lhs_var,)*) = lhs.ty();
|
|
let ($($rhs_var,)*) = rhs.ty();
|
|
// let them check that the types can be compared
|
|
$($Lhs::cmp_valueless_eq(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
|
|
Valueless::new(Bool)
|
|
}
|
|
|
|
#[track_caller]
|
|
fn cmp_valueless_ne(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
|
|
let ($($lhs_var,)*) = lhs.ty();
|
|
let ($($rhs_var,)*) = rhs.ty();
|
|
// let them check that the types can be compared
|
|
$($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
|
|
Valueless::new(Bool)
|
|
}
|
|
}
|
|
impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {}
|
|
};
|
|
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
|
impl_tuples!([$($lhs)*] []);
|
|
impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]);
|
|
};
|
|
}
|
|
|
|
impl_tuples! {
|
|
[] [
|
|
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
|
|
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
|
|
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
|
|
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
|
|
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
|
|
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
|
|
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
|
|
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
|
|
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
|
|
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
|
|
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
|
|
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
|
|
]
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
|
|
type BaseType = Bundle;
|
|
type MaskType = ();
|
|
type SimValue = PhantomData<T>;
|
|
type MatchVariant = PhantomData<T>;
|
|
type MatchActiveScope = ();
|
|
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
|
|
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
|
|
fn match_variants(
|
|
this: Expr<Self>,
|
|
source_location: SourceLocation,
|
|
) -> Self::MatchVariantsIter {
|
|
let _ = this;
|
|
let _ = source_location;
|
|
std::iter::once(MatchVariantWithoutScope(PhantomData))
|
|
}
|
|
fn mask_type(&self) -> Self::MaskType {
|
|
()
|
|
}
|
|
fn canonical(&self) -> CanonicalType {
|
|
Bundle::new(self.fields()).canonical()
|
|
}
|
|
#[track_caller]
|
|
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
|
let CanonicalType::Bundle(bundle) = canonical_type else {
|
|
panic!("expected bundle");
|
|
};
|
|
assert!(
|
|
bundle.fields().is_empty(),
|
|
"bundle has wrong number of fields"
|
|
);
|
|
PhantomData
|
|
}
|
|
fn source_location() -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
fn sim_value_from_opaque(&self, opaque: OpaqueSimValueSlice<'_>) -> Self::SimValue {
|
|
assert!(opaque.is_empty());
|
|
*self
|
|
}
|
|
fn sim_value_clone_from_opaque(
|
|
&self,
|
|
_value: &mut Self::SimValue,
|
|
opaque: OpaqueSimValueSlice<'_>,
|
|
) {
|
|
assert!(opaque.is_empty());
|
|
}
|
|
fn sim_value_to_opaque<'w>(
|
|
&self,
|
|
_value: &Self::SimValue,
|
|
writer: OpaqueSimValueWriter<'w>,
|
|
) -> OpaqueSimValueWritten<'w> {
|
|
writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty())
|
|
}
|
|
}
|
|
|
|
pub struct PhantomDataBuilder<T: ?Sized + Send + Sync + 'static>(PhantomData<T>);
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
|
|
fn default() -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomDataBuilder<T> {
|
|
type Type = PhantomData<T>;
|
|
type ValueCategory = ValueCategoryValue;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
PhantomData
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
PhantomData.to_expr()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
|
|
type Builder = PhantomDataBuilder<T>;
|
|
fn fields(&self) -> Interned<[BundleField]> {
|
|
Interned::default()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> TypeWithDeref for PhantomData<T> {
|
|
fn expr_deref(_this: &Expr<Self>) -> &Self::MatchVariant {
|
|
&PhantomData
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
|
|
const TYPE: Self = PhantomData;
|
|
const MASK_TYPE: Self::MaskType = ();
|
|
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
|
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomData<T> {
|
|
type Type = PhantomData<T>;
|
|
type ValueCategory = ValueCategoryValue;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
PhantomData
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
|
|
fn to_expr(&self) -> Expr<Self::Type> {
|
|
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
|
|
#[track_caller]
|
|
fn to_sim_value(&self) -> SimValue<Self> {
|
|
SimValue::from_value(*self, *self)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized + Send + Sync + 'static> ToSimValueWithType<Self> for PhantomData<T> {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, ty: Self) -> SimValue<Self> {
|
|
SimValue::from_value(ty, *self)
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> ToSimValueWithType<Bundle> for PhantomData<T> {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, ty: Bundle) -> SimValue<Bundle> {
|
|
assert!(ty.fields().is_empty());
|
|
SimValue::from_opaque(ty, OpaqueSimValue::empty())
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> ToSimValueWithType<CanonicalType> for PhantomData<T> {
|
|
#[track_caller]
|
|
fn to_sim_value_with_type(&self, canonical_ty: CanonicalType) -> SimValue<CanonicalType> {
|
|
let ty = Bundle::from_canonical(canonical_ty);
|
|
assert!(ty.fields().is_empty());
|
|
SimValue::from_opaque(canonical_ty, OpaqueSimValue::empty())
|
|
}
|
|
}
|