Merge pull request #44 from Amanieu/sdiv

Add signed division functions
This commit is contained in:
Jorge Aparicio 2016-08-19 09:16:53 -05:00 committed by GitHub
commit 8603e64554
6 changed files with 259 additions and 18 deletions

View File

@ -40,9 +40,9 @@ See [rust-lang/rust#35437][0].
- [x] arm/aeabi_uidivmod.S
- [x] arm/aeabi_uldivmod.S
- [ ] arm/divdf3vfp.S
- [ ] arm/divmodsi4.S
- [ ] arm/divmodsi4.S (generic version is done)
- [ ] arm/divsf3vfp.S
- [ ] arm/divsi3.S
- [ ] arm/divsi3.S (generic version is done)
- [ ] arm/eqdf2vfp.S
- [ ] arm/eqsf2vfp.S
- [ ] arm/extendsfdf2vfp.S
@ -62,7 +62,7 @@ See [rust-lang/rust#35437][0].
- [ ] arm/lesf2vfp.S
- [ ] arm/ltdf2vfp.S
- [ ] arm/ltsf2vfp.S
- [ ] arm/modsi3.S
- [ ] arm/modsi3.S (generic version is done)
- [ ] arm/muldf3vfp.S
- [ ] arm/mulsf3vfp.S
- [ ] arm/nedf2vfp.S
@ -73,17 +73,19 @@ See [rust-lang/rust#35437][0].
- [ ] arm/subdf3vfp.S
- [ ] arm/subsf3vfp.S
- [ ] arm/truncdfsf2vfp.S
- [ ] arm/udivmodsi4.S
- [ ] arm/udivsi3.S
- [ ] arm/umodsi3.S
- [ ] arm/udivmodsi4.S (generic version is done)
- [ ] arm/udivsi3.S (generic version is done)
- [ ] arm/umodsi3.S (generic version is done)
- [ ] arm/unorddf2vfp.S
- [ ] arm/unordsf2vfp.S
- [x] ashldi3.c
- [x] ashrdi3.c
- [ ] divdf3.c
- [ ] divdi3.c
- [x] divdi3.c
- [x] divmoddi4.c
- [x] divmodsi4.c
- [ ] divsf3.c
- [ ] divsi3.c
- [x] divsi3.c
- [ ] extendhfsf2.c
- [ ] extendsfdf2.c
- [ ] fixdfdi.c
@ -113,8 +115,8 @@ See [rust-lang/rust#35437][0].
- [ ] i386/udivdi3.S
- [ ] i386/umoddi3.S
- [x] lshrdi3.c
- [ ] moddi3.c
- [ ] modsi3.c
- [x] moddi3.c
- [x] modsi3.c
- [ ] muldf3.c
- [x] muldi3.c
- [x] mulodi4.c
@ -251,8 +253,6 @@ These builtins are never called by LLVM.
- ~~ctzdi2.c~~
- ~~ctzsi2.c~~
- ~~ctzti2.c~~
- ~~divmoddi4.c~~
- ~~divmodsi4.c~~
- ~~ffsdi2.c~~
- ~~ffsti2.c~~
- ~~mulvdi3.c~~

View File

@ -1,6 +1,6 @@
use core::intrinsics;
// NOTE This function and the one below are implemented using assembly because they using a custom
// NOTE This function and the ones below are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function
#[naked]
#[cfg_attr(not(test), no_mangle)]
@ -30,6 +30,44 @@ pub unsafe fn __aeabi_uldivmod() {
intrinsics::unreachable();
}
#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
asm!("push {r0, r1, r4, lr}
bl __divsi3
pop {r1, r2}
muls r2, r2, r0
subs r1, r1, r2
pop {r4, pc}");
intrinsics::unreachable();
}
#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
asm!("push {r4, lr}
sub sp, sp, #16
add r4, sp, #8
str r4, [sp]
bl __divmoddi4
ldr r2, [sp, #8]
ldr r3, [sp, #12]
add sp, sp, #16
pop {r4, pc}");
intrinsics::unreachable();
}
// TODO: These two functions should be defined as aliases
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_uidiv(a: u32, b: u32) -> u32 {
::udiv::__udivsi3(a, b)
}
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_idiv(a: i32, b: i32) -> i32 {
::sdiv::__divsi3(a, b)
}
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;
@ -90,3 +128,84 @@ pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
memset(dest, 0, n);
}
#[cfg(test)]
mod tests {
use quickcheck::TestResult;
use qc::{U32, U64};
quickcheck!{
fn uldivmod(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0, d.0);
if d == 0 {
TestResult::discard()
} else {
let q: u64;
let r: u64;
unsafe {
// The inline asm is a bit tricky here, LLVM will allocate
// both r0 and r1 when we specify a 64-bit value for {r0}.
asm!("bl __aeabi_uldivmod"
: "={r0}" (q), "={r2}" (r)
: "{r0}" (n), "{r2}" (d)
: "r12", "lr", "flags");
}
TestResult::from_bool(q == n / d && r == n % d)
}
}
fn uidivmod(n: U32, d: U32) -> TestResult {
let (n, d) = (n.0, d.0);
if d == 0 {
TestResult::discard()
} else {
let q: u32;
let r: u32;
unsafe {
asm!("bl __aeabi_uidivmod"
: "={r0}" (q), "={r1}" (r)
: "{r0}" (n), "{r1}" (d)
: "r2", "r3", "r12", "lr", "flags");
}
TestResult::from_bool(q == n / d && r == n % d)
}
}
fn ldivmod(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0 as i64, d.0 as i64);
if d == 0 {
TestResult::discard()
} else {
let q: i64;
let r: i64;
unsafe {
// The inline asm is a bit tricky here, LLVM will allocate
// both r0 and r1 when we specify a 64-bit value for {r0}.
asm!("bl __aeabi_ldivmod"
: "={r0}" (q), "={r2}" (r)
: "{r0}" (n), "{r2}" (d)
: "r12", "lr", "flags");
}
TestResult::from_bool(q == n / d && r == n % d)
}
}
fn idivmod(n: U32, d: U32) -> TestResult {
let (n, d) = (n.0 as i32, d.0 as i32);
if d == 0 {
TestResult::discard()
} else {
let q: i32;
let r: i32;
unsafe {
asm!("bl __aeabi_idivmod"
: "={r0}" (q), "={r1}" (r)
: "{r0}" (n), "{r1}" (d)
: "r2", "r3", "r12", "lr", "flags");
}
TestResult::from_bool(q == n / d && r == n % d)
}
}
}
}

View File

@ -27,6 +27,7 @@ pub mod arm;
pub mod x86_64;
pub mod udiv;
pub mod sdiv;
pub mod mul;
pub mod shift;

View File

@ -4,7 +4,7 @@ macro_rules! mul {
($intrinsic:ident: $ty:ty) => {
/// Returns `a * b`
#[cfg_attr(not(test), no_mangle)]
pub extern fn $intrinsic(a: $ty, b: $ty) -> $ty {
pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty {
let half_bits = <$ty>::bits() / 4;
let lower_mask = !0 >> half_bits;
let mut low = (a.low() & lower_mask) * (b.low() & lower_mask);
@ -29,7 +29,7 @@ macro_rules! mulo {
($intrinsic:ident: $ty:ty) => {
/// Returns `a * b` and sets `*overflow = 1` if `a * b` overflows
#[cfg_attr(not(test), no_mangle)]
pub extern fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty {
pub extern "C" fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty {
*overflow = 0;
let result = a.wrapping_mul(b);
if a == <$ty>::min_value() {

121
src/sdiv.rs Normal file
View File

@ -0,0 +1,121 @@
use Int;
macro_rules! div {
($intrinsic:ident: $ty:ty, $uty:ty) => {
/// Returns `a / b`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty {
let s_a = a >> (<$ty>::bits() - 1);
let s_b = b >> (<$ty>::bits() - 1);
let a = (a ^ s_a) - s_a;
let b = (b ^ s_b) - s_b;
let s = s_a ^ s_b;
let r = (a as $uty) / (b as $uty);
(r as $ty ^ s) - s
}
}
}
macro_rules! mod_ {
($intrinsic:ident: $ty:ty, $uty:ty) => {
/// Returns `a % b`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $ty {
let s = b >> (<$ty>::bits() - 1);
let b = (b ^ s) - s;
let s = a >> (<$ty>::bits() - 1);
let a = (a ^ s) - s;
let r = (a as $uty) % (b as $uty);
(r as $ty ^ s) - s
}
}
}
macro_rules! divmod {
($intrinsic:ident, $div:ident: $ty:ty) => {
/// Returns `a / b` and sets `*rem = n % d`
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn $intrinsic(a: $ty, b: $ty, rem: &mut $ty) -> $ty {
let r = $div(a, b);
*rem = a - (r * b);
r
}
}
}
div!(__divsi3: i32, u32);
div!(__divdi3: i64, u64);
mod_!(__modsi3: i32, u32);
mod_!(__moddi3: i64, u64);
divmod!(__divmodsi4, __divsi3: i32);
divmod!(__divmoddi4, __divdi3: i64);
#[cfg(test)]
mod tests {
use quickcheck::TestResult;
use qc::{U32, U64};
quickcheck!{
fn divdi3(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0 as i64, d.0 as i64);
if d == 0 {
TestResult::discard()
} else {
let q = super::__divdi3(n, d);
TestResult::from_bool(q == n / d)
}
}
fn moddi3(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0 as i64, d.0 as i64);
if d == 0 {
TestResult::discard()
} else {
let r = super::__moddi3(n, d);
TestResult::from_bool(r == n % d)
}
}
fn divmoddi4(n: U64, d: U64) -> TestResult {
let (n, d) = (n.0 as i64, d.0 as i64);
if d == 0 {
TestResult::discard()
} else {
let mut r = 0;
let q = super::__divmoddi4(n, d, &mut r);
TestResult::from_bool(q == n / d && r == n % d)
}
}
fn divsi3(n: U32, d: U32) -> TestResult {
let (n, d) = (n.0 as i32, d.0 as i32);
if d == 0 {
TestResult::discard()
} else {
let q = super::__divsi3(n, d);
TestResult::from_bool(q == n / d)
}
}
fn modsi3(n: U32, d: U32) -> TestResult {
let (n, d) = (n.0 as i32, d.0 as i32);
if d == 0 {
TestResult::discard()
} else {
let r = super::__modsi3(n, d);
TestResult::from_bool(r == n % d)
}
}
fn divmodsi4(n: U32, d: U32) -> TestResult {
let (n, d) = (n.0 as i32, d.0 as i32);
if d == 0 {
TestResult::discard()
} else {
let mut r = 0;
let q = super::__divmodsi4(n, d, &mut r);
TestResult::from_bool(q == n / d && r == n % d)
}
}
}
}

View File

@ -4,7 +4,7 @@ macro_rules! ashl {
($intrinsic:ident: $ty:ty) => {
/// Returns `a << b`, requires `b < $ty::bits()`
#[cfg_attr(not(test), no_mangle)]
pub extern fn $intrinsic(a: $ty, b: u32) -> $ty {
pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
let half_bits = <$ty>::bits() / 2;
if b & half_bits != 0 {
<$ty>::from_parts(0, a.low() << (b - half_bits))
@ -21,7 +21,7 @@ macro_rules! ashr {
($intrinsic:ident: $ty:ty) => {
/// Returns arithmetic `a >> b`, requires `b < $ty::bits()`
#[cfg_attr(not(test), no_mangle)]
pub extern fn $intrinsic(a: $ty, b: u32) -> $ty {
pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
let half_bits = <$ty>::bits() / 2;
if b & half_bits != 0 {
<$ty>::from_parts((a.high() >> (b - half_bits)) as <$ty as LargeInt>::LowHalf,
@ -41,7 +41,7 @@ macro_rules! lshr {
($intrinsic:ident: $ty:ty) => {
/// Returns logical `a >> b`, requires `b < $ty::bits()`
#[cfg_attr(not(test), no_mangle)]
pub extern fn $intrinsic(a: $ty, b: u32) -> $ty {
pub extern "C" fn $intrinsic(a: $ty, b: u32) -> $ty {
let half_bits = <$ty>::bits() / 2;
if b & half_bits != 0 {
<$ty>::from_parts(a.high() >> (b - half_bits), 0)