Browse Source

port udivmoddi4 and __aeabi_uldivmod

Jorge Aparicio 8 years ago
parent
commit
950564607d
4 changed files with 299 additions and 1 deletions
  1. 3 0
      Cargo.toml
  2. 17 0
      src/arm.rs
  3. 259 0
      src/lib.rs
  4. 20 1
      src/test.rs

+ 3 - 0
Cargo.toml

@@ -2,3 +2,6 @@
 authors = ["Jorge Aparicio <japaricious@gmail.com>"]
 name = "rustc_builtins"
 version = "0.1.0"
+
+[dev-dependencies]
+quickcheck = "0.3.1"

+ 17 - 0
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;

+ 259 - 0
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<u64> 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()
+}

+ 20 - 1
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)
+        }
+    }
+}