Преглед изворни кода

Add signed division functions

Amanieu d'Antras пре 8 година
родитељ
комит
f919660be5
6 измењених фајлова са 178 додато и 18 уклоњено
  1. 12 12
      README.md
  2. 39 1
      src/arm.rs
  3. 1 0
      src/lib.rs
  4. 2 2
      src/mul.rs
  5. 121 0
      src/sdiv.rs
  6. 3 3
      src/shift.rs

+ 12 - 12
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~~

+ 39 - 1
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;

+ 1 - 0
src/lib.rs

@@ -24,6 +24,7 @@ extern crate rlibc;
 pub mod arm;
 
 pub mod udiv;
+pub mod sdiv;
 pub mod mul;
 pub mod shift;
 

+ 2 - 2
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() {

+ 121 - 0
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)
+            }
+        }
+    }
+}

+ 3 - 3
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)