Browse Source

Port sdiv to traits + `intrinsics!`

Enhance `intrinsics!` along the way!
Alex Crichton 7 years ago
parent
commit
7886ae275b
6 changed files with 183 additions and 90 deletions
  1. 36 1
      src/int/mod.rs
  2. 1 1
      src/int/mul.rs
  3. 82 83
      src/int/sdiv.rs
  4. 3 3
      src/int/shift.rs
  5. 4 0
      src/lib.rs
  6. 57 2
      src/macros.rs

+ 36 - 1
src/int/mod.rs

@@ -57,14 +57,17 @@ pub trait Int:
     /// ```
     fn extract_sign(self) -> (bool, Self::UnsignedInt);
 
-    /// Convert to a signed representation
     fn unsigned(self) -> Self::UnsignedInt;
+    fn from_unsigned(unsigned: Self::UnsignedInt) -> Self;
 
     // copied from primitive integers, but put in a trait
     fn max_value() -> Self;
     fn min_value() -> Self;
     fn wrapping_add(self, other: Self) -> Self;
     fn wrapping_mul(self, other: Self) -> Self;
+    fn wrapping_sub(self, other: Self) -> Self;
+    fn checked_div(self, other: Self) -> Option<Self>;
+    fn checked_rem(self, other: Self) -> Option<Self>;
 }
 
 macro_rules! int_impl {
@@ -93,6 +96,10 @@ macro_rules! int_impl {
                 self
             }
 
+            fn from_unsigned(me: $uty) -> Self {
+                me
+            }
+
             fn max_value() -> Self {
                 <Self>::max_value()
             }
@@ -108,6 +115,18 @@ macro_rules! int_impl {
             fn wrapping_mul(self, other: Self) -> Self {
                 <Self>::wrapping_mul(self, other)
             }
+
+            fn wrapping_sub(self, other: Self) -> Self {
+                <Self>::wrapping_sub(self, other)
+            }
+
+            fn checked_div(self, other: Self) -> Option<Self> {
+                <Self>::checked_div(self, other)
+            }
+
+            fn checked_rem(self, other: Self) -> Option<Self> {
+                <Self>::checked_rem(self, other)
+            }
         }
 
         impl Int for $ity {
@@ -138,6 +157,10 @@ macro_rules! int_impl {
                 self as $uty
             }
 
+            fn from_unsigned(me: $uty) -> Self {
+                me as $ity
+            }
+
             fn max_value() -> Self {
                 <Self>::max_value()
             }
@@ -153,6 +176,18 @@ macro_rules! int_impl {
             fn wrapping_mul(self, other: Self) -> Self {
                 <Self>::wrapping_mul(self, other)
             }
+
+            fn wrapping_sub(self, other: Self) -> Self {
+                <Self>::wrapping_sub(self, other)
+            }
+
+            fn checked_div(self, other: Self) -> Option<Self> {
+                <Self>::checked_div(self, other)
+            }
+
+            fn checked_rem(self, other: Self) -> Option<Self> {
+                <Self>::checked_rem(self, other)
+            }
         }
     }
 }

+ 1 - 1
src/int/mul.rs

@@ -71,7 +71,7 @@ impl Mulo for i64 {}
 impl Mulo for i128 {}
 
 intrinsics! {
-    #[cfg(not(all(feature = "c", target_arch = "x86")))]
+    #[use_c_shim_if(target_arch = "x86")]
     pub extern "C" fn __muldi3(a: u64, b: u64) -> u64 {
         a.mul(b)
     }

+ 82 - 83
src/int/sdiv.rs

@@ -1,102 +1,101 @@
 use int::Int;
 
-macro_rules! div {
-    ($intrinsic:ident: $ty:ty, $uty:ty) => {
-        div!($intrinsic: $ty, $uty, $ty, |i| {i});
-    };
-    ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => {
-        /// Returns `a / b`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret {
-            let s_a = a >> (<$ty>::bits() - 1);
-            let s_b = b >> (<$ty>::bits() - 1);
-            // NOTE it's OK to overflow here because of the `as $uty` cast below
-            // This whole operation is computing the absolute value of the inputs
-            // So some overflow will happen when dealing with e.g. `i64::MIN`
-            // where the absolute value is `(-i64::MIN) as u64`
-            let a = (a ^ s_a).wrapping_sub(s_a);
-            let b = (b ^ s_b).wrapping_sub(s_b);
-            let s = s_a ^ s_b;
-
-            let r = udiv!(a as $uty, b as $uty);
-            ($conv)((r as $ty ^ s) - s)
-        }
+trait Div: Int {
+    /// Returns `a / b`
+    fn div(self, other: Self) -> Self {
+        let s_a = self >> (Self::bits() - 1);
+        let s_b = other >> (Self::bits() - 1);
+        // NOTE it's OK to overflow here because of the `as $uty` cast below
+        // This whole operation is computing the absolute value of the inputs
+        // So some overflow will happen when dealing with e.g. `i64::MIN`
+        // where the absolute value is `(-i64::MIN) as u64`
+        let a = (self ^ s_a).wrapping_sub(s_a);
+        let b = (other ^ s_b).wrapping_sub(s_b);
+        let s = s_a ^ s_b;
+
+        let r = a.unsigned().checked_div(b.unsigned())
+            .unwrap_or_else(|| ::abort());
+        (Self::from_unsigned(r) ^ s) - s
     }
 }
 
-macro_rules! mod_ {
-    ($intrinsic:ident: $ty:ty, $uty:ty) => {
-        mod_!($intrinsic: $ty, $uty, $ty, |i| {i});
-    };
-    ($intrinsic:ident: $ty:ty, $uty:ty, $tyret:ty, $conv:expr) => {
-        /// Returns `a % b`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern "C" fn $intrinsic(a: $ty, b: $ty) -> $tyret {
-            let s = b >> (<$ty>::bits() - 1);
-            // NOTE(wrapping_sub) see comment in the `div` macro
-            let b = (b ^ s).wrapping_sub(s);
-            let s = a >> (<$ty>::bits() - 1);
-            let a = (a ^ s).wrapping_sub(s);
-
-            let r = urem!(a as $uty, b as $uty);
-            ($conv)((r as $ty ^ s) - s)
-        }
+impl Div for i32 {}
+impl Div for i64 {}
+impl Div for i128 {}
+
+trait Mod: Int {
+    /// Returns `a % b`
+    fn mod_(self, other: Self) -> Self {
+        let s = other >> (Self::bits() - 1);
+        // NOTE(wrapping_sub) see comment in the `div`
+        let b = (other ^ s).wrapping_sub(s);
+        let s = self >> (Self::bits() - 1);
+        let a = (self ^ s).wrapping_sub(s);
+
+        let r = a.unsigned().checked_rem(b.unsigned())
+            .unwrap_or_else(|| ::abort());
+        (Self::from_unsigned(r) ^ s) - s
     }
 }
 
-macro_rules! divmod {
-    ($abi:tt, $intrinsic:ident, $div:ident: $ty:ty) => {
-        /// Returns `a / b` and sets `*rem = n % d`
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty, rem: &mut $ty) -> $ty {
-            #[cfg(all(feature = "c", any(target_arch = "x86")))]
-            extern {
-                fn $div(a: $ty, b: $ty) -> $ty;
-            }
-
-            let r = match () {
-                #[cfg(not(all(feature = "c", any(target_arch = "x86"))))]
-                () => $div(a, b),
-                #[cfg(all(feature = "c", any(target_arch = "x86")))]
-                () => unsafe { $div(a, b) },
-            };
-            // NOTE won't overflow because it's using the result from the
-            // previous division
-            *rem = a - r.wrapping_mul(b);
-            r
-        }
+impl Mod for i32 {}
+impl Mod for i64 {}
+impl Mod for i128 {}
+
+trait Divmod: Int {
+    /// Returns `a / b` and sets `*rem = n % d`
+    fn divmod<F>(self, other: Self, rem: &mut Self, div: F) -> Self
+        where F: Fn(Self, Self) -> Self,
+    {
+        let r = div(self, other);
+        // NOTE won't overflow because it's using the result from the
+        // previous division
+        *rem = self - r.wrapping_mul(other);
+        r
     }
 }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"), not(thumbv6m))))]
-div!(__divsi3: i32, u32);
+impl Divmod for i32 {}
+impl Divmod for i64 {}
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-div!(__divdi3: i64, u64);
-
-#[cfg(not(all(windows, target_pointer_width="64")))]
-div!(__divti3: i128, u128);
-
-#[cfg(all(windows, target_pointer_width="64"))]
-div!(__divti3: i128, u128, ::U64x2, ::sconv);
+intrinsics! {
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios"), not(thumbv6m)))]
+    pub extern "C" fn __divsi3(a: i32, b: i32) -> i32 {
+        a.div(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-mod_!(__modsi3: i32, u32);
+    #[use_c_shim_if(target_arch = "x86")]
+    pub extern "C" fn __divdi3(a: i64, b: i64) -> i64 {
+        a.div(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-mod_!(__moddi3: i64, u64);
+    #[win64_128bit_abi_hack]
+    pub extern "C" fn __divti3(a: i128, b: i128) -> i128 {
+        a.div(b)
+    }
 
-#[cfg(not(all(windows, target_pointer_width="64")))]
-mod_!(__modti3: i128, u128);
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios")))]
+    pub extern "C" fn __modsi3(a: i32, b: i32) -> i32 {
+        a.mod_(b)
+    }
 
-#[cfg(all(windows, target_pointer_width="64"))]
-mod_!(__modti3: i128, u128, ::U64x2, ::sconv);
+    #[use_c_shim_if(target_arch = "x86")]
+    pub extern "C" fn __moddi3(a: i64, b: i64) -> i64 {
+        a.mod_(b)
+    }
 
-#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))]
-divmod!("C", __divmodsi4, __divsi3: i32);
+    #[win64_128bit_abi_hack]
+    pub extern "C" fn __modti3(a: i128, b: i128) -> i128 {
+        a.mod_(b)
+    }
 
-#[cfg(target_arch = "arm")]
-divmod!("aapcs", __divmoddi4, __divdi3: i64);
+    #[use_c_shim_if(all(target_arch = "arm", not(target_os = "ios")))]
+    pub extern "C" fn __divmodsi4(a: i32, b: i32, rem: &mut i32) -> i32 {
+        a.divmod(b, rem, |a, b| __divsi3(a, b))
+    }
 
-#[cfg(not(target_arch = "arm"))]
-divmod!("C", __divmoddi4, __divdi3: i64);
+    #[aapcs_on_arm]
+    pub extern "C" fn __divmoddi4(a: i64, b: i64, rem: &mut i64) -> i64 {
+        a.divmod(b, rem, |a, b| __divdi3(a, b))
+    }
+}

+ 3 - 3
src/int/shift.rs

@@ -65,7 +65,7 @@ impl Lshr for u64 {}
 impl Lshr for u128 {}
 
 intrinsics! {
-    #[cfg(not(all(feature = "c", target_arch = "x86")))]
+    #[use_c_shim_if(target_arch = "x86")]
     pub extern "C" fn __ashldi3(a: u64, b: u32) -> u64 {
         a.ashl(b)
     }
@@ -74,7 +74,7 @@ intrinsics! {
         a.ashl(b)
     }
 
-    #[cfg(not(all(feature = "c", target_arch = "x86")))]
+    #[use_c_shim_if(target_arch = "x86")]
     pub extern "C" fn __ashrdi3(a: i64, b: u32) -> i64 {
         a.ashr(b)
     }
@@ -83,7 +83,7 @@ intrinsics! {
         a.ashr(b)
     }
 
-    #[cfg(not(all(feature = "c", target_arch = "x86")))]
+    #[use_c_shim_if(target_arch = "x86")]
     pub extern "C" fn __lshrdi3(a: u64, b: u32) -> u64 {
         a.lshr(b)
     }

+ 4 - 0
src/lib.rs

@@ -99,6 +99,10 @@ fn sconv(i: i128) -> U64x2 {
 #[cfg(test)]
 extern crate core;
 
+fn abort() -> ! {
+    unsafe { core::intrinsics::abort() }
+}
+
 #[macro_use]
 mod macros;
 

+ 57 - 2
src/macros.rs

@@ -6,7 +6,7 @@ macro_rules! intrinsics {
     // Otherwise if the `c` feature isn't enabled then we'll just have a normal
     // intrinsic.
     (
-        #[cfg(not(all(feature = "c", $($cfg_clause:tt)*)))]
+        #[use_c_shim_if($($cfg_clause:tt)*)]
         $(#[$attr:meta])*
         pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
             $($body:tt)*
@@ -37,7 +37,7 @@ macro_rules! intrinsics {
         intrinsics!($($rest)*);
     );
 
-    // We recognize the `#[aapcs_only_on_arm]` attribute here and generate the
+    // We recognize the `#[aapcs_on_arm]` attribute here and generate the
     // same intrinsic but force it to have the `"aapcs"` calling convention on
     // ARM and `"C"` elsewhere.
     (
@@ -98,6 +98,39 @@ macro_rules! intrinsics {
         intrinsics!($($rest)*);
     );
 
+    // Another attribute we recognize is an "abi hack" for win64 to get the 128
+    // bit calling convention correct.
+    (
+        #[win64_128bit_abi_hack]
+        $(#[$attr:meta])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(all(windows, target_pointer_width = "64"))]
+        intrinsics! {
+            $(#[$attr])*
+            pub extern $abi fn $name( $($argname: $ty),* )
+                -> ::macros::win64_abi_hack::U64x2
+            {
+                let e: $ret = { $($body)* };
+                ::macros::win64_abi_hack::U64x2::from(e)
+            }
+        }
+
+        #[cfg(not(all(windows, target_pointer_width = "64")))]
+        intrinsics! {
+            $(#[$attr])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
     (
         $(#[$attr:meta])*
         pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
@@ -115,3 +148,25 @@ macro_rules! intrinsics {
         intrinsics!($($rest)*);
     );
 }
+
+// Hack for LLVM expectations for ABI on windows
+#[cfg(all(windows, target_pointer_width="64"))]
+pub mod win64_abi_hack {
+    #[repr(simd)]
+    pub struct U64x2(u64, u64);
+
+    impl From<i128> for U64x2 {
+        fn from(i: i128) -> U64x2 {
+            use int::LargeInt;
+            let j = i as u128;
+            U64x2(j.low(), j.high())
+        }
+    }
+
+    impl From<u128> for U64x2 {
+        fn from(i: u128) -> U64x2 {
+            use int::LargeInt;
+            U64x2(i.low(), i.high())
+        }
+    }
+}