about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2025-04-21 09:35:55 +0000
committerTrevor Gross <tmgross@umich.edu>2025-05-29 03:04:59 +0000
commit851aa05aa0addf41507967952f1ac2d183751682 (patch)
tree4f4b5ac8109a9659381dbcb53bb7f1ce8b27284b
parent5978b8b875f6c53af9ca4c72f4ea5fcf74d55c84 (diff)
downloadrust-851aa05aa0addf41507967952f1ac2d183751682.tar.gz
rust-851aa05aa0addf41507967952f1ac2d183751682.zip
cleanup: Reuse `MinInt` and `Int` from `libm` in `compiler-builtins`
Since the two crates are now in the same repo, it is easier to share
code. Begin some deduplication with the integer traits.
-rw-r--r--library/compiler-builtins/builtins-test/src/lib.rs78
-rw-r--r--library/compiler-builtins/compiler-builtins/src/float/add.rs22
-rw-r--r--library/compiler-builtins/compiler-builtins/src/float/conv.rs24
-rw-r--r--library/compiler-builtins/compiler-builtins/src/float/div.rs2
-rw-r--r--library/compiler-builtins/compiler-builtins/src/float/mul.rs2
-rw-r--r--library/compiler-builtins/compiler-builtins/src/float/traits.rs4
-rw-r--r--library/compiler-builtins/compiler-builtins/src/int/addsub.rs6
-rw-r--r--library/compiler-builtins/compiler-builtins/src/int/big.rs4
-rw-r--r--library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs64
-rw-r--r--library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs25
-rw-r--r--library/compiler-builtins/compiler-builtins/src/int/traits.rs273
-rw-r--r--library/compiler-builtins/libm/src/math/support/int_traits.rs9
12 files changed, 168 insertions, 345 deletions
diff --git a/library/compiler-builtins/builtins-test/src/lib.rs b/library/compiler-builtins/builtins-test/src/lib.rs
index c596ac21380..f1673133be2 100644
--- a/library/compiler-builtins/builtins-test/src/lib.rs
+++ b/library/compiler-builtins/builtins-test/src/lib.rs
@@ -40,6 +40,75 @@ pub const N: u32 = if cfg!(target_arch = "x86_64") && !cfg!(debug_assertions) {
     10_000
 };
 
+/// Additional constants that determine how the integer gets fuzzed.
+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::BITS);
+
+    /// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128.
+    const FUZZ_NUM: usize = {
+        let log2 = Self::BITS.ilog2() 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
@@ -92,10 +161,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
             }
@@ -107,7 +175,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
@@ -128,7 +196,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, {
diff --git a/library/compiler-builtins/compiler-builtins/src/float/add.rs b/library/compiler-builtins/compiler-builtins/src/float/add.rs
index 0426c9cc44f..43e3ae931ff 100644
--- a/library/compiler-builtins/compiler-builtins/src/float/add.rs
+++ b/library/compiler-builtins/compiler-builtins/src/float/add.rs
@@ -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
@@ -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;
 
@@ -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.
         }
@@ -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;
         }
@@ -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;
     }
 
diff --git a/library/compiler-builtins/compiler-builtins/src/float/conv.rs b/library/compiler-builtins/compiler-builtins/src/float/conv.rs
index f5427a11390..9d732f2cdbc 100644
--- a/library/compiler-builtins/compiler-builtins/src/float/conv.rs
+++ b/library/compiler-builtins/compiler-builtins/src/float/conv.rs
@@ -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)
@@ -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)
@@ -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>,
 {
@@ -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);
diff --git a/library/compiler-builtins/compiler-builtins/src/float/div.rs b/library/compiler-builtins/compiler-builtins/src/float/div.rs
index 5df637c7e0f..3e4f0e20d10 100644
--- a/library/compiler-builtins/compiler-builtins/src/float/div.rs
+++ b/library/compiler-builtins/compiler-builtins/src/float/div.rs
@@ -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
diff --git a/library/compiler-builtins/compiler-builtins/src/float/mul.rs b/library/compiler-builtins/compiler-builtins/src/float/mul.rs
index 7f1f19d9bd7..c811f140670 100644
--- a/library/compiler-builtins/compiler-builtins/src/float/mul.rs
+++ b/library/compiler-builtins/compiler-builtins/src/float/mul.rs
@@ -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);
         }
diff --git a/library/compiler-builtins/compiler-builtins/src/float/traits.rs b/library/compiler-builtins/compiler-builtins/src/float/traits.rs
index 8ccaa7bcbd7..a30d20900b1 100644
--- a/library/compiler-builtins/compiler-builtins/src/float/traits.rs
+++ b/library/compiler-builtins/compiler-builtins/src/float/traits.rs
@@ -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;
diff --git a/library/compiler-builtins/compiler-builtins/src/int/addsub.rs b/library/compiler-builtins/compiler-builtins/src/int/addsub.rs
index 1f84e8eb1e1..b2b21fc2c44 100644
--- a/library/compiler-builtins/compiler-builtins/src/int/addsub.rs
+++ b/library/compiler-builtins/compiler-builtins/src/int/addsub.rs
@@ -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()))
@@ -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);
@@ -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);
diff --git a/library/compiler-builtins/compiler-builtins/src/int/big.rs b/library/compiler-builtins/compiler-builtins/src/int/big.rs
index 1402efb8ed4..8e06009090c 100644
--- a/library/compiler-builtins/compiler-builtins/src/int/big.rs
+++ b/library/compiler-builtins/compiler-builtins/src/int/big.rs
@@ -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;
@@ -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;
diff --git a/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs b/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs
index 112f4d03613..aa5cb39935a 100644
--- a/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs
+++ b/library/compiler-builtins/compiler-builtins/src/int/leading_zeros.rs
@@ -9,11 +9,14 @@ pub use implementation::{leading_zeros_default, leading_zeros_riscv};
 pub(crate) use implementation::{leading_zeros_default, leading_zeros_riscv};
 
 mod implementation {
-    use crate::int::{CastInto, Int};
+    use crate::int::{CastFrom, Int};
 
     /// Returns the number of leading binary zeros in `x`.
     #[allow(dead_code)]
-    pub fn leading_zeros_default<T: Int + CastInto<usize>>(x: T) -> usize {
+    pub fn leading_zeros_default<I: Int>(x: I) -> usize
+    where
+        usize: CastFrom<I>,
+    {
         // The basic idea is to test if the higher bits of `x` are zero and bisect the number
         // of leading zeros. It is possible for all branches of the bisection to use the same
         // code path by conditionally shifting the higher parts down to let the next bisection
@@ -23,44 +26,48 @@ mod implementation {
         // because it simplifies the final bisection step.
         let mut x = x;
         // the number of potential leading zeros
-        let mut z = T::BITS as usize;
+        let mut z = I::BITS as usize;
         // a temporary
-        let mut t: T;
+        let mut t: I;
 
-        const { assert!(T::BITS <= 64) };
-        if T::BITS >= 64 {
+        const { assert!(I::BITS <= 64) };
+        if I::BITS >= 64 {
             t = x >> 32;
-            if t != T::ZERO {
+            if t != I::ZERO {
                 z -= 32;
                 x = t;
             }
         }
-        if T::BITS >= 32 {
+        if I::BITS >= 32 {
             t = x >> 16;
-            if t != T::ZERO {
+            if t != I::ZERO {
                 z -= 16;
                 x = t;
             }
         }
-        const { assert!(T::BITS >= 16) };
+        const { assert!(I::BITS >= 16) };
         t = x >> 8;
-        if t != T::ZERO {
+        if t != I::ZERO {
             z -= 8;
             x = t;
         }
         t = x >> 4;
-        if t != T::ZERO {
+        if t != I::ZERO {
             z -= 4;
             x = t;
         }
         t = x >> 2;
-        if t != T::ZERO {
+        if t != I::ZERO {
             z -= 2;
             x = t;
         }
         // the last two bisections are combined into one conditional
         t = x >> 1;
-        if t != T::ZERO { z - 2 } else { z - x.cast() }
+        if t != I::ZERO {
+            z - 2
+        } else {
+            z - usize::cast_from(x)
+        }
 
         // We could potentially save a few cycles by using the LUT trick from
         // "https://embeddedgurus.com/state-space/2014/09/
@@ -82,10 +89,13 @@ mod implementation {
 
     /// Returns the number of leading binary zeros in `x`.
     #[allow(dead_code)]
-    pub fn leading_zeros_riscv<T: Int + CastInto<usize>>(x: T) -> usize {
+    pub fn leading_zeros_riscv<I: Int>(x: I) -> usize
+    where
+        usize: CastFrom<I>,
+    {
         let mut x = x;
         // the number of potential leading zeros
-        let mut z = T::BITS;
+        let mut z = I::BITS;
         // a temporary
         let mut t: u32;
 
@@ -97,11 +107,11 @@ mod implementation {
         // right). If we try to save an instruction by using `x < imm` for each bisection, we
         // have to shift `x` left and compare with powers of two approaching `usize::MAX + 1`,
         // but the immediate will never fit into 12 bits and never save an instruction.
-        const { assert!(T::BITS <= 64) };
-        if T::BITS >= 64 {
+        const { assert!(I::BITS <= 64) };
+        if I::BITS >= 64 {
             // If the upper 32 bits of `x` are not all 0, `t` is set to `1 << 5`, otherwise
             // `t` is set to 0.
-            t = ((x >= (T::ONE << 32)) as u32) << 5;
+            t = ((x >= (I::ONE << 32)) as u32) << 5;
             // If `t` was set to `1 << 5`, then the upper 32 bits are shifted down for the
             // next step to process.
             x >>= t;
@@ -109,27 +119,27 @@ mod implementation {
             // leading zeros
             z -= t;
         }
-        if T::BITS >= 32 {
-            t = ((x >= (T::ONE << 16)) as u32) << 4;
+        if I::BITS >= 32 {
+            t = ((x >= (I::ONE << 16)) as u32) << 4;
             x >>= t;
             z -= t;
         }
-        const { assert!(T::BITS >= 16) };
-        t = ((x >= (T::ONE << 8)) as u32) << 3;
+        const { assert!(I::BITS >= 16) };
+        t = ((x >= (I::ONE << 8)) as u32) << 3;
         x >>= t;
         z -= t;
-        t = ((x >= (T::ONE << 4)) as u32) << 2;
+        t = ((x >= (I::ONE << 4)) as u32) << 2;
         x >>= t;
         z -= t;
-        t = ((x >= (T::ONE << 2)) as u32) << 1;
+        t = ((x >= (I::ONE << 2)) as u32) << 1;
         x >>= t;
         z -= t;
-        t = (x >= (T::ONE << 1)) as u32;
+        t = (x >= (I::ONE << 1)) as u32;
         x >>= t;
         z -= t;
         // All bits except the LSB are guaranteed to be zero for this final bisection step.
         // If `x != 0` then `x == 1` and subtracts one potential zero from `z`.
-        z as usize - x.cast()
+        z as usize - usize::cast_from(x)
     }
 }
 
diff --git a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs
index c45d6b1cfe8..8f63c22c8f7 100644
--- a/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs
+++ b/library/compiler-builtins/compiler-builtins/src/int/trailing_zeros.rs
@@ -4,33 +4,38 @@ pub use implementation::trailing_zeros;
 pub(crate) use implementation::trailing_zeros;
 
 mod implementation {
-    use crate::int::{CastInto, Int};
+    use crate::int::{CastFrom, Int};
 
     /// Returns number of trailing binary zeros in `x`.
     #[allow(dead_code)]
-    pub fn trailing_zeros<T: Int + CastInto<u32> + CastInto<u16> + CastInto<u8>>(x: T) -> usize {
+    pub fn trailing_zeros<I: Int>(x: I) -> usize
+    where
+        u32: CastFrom<I>,
+        u16: CastFrom<I>,
+        u8: CastFrom<I>,
+    {
         let mut x = x;
         let mut r: u32 = 0;
         let mut t: u32;
 
-        const { assert!(T::BITS <= 64) };
-        if T::BITS >= 64 {
-            r += ((CastInto::<u32>::cast(x) == 0) as u32) << 5; // if (x has no 32 small bits) t = 32 else 0
+        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
             x >>= r; // remove 32 zero bits
         }
 
-        if T::BITS >= 32 {
-            t = ((CastInto::<u16>::cast(x) == 0) as u32) << 4; // if (x has no 16 small bits) t = 16 else 0
+        if I::BITS >= 32 {
+            t = ((u16::cast_from(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!(T::BITS >= 16) };
-        t = ((CastInto::<u8>::cast(x) == 0) as u32) << 3;
+        const { assert!(I::BITS >= 16) };
+        t = ((u8::cast_from(x) == 0) as u32) << 3;
         x >>= t; // x = [0 - 0xFF] + higher garbage bits
         r += t;
 
-        let mut x: u8 = x.cast();
+        let mut x: u8 = x.cast_lossy();
 
         t = (((x & 0x0F) == 0) as u32) << 2;
         x >>= t; // x = [0 - 0xF] + higher garbage bits
diff --git a/library/compiler-builtins/compiler-builtins/src/int/traits.rs b/library/compiler-builtins/compiler-builtins/src/int/traits.rs
index 152cb2eee2e..b474df366eb 100644
--- a/library/compiler-builtins/compiler-builtins/src/int/traits.rs
+++ b/library/compiler-builtins/compiler-builtins/src/int/traits.rs
@@ -1,275 +1,4 @@
-use core::ops;
-
-/// Minimal integer implementations needed on all integer types, including wide integers.
-#[allow(dead_code)]
-pub trait MinInt:
-    Copy
-    + core::fmt::Debug
-    + ops::BitOr<Output = Self>
-    + ops::Not<Output = Self>
-    + ops::Shl<u32, Output = Self>
-{
-    /// Type with the same width but other signedness
-    type OtherSign: MinInt;
-    /// Unsigned version of Self
-    type UnsignedInt: MinInt;
-
-    /// If `Self` is a signed integer
-    const SIGNED: bool;
-
-    /// The bitwidth of the int type
-    const BITS: u32;
-
-    const ZERO: Self;
-    const ONE: Self;
-    const MIN: Self;
-    const MAX: Self;
-}
-
-/// Trait for some basic operations on integers
-#[allow(dead_code)]
-pub trait Int:
-    MinInt
-    + PartialEq
-    + PartialOrd
-    + ops::AddAssign
-    + ops::SubAssign
-    + ops::BitAndAssign
-    + ops::BitOrAssign
-    + ops::BitXorAssign
-    + ops::ShlAssign<i32>
-    + ops::ShrAssign<u32>
-    + ops::Add<Output = Self>
-    + ops::Sub<Output = Self>
-    + ops::Mul<Output = Self>
-    + ops::Div<Output = Self>
-    + ops::Shr<u32, Output = Self>
-    + ops::BitXor<Output = Self>
-    + ops::BitAnd<Output = Self>
-{
-    /// 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))
-        }
-    };
-
-    fn unsigned(self) -> Self::UnsignedInt;
-    fn from_unsigned(unsigned: Self::UnsignedInt) -> Self;
-    fn unsigned_abs(self) -> Self::UnsignedInt;
-
-    fn from_bool(b: bool) -> Self;
-
-    /// Prevents the need for excessive conversions between signed and unsigned
-    fn logical_shr(self, other: u32) -> Self;
-
-    /// Absolute difference between two integers.
-    fn abs_diff(self, other: Self) -> Self::UnsignedInt;
-
-    // copied from primitive integers, but put in a trait
-    fn is_zero(self) -> bool;
-    fn wrapping_neg(self) -> Self;
-    fn wrapping_add(self, other: Self) -> Self;
-    fn wrapping_mul(self, other: Self) -> Self;
-    fn wrapping_sub(self, other: Self) -> Self;
-    fn wrapping_shl(self, other: u32) -> Self;
-    fn wrapping_shr(self, other: u32) -> Self;
-    fn rotate_left(self, other: u32) -> Self;
-    fn overflowing_add(self, other: Self) -> (Self, bool);
-    fn leading_zeros(self) -> u32;
-    fn ilog2(self) -> u32;
-}
-
-pub(crate) 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
-}
-
-macro_rules! int_impl_common {
-    ($ty:ty) => {
-        fn from_bool(b: bool) -> Self {
-            b as $ty
-        }
-
-        fn logical_shr(self, other: u32) -> Self {
-            Self::from_unsigned(self.unsigned().wrapping_shr(other))
-        }
-
-        fn is_zero(self) -> bool {
-            self == Self::ZERO
-        }
-
-        fn wrapping_neg(self) -> Self {
-            <Self>::wrapping_neg(self)
-        }
-
-        fn wrapping_add(self, other: Self) -> Self {
-            <Self>::wrapping_add(self, other)
-        }
-
-        fn wrapping_mul(self, other: Self) -> Self {
-            <Self>::wrapping_mul(self, other)
-        }
-        fn wrapping_sub(self, other: Self) -> Self {
-            <Self>::wrapping_sub(self, other)
-        }
-
-        fn wrapping_shl(self, other: u32) -> Self {
-            <Self>::wrapping_shl(self, other)
-        }
-
-        fn wrapping_shr(self, other: u32) -> Self {
-            <Self>::wrapping_shr(self, other)
-        }
-
-        fn rotate_left(self, other: u32) -> Self {
-            <Self>::rotate_left(self, other)
-        }
-
-        fn overflowing_add(self, other: Self) -> (Self, bool) {
-            <Self>::overflowing_add(self, other)
-        }
-
-        fn leading_zeros(self) -> u32 {
-            <Self>::leading_zeros(self)
-        }
-
-        fn ilog2(self) -> u32 {
-            <Self>::ilog2(self)
-        }
-    };
-}
-
-macro_rules! int_impl {
-    ($ity:ty, $uty:ty) => {
-        impl MinInt for $uty {
-            type OtherSign = $ity;
-            type UnsignedInt = $uty;
-
-            const BITS: u32 = <Self as MinInt>::ZERO.count_zeros();
-            const SIGNED: bool = Self::MIN != Self::ZERO;
-
-            const ZERO: Self = 0;
-            const ONE: Self = 1;
-            const MIN: Self = <Self>::MIN;
-            const MAX: Self = <Self>::MAX;
-        }
-
-        impl Int for $uty {
-            fn unsigned(self) -> $uty {
-                self
-            }
-
-            // It makes writing macros easier if this is implemented for both signed and unsigned
-            #[allow(clippy::wrong_self_convention)]
-            fn from_unsigned(me: $uty) -> Self {
-                me
-            }
-
-            fn unsigned_abs(self) -> Self {
-                self
-            }
-
-            fn abs_diff(self, other: Self) -> Self {
-                self.abs_diff(other)
-            }
-
-            int_impl_common!($uty);
-        }
-
-        impl MinInt for $ity {
-            type OtherSign = $uty;
-            type UnsignedInt = $uty;
-
-            const BITS: u32 = <Self as MinInt>::ZERO.count_zeros();
-            const SIGNED: bool = Self::MIN != Self::ZERO;
-
-            const ZERO: Self = 0;
-            const ONE: Self = 1;
-            const MIN: Self = <Self>::MIN;
-            const MAX: Self = <Self>::MAX;
-        }
-
-        impl Int for $ity {
-            fn unsigned(self) -> $uty {
-                self as $uty
-            }
-
-            fn from_unsigned(me: $uty) -> Self {
-                me as $ity
-            }
-
-            fn unsigned_abs(self) -> Self::UnsignedInt {
-                self.unsigned_abs()
-            }
-
-            fn abs_diff(self, other: Self) -> $uty {
-                self.abs_diff(other)
-            }
-
-            int_impl_common!($ity);
-        }
-    };
-}
-
-int_impl!(isize, usize);
-int_impl!(i8, u8);
-int_impl!(i16, u16);
-int_impl!(i32, u32);
-int_impl!(i64, u64);
-int_impl!(i128, u128);
+pub use crate::support::{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.
diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs
index 3ec1faba170..fa9e06066d7 100644
--- a/library/compiler-builtins/libm/src/math/support/int_traits.rs
+++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs
@@ -78,6 +78,7 @@ pub trait Int:
     fn unsigned(self) -> Self::Unsigned;
     fn from_unsigned(unsigned: Self::Unsigned) -> Self;
     fn abs(self) -> Self;
+    fn unsigned_abs(self) -> Self::Unsigned;
 
     fn from_bool(b: bool) -> Self;
 
@@ -203,6 +204,10 @@ macro_rules! int_impl {
                 unimplemented!()
             }
 
+            fn unsigned_abs(self) -> Self {
+                unimplemented!()
+            }
+
             // It makes writing macros easier if this is implemented for both signed and unsigned
             #[allow(clippy::wrong_self_convention)]
             fn from_unsigned(me: $uty) -> Self {
@@ -242,6 +247,10 @@ macro_rules! int_impl {
                 self.abs()
             }
 
+            fn unsigned_abs(self) -> Self::Unsigned {
+                self.unsigned_abs()
+            }
+
             fn from_unsigned(me: $uty) -> Self {
                 me as $ity
             }