Skip to content

Commit

Permalink
Remove LargeInt and improve testing
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronKutch committed Oct 13, 2020
1 parent eae97ba commit 8ec278e
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 127 deletions.
27 changes: 9 additions & 18 deletions src/int/addsub.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,16 @@
use int::Int;
use int::LargeInt;
use int::{DInt, HInt, Int};

trait UAddSub: LargeInt {
trait UAddSub: DInt {
fn uadd(self, other: Self) -> Self {
let (low, carry) = self.low().overflowing_add(other.low());
let high = self.high().wrapping_add(other.high());
let carry = if carry {
Self::HighHalf::ONE
} else {
Self::HighHalf::ZERO
};
Self::from_parts(low, high.wrapping_add(carry))
let (lo, carry) = self.lo().overflowing_add(other.lo());
let hi = self.hi().wrapping_add(other.hi());
let carry = if carry { Self::H::ONE } else { Self::H::ZERO };
Self::H::from_lo_hi(lo, hi.wrapping_add(carry))
}
fn uadd_one(self) -> Self {
let (low, carry) = self.low().overflowing_add(Self::LowHalf::ONE);
let carry = if carry {
Self::HighHalf::ONE
} else {
Self::HighHalf::ZERO
};
Self::from_parts(low, self.high().wrapping_add(carry))
let (lo, carry) = self.lo().overflowing_add(Self::H::ONE);
let carry = if carry { Self::H::ONE } else { Self::H::ZERO };
Self::H::from_lo_hi(lo, self.hi().wrapping_add(carry))
}
fn usub(self, other: Self) -> Self {
let uneg = (!other).uadd_one();
Expand Down
44 changes: 0 additions & 44 deletions src/int/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,50 +384,6 @@ impl_h_int!(
i64 u64 i128
);

/// Trait to convert an integer to/from smaller parts
pub(crate) trait LargeInt: Int {
type LowHalf: Int;
type HighHalf: Int;

fn low(self) -> Self::LowHalf;
fn low_as_high(low: Self::LowHalf) -> Self::HighHalf;
fn high(self) -> Self::HighHalf;
fn high_as_low(low: Self::HighHalf) -> Self::LowHalf;
fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self;
}

macro_rules! large_int {
($ty:ty, $tylow:ty, $tyhigh:ty, $halfbits:expr) => {
impl LargeInt for $ty {
type LowHalf = $tylow;
type HighHalf = $tyhigh;

fn low(self) -> $tylow {
self as $tylow
}
fn low_as_high(low: $tylow) -> $tyhigh {
low as $tyhigh
}
fn high(self) -> $tyhigh {
(self >> $halfbits) as $tyhigh
}
fn high_as_low(high: $tyhigh) -> $tylow {
high as $tylow
}
fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
low as $ty | ((high as $ty) << $halfbits)
}
}
};
}

large_int!(u32, u16, u16, 16);
large_int!(i32, u16, i16, 16);
large_int!(u64, u32, u32, 32);
large_int!(i64, u32, i32, 32);
large_int!(u128, u64, u64, 64);
large_int!(i128, u64, i64, 64);

/// Trait to express (possibly lossy) casting of integers
pub(crate) trait CastInto<T: Copy>: Copy {
fn cast(self) -> T;
Expand Down
45 changes: 24 additions & 21 deletions src/int/mul.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
use int::LargeInt;
use int::{DInt, HInt, Int};

trait Mul: LargeInt {
fn mul(self, other: Self) -> Self {
let half_bits = Self::BITS / 4;
let lower_mask = !<<Self as LargeInt>::LowHalf>::ZERO >> half_bits;
let mut low = (self.low() & lower_mask).wrapping_mul(other.low() & lower_mask);
let mut t = low >> half_bits;
low &= lower_mask;
t += (self.low() >> half_bits).wrapping_mul(other.low() & lower_mask);
low += (t & lower_mask) << half_bits;
let mut high = Self::low_as_high(t >> half_bits);
t = low >> half_bits;
low &= lower_mask;
t += (other.low() >> half_bits).wrapping_mul(self.low() & lower_mask);
low += (t & lower_mask) << half_bits;
high += Self::low_as_high(t >> half_bits);
high += Self::low_as_high((self.low() >> half_bits).wrapping_mul(other.low() >> half_bits));
high = high
.wrapping_add(self.high().wrapping_mul(Self::low_as_high(other.low())))
.wrapping_add(Self::low_as_high(self.low()).wrapping_mul(other.high()));
Self::from_parts(low, high)
trait Mul: DInt
where
Self::H: DInt,
{
fn mul(self, rhs: Self) -> Self {
// In order to prevent infinite recursion, we cannot use the `widen_mul` in this:
//self.lo().widen_mul(rhs.lo())
// .wrapping_add(self.lo().wrapping_mul(rhs.hi()).widen_hi())
// .wrapping_add(self.hi().wrapping_mul(rhs.lo()).widen_hi())

let lhs_lo = self.lo();
let rhs_lo = rhs.lo();
// construct the widening multiplication using only `Self::H` sized multiplications
let tmp_0 = lhs_lo.lo().zero_widen_mul(rhs_lo.lo());
let tmp_1 = lhs_lo.lo().zero_widen_mul(rhs_lo.hi());
let tmp_2 = lhs_lo.hi().zero_widen_mul(rhs_lo.lo());
let tmp_3 = lhs_lo.hi().zero_widen_mul(rhs_lo.hi());
// sum up all widening partials
let mul = Self::H::from_lo_hi(tmp_0, tmp_3)
.wrapping_add(tmp_1.zero_widen() << (Self::BITS / 4))
.wrapping_add(tmp_2.zero_widen() << (Self::BITS / 4));
// add the higher partials
mul.wrapping_add(lhs_lo.wrapping_mul(rhs.hi()).widen_hi())
.wrapping_add(self.hi().wrapping_mul(rhs_lo).widen_hi())
}
}

Expand Down
72 changes: 32 additions & 40 deletions src/int/shift.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
use int::{Int, LargeInt};
use int::{DInt, HInt, Int};

trait Ashl: Int + LargeInt {
trait Ashl: DInt {
/// Returns `a << b`, requires `b < Self::BITS`
fn ashl(self, offset: u32) -> Self
where
Self: LargeInt<HighHalf = <Self as LargeInt>::LowHalf>,
{
let half_bits = Self::BITS / 2;
if offset & half_bits != 0 {
Self::from_parts(Int::ZERO, self.low() << (offset - half_bits))
} else if offset == 0 {
fn ashl(self, shl: u32) -> Self {
let n_h = Self::H::BITS;
if shl & n_h != 0 {
// we only need `self.lo()` because `self.hi()` will be shifted out entirely
(self.lo() << (shl - n_h)).widen_hi()
} else if shl == 0 {
self
} else {
Self::from_parts(
self.low() << offset,
(self.high() << offset) | (self.low() >> (half_bits - offset)),
Self::H::from_lo_hi(
self.lo() << shl,
self.lo().logical_shr(n_h - shl) | (self.hi() << shl),
)
}
}
Expand All @@ -24,25 +22,22 @@ impl Ashl for u32 {}
impl Ashl for u64 {}
impl Ashl for u128 {}

trait Ashr: Int + LargeInt {
trait Ashr: DInt {
/// Returns arithmetic `a >> b`, requires `b < Self::BITS`
fn ashr(self, offset: u32) -> Self
where
Self: LargeInt<LowHalf = <<Self as LargeInt>::HighHalf as Int>::UnsignedInt>,
{
let half_bits = Self::BITS / 2;
if offset & half_bits != 0 {
Self::from_parts(
(self.high() >> (offset - half_bits)).unsigned(),
self.high() >> (half_bits - 1),
fn ashr(self, shr: u32) -> Self {
let n_h = Self::H::BITS;
if shr & n_h != 0 {
Self::H::from_lo_hi(
self.hi() >> (shr - n_h),
// smear the sign bit
self.hi() >> (n_h - 1),
)
} else if offset == 0 {
} else if shr == 0 {
self
} else {
let high_unsigned = self.high().unsigned();
Self::from_parts(
(high_unsigned << (half_bits - offset)) | (self.low() >> offset),
self.high() >> offset,
Self::H::from_lo_hi(
self.lo().logical_shr(shr) | (self.hi() << (n_h - shr)),
self.hi() >> shr,
)
}
}
Expand All @@ -52,21 +47,18 @@ impl Ashr for i32 {}
impl Ashr for i64 {}
impl Ashr for i128 {}

trait Lshr: Int + LargeInt {
trait Lshr: DInt {
/// Returns logical `a >> b`, requires `b < Self::BITS`
fn lshr(self, offset: u32) -> Self
where
Self: LargeInt<HighHalf = <Self as LargeInt>::LowHalf>,
{
let half_bits = Self::BITS / 2;
if offset & half_bits != 0 {
Self::from_parts(self.high() >> (offset - half_bits), Int::ZERO)
} else if offset == 0 {
fn lshr(self, shr: u32) -> Self {
let n_h = Self::H::BITS;
if shr & n_h != 0 {
self.hi().logical_shr(shr - n_h).zero_widen()
} else if shr == 0 {
self
} else {
Self::from_parts(
(self.high() << (half_bits - offset)) | (self.low() >> offset),
self.high() >> offset,
Self::H::from_lo_hi(
self.lo().logical_shr(shr) | (self.hi() << (n_h - shr)),
self.hi().logical_shr(shr),
)
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,16 +284,16 @@ pub mod win64_128bit_abi_hack {

impl From<i128> for U64x2 {
fn from(i: i128) -> U64x2 {
use int::LargeInt;
use int::DInt;
let j = i as u128;
U64x2(j.low(), j.high())
U64x2(j.lo(), j.hi())
}
}

impl From<u128> for U64x2 {
fn from(i: u128) -> U64x2 {
use int::LargeInt;
U64x2(i.low(), i.high())
use int::DInt;
U64x2(i.lo(), i.hi())
}
}
}
25 changes: 25 additions & 0 deletions testcrate/tests/mul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use compiler_builtins::int::mul::{__muldi3, __multi3};

#[test]
fn mul_test() {
testcrate::fuzz_2(10_000, |x: u64, y: u64| {
let mul0 = x.wrapping_mul(y);
let mul1 = __muldi3(x, y);
if mul0 != mul1 {
panic!(
"__muldi3({}, {}): expected: {}, found: {}",
x, y, mul0, mul1
);
}
});
testcrate::fuzz_2(10_000, |x: i128, y: i128| {
let mul0 = x.wrapping_mul(y);
let mul1 = __multi3(x, y);
if mul0 != mul1 {
panic!(
"__multi3({}, {}): expected: {}, found: {}",
x, y, mul0, mul1
);
}
});
}
Loading

0 comments on commit 8ec278e

Please sign in to comment.