From 950564607d778d584aaaeda62632eca2b9fcb1b7 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Aug 2016 19:12:37 -0500 Subject: [PATCH] port udivmoddi4 and __aeabi_uldivmod --- Cargo.toml | 3 + src/arm.rs | 17 ++++ src/lib.rs | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/test.rs | 21 ++++- 4 files changed, 299 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 663d9b1..dfd1fde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,3 +2,6 @@ authors = ["Jorge Aparicio "] name = "rustc_builtins" version = "0.1.0" + +[dev-dependencies] +quickcheck = "0.3.1" diff --git a/src/arm.rs b/src/arm.rs index 7beb91a..55e9bb9 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -1,3 +1,20 @@ +use core::mem; + +#[repr(C)] +pub struct u64x2 { + a: u64, + b: u64, +} + +#[no_mangle] +pub unsafe extern "aapcs" fn __aeabi_uldivmod(num: u64, den: u64) -> u64x2 { + + let mut rem = mem::uninitialized(); + let quot = ::__udivmoddi4(num, den, &mut rem); + + u64x2 { a: quot, b: rem } +} + extern "C" { fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8; diff --git a/src/lib.rs b/src/lib.rs index 3ef1354..e5caa83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,9 @@ #[cfg(test)] extern crate core; +#[cfg(test)] +#[macro_use] +extern crate quickcheck; #[cfg(target_arch = "arm")] pub mod arm; @@ -15,6 +18,8 @@ pub mod arm; #[cfg(test)] mod test; +use core::mem; + /// Trait for some basic operations on integers trait Int { fn bits() -> usize; @@ -93,3 +98,257 @@ absv_i2!(__absvsi2: i32); absv_i2!(__absvdi2: i64); // TODO(rust-lang/35118)? // absv_i2!(__absvti2, i128); + +#[no_mangle] +pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { + #[cfg(target_endian = "little")] + #[repr(C)] + #[derive(Debug)] + struct words { + low: u32, + high: u32, + } + + #[cfg(target_endian = "big")] + #[repr(C)] + #[derive(Debug)] + struct words { + high: u32, + low: u32, + } + + impl words { + fn all(&mut self) -> &mut u64 { + unsafe { mem::transmute(self) } + } + + fn u64(&self) -> u64 { + unsafe { *(self as *const _ as *const u64) } + } + } + + impl From for words { + fn from(x: u64) -> words { + unsafe { mem::transmute(x) } + } + } + + let u32_bits = u32::bits() as u32; + let u64_bits = u64::bits() as u32; + + let n = words::from(a); + let d = words::from(b); + + // NOTE X is unknown, K != 0 + if n.high == 0 { + return if d.high == 0 { + // 0 X + // --- + // 0 X + + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = u64::from(n.low % d.low); + } + u64::from(n.low / d.low) + } else + // d.high != 0 + { + // 0 X + // --- + // K X + + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = u64::from(n.low); + } + 0 + }; + } + + let mut sr; + // NOTE IMO it should be possible to leave these "uninitialized" (just declare them here) + // because these variables get initialized below, but if I do that the compiler complains about + // them being used before being initialized. + let mut q = words { low: 0, high: 0 }; + let mut r = words { low: 0, high: 0 }; + + // n.high != 0 + if d.low == 0 { + if d.high == 0 { + // K X + // --- + // 0 0 + + // NOTE copied verbatim from compiler-rt, but does division by zero even make sense? + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = u64::from(n.high % d.low); + } + return u64::from(n.high / d.low); + } + + // d.high != 0 + if n.low == 0 { + // K 0 + // --- + // K 0 + + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = words { + low: 0, + high: n.high % d.high, + } + .u64(); + } + return u64::from(n.high / d.high); + } + + // n.low != 0 + // K K + // --- + // K 0 + + if d.high.is_power_of_two() { + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = words { + low: n.low, + high: n.high & (d.high - 1), + } + .u64() + } + + 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) = unsafe { rem.as_mut() } { + *rem = n.u64(); + } + return 0; + } + + sr = sr + 1; + + // 1 <= sr <= u32_bits - 1 + // *q.all() = n.u64() << (u64_bits - sr); + q.low = 0; + q.high = n.low << (u32_bits - sr); + // *r.all() = n.u64() >> sr + r.high = n.high >> sr; + r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + } else + // d.low != 0 + { + if d.high == 0 { + // K X + // --- + // 0 K + if d.low.is_power_of_two() { + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = u64::from(n.low & (d.low - 1)); + } + + return if d.low == 1 { + n.u64() + } else { + let sr = d.low.trailing_zeros(); + words { + low: (n.high << (u32_bits - sr)) | (n.low >> sr), + high: n.high >> sr, + } + .u64() + }; + } + + sr = 1 + u32_bits + d.low.leading_zeros() - n.high.leading_zeros(); + + // 2 <= sr <= u64_bits - 1 + // *q.all() = n.u64() << (u64_bits - sr) + // *r.all() = n.u64() >> sr; + if sr == u32_bits { + q.low = 0; + q.high = n.low; + r.high = 0; + r.low = n.high; + } else if sr < u32_bits + // 2 <= sr <= u32_bits - 1 + { + q.low = 0; + q.high = n.low << (u32_bits - sr); + r.high = n.high >> sr; + r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + } else + // u32_bits + 1 <= sr <= u64_bits - 1 + { + q.low = n.low << (u64_bits - sr); + q.high = (n.high << (u64_bits - sr)) | (n.low >> (sr - u32_bits)); + r.high = 0; + r.low = n.high >> (sr - u32_bits); + } + + } else + // d.high != 0 + { + // K X + // --- + // K K + + sr = d.high.leading_zeros().wrapping_sub(n.high.leading_zeros()); + + // D > N + if sr > u32_bits - 1 { + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = a; + return 0; + } + } + + sr += 1; + + // 1 <= sr <= u32_bits + // *q.all() = n.u64() << (u64_bits - sr) + q.low = 0; + if sr == u32_bits { + q.high = n.low; + r.high = 0; + r.low = n.high; + } else { + q.high = n.low << (u32_bits - sr); + r.high = n.high >> sr; + r.low = (n.high << (u32_bits - sr)) | (n.low >> sr); + } + } + } + + // Not a special case + // q and r are initialized with + // *q.all() = n.u64() << (u64_bits - sr) + // *.r.all() = n.u64() >> sr + // 1 <= sr <= u64_bits - 1 + let mut carry = 0; + + for _ in 0..sr { + // r:q = ((r:q) << 1) | carry + r.high = (r.high << 1) | (r.low >> (u32_bits - 1)); + r.low = (r.low << 1) | (q.high >> (u32_bits - 1)); + q.high = (q.high << 1) | (q.low >> (u32_bits - 1)); + q.low = (q.low << 1) | carry; + + // carry = 0 + // if r.u64() >= d.u64() { + // *r.all() -= d.u64(); + // carry = 1; + // } + + let s = (d.u64().wrapping_sub(r.u64()).wrapping_sub(1)) as i64 >> (u64_bits - 1); + carry = (s & 1) as u32; + *r.all() -= d.u64() & s as u64; + } + + *q.all() = (q.u64() << 1) | carry as u64; + if let Some(rem) = unsafe { rem.as_mut() } { + *rem = r.u64(); + } + q.u64() +} diff --git a/src/test.rs b/src/test.rs index 9abfec7..8d5a42c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,6 @@ -use std::panic; +use std::{mem, panic}; + +use quickcheck::TestResult; macro_rules! absv_i2 { ($intrinsic:ident: $ty:ident) => { @@ -23,3 +25,20 @@ absv_i2!(__absvsi2: i32); absv_i2!(__absvdi2: i64); // TODO(rust-lang/35118)? // absv_i2!(__absvti2: i128); + +quickcheck! { + fn udivmoddi4(a: (u32, u32), b: (u32, u32)) -> TestResult { + let (a, b) = unsafe { + (mem::transmute(a), mem::transmute(b)) + }; + + if b == 0 { + TestResult::discard() + } else { + let mut r = 0; + let q = ::__udivmoddi4(a, b, &mut r); + + TestResult::from_bool(q * b + r == a) + } + } +}