use sv_lang::{Bit, ConstantValue, Type};
pub fn storage_width(ty: &Type<'_>) -> Option<u64> {
if ty.is_integral() || ty.is_floating() {
return Some(ty.bit_width()).filter(|w| *w > 0);
}
if ty.is_string()
|| ty.is_chandle()
|| ty.is_covergroup()
|| ty.is_virtual_interface()
|| ty.is_class()
|| is_collection(ty)
{
return Some(64);
}
if ty.is_unpacked_struct() {
return ty
.fields()
.map(|f| storage_width(&f.value_type()?))
.sum::<Option<u64>>()
.filter(|w| *w > 0);
}
if ty.is_unpacked_union() {
if !ty.is_tagged_union()
&& ty
.fields()
.any(|f| f.value_type().is_some_and(|t| holds_refcounted(&t)))
{
return None;
}
return ty
.fields()
.map(|f| member_width(&f.value_type()?))
.try_fold(0u64, |m, w| Some(m.max(w?)))
.map(|payload| payload + tagged_tag_bits(ty))
.filter(|w| *w > 0);
}
if ty.is_unpacked_array() && !ty.is_dynamically_sized_array() {
let n = u64::from(ty.fixed_unpacked_array_range().width());
return Some(n * storage_width(&ty.element_type()?)?);
}
None
}
fn member_width(ty: &Type<'_>) -> Option<u64> {
if ty.is_void() {
return Some(0);
}
storage_width(ty)
}
pub fn tagged_tag_bits(ty: &Type<'_>) -> u64 {
if !ty.is_tagged_union() {
return 0;
}
if ty.is_packed_union() {
return u64::from(ty.packed_union_tag_bits());
}
let n = ty.fields().count() as u64;
u64::from(n.next_power_of_two().trailing_zeros())
}
pub fn tagged_tag_slice(ty: &Type<'_>) -> Option<(u64, u64)> {
if !ty.is_tagged_union() {
return None;
}
let total = storage_width(ty)?;
let tag = tagged_tag_bits(ty);
Some((total - tag, tag))
}
pub fn is_class_keyed_assoc(ty: &Type<'_>) -> bool {
ty.is_associative_array() && ty.associative_index_type().is_some_and(|k| k.is_class())
}
pub fn holds_refcounted(ty: &Type<'_>) -> bool {
if ty.is_class() || is_collection(ty) {
return true;
}
if ty.is_unpacked_struct() || ty.is_unpacked_union() {
return ty
.fields()
.any(|f| f.value_type().is_some_and(|t| holds_refcounted(&t)));
}
ty.is_unpacked_array() && ty.element_type().is_some_and(|e| holds_refcounted(&e))
}
pub fn is_collection(ty: &Type<'_>) -> bool {
ty.is_dynamically_sized_array()
}
pub fn is_unpacked_aggregate(ty: &Type<'_>) -> bool {
ty.is_unpacked_struct() || ty.is_unpacked_union()
}
pub fn interconnect_dims(ty: &Type<'_>) -> Option<Vec<(i32, i32)>> {
let mut dims = Vec::new();
let mut cur = *ty;
while cur.is_unpacked_array() && !is_collection(&cur) {
let r = cur.fixed_unpacked_array_range();
dims.push((r.left, r.right));
cur = cur.element_type()?;
}
cur.is_untyped_type().then_some(dims)
}
pub fn contains_tagged_union(ty: &Type<'_>) -> bool {
if ty.is_unpacked_union() && ty.is_tagged_union() {
return true;
}
if ty.is_unpacked_struct() || ty.is_unpacked_union() {
return ty
.fields()
.any(|f| f.value_type().is_some_and(|t| contains_tagged_union(&t)));
}
if ty.is_unpacked_array() && !is_collection(ty) {
return ty.element_type().is_some_and(|t| contains_tagged_union(&t));
}
false
}
pub fn field_slice(ty: &Type<'_>, index: u32) -> Option<(u64, u64)> {
let widths = ty
.fields()
.map(|f| member_width(&f.value_type()?))
.collect::<Option<Vec<u64>>>()?;
let width = *widths.get(index as usize)?;
if ty.is_unpacked_union() {
return Some((0, width));
}
Some((widths[index as usize + 1..].iter().sum(), width))
}
pub fn default_bits(ty: &Type<'_>) -> Option<Vec<Bit>> {
let width = usize::try_from(storage_width(ty)?).ok()?;
if ty.is_integral() {
let fill = if ty.is_four_state() {
Bit::X
} else {
Bit::Zero
};
return Some(vec![fill; width]);
}
if ty.is_unpacked_struct() {
let mut out = Vec::with_capacity(width);
let fields: Vec<_> = ty.fields().collect();
for f in fields.iter().rev() {
let fty = f.value_type()?;
let declared = f
.initializer()
.and_then(|init| init.constant_value())
.and_then(|cv| constant_bits(&cv, &fty));
out.extend(match declared {
Some(bits) => bits,
None => default_bits(&fty)?,
});
}
return Some(out);
}
if ty.is_unpacked_union() && ty.is_tagged_union() {
return Some(vec![Bit::X; width]);
}
if ty.is_unpacked_union() {
let mut out = default_bits(&ty.fields().next()?.value_type()?)?;
out.resize(width, Bit::Zero);
return Some(out);
}
if ty.is_unpacked_array() && !is_collection(ty) {
let n = ty.fixed_unpacked_array_range().width() as usize;
return Some(default_bits(&ty.element_type()?)?.repeat(n));
}
Some(vec![Bit::Zero; width])
}
pub fn constant_bits(cv: &ConstantValue, ty: &Type<'_>) -> Option<Vec<Bit>> {
let width = usize::try_from(storage_width(ty)?).ok()?;
let word_bits = |w: u64| -> Vec<Bit> {
(0..width)
.map(|i| {
if i < 64 && (w >> i) & 1 == 1 {
Bit::One
} else {
Bit::Zero
}
})
.collect()
};
match cv {
ConstantValue::Integer(i) => {
let mut bits = super::svint_bits(i)?.into_owned();
let fill = if i.is_signed() {
*bits.last()?
} else {
Bit::Zero
};
bits.resize(width, fill);
Some(bits)
}
ConstantValue::Real(f) if width == 32 => Some(word_bits(u64::from((*f as f32).to_bits()))),
ConstantValue::Real(f) => Some(word_bits(f.to_bits())),
ConstantValue::String(s) if ty.is_string() => {
Some(word_bits(crate::strings::intern(s.as_bytes())))
}
ConstantValue::TaggedUnion { member, value, .. }
if ty.is_unpacked_union() && ty.is_tagged_union() =>
{
let field = ty.fields().nth(*member as usize)?;
let fty = field.value_type()?;
let mw = usize::try_from(member_width(&fty)?).ok()?;
let mut out = if mw == 0 {
Vec::new()
} else {
constant_bits(value, &fty)?
};
if out.len() != mw {
return None;
}
let tag = tagged_tag_bits(ty) as usize;
out.resize(width.checked_sub(tag)?, Bit::Zero);
out.extend((0..tag).map(|i| {
if (*member >> i) & 1 == 1 {
Bit::One
} else {
Bit::Zero
}
}));
Some(out)
}
_ => {
let elems = cv.as_array()?;
if is_collection(ty) {
if ty.is_associative_array() {
return None;
}
let elem_ty = ty.element_type()?;
let ew = (2 * storage_width(&elem_ty)?).div_ceil(64).max(1) as usize;
let mut words = Vec::with_capacity(elems.len() * ew);
for e in elems {
let mut w = super::value::packed_words(&constant_bits(e, &elem_ty)?);
w.resize(ew, 0);
words.extend(w);
}
let h = if elems.is_empty() {
0
} else {
crate::collections::frozen_from_words(ew, &words)
};
return Some(word_bits(h));
}
if ty.is_unpacked_struct() {
let fields: Vec<_> = ty.fields().collect();
if fields.len() != elems.len() {
return None;
}
let mut out = Vec::with_capacity(width);
for (f, e) in fields.iter().zip(elems).rev() {
out.extend(constant_bits(e, &f.value_type()?)?);
}
return Some(out);
}
if ty.is_unpacked_array() {
let range = ty.fixed_unpacked_array_range();
let elem_ty = ty.element_type()?;
let mut rows = elems
.iter()
.map(|e| constant_bits(e, &elem_ty))
.collect::<Option<Vec<_>>>()?;
if range.left > range.right {
rows.reverse();
}
return Some(rows.concat());
}
None
}
}
}