Skip to content

cleanup: Reuse MinInt and Int from libm in compiler-builtins #879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 72 additions & 5 deletions builtins-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,74 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) {
10_000
};

trait FuzzInt: MinInt {
/// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing
/// in `builtins-test`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96,
/// 111,112,119,120,125,126,127].
const FUZZ_LENGTHS: [u8; 20] = make_fuzz_lengths(<Self as MinInt>::BITS);

/// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128.
const FUZZ_NUM: usize = {
let log2 = (<Self as MinInt>::BITS - 1).count_ones() as usize;
if log2 == 3 {
// case for u8
6
} else {
// 3 entries on each extreme, 2 in the middle, and 4 for each scale of intermediate
// boundaries.
8 + (4 * (log2 - 4))
}
};
}

impl<I> FuzzInt for I where I: MinInt {}

const fn make_fuzz_lengths(bits: u32) -> [u8; 20] {
let mut v = [0u8; 20];
v[0] = 0;
v[1] = 1;
v[2] = 2; // important for parity and the iX::MIN case when reversed
let mut i = 3;

// No need for any more until the byte boundary, because there should be no algorithms
// that are sensitive to anything not next to byte boundaries after 2. We also scale
// in powers of two, which is important to prevent u128 corner tests from getting too
// big.
let mut l = 8;
loop {
if l >= ((bits / 2) as u8) {
break;
}
// get both sides of the byte boundary
v[i] = l - 1;
i += 1;
v[i] = l;
i += 1;
l *= 2;
}

if bits != 8 {
// add the lower side of the middle boundary
v[i] = ((bits / 2) - 1) as u8;
i += 1;
}

// We do not want to jump directly from the Self::BITS/2 boundary to the Self::BITS
// boundary because of algorithms that split the high part up. We reverse the scaling
// as we go to Self::BITS.
let mid = i;
let mut j = 1;
loop {
v[i] = (bits as u8) - (v[mid - j]) - 1;
if j == mid {
break;
}
i += 1;
j += 1;
}
v
}

/// Random fuzzing step. When run several times, it results in excellent fuzzing entropy such as:
/// 11110101010101011110111110011111
/// 10110101010100001011101011001010
Expand Down Expand Up @@ -92,10 +160,9 @@ fn fuzz_step<I: Int>(rng: &mut Xoshiro128StarStar, x: &mut I) {
macro_rules! edge_cases {
($I:ident, $case:ident, $inner:block) => {
for i0 in 0..$I::FUZZ_NUM {
let mask_lo = (!$I::UnsignedInt::ZERO).wrapping_shr($I::FUZZ_LENGTHS[i0] as u32);
let mask_lo = (!$I::Unsigned::ZERO).wrapping_shr($I::FUZZ_LENGTHS[i0] as u32);
for i1 in i0..I::FUZZ_NUM {
let mask_hi =
(!$I::UnsignedInt::ZERO).wrapping_shl($I::FUZZ_LENGTHS[i1 - i0] as u32);
let mask_hi = (!$I::Unsigned::ZERO).wrapping_shl($I::FUZZ_LENGTHS[i1 - i0] as u32);
let $case = I::from_unsigned(mask_lo & mask_hi);
$inner
}
Expand All @@ -107,7 +174,7 @@ macro_rules! edge_cases {
/// edge cases, followed by a more random fuzzer that runs `n` times.
pub fn fuzz<I: Int, F: FnMut(I)>(n: u32, mut f: F)
where
<I as MinInt>::UnsignedInt: Int,
<I as MinInt>::Unsigned: Int,
{
// edge case tester. Calls `f` 210 times for u128.
// zero gets skipped by the loop
Expand All @@ -128,7 +195,7 @@ where
/// The same as `fuzz`, except `f` has two inputs.
pub fn fuzz_2<I: Int, F: Fn(I, I)>(n: u32, f: F)
where
<I as MinInt>::UnsignedInt: Int,
<I as MinInt>::Unsigned: Int,
{
// Check cases where the first and second inputs are zero. Both call `f` 210 times for `u128`.
edge_cases!(I, case, {
Expand Down
22 changes: 12 additions & 10 deletions compiler-builtins/src/float/add.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::float::Float;
use crate::int::{CastInto, Int, MinInt};
use crate::int::{CastFrom, CastInto, Int, MinInt};

/// Returns `a + b`
fn add<F: Float>(a: F, b: F) -> F
Expand All @@ -12,7 +12,7 @@ where
let one = F::Int::ONE;
let zero = F::Int::ZERO;

let bits = F::BITS.cast();
let bits: F::Int = F::BITS.cast();
let significand_bits = F::SIG_BITS;
let max_exponent = F::EXP_SAT;

Expand Down Expand Up @@ -115,9 +115,10 @@ where
let align = a_exponent.wrapping_sub(b_exponent).cast();
if align != MinInt::ZERO {
if align < bits {
let sticky =
F::Int::from_bool(b_significand << bits.wrapping_sub(align).cast() != MinInt::ZERO);
b_significand = (b_significand >> align.cast()) | sticky;
let sticky = F::Int::from_bool(
b_significand << u32::cast_from(bits.wrapping_sub(align)) != MinInt::ZERO,
);
b_significand = (b_significand >> u32::cast_from(align)) | sticky;
} else {
b_significand = one; // sticky; b is known to be non-zero.
}
Expand All @@ -132,8 +133,8 @@ where
// If partial cancellation occured, we need to left-shift the result
// and adjust the exponent:
if a_significand < implicit_bit << 3 {
let shift =
a_significand.leading_zeros() as i32 - (implicit_bit << 3).leading_zeros() as i32;
let shift = a_significand.leading_zeros() as i32
- (implicit_bit << 3u32).leading_zeros() as i32;
a_significand <<= shift;
a_exponent -= shift;
}
Expand All @@ -159,9 +160,10 @@ where
// Result is denormal before rounding; the exponent is zero and we
// need to shift the significand.
let shift = (1 - a_exponent).cast();
let sticky =
F::Int::from_bool((a_significand << bits.wrapping_sub(shift).cast()) != MinInt::ZERO);
a_significand = (a_significand >> shift.cast()) | sticky;
let sticky = F::Int::from_bool(
(a_significand << u32::cast_from(bits.wrapping_sub(shift))) != MinInt::ZERO,
);
a_significand = (a_significand >> u32::cast_from(shift)) | sticky;
a_exponent = 0;
}

Expand Down
24 changes: 12 additions & 12 deletions compiler-builtins/src/float/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ mod int_to_float {
F: Float,
I: Int,
F::Int: CastFrom<I>,
Conv: Fn(I::UnsignedInt) -> F::Int,
Conv: Fn(I::Unsigned) -> F::Int,
{
let sign_bit = F::Int::cast_from(i >> (I::BITS - 1)) << (F::BITS - 1);
F::from_bits(conv(i.unsigned_abs()) | sign_bit)
Expand Down Expand Up @@ -313,10 +313,10 @@ intrinsics! {
fn float_to_unsigned_int<F, U>(f: F) -> U
where
F: Float,
U: Int<UnsignedInt = U>,
U: Int<Unsigned = U>,
F::Int: CastInto<U>,
F::Int: CastFrom<u32>,
F::Int: CastInto<U::UnsignedInt>,
F::Int: CastInto<U::Unsigned>,
u32: CastFrom<F::Int>,
{
float_to_int_inner::<F, U, _, _>(f.to_bits(), |i: U| i, || U::MAX)
Expand All @@ -327,8 +327,8 @@ fn float_to_signed_int<F, I>(f: F) -> I
where
F: Float,
I: Int + Neg<Output = I>,
I::UnsignedInt: Int,
F::Int: CastInto<I::UnsignedInt>,
I::Unsigned: Int,
F::Int: CastInto<I::Unsigned>,
F::Int: CastFrom<u32>,
u32: CastFrom<F::Int>,
{
Expand All @@ -355,27 +355,27 @@ where
I: Int,
FnFoo: FnOnce(I) -> I,
FnOob: FnOnce() -> I,
I::UnsignedInt: Int,
F::Int: CastInto<I::UnsignedInt>,
I::Unsigned: Int,
F::Int: CastInto<I::Unsigned>,
F::Int: CastFrom<u32>,
u32: CastFrom<F::Int>,
{
let int_max_exp = F::EXP_BIAS + I::MAX.ilog2() + 1;
let foobar = F::EXP_BIAS + I::UnsignedInt::BITS - 1;
let foobar = F::EXP_BIAS + I::Unsigned::BITS - 1;

if fbits < F::ONE.to_bits() {
// < 0 gets rounded to 0
I::ZERO
} else if fbits < F::Int::cast_from(int_max_exp) << F::SIG_BITS {
// >= 1, < integer max
let m_base = if I::UnsignedInt::BITS >= F::Int::BITS {
I::UnsignedInt::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1)
let m_base = if I::Unsigned::BITS >= F::Int::BITS {
I::Unsigned::cast_from(fbits) << (I::BITS - F::SIG_BITS - 1)
} else {
I::UnsignedInt::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1))
I::Unsigned::cast_from(fbits >> (F::SIG_BITS - I::BITS + 1))
};

// Set the implicit 1-bit.
let m: I::UnsignedInt = (I::UnsignedInt::ONE << (I::BITS - 1)) | m_base;
let m: I::Unsigned = (I::Unsigned::ONE << (I::BITS - 1)) | m_base;

// Shift based on the exponent and bias.
let s: u32 = (foobar) - u32::cast_from(fbits >> F::SIG_BITS);
Expand Down
2 changes: 1 addition & 1 deletion compiler-builtins/src/float/div.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ where
let hi_corr: F::Int = corr_uq1 >> hw;

// x_UQ0 * corr_UQ1 = (x_UQ0_hw * 2^HW) * (hi_corr * 2^HW + lo_corr) - corr_UQ1
let mut x_uq0: F::Int = ((F::Int::from(x_uq0_hw) * hi_corr) << 1)
let mut x_uq0: F::Int = ((F::Int::from(x_uq0_hw) * hi_corr) << 1u32)
.wrapping_add((F::Int::from(x_uq0_hw) * lo_corr) >> (hw - 1))
// 1 to account for the highest bit of corr_UQ1 can be 1
// 1 to account for possible carry
Expand Down
2 changes: 1 addition & 1 deletion compiler-builtins/src/float/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = one.wrapping_sub(product_exponent.cast()).cast();
let shift: u32 = one.wrapping_sub(product_exponent.cast()).cast();
if shift >= bits {
return F::from_bits(product_sign);
}
Expand Down
4 changes: 2 additions & 2 deletions compiler-builtins/src/float/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ pub trait Float:
+ ops::Rem<Output = Self>
{
/// A uint of the same width as the float
type Int: Int<OtherSign = Self::SignedInt, UnsignedInt = Self::Int>;
type Int: Int<OtherSign = Self::SignedInt, Unsigned = Self::Int>;

/// A int of the same width as the float
type SignedInt: Int + MinInt<OtherSign = Self::Int, UnsignedInt = Self::Int>;
type SignedInt: Int + MinInt<OtherSign = Self::Int, Unsigned = Self::Int>;

/// An int capable of containing the exponent bits plus a sign bit. This is signed.
type ExpInt: Int;
Expand Down
6 changes: 3 additions & 3 deletions compiler-builtins/src/int/addsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl UAddSub for u128 {}

trait AddSub: Int
where
<Self as MinInt>::UnsignedInt: UAddSub,
<Self as MinInt>::Unsigned: UAddSub,
{
fn add(self, other: Self) -> Self {
Self::from_unsigned(self.unsigned().uadd(other.unsigned()))
Expand All @@ -37,7 +37,7 @@ impl AddSub for i128 {}

trait Addo: AddSub
where
<Self as MinInt>::UnsignedInt: UAddSub,
<Self as MinInt>::Unsigned: UAddSub,
{
fn addo(self, other: Self) -> (Self, bool) {
let sum = AddSub::add(self, other);
Expand All @@ -50,7 +50,7 @@ impl Addo for u128 {}

trait Subo: AddSub
where
<Self as MinInt>::UnsignedInt: UAddSub,
<Self as MinInt>::Unsigned: UAddSub,
{
fn subo(self, other: Self) -> (Self, bool) {
let sum = AddSub::sub(self, other);
Expand Down
4 changes: 2 additions & 2 deletions compiler-builtins/src/int/big.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl i256 {
impl MinInt for u256 {
type OtherSign = i256;

type UnsignedInt = u256;
type Unsigned = u256;

const SIGNED: bool = false;
const BITS: u32 = 256;
Expand All @@ -58,7 +58,7 @@ impl MinInt for u256 {
impl MinInt for i256 {
type OtherSign = u256;

type UnsignedInt = u256;
type Unsigned = u256;

const SIGNED: bool = false;
const BITS: u32 = 256;
Expand Down
Loading
Loading