From 950564607d778d584aaaeda62632eca2b9fcb1b7 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Aug 2016 19:12:37 -0500 Subject: [PATCH 01/11] 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) + } + } +} From 732f1e20afb3c1df416f097f0a4d27acc71490d6 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Aug 2016 19:51:18 -0500 Subject: [PATCH 02/11] some targets need gcc to be installed --- ci/install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/install.sh b/ci/install.sh index d60571d..e163530 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -49,19 +49,19 @@ install_c_toolchain() { ;; mips-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc-mips-linux-gnu libc6-dev-mips-cross + gcc gcc-mips-linux-gnu libc6-dev-mips-cross ;; mipsel-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc-mipsel-linux-gnu libc6-dev-mipsel-cross + gcc gcc-mipsel-linux-gnu libc6-dev-mipsel-cross ;; powerpc64-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross + gcc gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross ;; powerpc64le-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross + gcc gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross ;; esac } From 39ede7ee2791f56ba5c8334351bbe288c7e61626 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Aug 2016 20:00:51 -0500 Subject: [PATCH 03/11] and libc6-dev --- ci/install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/install.sh b/ci/install.sh index e163530..9c971c6 100644 --- a/ci/install.sh +++ b/ci/install.sh @@ -49,19 +49,19 @@ install_c_toolchain() { ;; mips-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc gcc-mips-linux-gnu libc6-dev-mips-cross + gcc gcc-mips-linux-gnu libc6-dev libc6-dev-mips-cross ;; mipsel-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc gcc-mipsel-linux-gnu libc6-dev-mipsel-cross + gcc gcc-mipsel-linux-gnu libc6-dev libc6-dev-mipsel-cross ;; powerpc64-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross + gcc gcc-powerpc64-linux-gnu libc6-dev libc6-dev-ppc64-cross ;; powerpc64le-unknown-linux-gnu) apt-get install -y --no-install-recommends \ - gcc gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross + gcc gcc-powerpc64le-linux-gnu libc6-dev libc6-dev-ppc64el-cross ;; esac } From cc9d3e8e5ff74ef2379239a7229f1991f4084495 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 10 Aug 2016 23:40:35 -0500 Subject: [PATCH 04/11] port __udivmodsi4, aeabi_uidivmod and udivsi3 also rewrite these last two new aeabi intrinsics as naked functions --- src/arm.rs | 37 ++++++++++++++++++++++---------- src/lib.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/test.rs | 13 ++++++++++++ 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/arm.rs b/src/arm.rs index 55e9bb9..bcfc6c8 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -1,18 +1,33 @@ -use core::mem; +use core::intrinsics; -#[repr(C)] -pub struct u64x2 { - a: u64, - b: u64, +// TODO use `global_asm!` +#[naked] +#[no_mangle] +pub unsafe extern "aapcs" fn __aeabi_uidivmod() { + asm!("push { lr } + sub sp, sp, #4 + mov r2, sp + bl __udivmodsi4 + ldr r1, [sp] + add sp, sp, #4 + pop { pc }"); + intrinsics::unreachable(); } +// TODO use `global_asm!` +#[naked] #[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 } +pub unsafe extern "aapcs" fn __aeabi_uldivmod() { + asm!("push {r11, lr} + sub sp, sp, #16 + add r12, sp, #8 + str r12, [sp] + bl __udivmoddi4 + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r11, pc}"); + intrinsics::unreachable(); } extern "C" { diff --git a/src/lib.rs b/src/lib.rs index e5caa83..2c016a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -352,3 +352,64 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { } q.u64() } + +#[no_mangle] +pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: *mut u32) -> u32 { + let d = __udivsi3(a, b); + if let Some(rem) = unsafe {rem.as_mut()} { + *rem = a - (d*b); + } + return d; +} + +#[no_mangle] +pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { + let u32_bits = u32::bits() as u32; + + // Special cases + if d == 0 { + return 0; // ?! + } + + if n == 0 { + return 0; + } + + let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros()); + + // d > n + if sr > u32_bits - 1 { + return 0; + } + + // d == 1 + if sr == u32_bits - 1 { + return n; + } + + sr = sr + 1; + + // 1 <= sr <= u32_bits - 1 + let mut q = n << (u32_bits - sr); + let mut r = n >> sr; + + let mut carry = 0; + for _ in 0..sr { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> (u32_bits - 1)); + q = (q << 1) | carry; + + // carry = 0; + // if r > d { + // r -= d; + // carry = 1; + // } + + let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32_bits - 1); + carry = (s & 1) as u32; + r -= d & s as u32; + } + + q = (q << 1) | carry; + q +} diff --git a/src/test.rs b/src/test.rs index 8d5a42c..40e2885 100644 --- a/src/test.rs +++ b/src/test.rs @@ -42,3 +42,16 @@ quickcheck! { } } } + +quickcheck! { + fn udivmodsi4(a: u32, b: u32) -> TestResult { + if b == 0 { + TestResult::discard() + } else { + let mut r = 0; + let q = ::__udivmodsi4(a, b, &mut r); + + TestResult::from_bool(q * b + r == a) + } + } +} From 0915e15c9adea3b8e51ce64afd97d7b71efab7f0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 00:31:49 -0500 Subject: [PATCH 05/11] *mut T -> Option<&mut T> --- src/lib.rs | 24 ++++++++++++------------ src/test.rs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2c016a7..d2dcbdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,7 +100,7 @@ absv_i2!(__absvdi2: i64); // absv_i2!(__absvti2, i128); #[no_mangle] -pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { +pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { #[cfg(target_endian = "little")] #[repr(C)] #[derive(Debug)] @@ -146,7 +146,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // --- // 0 X - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = u64::from(n.low % d.low); } u64::from(n.low / d.low) @@ -157,7 +157,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // --- // K X - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = u64::from(n.low); } 0 @@ -179,7 +179,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // 0 0 // NOTE copied verbatim from compiler-rt, but does division by zero even make sense? - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = u64::from(n.high % d.low); } return u64::from(n.high / d.low); @@ -191,7 +191,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // --- // K 0 - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = words { low: 0, high: n.high % d.high, @@ -207,7 +207,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // K 0 if d.high.is_power_of_two() { - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = words { low: n.low, high: n.high & (d.high - 1), @@ -222,7 +222,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // D > N if sr > u32_bits - 2 { - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = n.u64(); } return 0; @@ -245,7 +245,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // --- // 0 K if d.low.is_power_of_two() { - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = u64::from(n.low & (d.low - 1)); } @@ -298,7 +298,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { // D > N if sr > u32_bits - 1 { - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = a; return 0; } @@ -347,16 +347,16 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: *mut u64) -> u64 { } *q.all() = (q.u64() << 1) | carry as u64; - if let Some(rem) = unsafe { rem.as_mut() } { + if let Some(rem) = rem { *rem = r.u64(); } q.u64() } #[no_mangle] -pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: *mut u32) -> u32 { +pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { let d = __udivsi3(a, b); - if let Some(rem) = unsafe {rem.as_mut()} { + if let Some(rem) = rem { *rem = a - (d*b); } return d; diff --git a/src/test.rs b/src/test.rs index 40e2885..62cf59c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -36,7 +36,7 @@ quickcheck! { TestResult::discard() } else { let mut r = 0; - let q = ::__udivmoddi4(a, b, &mut r); + let q = ::__udivmoddi4(a, b, Some(&mut r)); TestResult::from_bool(q * b + r == a) } @@ -49,7 +49,7 @@ quickcheck! { TestResult::discard() } else { let mut r = 0; - let q = ::__udivmodsi4(a, b, &mut r); + let q = ::__udivmodsi4(a, b, Some(&mut r)); TestResult::from_bool(q * b + r == a) } From a2fc4da7a52c31e4b83692f34cf96d7c0dd9bf69 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 00:33:27 -0500 Subject: [PATCH 06/11] move return into if branches --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d2dcbdc..581189f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,7 +141,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { // NOTE X is unknown, K != 0 if n.high == 0 { - return if d.high == 0 { + if d.high == 0 { // 0 X // --- // 0 X @@ -149,7 +149,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { if let Some(rem) = rem { *rem = u64::from(n.low % d.low); } - u64::from(n.low / d.low) + return u64::from(n.low / d.low); } else // d.high != 0 { @@ -160,7 +160,7 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { if let Some(rem) = rem { *rem = u64::from(n.low); } - 0 + return 0; }; } From d6be95a3bb476f66515e034d337d14cbac0ff8ed Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 00:36:19 -0500 Subject: [PATCH 07/11] note about calling convention --- src/arm.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/arm.rs b/src/arm.rs index bcfc6c8..51206ed 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -1,5 +1,7 @@ use core::intrinsics; +// NOTE This function and the one below are implemented using assembly because they using a custom +// calling convention which can't be implemented using a normal Rust function // TODO use `global_asm!` #[naked] #[no_mangle] From a84579d3c151647c8ae69cb522379fb662690b86 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 01:15:51 -0500 Subject: [PATCH 08/11] refactor: use Int traits, words -> U64, fmt --- src/lib.rs | 218 ++++++++++++++++++++++++++--------------------------- 1 file changed, 108 insertions(+), 110 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 581189f..527f444 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,8 +18,6 @@ pub mod arm; #[cfg(test)] mod test; -use core::mem; - /// Trait for some basic operations on integers trait Int { fn bits() -> usize; @@ -27,16 +25,24 @@ trait Int { // TODO: Once i128/u128 support lands, we'll want to add impls for those as well impl Int for u32 { - fn bits() -> usize { 32 } + fn bits() -> usize { + 32 + } } impl Int for i32 { - fn bits() -> usize { 32 } + fn bits() -> usize { + 32 + } } impl Int for u64 { - fn bits() -> usize { 64 } + fn bits() -> usize { + 64 + } } impl Int for i64 { - fn bits() -> usize { 64 } + fn bits() -> usize { + 64 + } } /// Trait to convert an integer to/from smaller parts @@ -99,131 +105,124 @@ absv_i2!(__absvdi2: i64); // TODO(rust-lang/35118)? // absv_i2!(__absvti2, i128); +/// Return `n / d` and `*rem = n % d` #[no_mangle] -pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { +pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { + use core::ops::{Index, IndexMut, RangeFull}; + #[cfg(target_endian = "little")] #[repr(C)] - #[derive(Debug)] - struct words { + struct U64 { low: u32, high: u32, } #[cfg(target_endian = "big")] #[repr(C)] - #[derive(Debug)] - struct words { + struct U64 { high: u32, low: u32, } - impl words { - fn all(&mut self) -> &mut u64 { - unsafe { mem::transmute(self) } - } + impl Index for U64 { + type Output = u64; - fn u64(&self) -> u64 { - unsafe { *(self as *const _ as *const u64) } + fn index(&self, _: RangeFull) -> &u64 { + unsafe { &*(self as *const _ as *const u64) } } } - impl From for words { - fn from(x: u64) -> words { - unsafe { mem::transmute(x) } + impl IndexMut for U64 { + fn index_mut(&mut self, _: RangeFull) -> &mut u64 { + unsafe { &mut *(self as *const _ as *mut u64) } } } 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 { - if d.high == 0 { + if n.high() == 0 { + if d.high() == 0 { // 0 X // --- // 0 X if let Some(rem) = rem { - *rem = u64::from(n.low % d.low); + *rem = u64::from(n.low() % d.low()); } - return u64::from(n.low / d.low); + return u64::from(n.low() / d.low()); } else - // d.high != 0 - { + // d.high() != 0 + { // 0 X // --- // K X if let Some(rem) = rem { - *rem = u64::from(n.low); + *rem = u64::from(n.low()); } return 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 }; + let mut q = U64 { low: 0, high: 0 }; + let mut r = U64 { low: 0, high: 0 }; - // n.high != 0 - if d.low == 0 { - if d.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? + // NOTE copied verbatim from compiler-rt. This probably lets the intrinsic decide how to + // handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable + // from safe code. if let Some(rem) = rem { - *rem = u64::from(n.high % d.low); + *rem = u64::from(n.high() % d.low()); } - return u64::from(n.high / d.low); + return u64::from(n.high() / d.low()); } - // d.high != 0 - if n.low == 0 { + // d.high() != 0 + if n.low() == 0 { // K 0 // --- // K 0 if let Some(rem) = rem { - *rem = words { - low: 0, - high: n.high % d.high, - } - .u64(); + *rem = U64 { + low: 0, + high: n.high() % d.high(), + }[..]; } - return u64::from(n.high / d.high); + return u64::from(n.high() / d.high()); } - // n.low != 0 + // n.low() != 0 // K K // --- // K 0 - if d.high.is_power_of_two() { + if d.high().is_power_of_two() { if let Some(rem) = rem { - *rem = words { - low: n.low, - high: n.high & (d.high - 1), - } - .u64() + *rem = U64 { + low: n.low(), + high: n.high() & (d.high() - 1), + }[..]; } - return u64::from(n.high >> d.high.trailing_zeros()); + return u64::from(n.high() >> d.high().trailing_zeros()); } - sr = d.high.leading_zeros().wrapping_sub(n.high.leading_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.u64(); + *rem = n; } return 0; } @@ -231,75 +230,74 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { sr = sr + 1; // 1 <= sr <= u32_bits - 1 - // *q.all() = n.u64() << (u64_bits - sr); + // q = n << (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); + q.high = n.low() << (u32_bits - sr); + // r = n >> sr + r.high = n.high() >> sr; + r.low = (n.high() << (u32_bits - sr)) | (n.low() >> sr); } else - // d.low != 0 + // d.low() != 0 { - if d.high == 0 { + if d.high() == 0 { // K X // --- // 0 K - if d.low.is_power_of_two() { + if d.low().is_power_of_two() { if let Some(rem) = rem { - *rem = u64::from(n.low & (d.low - 1)); + *rem = u64::from(n.low() & (d.low() - 1)); } - return if d.low == 1 { - n.u64() + if d.low() == 1 { + return n; } else { - let sr = d.low.trailing_zeros(); - words { - low: (n.high << (u32_bits - sr)) | (n.low >> sr), - high: n.high >> sr, - } - .u64() + let sr = d.low().trailing_zeros(); + return U64 { + low: (n.high() << (u32_bits - sr)) | (n.low() >> sr), + high: n.high() >> sr, + }[..]; }; } - sr = 1 + u32_bits + d.low.leading_zeros() - n.high.leading_zeros(); + 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; + // q = n << (u64_bits - sr) + // r = n >> sr; if sr == u32_bits { q.low = 0; - q.high = n.low; + q.high = n.low(); r.high = 0; - r.low = n.high; + 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); + 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)); + 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); + r.low = n.high() >> (sr - u32_bits); } } else - // d.high != 0 + // d.high() != 0 { // K X // --- // K K - sr = d.high.leading_zeros().wrapping_sub(n.high.leading_zeros()); + sr = d.high().leading_zeros().wrapping_sub(n.high().leading_zeros()); // D > N if sr > u32_bits - 1 { if let Some(rem) = rem { - *rem = a; + *rem = n; return 0; } } @@ -307,61 +305,61 @@ pub extern "C" fn __udivmoddi4(a: u64, b: u64, rem: Option<&mut u64>) -> u64 { sr += 1; // 1 <= sr <= u32_bits - // *q.all() = n.u64() << (u64_bits - sr) + // q = n << (u64_bits - sr) q.low = 0; if sr == u32_bits { - q.high = n.low; + q.high = n.low(); r.high = 0; - r.low = n.high; + 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); + 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 + // q = n << (u64_bits - sr) + // r = n >> 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; + r[..] = (r[..] << 1) | (q[..] >> 63); + q[..] = (q[..] << 1) | carry as u64; // carry = 0 - // if r.u64() >= d.u64() { - // *r.all() -= d.u64(); + // if r >= d { + // r -= d; // carry = 1; // } - let s = (d.u64().wrapping_sub(r.u64()).wrapping_sub(1)) as i64 >> (u64_bits - 1); + let s = (d.wrapping_sub(r[..]).wrapping_sub(1)) as i64 >> (u64_bits - 1); carry = (s & 1) as u32; - *r.all() -= d.u64() & s as u64; + r[..] -= d & s as u64; } - *q.all() = (q.u64() << 1) | carry as u64; + q[..] = (q[..] << 1) | carry as u64; if let Some(rem) = rem { - *rem = r.u64(); + *rem = r[..]; } - q.u64() + q[..] } +/// Return `n / d` and `*rem = n % d` #[no_mangle] pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { let d = __udivsi3(a, b); if let Some(rem) = rem { - *rem = a - (d*b); + *rem = a - (d * b); } return d; } +/// Return `n / d` #[no_mangle] pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { let u32_bits = u32::bits() as u32; From 3ff25d956a749e293fdc0b70543ace65ffeefdd9 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 01:26:27 -0500 Subject: [PATCH 09/11] put div intrinsics in their own module + some docs --- src/div.rs | 278 ++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 341 ++++++---------------------------------------------- src/test.rs | 4 +- 3 files changed, 314 insertions(+), 309 deletions(-) create mode 100644 src/div.rs diff --git a/src/div.rs b/src/div.rs new file mode 100644 index 0000000..cb6568e --- /dev/null +++ b/src/div.rs @@ -0,0 +1,278 @@ +use {Int, LargeInt, U64}; + +/// Returns `n / d` +#[no_mangle] +pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { + let u32_bits = u32::bits() as u32; + + // Special cases + if d == 0 { + return 0; // ?! + } + + if n == 0 { + return 0; + } + + let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros()); + + // d > n + if sr > u32_bits - 1 { + return 0; + } + + // d == 1 + if sr == u32_bits - 1 { + return n; + } + + sr = sr + 1; + + // 1 <= sr <= u32_bits - 1 + let mut q = n << (u32_bits - sr); + let mut r = n >> sr; + + let mut carry = 0; + for _ in 0..sr { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> (u32_bits - 1)); + q = (q << 1) | carry; + + // carry = 0; + // if r > d { + // r -= d; + // carry = 1; + // } + + let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32_bits - 1); + carry = (s & 1) as u32; + r -= d & s as u32; + } + + q = (q << 1) | carry; + q +} + +/// Returns `n / d` and sets `*rem = n % d` +#[no_mangle] +pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { + let d = __udivsi3(a, b); + if let Some(rem) = rem { + *rem = a - (d * b); + } + return d; +} + +/// Returns `n / d` and sets `*rem = n % d` +#[no_mangle] +pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { + let u32_bits = u32::bits() as u32; + let u64_bits = u64::bits() as u32; + + // NOTE X is unknown, K != 0 + if n.high() == 0 { + if d.high() == 0 { + // 0 X + // --- + // 0 X + + if let Some(rem) = rem { + *rem = u64::from(n.low() % d.low()); + } + return u64::from(n.low() / d.low()); + } else + // d.high() != 0 + { + // 0 X + // --- + // K X + + if let Some(rem) = rem { + *rem = u64::from(n.low()); + } + return 0; + }; + } + + let mut sr; + let mut q = U64 { low: 0, high: 0 }; + let mut r = U64 { 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. This probably lets the intrinsic decide how to + // handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable + // from safe code. + if let Some(rem) = rem { + *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) = rem { + *rem = U64 { + low: 0, + high: n.high() % d.high(), + }[..]; + } + 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) = rem { + *rem = U64 { + low: n.low(), + high: 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 = sr + 1; + + // 1 <= sr <= u32_bits - 1 + // q = n << (u64_bits - sr); + q.low = 0; + q.high = n.low() << (u32_bits - sr); + // r = n >> 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) = rem { + *rem = u64::from(n.low() & (d.low() - 1)); + } + + if d.low() == 1 { + return n; + } else { + let sr = d.low().trailing_zeros(); + return U64 { + low: (n.high() << (u32_bits - sr)) | (n.low() >> sr), + high: n.high() >> sr, + }[..]; + }; + } + + sr = 1 + u32_bits + d.low().leading_zeros() - n.high().leading_zeros(); + + // 2 <= sr <= u64_bits - 1 + // q = n << (u64_bits - sr) + // r = n >> 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) = rem { + *rem = n; + return 0; + } + } + + sr += 1; + + // 1 <= sr <= u32_bits + // q = n << (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 = n << (u64_bits - sr) + // r = n >> sr + // 1 <= sr <= u64_bits - 1 + let mut carry = 0; + + for _ in 0..sr { + // r:q = ((r:q) << 1) | carry + r[..] = (r[..] << 1) | (q[..] >> 63); + q[..] = (q[..] << 1) | carry as u64; + + // carry = 0 + // if r >= d { + // r -= d; + // carry = 1; + // } + + let s = (d.wrapping_sub(r[..]).wrapping_sub(1)) as i64 >> (u64_bits - 1); + carry = (s & 1) as u32; + r[..] -= d & s as u64; + } + + q[..] = (q[..] << 1) | carry as u64; + if let Some(rem) = rem { + *rem = r[..]; + } + q[..] +} diff --git a/src/lib.rs b/src/lib.rs index 527f444..3a24025 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,9 +15,13 @@ extern crate quickcheck; #[cfg(target_arch = "arm")] pub mod arm; +pub mod div; + #[cfg(test)] mod test; +use core::ops::{Index, IndexMut, RangeFull}; + /// Trait for some basic operations on integers trait Int { fn bits() -> usize; @@ -85,6 +89,36 @@ impl LargeInt for i64 { } } +/// Union-like access to the 32-bit words that make an `u64`: `x.low` and `x.high`. The whole `u64` +/// can be accessed via the expression `x[..]`, which can be used in lvalue or rvalue position. +#[cfg(target_endian = "little")] +#[repr(C)] +struct U64 { + low: u32, + high: u32, +} + +#[cfg(target_endian = "big")] +#[repr(C)] +struct U64 { + high: u32, + low: u32, +} + +impl Index for U64 { + type Output = u64; + + fn index(&self, _: RangeFull) -> &u64 { + unsafe { &*(self as *const _ as *const u64) } + } +} + +impl IndexMut for U64 { + fn index_mut(&mut self, _: RangeFull) -> &mut u64 { + unsafe { &mut *(self as *const _ as *mut u64) } + } +} + macro_rules! absv_i2 { ($intrinsic:ident : $ty:ty) => { #[no_mangle] @@ -104,310 +138,3 @@ absv_i2!(__absvsi2: i32); absv_i2!(__absvdi2: i64); // TODO(rust-lang/35118)? // absv_i2!(__absvti2, i128); - -/// Return `n / d` and `*rem = n % d` -#[no_mangle] -pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { - use core::ops::{Index, IndexMut, RangeFull}; - - #[cfg(target_endian = "little")] - #[repr(C)] - struct U64 { - low: u32, - high: u32, - } - - #[cfg(target_endian = "big")] - #[repr(C)] - struct U64 { - high: u32, - low: u32, - } - - impl Index for U64 { - type Output = u64; - - fn index(&self, _: RangeFull) -> &u64 { - unsafe { &*(self as *const _ as *const u64) } - } - } - - impl IndexMut for U64 { - fn index_mut(&mut self, _: RangeFull) -> &mut u64 { - unsafe { &mut *(self as *const _ as *mut u64) } - } - } - - let u32_bits = u32::bits() as u32; - let u64_bits = u64::bits() as u32; - - // NOTE X is unknown, K != 0 - if n.high() == 0 { - if d.high() == 0 { - // 0 X - // --- - // 0 X - - if let Some(rem) = rem { - *rem = u64::from(n.low() % d.low()); - } - return u64::from(n.low() / d.low()); - } else - // d.high() != 0 - { - // 0 X - // --- - // K X - - if let Some(rem) = rem { - *rem = u64::from(n.low()); - } - return 0; - }; - } - - let mut sr; - let mut q = U64 { low: 0, high: 0 }; - let mut r = U64 { 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. This probably lets the intrinsic decide how to - // handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable - // from safe code. - if let Some(rem) = rem { - *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) = rem { - *rem = U64 { - low: 0, - high: n.high() % d.high(), - }[..]; - } - 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) = rem { - *rem = U64 { - low: n.low(), - high: 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 = sr + 1; - - // 1 <= sr <= u32_bits - 1 - // q = n << (u64_bits - sr); - q.low = 0; - q.high = n.low() << (u32_bits - sr); - // r = n >> 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) = rem { - *rem = u64::from(n.low() & (d.low() - 1)); - } - - if d.low() == 1 { - return n; - } else { - let sr = d.low().trailing_zeros(); - return U64 { - low: (n.high() << (u32_bits - sr)) | (n.low() >> sr), - high: n.high() >> sr, - }[..]; - }; - } - - sr = 1 + u32_bits + d.low().leading_zeros() - n.high().leading_zeros(); - - // 2 <= sr <= u64_bits - 1 - // q = n << (u64_bits - sr) - // r = n >> 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) = rem { - *rem = n; - return 0; - } - } - - sr += 1; - - // 1 <= sr <= u32_bits - // q = n << (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 = n << (u64_bits - sr) - // r = n >> sr - // 1 <= sr <= u64_bits - 1 - let mut carry = 0; - - for _ in 0..sr { - // r:q = ((r:q) << 1) | carry - r[..] = (r[..] << 1) | (q[..] >> 63); - q[..] = (q[..] << 1) | carry as u64; - - // carry = 0 - // if r >= d { - // r -= d; - // carry = 1; - // } - - let s = (d.wrapping_sub(r[..]).wrapping_sub(1)) as i64 >> (u64_bits - 1); - carry = (s & 1) as u32; - r[..] -= d & s as u64; - } - - q[..] = (q[..] << 1) | carry as u64; - if let Some(rem) = rem { - *rem = r[..]; - } - q[..] -} - -/// Return `n / d` and `*rem = n % d` -#[no_mangle] -pub extern "C" fn __udivmodsi4(a: u32, b: u32, rem: Option<&mut u32>) -> u32 { - let d = __udivsi3(a, b); - if let Some(rem) = rem { - *rem = a - (d * b); - } - return d; -} - -/// Return `n / d` -#[no_mangle] -pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { - let u32_bits = u32::bits() as u32; - - // Special cases - if d == 0 { - return 0; // ?! - } - - if n == 0 { - return 0; - } - - let mut sr = d.leading_zeros().wrapping_sub(n.leading_zeros()); - - // d > n - if sr > u32_bits - 1 { - return 0; - } - - // d == 1 - if sr == u32_bits - 1 { - return n; - } - - sr = sr + 1; - - // 1 <= sr <= u32_bits - 1 - let mut q = n << (u32_bits - sr); - let mut r = n >> sr; - - let mut carry = 0; - for _ in 0..sr { - // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> (u32_bits - 1)); - q = (q << 1) | carry; - - // carry = 0; - // if r > d { - // r -= d; - // carry = 1; - // } - - let s = (d.wrapping_sub(r).wrapping_sub(1)) as i32 >> (u32_bits - 1); - carry = (s & 1) as u32; - r -= d & s as u32; - } - - q = (q << 1) | carry; - q -} diff --git a/src/test.rs b/src/test.rs index 62cf59c..f1fa4f3 100644 --- a/src/test.rs +++ b/src/test.rs @@ -36,7 +36,7 @@ quickcheck! { TestResult::discard() } else { let mut r = 0; - let q = ::__udivmoddi4(a, b, Some(&mut r)); + let q = ::div::__udivmoddi4(a, b, Some(&mut r)); TestResult::from_bool(q * b + r == a) } @@ -49,7 +49,7 @@ quickcheck! { TestResult::discard() } else { let mut r = 0; - let q = ::__udivmodsi4(a, b, Some(&mut r)); + let q = ::div::__udivmodsi4(a, b, Some(&mut r)); TestResult::from_bool(q * b + r == a) } From 1c22269948afd7a1afebe1da9889e93cdd684725 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 01:42:34 -0500 Subject: [PATCH 10/11] don't transmute in test --- src/test.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/test.rs b/src/test.rs index f1fa4f3..fa8691c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,4 +1,4 @@ -use std::{mem, panic}; +use std::panic; use quickcheck::TestResult; @@ -27,31 +27,30 @@ absv_i2!(__absvdi2: i64); // absv_i2!(__absvti2: i128); quickcheck! { - fn udivmoddi4(a: (u32, u32), b: (u32, u32)) -> TestResult { - let (a, b) = unsafe { - (mem::transmute(a), mem::transmute(b)) - }; + fn udivmoddi4(n: (u32, u32), d: (u32, u32)) -> TestResult { + let n = ::U64 { low: n.0, high: n.1 }[..]; + let d = ::U64 { low: d.0, high: d.1 }[..]; - if b == 0 { + if d == 0 { TestResult::discard() } else { let mut r = 0; - let q = ::div::__udivmoddi4(a, b, Some(&mut r)); + let q = ::div::__udivmoddi4(n, d, Some(&mut r)); - TestResult::from_bool(q * b + r == a) + TestResult::from_bool(q * d + r == n) } } } quickcheck! { - fn udivmodsi4(a: u32, b: u32) -> TestResult { - if b == 0 { + fn udivmodsi4(n: u32, d: u32) -> TestResult { + if d == 0 { TestResult::discard() } else { let mut r = 0; - let q = ::div::__udivmodsi4(a, b, Some(&mut r)); + let q = ::div::__udivmodsi4(n, d, Some(&mut r)); - TestResult::from_bool(q * b + r == a) + TestResult::from_bool(q * d + r == n) } } } From eb206842f20ec4a17ff5296fb21f32e875fadcc3 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 11 Aug 2016 01:45:48 -0500 Subject: [PATCH 11/11] address second row of review --- src/div.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/div.rs b/src/div.rs index cb6568e..44d59b7 100644 --- a/src/div.rs +++ b/src/div.rs @@ -7,7 +7,7 @@ pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { // Special cases if d == 0 { - return 0; // ?! + panic!("Division by zero"); } if n == 0 { @@ -49,8 +49,7 @@ pub extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { r -= d & s as u32; } - q = (q << 1) | carry; - q + (q << 1) | carry } /// Returns `n / d` and sets `*rem = n % d` @@ -105,13 +104,7 @@ pub extern "C" fn __udivmoddi4(n: u64, d: u64, rem: Option<&mut u64>) -> u64 { // --- // 0 0 - // NOTE copied verbatim from compiler-rt. This probably lets the intrinsic decide how to - // handle the division by zero (SIGFPE, 0, etc.). But this part shouldn't be reachable - // from safe code. - if let Some(rem) = rem { - *rem = u64::from(n.high() % d.low()); - } - return u64::from(n.high() / d.low()); + panic!("Division by zero"); } // d.high() != 0