Macro-ify udivmod

master
est31 2017-01-05 02:22:44 +01:00
parent 1e27c3f937
commit 8fe50d813c
3 changed files with 163 additions and 141 deletions

View File

@ -1,3 +1,14 @@
macro_rules! hty {
($ty:ty) => {
<$ty as LargeInt>::HighHalf
}
}
macro_rules! os_ty {
($ty:ty) => {
<$ty as Int>::OtherSign
}
}
pub mod mul; pub mod mul;
pub mod sdiv; pub mod sdiv;
@ -6,6 +17,8 @@ pub mod udiv;
/// Trait for some basic operations on integers /// Trait for some basic operations on integers
pub trait Int { pub trait Int {
/// Type with the same width but other signedness
type OtherSign;
/// Returns the bitwidth of the int type /// Returns the bitwidth of the int type
fn bits() -> u32; fn bits() -> u32;
} }
@ -13,11 +26,13 @@ pub trait Int {
macro_rules! int_impl { macro_rules! int_impl {
($ity:ty, $sty:ty, $bits:expr) => { ($ity:ty, $sty:ty, $bits:expr) => {
impl Int for $ity { impl Int for $ity {
type OtherSign = $sty;
fn bits() -> u32 { fn bits() -> u32 {
$bits $bits
} }
} }
impl Int for $sty { impl Int for $sty {
type OtherSign = $ity;
fn bits() -> u32 { fn bits() -> u32 {
$bits $bits
} }

View File

@ -2,7 +2,7 @@ use int::LargeInt;
use int::Int; use int::Int;
macro_rules! mul { macro_rules! mul {
($intrinsic:ident: $ty:ty, $tyh:ty) => { ($intrinsic:ident: $ty:ty) => {
/// Returns `a * b` /// Returns `a * b`
#[cfg_attr(not(test), no_mangle)] #[cfg_attr(not(test), no_mangle)]
pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty { pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty {
@ -13,15 +13,15 @@ macro_rules! mul {
low &= lower_mask; low &= lower_mask;
t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask); t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask);
low += (t & lower_mask) << half_bits; low += (t & lower_mask) << half_bits;
let mut high = (t >> half_bits) as $tyh; let mut high = (t >> half_bits) as hty!($ty);
t = low >> half_bits; t = low >> half_bits;
low &= lower_mask; low &= lower_mask;
t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask); t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask);
low += (t & lower_mask) << half_bits; low += (t & lower_mask) << half_bits;
high += (t >> half_bits) as $tyh; high += (t >> half_bits) as hty!($ty);
high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as $tyh; high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as hty!($ty);
high = high.wrapping_add(a.high().wrapping_mul(b.low() as $tyh)) high = high.wrapping_add(a.high().wrapping_mul(b.low() as hty!($ty)))
.wrapping_add((a.low() as $tyh).wrapping_mul(b.high())); .wrapping_add((a.low() as hty!($ty)).wrapping_mul(b.high()));
<$ty>::from_parts(low, high) <$ty>::from_parts(low, high)
} }
} }
@ -73,9 +73,9 @@ macro_rules! mulo {
} }
#[cfg(not(all(feature = "c", target_arch = "x86")))] #[cfg(not(all(feature = "c", target_arch = "x86")))]
mul!(__muldi3: u64, u32); mul!(__muldi3: u64);
mul!(__multi3: i128, i64); mul!(__multi3: i128);
mulo!(__mulosi4: i32); mulo!(__mulosi4: i32);
mulo!(__mulodi4: i64); mulo!(__mulodi4: i64);

View File

@ -114,152 +114,159 @@ pub extern "C" fn __umoddi3(a: u64, b: u64) -> u64 {
rem rem
} }
/// Returns `n / d` and sets `*rem = n % d` macro_rules! udivmod_inner {
#[cfg_attr(not(test), no_mangle)] ($n:expr, $d:expr, $rem:expr, $ty:ty) => {{
pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { let (n, d, rem) = ($n, $d, $rem);
// NOTE X is unknown, K != 0 // NOTE X is unknown, K != 0
if n.high() == 0 { if n.high() == 0 {
if d.high() == 0 { if d.high() == 0 {
// 0 X // 0 X
// --- // ---
// 0 X // 0 X
if let Some(rem) = rem { if let Some(rem) = rem {
*rem = u64::from(urem!(n.low(), d.low())); *rem = <$ty>::from(urem!(n.low(), d.low()));
} }
return u64::from(udiv!(n.low(), d.low())); return <$ty>::from(udiv!(n.low(), d.low()));
} else {
// 0 X
// ---
// K X
if let Some(rem) = rem {
*rem = n;
}
return 0;
};
}
let mut sr;
let mut q;
let mut r;
if d.low() == 0 {
if d.high() == 0 {
// K X
// ---
// 0 0
// NOTE This should be unreachable in safe Rust because the program will panic before
// this intrinsic is called
unsafe {
intrinsics::abort()
}
}
if n.low() == 0 {
// K 0
// ---
// K 0
if let Some(rem) = rem {
*rem = u64::from_parts(0, urem!(n.high(), d.high()));
}
return u64::from(udiv!(n.high(), d.high()));
}
// K K
// ---
// K 0
if d.high().is_power_of_two() {
if let Some(rem) = rem {
*rem = u64::from_parts(n.low(), n.high() & (d.high() - 1));
}
return u64::from(n.high() >> d.high().trailing_zeros());
}
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());
// D > N
if sr > u32::bits() - 2 {
if let Some(rem) = rem {
*rem = n;
}
return 0;
}
sr += 1;
// 1 <= sr <= u32::bits() - 1
q = n << (u64::bits() - sr);
r = n >> sr;
} else if d.high() == 0 {
// K X
// ---
// 0 K
if d.low().is_power_of_two() {
if let Some(rem) = rem {
*rem = u64::from(n.low() & (d.low() - 1));
}
if d.low() == 1 {
return n;
} else { } else {
let sr = d.low().trailing_zeros(); // 0 X
return n >> sr; // ---
// K X
if let Some(rem) = rem {
*rem = n;
}
return 0;
}; };
} }
sr = 1 + u32::bits() + d.low().leading_zeros() - n.high().leading_zeros(); let mut sr;
let mut q;
let mut r;
// 2 <= sr <= u64::bits() - 1 if d.low() == 0 {
q = n << (u64::bits() - sr); if d.high() == 0 {
r = n >> sr; // K X
} else { // ---
// K X // 0 0
// --- // NOTE This should be unreachable in safe Rust because the program will panic before
// K K // this intrinsic is called
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); unsafe {
intrinsics::abort()
// D > N }
if sr > u32::bits() - 1 {
if let Some(rem) = rem {
*rem = n;
} }
return 0;
if n.low() == 0 {
// K 0
// ---
// K 0
if let Some(rem) = rem {
*rem = <$ty>::from_parts(0, urem!(n.high(), d.high()));
}
return <$ty>::from(udiv!(n.high(), d.high()));
}
// K K
// ---
// K 0
if d.high().is_power_of_two() {
if let Some(rem) = rem {
*rem = <$ty>::from_parts(n.low(), n.high() & (d.high() - 1));
}
return <$ty>::from(n.high() >> d.high().trailing_zeros());
}
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());
// D > N
if sr > <hty!($ty)>::bits() - 2 {
if let Some(rem) = rem {
*rem = n;
}
return 0;
}
sr += 1;
// 1 <= sr <= <hty!($ty)>::bits() - 1
q = n << (<$ty>::bits() - sr);
r = n >> sr;
} else if d.high() == 0 {
// K X
// ---
// 0 K
if d.low().is_power_of_two() {
if let Some(rem) = rem {
*rem = <$ty>::from(n.low() & (d.low() - 1));
}
if d.low() == 1 {
return n;
} else {
let sr = d.low().trailing_zeros();
return n >> sr;
};
}
sr = 1 + <hty!($ty)>::bits() + d.low().leading_zeros() - n.high().leading_zeros();
// 2 <= sr <= u64::bits() - 1
q = n << (<$ty>::bits() - sr);
r = n >> sr;
} else {
// K X
// ---
// K K
sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros());
// D > N
if sr > <hty!($ty)>::bits() - 1 {
if let Some(rem) = rem {
*rem = n;
}
return 0;
}
sr += 1;
// 1 <= sr <= <hty!($ty)>::bits()
q = n << (<$ty>::bits() - sr);
r = n >> sr;
} }
sr += 1; // Not a special case
// q and r are initialized with
// q = n << (u64::bits() - sr)
// r = n >> sr
// 1 <= sr <= u64::bits() - 1
let mut carry = 0;
// 1 <= sr <= u32::bits() for _ in 0..sr {
q = n << (u64::bits() - sr); // r:q = ((r:q) << 1) | carry
r = n >> sr; r = (r << 1) | (q >> (<$ty>::bits() - 1));
} q = (q << 1) | carry as $ty;
// Not a special case // carry = 0
// q and r are initialized with // if r >= d {
// q = n << (u64::bits() - sr) // r -= d;
// r = n >> sr // carry = 1;
// 1 <= sr <= u64::bits() - 1 // }
let mut carry = 0; let s = (d.wrapping_sub(r).wrapping_sub(1)) as os_ty!($ty) >> (<$ty>::bits() - 1);
carry = (s & 1) as hty!($ty);
r -= d & s as $ty;
}
for _ in 0..sr { if let Some(rem) = rem {
// r:q = ((r:q) << 1) | carry *rem = r;
r = (r << 1) | (q >> (u64::bits() - 1)); }
q = (q << 1) | carry as u64; (q << 1) | carry as $ty
}}
}
// carry = 0 /// Returns `n / d` and sets `*rem = n % d`
// if r >= d { #[cfg_attr(not(test), no_mangle)]
// r -= d; pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 {
// carry = 1; udivmod_inner!(n, d, rem, u64)
// }
let s = (d.wrapping_sub(r).wrapping_sub(1)) as i64 >> (u64::bits() - 1);
carry = (s & 1) as u32;
r -= d & s as u64;
}
if let Some(rem) = rem {
*rem = r;
}
(q << 1) | carry as u64
} }
#[cfg(test)] #[cfg(test)]