diff --git a/README.md b/README.md index 4ff8b45..8f229a1 100644 --- a/README.md +++ b/README.md @@ -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~~ diff --git a/src/arm.rs b/src/arm.rs index ad2b39a..4555d5b 100644 --- a/src/arm.rs +++ b/src/arm.rs @@ -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) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7941b20..1351c38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ pub mod arm; pub mod x86_64; pub mod udiv; +pub mod sdiv; pub mod mul; pub mod shift; diff --git a/src/mul.rs b/src/mul.rs index cb31396..2c4ce52 100644 --- a/src/mul.rs +++ b/src/mul.rs @@ -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() { diff --git a/src/sdiv.rs b/src/sdiv.rs new file mode 100644 index 0000000..b123161 --- /dev/null +++ b/src/sdiv.rs @@ -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) + } + } + } +} diff --git a/src/shift.rs b/src/shift.rs index 7fa88ab..ccbfe13 100644 --- a/src/shift.rs +++ b/src/shift.rs @@ -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)