Reuse libm's Caat and CastFrom in compiler-builtins

This commit is contained in:
Trevor Gross 2025-05-29 03:51:43 +00:00
parent 851aa05aa0
commit 877feef541
8 changed files with 16 additions and 52 deletions

View file

@ -168,7 +168,7 @@ where
}
// Low three bits are round, guard, and sticky.
let a_significand_i32: i32 = a_significand.cast();
let a_significand_i32: i32 = a_significand.cast_lossy();
let round_guard_sticky: i32 = a_significand_i32 & 0x7;
// Shift the significand into place, and mask off the implicit bit.

View file

@ -74,7 +74,7 @@ mod int_to_float {
F::Int: CastFrom<I>,
Conv: Fn(I::Unsigned) -> F::Int,
{
let sign_bit = F::Int::cast_from(i >> (I::BITS - 1)) << (F::BITS - 1);
let sign_bit = F::Int::cast_from_lossy(i >> (I::BITS - 1)) << (F::BITS - 1);
F::from_bits(conv(i.unsigned_abs()) | sign_bit)
}
@ -166,7 +166,7 @@ mod int_to_float {
// Within the upper `F::BITS`, everything except for the signifcand
// gets truncated
let d1: u32 = (i_m >> (u128::BITS - f32::BITS - f32::SIG_BITS - 1)).cast();
let d1: u32 = (i_m >> (u128::BITS - f32::BITS - f32::SIG_BITS - 1)).cast_lossy();
// The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just
// check if it is nonzero.
@ -371,7 +371,7 @@ where
let m_base = if I::Unsigned::BITS >= F::Int::BITS {
I::Unsigned::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1)
} else {
I::Unsigned::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1))
I::Unsigned::cast_from_lossy(fbits >> (F::SIG_BITS - I::BITS + 1))
};
// Set the implicit 1-bit.

View file

@ -482,7 +482,7 @@ where
let ret = quotient.wrapping_shr(u32::cast_from(res_exponent.wrapping_neg()) + 1);
residual_lo = a_significand
.wrapping_shl(significand_bits.wrapping_add(CastInto::<u32>::cast(res_exponent)))
.wrapping_shl(significand_bits.wrapping_add(CastInto::<u32>::cast_lossy(res_exponent)))
.wrapping_sub(ret.wrapping_mul(b_significand) << 1);
ret
};

View file

@ -143,7 +143,7 @@ where
// a zero of the appropriate sign. Mathematically there is no need to
// handle this case separately, but we make it a special case to
// simplify the shift logic.
let shift: u32 = one.wrapping_sub(product_exponent.cast()).cast();
let shift: u32 = one.wrapping_sub(product_exponent.cast_lossy()).cast();
if shift >= bits {
return F::from_bits(product_sign);
}

View file

@ -50,7 +50,7 @@ where
// The exponent of a is within the range of normal numbers in the
// destination format. We can convert by simply right-shifting with
// rounding and adjusting the exponent.
abs_result = (a_abs >> sig_bits_delta).cast();
abs_result = (a_abs >> sig_bits_delta).cast_lossy();
// Cast before shifting to prevent overflow.
let bias_diff: R::Int = src_exp_bias.wrapping_sub(dst_exp_bias).cast();
let tmp = bias_diff << R::SIG_BITS;

View file

@ -20,18 +20,18 @@ mod implementation {
const { assert!(I::BITS <= 64) };
if I::BITS >= 64 {
r += ((u32::cast_from(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0
r += ((u32::cast_from_lossy(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0
x >>= r; // remove 32 zero bits
}
if I::BITS >= 32 {
t = ((u16::cast_from(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0
t = ((u16::cast_from_lossy(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0
r += t;
x >>= t; // x = [0 - 0xFFFF] + higher garbage bits
}
const { assert!(I::BITS >= 16) };
t = ((u8::cast_from(x) == 0) as u32) << 3;
t = ((u8::cast_from_lossy(x) == 0) as u32) << 3;
x >>= t; // x = [0 - 0xFF] + higher garbage bits
r += t;

View file

@ -1,4 +1,4 @@
pub use crate::support::{Int, MinInt};
pub use crate::support::{CastFrom, CastInto, Int, MinInt};
/// Trait for integers twice the bit width of another integer. This is implemented for all
/// primitives except for `u8`, because there is not a smaller primitive.
@ -97,44 +97,3 @@ impl_h_int!(
i32 u32 i64,
i64 u64 i128
);
/// Trait to express (possibly lossy) casting of integers
pub trait CastInto<T: Copy>: Copy {
fn cast(self) -> T;
}
pub trait CastFrom<T: Copy>: Copy {
fn cast_from(value: T) -> Self;
}
impl<T: Copy, U: CastInto<T> + Copy> CastFrom<U> for T {
fn cast_from(value: U) -> Self {
value.cast()
}
}
macro_rules! cast_into {
($ty:ty) => {
cast_into!($ty; usize, isize, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
};
($ty:ty; $($into:ty),*) => {$(
impl CastInto<$into> for $ty {
fn cast(self) -> $into {
self as $into
}
}
)*};
}
cast_into!(usize);
cast_into!(isize);
cast_into!(u8);
cast_into!(i8);
cast_into!(u16);
cast_into!(i16);
cast_into!(u32);
cast_into!(i32);
cast_into!(u64);
cast_into!(i64);
cast_into!(u128);
cast_into!(i128);

View file

@ -374,14 +374,19 @@ impl_h_int!(
/// Trait to express (possibly lossy) casting of integers
pub trait CastInto<T: Copy>: Copy {
/// By default, casts should be exact.
#[track_caller]
fn cast(self) -> T;
/// Call for casts that are expected to truncate.
///
/// In practice, this is exactly the same as `cast`; the main difference is to document intent
/// in code. `cast` may panic in debug mode.
fn cast_lossy(self) -> T;
}
pub trait CastFrom<T: Copy>: Copy {
/// By default, casts should be exact.
#[track_caller]
fn cast_from(value: T) -> Self;
/// Call for casts that are expected to truncate.