forked from libre-chip/fayalite
1493 lines
46 KiB
Rust
1493 lines
46 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
use crate::{
|
|
expr::{Expr, ToExpr},
|
|
intern::{Intern, Interned, Memoize},
|
|
source_location::SourceLocation,
|
|
ty::{
|
|
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
|
|
DynCanonicalType, FixedType, Type, TypeEnum, Value, ValueEnum,
|
|
},
|
|
util::{ConstBool, GenericConstBool},
|
|
valueless::Valueless,
|
|
};
|
|
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
|
|
use num_bigint::{BigInt, BigUint, Sign};
|
|
use num_traits::{ToPrimitive, Zero};
|
|
use std::{
|
|
fmt,
|
|
hash::Hash,
|
|
marker::PhantomData,
|
|
ops::{
|
|
Add, BitAnd, BitOr, BitXor, Bound, Mul, Neg, Not, Range, RangeBounds, RangeInclusive, Shl,
|
|
Shr, Sub,
|
|
},
|
|
};
|
|
|
|
#[derive(Clone, Eq, PartialEq, Hash, Default)]
|
|
pub struct IntValue<T> {
|
|
ty: T,
|
|
uint_value: BigUint,
|
|
}
|
|
|
|
pub type DynInt<Signed> = IntValue<DynIntType<Signed>>;
|
|
pub type DynUInt = DynInt<ConstBool<false>>;
|
|
pub type DynSInt = DynInt<ConstBool<true>>;
|
|
|
|
pub type Int<Signed, const WIDTH: usize> = IntValue<IntType<Signed, WIDTH>>;
|
|
pub type UInt<const WIDTH: usize> = Int<ConstBool<false>, WIDTH>;
|
|
pub type SInt<const WIDTH: usize> = Int<ConstBool<true>, WIDTH>;
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> ToExpr for IntValue<T>
|
|
{
|
|
type Type = T;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
self.ty
|
|
}
|
|
|
|
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
|
Expr::from_value(self)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> fmt::Display for IntValue<T>
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(&self.to_canonical(), f)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> fmt::Debug for IntValue<T>
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let neg;
|
|
let (sign, magnitude) = if self.is_negative() {
|
|
neg = self.ty.modulo() - &self.uint_value;
|
|
("-", &neg)
|
|
} else if T::Signed::VALUE {
|
|
("+", &self.uint_value)
|
|
} else {
|
|
("", &self.uint_value)
|
|
};
|
|
write!(f, "{sign}0x{magnitude:X}_{:?}", self.ty)
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> Int<Signed, WIDTH> {
|
|
pub fn new<I: Into<BigInt>>(value: I) -> Self {
|
|
Self::with_type::<BigInt>(IntType::new(), value.into())
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> DynInt<Signed> {
|
|
pub fn from_bit_slice(v: &BitSlice) -> Self {
|
|
let mut small_buf = [0u32; 8];
|
|
let small_buf_view = small_buf.view_bits_mut::<Lsb0>();
|
|
let uint_value = if v.len() <= small_buf_view.len() {
|
|
small_buf_view[..v.len()].clone_from_bitslice(v);
|
|
BigUint::from_slice(&small_buf)
|
|
} else {
|
|
let mut buf = BitVec::<u32, Lsb0>::with_capacity(v.len());
|
|
buf.extend_from_bitslice(v);
|
|
BigUint::from_slice(buf.as_raw_slice())
|
|
};
|
|
Self {
|
|
ty: DynIntType::new(v.len()),
|
|
uint_value,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> IntValue<T>
|
|
{
|
|
pub fn with_type<I: Into<BigInt>>(ty: T, value: I) -> Self {
|
|
let int_type = ty.canonical();
|
|
let mut value: BigInt = value.into();
|
|
if value.sign() == Sign::Minus
|
|
|| usize::try_from(value.bits())
|
|
.map(|v| v > int_type.width)
|
|
.unwrap_or(true)
|
|
{
|
|
value &= BigInt::from(int_type.as_same_width_uint().mask());
|
|
}
|
|
Self {
|
|
ty,
|
|
uint_value: value.try_into().unwrap(),
|
|
}
|
|
}
|
|
pub fn into_canonical(self) -> IntValue<DynIntType<T::Signed>> {
|
|
IntValue {
|
|
ty: self.ty.canonical(),
|
|
uint_value: self.uint_value,
|
|
}
|
|
}
|
|
pub fn to_canonical(&self) -> IntValue<DynIntType<T::Signed>> {
|
|
IntValue {
|
|
ty: self.ty.canonical(),
|
|
uint_value: self.uint_value.clone(),
|
|
}
|
|
}
|
|
pub fn ty(&self) -> T {
|
|
self.ty
|
|
}
|
|
pub fn uint_value(&self) -> BigUint {
|
|
self.uint_value.clone()
|
|
}
|
|
pub fn into_uint_value(self) -> BigUint {
|
|
self.uint_value
|
|
}
|
|
pub fn value(&self) -> BigInt {
|
|
if self.is_negative() {
|
|
BigInt::from(self.uint_value.clone()) - BigInt::from(self.ty.modulo())
|
|
} else {
|
|
self.uint_value.clone().into()
|
|
}
|
|
}
|
|
pub fn into_value(self) -> BigInt {
|
|
if self.is_negative() {
|
|
BigInt::from(self.uint_value) - BigInt::from(self.ty.modulo())
|
|
} else {
|
|
self.uint_value.into()
|
|
}
|
|
}
|
|
pub fn zero() -> Self
|
|
where
|
|
T: Default,
|
|
{
|
|
Self::default()
|
|
}
|
|
pub fn is_zero(&self) -> bool {
|
|
self.uint_value.is_zero()
|
|
}
|
|
pub fn set_zero(&mut self) {
|
|
self.uint_value.set_zero();
|
|
}
|
|
pub fn signum(&self) -> SInt<2> {
|
|
if self.is_negative() {
|
|
IntValue::new(-1)
|
|
} else if self.is_zero() {
|
|
IntValue::new(0)
|
|
} else {
|
|
IntValue::new(1)
|
|
}
|
|
}
|
|
pub fn is_positive(&self) -> bool {
|
|
!self.is_negative() && !self.is_zero()
|
|
}
|
|
pub fn is_negative(&self) -> bool {
|
|
let width = self.ty.width();
|
|
T::Signed::VALUE && width > 0 && self.uint_value.bit((width - 1).try_into().unwrap())
|
|
}
|
|
pub fn into_cast<
|
|
NewType: IntTypeTrait<
|
|
CanonicalType = DynIntType<<NewType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<NewType as IntTypeTrait>::Signed>,
|
|
>,
|
|
>(
|
|
self,
|
|
new_type: NewType,
|
|
) -> IntValue<NewType> {
|
|
if new_type.width() > self.ty.width() && self.is_negative() {
|
|
IntValue::with_type(new_type, self.into_value())
|
|
} else {
|
|
IntValue::with_type(new_type, self.into_uint_value())
|
|
}
|
|
}
|
|
pub fn cast_as_type<
|
|
NewType: IntTypeTrait<
|
|
CanonicalType = DynIntType<<NewType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<NewType as IntTypeTrait>::Signed>,
|
|
>,
|
|
>(
|
|
&self,
|
|
new_type: NewType,
|
|
) -> IntValue<NewType> {
|
|
if new_type.width() > self.ty.width() && self.is_negative() {
|
|
IntValue::with_type(new_type, self.value())
|
|
} else {
|
|
IntValue::with_type(new_type, self.uint_value())
|
|
}
|
|
}
|
|
pub fn cast<Signed: GenericConstBool, const WIDTH: usize>(self) -> Int<Signed, WIDTH> {
|
|
self.cast_as_type(IntType::new())
|
|
}
|
|
pub fn as_same_width_uint(self) -> IntValue<T::SameWidthUInt> {
|
|
IntValue {
|
|
ty: self.ty.as_same_width_uint(),
|
|
uint_value: self.uint_value,
|
|
}
|
|
}
|
|
pub fn as_same_width_sint(self) -> IntValue<T::SameWidthSInt> {
|
|
IntValue {
|
|
ty: self.ty.as_same_width_sint(),
|
|
uint_value: self.uint_value,
|
|
}
|
|
}
|
|
pub fn as_same_value_uint(self) -> IntValue<DynUIntType> {
|
|
IntValue {
|
|
ty: self.ty.as_same_value_uint(),
|
|
uint_value: self.uint_value,
|
|
}
|
|
}
|
|
pub fn as_same_value_sint(self) -> IntValue<DynSIntType> {
|
|
IntValue {
|
|
ty: self.ty.as_same_value_sint(),
|
|
uint_value: self.uint_value,
|
|
}
|
|
}
|
|
pub fn bit(&self, index: usize) -> bool {
|
|
if index >= self.ty.width() {
|
|
self.is_negative()
|
|
} else {
|
|
self.uint_value.bit(index.try_into().unwrap())
|
|
}
|
|
}
|
|
pub fn set_bit(&mut self, index: usize, value: bool) {
|
|
assert!(index < self.ty.width(), "bit index out of range");
|
|
self.uint_value.set_bit(index.try_into().unwrap(), value)
|
|
}
|
|
pub fn set_slice<
|
|
I: RangeBounds<usize>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
>(
|
|
&mut self,
|
|
index: I,
|
|
value: impl Into<IntValue<RhsType>>,
|
|
) {
|
|
let (ty, shift) = self.ty.slice_and_shift(index);
|
|
let value: IntValue<RhsType> = value.into();
|
|
let value = value.into_cast(ty);
|
|
let mut mask = ty.mask();
|
|
let mut uint_value = value.uint_value;
|
|
mask <<= shift;
|
|
uint_value <<= shift;
|
|
// can't just use self.uint_value &= !mask since Not isn't implemented...work around with & and subtraction
|
|
mask &= &self.uint_value;
|
|
self.uint_value -= mask;
|
|
self.uint_value |= uint_value;
|
|
}
|
|
pub fn with_replaced_slice<
|
|
I: RangeBounds<usize>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
>(
|
|
mut self,
|
|
index: I,
|
|
value: impl Into<IntValue<RhsType>>,
|
|
) -> Self {
|
|
self.set_slice(index, value);
|
|
self
|
|
}
|
|
pub fn concat<
|
|
HighType: IntTypeTrait<
|
|
CanonicalType = DynIntType<<HighType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<HighType as IntTypeTrait>::Signed>,
|
|
>,
|
|
>(
|
|
&self,
|
|
high_part: IntValue<HighType>,
|
|
) -> IntValue<DynIntType<HighType::Signed>> {
|
|
let self_type = self.ty.canonical();
|
|
let ty = self.valueless().concat(high_part.valueless()).ty;
|
|
let mut uint_value = high_part.uint_value << self_type.width;
|
|
uint_value |= &self.uint_value;
|
|
IntValue { ty, uint_value }
|
|
}
|
|
pub fn repeat(&self, count: usize) -> IntValue<DynIntType<T::Signed>> {
|
|
let width = self.ty.width();
|
|
let ty = self.valueless().repeat(count).ty;
|
|
let mut factor = BigUint::from(0u8);
|
|
// reversed so BigUint only reallocates once
|
|
for i in (0..count).rev() {
|
|
factor.set_bit((i * width).try_into().unwrap(), true);
|
|
}
|
|
IntValue {
|
|
ty,
|
|
uint_value: &self.uint_value * factor,
|
|
}
|
|
}
|
|
pub fn slice<I: RangeBounds<usize>>(&self, index: I) -> DynUInt {
|
|
let (ty, shift) = self.ty.slice_and_shift(index);
|
|
// correct since slice_and_shift ensures we're not trying to slice out of range
|
|
IntValue::with_type(ty, &self.uint_value >> shift)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Ty: IntTypeTrait<
|
|
CanonicalType = DynIntType<<Ty as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<Ty as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Value for IntValue<Ty>
|
|
{
|
|
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
|
IntValue {
|
|
ty: self.ty.canonical(),
|
|
uint_value: self.uint_value.clone(),
|
|
}
|
|
}
|
|
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<
|
|
Ty: IntTypeTrait<
|
|
CanonicalType = DynIntType<<Ty as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<Ty as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Memoize for ToBitsMemoize<IntValue<Ty>>
|
|
{
|
|
type Input = IntValue<Ty>;
|
|
type InputOwned = IntValue<Ty>;
|
|
type Output = Interned<BitSlice>;
|
|
|
|
fn inner(self, input: &Self::Input) -> Self::Output {
|
|
let u64_digits = input.uint_value.to_u64_digits();
|
|
let width = input.ty.width();
|
|
let mut bits = BitVec::with_capacity(width);
|
|
let mut slice = u64_digits.view_bits::<Lsb0>();
|
|
if slice.len() > width {
|
|
slice = &slice[..width];
|
|
}
|
|
bits.extend_from_bitslice(slice);
|
|
bits.resize(width, false);
|
|
Intern::intern_owned(bits)
|
|
}
|
|
}
|
|
ToBitsMemoize::<Self>(PhantomData).get(this)
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_add_sub {
|
|
($Op:ident::$op:ident) => {
|
|
impl<LhsType, RhsType> $Op<IntValue<RhsType>> for IntValue<LhsType>
|
|
where
|
|
LhsType: IntTypeTrait<
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = <LhsType as IntTypeTrait>::Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
{
|
|
type Output = IntValue<DynIntType<LhsType::Signed>>;
|
|
|
|
fn $op(self, rhs: IntValue<RhsType>) -> Self::Output {
|
|
let ty = $Op::$op(self.valueless(), rhs.valueless()).ty;
|
|
IntValue::with_type(ty, $Op::$op(self.into_value(), rhs.into_value()))
|
|
}
|
|
}
|
|
|
|
impl<LhsType, RhsType> $Op<Valueless<RhsType>> for Valueless<LhsType>
|
|
where
|
|
LhsType: IntTypeTrait<
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = <LhsType as IntTypeTrait>::Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
{
|
|
type Output = Valueless<DynIntType<LhsType::Signed>>;
|
|
|
|
fn $op(self, rhs: Valueless<RhsType>) -> Self::Output {
|
|
let ty = DynIntType::new(
|
|
self.ty
|
|
.width()
|
|
.max(rhs.ty.width())
|
|
.checked_add(1)
|
|
.expect("result has too many bits"),
|
|
);
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! impl_bitwise {
|
|
($Op:ident::$op:ident) => {
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> $Op<IntValue<RhsType>> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
|
|
fn $op(self, rhs: IntValue<RhsType>) -> Self::Output {
|
|
let ty = $Op::$op(self.valueless(), rhs.valueless()).ty;
|
|
IntValue::with_type(ty, $Op::$op(self.into_value(), rhs.into_value()))
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> $Op<Valueless<RhsType>> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
|
|
fn $op(self, rhs: Valueless<RhsType>) -> Self::Output {
|
|
let ty = DynIntType::new(self.ty.width().max(rhs.ty.width()));
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_add_sub!(Add::add);
|
|
impl_add_sub!(Sub::sub);
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Mul<IntValue<RhsType>> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
fn mul(self, rhs: IntValue<RhsType>) -> Self::Output {
|
|
let ty = self.valueless().mul(rhs.valueless()).ty;
|
|
IntValue::with_type(ty, Mul::mul(self.into_value(), rhs.into_value()))
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<RhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<RhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Mul<Valueless<RhsType>> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
fn mul(self, rhs: Valueless<RhsType>) -> Self::Output {
|
|
let ty = DynIntType::new(
|
|
self.ty
|
|
.width()
|
|
.checked_add(rhs.ty.width())
|
|
.expect("product has too many bits"),
|
|
);
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
|
|
impl_bitwise!(BitAnd::bitand);
|
|
impl_bitwise!(BitOr::bitor);
|
|
impl_bitwise!(BitXor::bitxor);
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
> Shl<IntValue<RhsType>> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
|
|
fn shl(self, rhs: IntValue<RhsType>) -> Self::Output {
|
|
let ty = self.valueless().shl(rhs.valueless()).ty;
|
|
IntValue::with_type(
|
|
ty,
|
|
Shl::shl(
|
|
self.into_value(),
|
|
rhs.into_value()
|
|
.to_usize()
|
|
.expect("ty was checked, so the shift must be in-range"),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
> Shl<Valueless<RhsType>> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
|
|
fn shl(self, rhs: Valueless<RhsType>) -> Self::Output {
|
|
let ty = DynIntType::new(
|
|
rhs.ty
|
|
.width()
|
|
.try_into()
|
|
.ok()
|
|
.and_then(|v| 2usize.checked_pow(v))
|
|
.and_then(|v| self.ty.width().checked_add(v - 1))
|
|
.expect("shift amount can be too big"),
|
|
);
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
> Shr<IntValue<RhsType>> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
|
|
fn shr(self, rhs: IntValue<RhsType>) -> Self::Output {
|
|
IntValue::with_type(
|
|
self.valueless().shr(rhs.valueless()).ty,
|
|
Shr::shr(
|
|
self.into_value(),
|
|
rhs.into_value().to_usize().unwrap_or(usize::MAX),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<Signed = Signed, CanonicalType = DynIntType<Signed>>,
|
|
RhsType: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
> Shr<Valueless<RhsType>> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
|
|
fn shr(self, _rhs: Valueless<RhsType>) -> Self::Output {
|
|
Valueless {
|
|
ty: self.ty.canonical(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Shl<usize> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
|
|
fn shl(self, rhs: usize) -> Self::Output {
|
|
let ty = self.valueless().shl(rhs).ty;
|
|
IntValue::with_type(ty, Shl::shl(self.into_value(), rhs))
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Shl<usize> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
|
|
fn shl(self, rhs: usize) -> Self::Output {
|
|
let ty = DynIntType::new(
|
|
self.ty
|
|
.width()
|
|
.checked_add(rhs)
|
|
.expect("shift amount is too big"),
|
|
);
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Shr<usize> for IntValue<LhsType>
|
|
{
|
|
type Output = IntValue<DynIntType<Signed>>;
|
|
|
|
fn shr(self, rhs: usize) -> Self::Output {
|
|
IntValue::with_type(
|
|
self.valueless().shr(rhs).ty,
|
|
Shr::shr(self.into_value(), rhs),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Signed: GenericConstBool,
|
|
LhsType: IntTypeTrait<
|
|
Signed = Signed,
|
|
CanonicalType = DynIntType<<LhsType as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<LhsType as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Shr<usize> for Valueless<LhsType>
|
|
{
|
|
type Output = Valueless<DynIntType<Signed>>;
|
|
|
|
fn shr(self, rhs: usize) -> Self::Output {
|
|
let ty = DynIntType::new(self.ty.width().saturating_sub(rhs).max(1));
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
Signed = ConstBool<true>,
|
|
CanonicalType = DynSIntType,
|
|
CanonicalValue = DynSInt,
|
|
>,
|
|
> Neg for IntValue<T>
|
|
{
|
|
type Output = IntValue<DynSIntType>;
|
|
fn neg(self) -> Self::Output {
|
|
let ty = self.valueless().neg().ty;
|
|
IntValue::with_type(ty, Neg::neg(self.into_value()))
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
Signed = ConstBool<true>,
|
|
CanonicalType = DynSIntType,
|
|
CanonicalValue = DynSInt,
|
|
>,
|
|
> Neg for Valueless<T>
|
|
{
|
|
type Output = Valueless<DynSIntType>;
|
|
fn neg(self) -> Self::Output {
|
|
let ty = DynIntType::new(
|
|
self.ty
|
|
.width()
|
|
.checked_add(1)
|
|
.expect("result has too many bits"),
|
|
);
|
|
Valueless { ty }
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Not for IntValue<T>
|
|
{
|
|
type Output = IntValue<T>;
|
|
fn not(self) -> Self::Output {
|
|
IntValue::with_type(self.valueless().not().ty, Not::not(self.into_value()))
|
|
}
|
|
}
|
|
|
|
impl<
|
|
T: IntTypeTrait<
|
|
CanonicalType = DynIntType<<T as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<T as IntTypeTrait>::Signed>,
|
|
>,
|
|
> Not for Valueless<T>
|
|
{
|
|
type Output = Valueless<T>;
|
|
fn not(self) -> Self::Output {
|
|
self
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_int {
|
|
($ty:ident, $SIGNED:literal) => {
|
|
impl From<$ty> for Int<ConstBool<$SIGNED>, { $ty::BITS as usize }> {
|
|
fn from(v: $ty) -> Self {
|
|
Self::new(v)
|
|
}
|
|
}
|
|
|
|
impl From<Int<ConstBool<$SIGNED>, { $ty::BITS as usize }>> for $ty {
|
|
fn from(v: Int<ConstBool<$SIGNED>, { $ty::BITS as usize }>) -> Self {
|
|
v.value().try_into().unwrap()
|
|
}
|
|
}
|
|
|
|
impl ToExpr for $ty {
|
|
type Type = IntType<ConstBool<$SIGNED>, { $ty::BITS as usize }>;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
IntType::new()
|
|
}
|
|
|
|
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
|
IntValue::from(*self).to_expr()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_int!(u8, false);
|
|
impl_int!(u16, false);
|
|
impl_int!(u32, false);
|
|
impl_int!(u64, false);
|
|
impl_int!(u128, false);
|
|
impl_int!(i8, true);
|
|
impl_int!(i16, true);
|
|
impl_int!(i32, true);
|
|
impl_int!(i64, true);
|
|
impl_int!(i128, true);
|
|
|
|
impl<
|
|
T: FixedOrDynIntType<
|
|
1,
|
|
Signed = ConstBool<false>,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>,
|
|
> From<bool> for IntValue<T>
|
|
{
|
|
fn from(v: bool) -> Self {
|
|
IntValue::with_type(T::new(), v)
|
|
}
|
|
}
|
|
|
|
impl ToExpr for bool {
|
|
type Type = UIntType<1>;
|
|
|
|
fn ty(&self) -> Self::Type {
|
|
IntType::new()
|
|
}
|
|
|
|
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
|
UInt::from(*self).to_expr()
|
|
}
|
|
}
|
|
|
|
pub trait IntCmp<Rhs> {
|
|
type Output;
|
|
fn cmp_eq(self, rhs: Rhs) -> Self::Output;
|
|
fn cmp_ne(self, rhs: Rhs) -> Self::Output;
|
|
fn cmp_lt(self, rhs: Rhs) -> Self::Output;
|
|
fn cmp_le(self, rhs: Rhs) -> Self::Output;
|
|
fn cmp_gt(self, rhs: Rhs) -> Self::Output;
|
|
fn cmp_ge(self, rhs: Rhs) -> Self::Output;
|
|
}
|
|
|
|
macro_rules! forward_prim_int_cmp {
|
|
($prim_ty:ident) => {
|
|
impl<Rhs> IntCmp<Rhs> for $prim_ty
|
|
where
|
|
IntValue<<$prim_ty as ToExpr>::Type>: IntCmp<Rhs>,
|
|
{
|
|
type Output = <IntValue<<$prim_ty as ToExpr>::Type> as IntCmp<Rhs>>::Output;
|
|
fn cmp_eq(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_eq(rhs)
|
|
}
|
|
fn cmp_ne(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_ne(rhs)
|
|
}
|
|
fn cmp_lt(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_lt(rhs)
|
|
}
|
|
fn cmp_le(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_le(rhs)
|
|
}
|
|
fn cmp_gt(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_gt(rhs)
|
|
}
|
|
fn cmp_ge(self, rhs: Rhs) -> Self::Output {
|
|
IntValue::<<$prim_ty as ToExpr>::Type>::from(self).cmp_ge(rhs)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
forward_prim_int_cmp!(bool);
|
|
forward_prim_int_cmp!(u8);
|
|
forward_prim_int_cmp!(u16);
|
|
forward_prim_int_cmp!(u32);
|
|
forward_prim_int_cmp!(u64);
|
|
forward_prim_int_cmp!(u128);
|
|
forward_prim_int_cmp!(i8);
|
|
forward_prim_int_cmp!(i16);
|
|
forward_prim_int_cmp!(i32);
|
|
forward_prim_int_cmp!(i64);
|
|
forward_prim_int_cmp!(i128);
|
|
|
|
mod sealed {
|
|
pub trait Sealed {}
|
|
}
|
|
|
|
pub trait IntTypeTrait:
|
|
sealed::Sealed
|
|
+ Copy
|
|
+ 'static
|
|
+ Eq
|
|
+ Hash
|
|
+ fmt::Debug
|
|
+ Type<Value = IntValue<Self>>
|
|
+ Connect<Self>
|
|
+ Connect<DynIntType<<Self as IntTypeTrait>::Signed>>
|
|
{
|
|
type Signed: GenericConstBool;
|
|
type SameWidthUInt: IntTypeTrait<
|
|
Signed = ConstBool<false>,
|
|
SameWidthUInt = Self::SameWidthUInt,
|
|
SameWidthSInt = Self::SameWidthSInt,
|
|
CanonicalType = DynUIntType,
|
|
CanonicalValue = DynUInt,
|
|
>;
|
|
type SameWidthSInt: IntTypeTrait<
|
|
Signed = ConstBool<true>,
|
|
SameWidthUInt = Self::SameWidthUInt,
|
|
SameWidthSInt = Self::SameWidthSInt,
|
|
CanonicalType = DynSIntType,
|
|
CanonicalValue = DynSInt,
|
|
>;
|
|
fn literal(self, value: impl Into<BigInt>) -> Expr<IntValue<Self>>
|
|
where
|
|
Self: IntTypeTrait<
|
|
CanonicalType = DynIntType<<Self as IntTypeTrait>::Signed>,
|
|
CanonicalValue = DynInt<<Self as IntTypeTrait>::Signed>,
|
|
>,
|
|
{
|
|
IntValue::with_type(self, value).to_expr()
|
|
}
|
|
fn from_width_unchecked(width: usize) -> Self;
|
|
fn width(self) -> usize;
|
|
fn as_same_width_uint(self) -> Self::SameWidthUInt;
|
|
fn as_same_width_sint(self) -> Self::SameWidthSInt;
|
|
fn as_same_value_uint(self) -> DynUIntType {
|
|
let mut width = self.width();
|
|
if Self::Signed::VALUE {
|
|
width = width.saturating_sub(1);
|
|
}
|
|
DynIntType {
|
|
width,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
fn as_same_value_sint(self) -> DynSIntType {
|
|
let mut width = self.width();
|
|
if !Self::Signed::VALUE {
|
|
width = width.checked_add(1).expect("result too big");
|
|
}
|
|
DynIntType::new(width)
|
|
}
|
|
fn min_value(self) -> BigInt {
|
|
let width = self.width();
|
|
if Self::Signed::VALUE && width > 0 {
|
|
BigInt::from(-1i8) << (width - 1)
|
|
} else {
|
|
BigInt::from(0u8)
|
|
}
|
|
}
|
|
fn max_value(self) -> BigUint {
|
|
(BigUint::from(1u8) << self.width().saturating_sub(Self::Signed::VALUE.into())) - 1u8
|
|
}
|
|
fn modulo(self) -> BigUint {
|
|
BigUint::from(1u8) << self.width()
|
|
}
|
|
fn slice_index_to_range<I: RangeBounds<usize>>(self, index: I) -> Range<usize> {
|
|
let width = self.width();
|
|
let start = match index.start_bound() {
|
|
Bound::Included(start) => *start,
|
|
Bound::Excluded(start) => *start + 1,
|
|
Bound::Unbounded => 0,
|
|
};
|
|
let end = match index.end_bound() {
|
|
Bound::Included(end) => *end + 1,
|
|
Bound::Excluded(end) => *end,
|
|
Bound::Unbounded => width,
|
|
};
|
|
assert!(start <= end && end <= width, "slice range out-of-range");
|
|
start..end
|
|
}
|
|
fn slice_and_shift<I: RangeBounds<usize>>(self, index: I) -> (DynUIntType, usize) {
|
|
let range = self.slice_index_to_range(index);
|
|
let width = range.end - range.start;
|
|
(DynUIntType::new(width), range.start)
|
|
}
|
|
fn slice<I: RangeBounds<usize>>(self, index: I) -> DynUIntType {
|
|
self.slice_and_shift(index).0
|
|
}
|
|
}
|
|
|
|
pub trait FixedOrDynIntType<const WIDTH: usize>: IntTypeTrait {
|
|
fn new() -> Self;
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
|
|
pub struct DynIntType<Signed: GenericConstBool> {
|
|
pub width: usize,
|
|
_phantom: PhantomData<Signed>,
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> fmt::Debug for DynIntType<Signed> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if Signed::VALUE {
|
|
write!(f, "dyn_i{}", self.width)
|
|
} else {
|
|
write!(f, "dyn_u{}", self.width)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> DynIntType<Signed> {
|
|
pub const fn new(width: usize) -> Self {
|
|
Self {
|
|
width,
|
|
_phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type DynUIntType = DynIntType<ConstBool<false>>;
|
|
pub type DynSIntType = DynIntType<ConstBool<true>>;
|
|
|
|
impl<Signed: GenericConstBool> fmt::Display for DynIntType<Signed> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(
|
|
f,
|
|
"{}({})",
|
|
if Signed::VALUE { "SInt" } else { "UInt" },
|
|
self.width
|
|
)
|
|
}
|
|
}
|
|
|
|
impl DynUIntType {
|
|
pub fn mask(self) -> BigUint {
|
|
self.modulo() - 1u8
|
|
}
|
|
/// gets the smallest `UInt` that fits `v` losslessly
|
|
pub fn for_value(v: impl Into<BigUint>) -> Self {
|
|
let v: BigUint = v.into();
|
|
Self::new(v.bits().try_into().expect("too big"))
|
|
}
|
|
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range(r: Range<impl Into<BigUint>>) -> Self {
|
|
let start: BigUint = r.start.into();
|
|
let end: BigUint = r.end.into();
|
|
assert!(!end.is_zero(), "empty range");
|
|
Self::range_inclusive(start..=(end - 1u8))
|
|
}
|
|
/// gets the smallest `UInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range_inclusive(r: RangeInclusive<impl Into<BigUint>>) -> Self {
|
|
let (start, end) = r.into_inner();
|
|
let start: BigUint = start.into();
|
|
let end: BigUint = end.into();
|
|
assert!(start <= end, "empty range");
|
|
// no need to check `start`` since it's no larger than `end`
|
|
// so must not take more bits than `end`
|
|
Self::for_value(end)
|
|
}
|
|
}
|
|
|
|
impl DynSIntType {
|
|
/// gets the smallest `SInt` that fits `v` losslessly
|
|
pub fn for_value(v: impl Into<BigInt>) -> Self {
|
|
let v: BigInt = v.into();
|
|
Self::new(
|
|
match v.sign() {
|
|
Sign::Minus => {
|
|
// account for sign bit and for the minimum value of an `SInt`
|
|
// being the negative of the maximum value minus one.
|
|
v.not().bits().checked_add(1).expect("too big")
|
|
}
|
|
Sign::NoSign => 0,
|
|
Sign::Plus => v.bits(),
|
|
}
|
|
.try_into()
|
|
.expect("too big"),
|
|
)
|
|
}
|
|
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range(r: Range<impl Into<BigInt>>) -> Self {
|
|
let start: BigInt = r.start.into();
|
|
let end: BigInt = r.end.into();
|
|
Self::range_inclusive(start..=(end - 1))
|
|
}
|
|
/// gets the smallest `SInt` that fits `r` losslessly, panics if `r` is empty
|
|
#[track_caller]
|
|
pub fn range_inclusive(r: RangeInclusive<impl Into<BigInt>>) -> Self {
|
|
let (start, end) = r.into_inner();
|
|
let start: BigInt = start.into();
|
|
let end: BigInt = end.into();
|
|
assert!(start <= end, "empty range");
|
|
Self::new(Self::for_value(start).width.max(Self::for_value(end).width))
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> sealed::Sealed for DynIntType<Signed> {}
|
|
|
|
impl<Signed: GenericConstBool> Type for DynIntType<Signed> {
|
|
type CanonicalType = DynIntType<Signed>;
|
|
type Value = IntValue<DynIntType<Signed>>;
|
|
type CanonicalValue = IntValue<DynIntType<Signed>>;
|
|
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 {
|
|
if Signed::VALUE {
|
|
TypeEnum::SInt(self.as_same_width_sint())
|
|
} else {
|
|
TypeEnum::UInt(self.as_same_width_uint())
|
|
}
|
|
}
|
|
|
|
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
|
t
|
|
}
|
|
|
|
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
|
Some(this)
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> Connect<Self> for DynIntType<Signed> {}
|
|
|
|
impl<Signed: GenericConstBool, const A: usize> Connect<IntType<Signed, A>> for DynIntType<Signed> {}
|
|
|
|
impl<Signed: GenericConstBool, const B: usize> Connect<DynIntType<Signed>> for IntType<Signed, B> {}
|
|
|
|
impl<Signed: GenericConstBool, const A: usize, const B: usize> Connect<IntType<Signed, A>>
|
|
for IntType<Signed, B>
|
|
{
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> CanonicalType for DynIntType<Signed> {
|
|
const CANONICAL_TYPE_KIND: CanonicalTypeKind = if Signed::VALUE {
|
|
CanonicalTypeKind::SInt
|
|
} else {
|
|
CanonicalTypeKind::UInt
|
|
};
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> CanonicalValue for IntValue<DynIntType<Signed>> {
|
|
fn value_enum_impl(this: &Self) -> ValueEnum {
|
|
if Signed::VALUE {
|
|
ValueEnum::SInt(this.clone().as_same_width_sint())
|
|
} else {
|
|
ValueEnum::UInt(this.clone().as_same_width_uint())
|
|
}
|
|
}
|
|
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
|
<Self as Value>::to_bits_impl(this)
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool> IntTypeTrait for DynIntType<Signed> {
|
|
type Signed = Signed;
|
|
type SameWidthUInt = DynUIntType;
|
|
type SameWidthSInt = DynSIntType;
|
|
|
|
fn width(self) -> usize {
|
|
self.width
|
|
}
|
|
|
|
fn as_same_width_uint(self) -> Self::SameWidthUInt {
|
|
DynIntType::new(self.width)
|
|
}
|
|
|
|
fn as_same_width_sint(self) -> Self::SameWidthSInt {
|
|
DynIntType::new(self.width)
|
|
}
|
|
|
|
fn from_width_unchecked(width: usize) -> Self {
|
|
DynIntType::new(width)
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> FixedOrDynIntType<WIDTH> for DynIntType<Signed> {
|
|
fn new() -> Self {
|
|
DynIntType::new(WIDTH)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
pub struct IntType<Signed: GenericConstBool, const WIDTH: usize>(PhantomData<Signed>);
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> fmt::Debug for IntType<Signed, WIDTH> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
if Signed::VALUE {
|
|
write!(f, "i{WIDTH}")
|
|
} else {
|
|
write!(f, "u{WIDTH}")
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> IntType<Signed, WIDTH> {
|
|
pub const fn new() -> Self {
|
|
Self(PhantomData)
|
|
}
|
|
}
|
|
|
|
pub type UIntType<const WIDTH: usize> = IntType<ConstBool<false>, WIDTH>;
|
|
pub type SIntType<const WIDTH: usize> = IntType<ConstBool<true>, WIDTH>;
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> sealed::Sealed for IntType<Signed, WIDTH> {}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> Type for IntType<Signed, WIDTH> {
|
|
type CanonicalType = DynIntType<Signed>;
|
|
type Value = IntValue<IntType<Signed, WIDTH>>;
|
|
type CanonicalValue = IntValue<DynIntType<Signed>>;
|
|
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 {
|
|
DynIntType::new(WIDTH)
|
|
}
|
|
|
|
fn source_location(&self) -> SourceLocation {
|
|
SourceLocation::builtin()
|
|
}
|
|
|
|
fn type_enum(&self) -> TypeEnum {
|
|
self.canonical().type_enum()
|
|
}
|
|
|
|
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
|
assert_eq!(t.width, WIDTH);
|
|
IntType::new()
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> FixedType for IntType<Signed, WIDTH> {
|
|
fn fixed_type() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> IntTypeTrait for IntType<Signed, WIDTH> {
|
|
type Signed = Signed;
|
|
type SameWidthUInt = UIntType<WIDTH>;
|
|
type SameWidthSInt = SIntType<WIDTH>;
|
|
|
|
fn width(self) -> usize {
|
|
WIDTH
|
|
}
|
|
|
|
fn as_same_width_uint(self) -> Self::SameWidthUInt {
|
|
IntType::new()
|
|
}
|
|
|
|
fn as_same_width_sint(self) -> Self::SameWidthSInt {
|
|
IntType::new()
|
|
}
|
|
|
|
fn from_width_unchecked(_width: usize) -> Self {
|
|
IntType::new()
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> FixedOrDynIntType<WIDTH>
|
|
for IntType<Signed, WIDTH>
|
|
{
|
|
fn new() -> Self {
|
|
IntType::new()
|
|
}
|
|
}
|
|
|
|
impl<Signed: GenericConstBool, const WIDTH: usize> fmt::Display for IntType<Signed, WIDTH> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.canonical().fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
|
pub struct IntLiteral<'a, Signed: GenericConstBool, const WIDTH: usize> {
|
|
magnitude_le_bytes: &'a [u8],
|
|
negative: bool,
|
|
_phantom: PhantomData<Signed>,
|
|
}
|
|
|
|
#[track_caller]
|
|
pub const fn make_int_literal<const SIGNED: bool, const WIDTH: usize>(
|
|
negative: bool,
|
|
magnitude_le_bytes: &[u8],
|
|
) -> IntLiteral<'_, ConstBool<SIGNED>, WIDTH> {
|
|
IntLiteral::new(negative, magnitude_le_bytes)
|
|
}
|
|
|
|
impl<'a, Signed: GenericConstBool, const WIDTH: usize> IntLiteral<'a, Signed, WIDTH> {
|
|
pub const ZERO: IntLiteral<'static, Signed, WIDTH> = IntLiteral {
|
|
magnitude_le_bytes: &[],
|
|
negative: false,
|
|
_phantom: PhantomData,
|
|
};
|
|
#[track_caller]
|
|
pub const fn new(negative: bool, mut magnitude_le_bytes: &'a [u8]) -> Self {
|
|
while let [prefix @ .., 0] = magnitude_le_bytes {
|
|
magnitude_le_bytes = prefix;
|
|
}
|
|
assert!(
|
|
magnitude_le_bytes.len() <= Self::WIDTH_IN_BYTES,
|
|
"literal magnitude is too large"
|
|
);
|
|
if magnitude_le_bytes.is_empty() {
|
|
return Self::ZERO;
|
|
}
|
|
assert!(WIDTH != 0, "zero-bit integer literal must be zero");
|
|
let retval = Self {
|
|
magnitude_le_bytes,
|
|
negative,
|
|
_phantom: PhantomData,
|
|
};
|
|
let Some(magnitude_ilog2) = retval.magnitude_ilog2() else {
|
|
panic!("literal magnitude is too large");
|
|
};
|
|
if Self::SIGNED {
|
|
if negative {
|
|
assert!(
|
|
magnitude_ilog2 < Self::WIDTH,
|
|
"literal magnitude is too large"
|
|
);
|
|
if magnitude_ilog2 == Self::WIDTH - 1 {
|
|
// must be `1 << (WIDTH - 1)`, so the msb byte is a
|
|
// power of two, and all other bytes are zero
|
|
let [prefix @ .., last] = magnitude_le_bytes else {
|
|
panic!("checked that magnitude_le_bytes is not empty");
|
|
};
|
|
assert!(last.is_power_of_two(), "literal magnitude is too large");
|
|
let mut i = 0;
|
|
while i < prefix.len() {
|
|
assert!(prefix[i] == 0, "literal magnitude is too large");
|
|
i += 1;
|
|
}
|
|
}
|
|
} else {
|
|
assert!(
|
|
magnitude_ilog2 < Self::WIDTH - 1,
|
|
"literal magnitude is too large"
|
|
);
|
|
}
|
|
} else {
|
|
assert!(!negative, "unsigned literal can't be negative");
|
|
assert!(
|
|
magnitude_ilog2 < Self::WIDTH,
|
|
"literal magnitude is too large"
|
|
);
|
|
}
|
|
retval
|
|
}
|
|
pub const fn magnitude_ilog2(self) -> Option<usize> {
|
|
let Some(&last) = self.magnitude_le_bytes.last() else {
|
|
return None; // trying to calculate ilog2 of 0
|
|
};
|
|
// last is known to be non-zero, so ilog2 won't panic
|
|
let last_ilog2 = last.ilog2() as usize;
|
|
let Some(prod) = (u8::BITS as usize).checked_mul(self.magnitude_le_bytes.len() - 1) else {
|
|
return None; // product overflows
|
|
};
|
|
last_ilog2.checked_add(prod)
|
|
}
|
|
pub const fn magnitude_le_bytes(self) -> &'a [u8] {
|
|
self.magnitude_le_bytes
|
|
}
|
|
#[track_caller]
|
|
pub const fn magnitude_le_bytes_array<const N: usize>(self) -> [u8; N] {
|
|
assert!(self.magnitude_le_bytes.len() <= N, "literal too big");
|
|
let mut retval = [0u8; N];
|
|
let mut i = 0;
|
|
while i < self.magnitude_le_bytes.len() {
|
|
retval[i] = self.magnitude_le_bytes[i];
|
|
i += 1;
|
|
}
|
|
retval
|
|
}
|
|
pub const fn wrapping_le_bytes_array<const N: usize>(self) -> [u8; N] {
|
|
let mut retval = [0u8; N];
|
|
let mut i = 0;
|
|
let mut carry = self.negative;
|
|
while i < N {
|
|
let mut byte = if i < self.magnitude_le_bytes.len() {
|
|
self.magnitude_le_bytes[i]
|
|
} else {
|
|
0
|
|
};
|
|
if self.negative {
|
|
(byte, carry) = (carry as u8).overflowing_add(!byte);
|
|
}
|
|
retval[i] = byte;
|
|
i += 1;
|
|
}
|
|
retval
|
|
}
|
|
pub const fn negative(self) -> bool {
|
|
self.negative
|
|
}
|
|
pub const SIGNED: bool = Signed::VALUE;
|
|
pub const WIDTH: usize = {
|
|
// check isize::MAX instead of usize::MAX because we need to be able to have arrays that big
|
|
assert!(
|
|
WIDTH.div_ceil(u8::BITS as usize) < isize::MAX as usize,
|
|
"WIDTH too big"
|
|
);
|
|
WIDTH
|
|
};
|
|
pub const WIDTH_IN_BYTES: usize = {
|
|
// use Self::WIDTH instead of WIDTH to assert WIDTH isn't too big
|
|
Self::WIDTH.div_ceil(u8::BITS as usize)
|
|
};
|
|
pub fn to_int_value(self) -> Int<Signed, WIDTH> {
|
|
Int::new(BigInt::from_bytes_le(
|
|
if self.negative() {
|
|
Sign::Minus
|
|
} else {
|
|
Sign::Plus
|
|
},
|
|
self.magnitude_le_bytes(),
|
|
))
|
|
}
|
|
pub fn to_int_expr(self) -> Expr<Int<Signed, WIDTH>> {
|
|
self.to_int_value().to_expr()
|
|
}
|
|
}
|
|
|
|
const _: () = {
|
|
// test make_int_literal
|
|
#[track_caller]
|
|
const fn check<const SIGNED: bool, const WIDTH: usize>(
|
|
negative: bool,
|
|
magnitude_le_bytes: &[u8],
|
|
expected: i64,
|
|
) {
|
|
let value = i64::from_le_bytes(
|
|
make_int_literal::<SIGNED, WIDTH>(negative, magnitude_le_bytes)
|
|
.wrapping_le_bytes_array(),
|
|
);
|
|
assert!(value == expected);
|
|
}
|
|
check::<false, 0>(false, &[0], 0);
|
|
check::<true, 0>(true, &[0], 0);
|
|
check::<false, 1>(false, &[1], 1);
|
|
check::<true, 1>(true, &[1], -1);
|
|
check::<false, 7>(false, &[0x7F], 0x7F);
|
|
check::<true, 7>(false, &[0x3F], 0x3F);
|
|
check::<true, 7>(true, &[0x40], -0x40);
|
|
check::<false, 8>(false, &[0xFF], 0xFF);
|
|
check::<true, 8>(false, &[0x7F], 0x7F);
|
|
check::<true, 8>(true, &[0x80], -0x80);
|
|
check::<false, 15>(false, &[0xFF, 0x7F], 0x7FFF);
|
|
check::<true, 15>(false, &[0xFF, 0x3F], 0x3FFF);
|
|
check::<true, 15>(true, &[0, 0x40], -0x4000);
|
|
check::<false, 16>(false, &[0xFF, 0xFF], 0xFFFF);
|
|
check::<true, 16>(false, &[0xFF, 0x7F], 0x7FFF);
|
|
check::<true, 16>(true, &[0, 0x80], -0x8000);
|
|
check::<false, 72>(false, &[0xFF; 9], !0);
|
|
check::<true, 73>(false, &[0xFF; 9], !0);
|
|
check::<true, 73>(true, &[0xFF; 9], 1);
|
|
};
|