Procházet zdrojové kódy

Port mul intrinsics to traits

Also add a few features to the `intrinsics!` macro
Alex Crichton před 7 roky
rodič
revize
275d1032b5
3 změnil soubory, kde provedl 208 přidání a 79 odebrání
  1. 65 3
      src/int/mod.rs
  2. 77 76
      src/int/mul.rs
  3. 66 0
      src/macros.rs

+ 65 - 3
src/int/mod.rs

@@ -20,11 +20,19 @@ pub mod udiv;
 /// Trait for some basic operations on integers
 pub trait Int:
     Copy +
-    PartialEq  +
+    PartialEq +
+    PartialOrd +
+    ops::AddAssign +
+    ops::Add<Output = Self> +
+    ops::Sub<Output = Self> +
+    ops::Div<Output = Self> +
     ops::Shl<u32, Output = Self> +
     ops::Shr<u32, Output = Self> +
     ops::BitOr<Output = Self> +
-    // ops::BitAnd<Output = Self> +
+    ops::BitXor<Output = Self> +
+    ops::BitAnd<Output = Self> +
+    ops::BitAndAssign +
+    ops::Not<Output = Self> +
 {
     /// Type with the same width but other signedness
     type OtherSign: Int;
@@ -34,8 +42,8 @@ pub trait Int:
     /// Returns the bitwidth of the int type
     fn bits() -> u32;
 
-    /// Returns the zero representation of this number
     fn zero() -> Self;
+    fn one() -> Self;
 
     /// Extracts the sign from self and returns a tuple.
     ///
@@ -51,6 +59,12 @@ pub trait Int:
 
     /// Convert to a signed representation
     fn unsigned(self) -> Self::UnsignedInt;
+
+    // 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;
 }
 
 macro_rules! int_impl {
@@ -63,6 +77,10 @@ macro_rules! int_impl {
                 0
             }
 
+            fn one() -> Self {
+                1
+            }
+
             fn bits() -> u32 {
                 $bits
             }
@@ -74,6 +92,22 @@ macro_rules! int_impl {
             fn unsigned(self) -> $uty {
                 self
             }
+
+            fn max_value() -> Self {
+                <Self>::max_value()
+            }
+
+            fn min_value() -> Self {
+                <Self>::min_value()
+            }
+
+            fn wrapping_add(self, other: Self) -> Self {
+                <Self>::wrapping_add(self, other)
+            }
+
+            fn wrapping_mul(self, other: Self) -> Self {
+                <Self>::wrapping_mul(self, other)
+            }
         }
 
         impl Int for $ity {
@@ -88,6 +122,10 @@ macro_rules! int_impl {
                 0
             }
 
+            fn one() -> Self {
+                1
+            }
+
             fn extract_sign(self) -> (bool, $uty) {
                 if self < 0 {
                     (true, (!(self as $uty)).wrapping_add(1))
@@ -99,6 +137,22 @@ macro_rules! int_impl {
             fn unsigned(self) -> $uty {
                 self as $uty
             }
+
+            fn max_value() -> Self {
+                <Self>::max_value()
+            }
+
+            fn min_value() -> Self {
+                <Self>::min_value()
+            }
+
+            fn wrapping_add(self, other: Self) -> Self {
+                <Self>::wrapping_add(self, other)
+            }
+
+            fn wrapping_mul(self, other: Self) -> Self {
+                <Self>::wrapping_mul(self, other)
+            }
         }
     }
 }
@@ -113,7 +167,9 @@ pub trait LargeInt: Int {
     type HighHalf: Int;
 
     fn low(self) -> Self::LowHalf;
+    fn low_as_high(low: Self::LowHalf) -> Self::HighHalf;
     fn high(self) -> Self::HighHalf;
+    fn high_as_low(low: Self::HighHalf) -> Self::LowHalf;
     fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self;
 }
 
@@ -126,9 +182,15 @@ macro_rules! large_int {
             fn low(self) -> $tylow {
                 self as $tylow
             }
+            fn low_as_high(low: $tylow) -> $tyhigh {
+                low as $tyhigh
+            }
             fn high(self) -> $tyhigh {
                 (self >> $halfbits) as $tyhigh
             }
+            fn high_as_low(high: $tyhigh) -> $tylow {
+                high as $tylow
+            }
             fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
                 low as $ty | ((high as $ty) << $halfbits)
             }

+ 77 - 76
src/int/mul.rs

@@ -1,95 +1,96 @@
+use core::ops;
+
 use int::LargeInt;
 use int::Int;
 
-macro_rules! mul {
-    ($(#[$attr:meta])+ |
-     $abi:tt, $intrinsic:ident: $ty:ty) => {
-        /// Returns `a * b`
-        $(#[$attr])+
-        pub extern $abi 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).wrapping_mul(b.low() & lower_mask);
-            let mut t = low >> half_bits;
-            low &= lower_mask;
-            t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask);
-            low += (t & lower_mask) << half_bits;
-            let mut high = (t >> half_bits) as hty!($ty);
-            t = low >> half_bits;
-            low &= lower_mask;
-            t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask);
-            low += (t & lower_mask) << half_bits;
-            high += (t >> half_bits) as hty!($ty);
-            high += (a.low() >> half_bits).wrapping_mul(b.low() >> half_bits) as hty!($ty);
-            high = high.wrapping_add(a.high().wrapping_mul(b.low() as hty!($ty)))
-                       .wrapping_add((a.low() as hty!($ty)).wrapping_mul(b.high()));
-            <$ty>::from_parts(low, high)
-        }
+trait Mul: LargeInt {
+    fn mul(self, other: Self) -> Self {
+        let half_bits = Self::bits() / 4;
+        let lower_mask = !<<Self as LargeInt>::LowHalf>::zero() >> half_bits;
+        let mut low = (self.low() & lower_mask).wrapping_mul(other.low() & lower_mask);
+        let mut t = low >> half_bits;
+        low &= lower_mask;
+        t += (self.low() >> half_bits).wrapping_mul(other.low() & lower_mask);
+        low += (t & lower_mask) << half_bits;
+        let mut high = Self::low_as_high(t >> half_bits);
+        t = low >> half_bits;
+        low &= lower_mask;
+        t += (other.low() >> half_bits).wrapping_mul(self.low() & lower_mask);
+        low += (t & lower_mask) << half_bits;
+        high += Self::low_as_high(t >> half_bits);
+        high += Self::low_as_high((self.low() >> half_bits).wrapping_mul(other.low() >> half_bits));
+        high = high.wrapping_add(self.high().wrapping_mul(Self::low_as_high(other.low())))
+                   .wrapping_add(Self::low_as_high(self.low()).wrapping_mul(other.high()));
+        Self::from_parts(low, high)
     }
 }
 
-macro_rules! mulo {
-    ($intrinsic:ident: $ty:ty) => {
-        // Default is "C" ABI
-        mulo!($intrinsic: $ty, "C");
-    };
-    ($intrinsic:ident: $ty:ty, $abi:tt) => {
-        /// Returns `a * b` and sets `*overflow = 1` if `a * b` overflows
-        #[cfg_attr(not(test), no_mangle)]
-        pub extern $abi fn $intrinsic(a: $ty, b: $ty, overflow: &mut i32) -> $ty {
-            *overflow = 0;
-            let result = a.wrapping_mul(b);
-            if a == <$ty>::min_value() {
-                if b != 0 && b != 1 {
-                    *overflow = 1;
-                }
-                return result;
+impl Mul for u64 {}
+impl Mul for i128 {}
+
+trait Mulo: Int + ops::Neg<Output = Self> {
+    fn mulo(self, other: Self, overflow: &mut i32) -> Self {
+        *overflow = 0;
+        let result = self.wrapping_mul(other);
+        if self == Self::min_value() {
+            if other != Self::zero() && other != Self::one() {
+                *overflow = 1;
             }
-            if b == <$ty>::min_value() {
-                if a != 0 && a != 1 {
-                    *overflow = 1;
-                }
-                return result;
+            return result;
+        }
+        if other == Self::min_value() {
+            if self != Self::zero() && self != Self::one() {
+                *overflow = 1;
             }
+            return result;
+        }
 
-            let sa = a >> (<$ty>::bits() - 1);
-            let abs_a = (a ^ sa) - sa;
-            let sb = b >> (<$ty>::bits() - 1);
-            let abs_b = (b ^ sb) - sb;
-            if abs_a < 2 || abs_b < 2 {
-                return result;
+        let sa = self >> (Self::bits() - 1);
+        let abs_a = (self ^ sa) - sa;
+        let sb = other >> (Self::bits() - 1);
+        let abs_b = (other ^ sb) - sb;
+        let two = Self::one() + Self::one();
+        if abs_a < two || abs_b < two {
+            return result;
+        }
+        if sa == sb {
+            if abs_a > Self::max_value() / abs_b {
+                *overflow = 1;
             }
-            if sa == sb {
-                if abs_a > <$ty>::max_value() / abs_b {
-                    *overflow = 1;
-                }
-            } else {
-                if abs_a > <$ty>::min_value() / -abs_b {
-                    *overflow = 1;
-                }
+        } else {
+            if abs_a > Self::min_value() / -abs_b {
+                *overflow = 1;
             }
-            result
         }
+        result
     }
 }
 
-#[cfg(not(all(feature = "c", target_arch = "x86")))]
-mul!(#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
-     #[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
-     | "C", __muldi3: u64);
+impl Mulo for i32 {}
+impl Mulo for i64 {}
+impl Mulo for i128 {}
 
-#[cfg(not(target_arch = "arm"))]
-mul!(#[cfg_attr(not(test), no_mangle)]
-     | "C", __multi3: i128);
+intrinsics! {
+    #[cfg(not(all(feature = "c", target_arch = "x86")))]
+    pub extern "C" fn __muldi3(a: u64, b: u64) -> u64 {
+        a.mul(b)
+    }
 
-#[cfg(target_arch = "arm")]
-mul!(#[cfg_attr(not(test), no_mangle)]
-     | "aapcs", __multi3: i128);
+    #[aapcs_on_arm]
+    pub extern "C" fn __multi3(a: i128, b: i128) -> i128 {
+        a.mul(b)
+    }
+
+    pub extern "C" fn __mulosi4(a: i32, b: i32, oflow: &mut i32) -> i32 {
+        a.mulo(b, oflow)
+    }
 
-mulo!(__mulosi4: i32);
-mulo!(__mulodi4: i64);
+    pub extern "C" fn __mulodi4(a: i64, b: i64, oflow: &mut i32) -> i64 {
+        a.mulo(b, oflow)
+    }
 
-#[cfg(all(windows, target_pointer_width="64"))]
-mulo!(__muloti4: i128, "unadjusted");
-#[cfg(not(all(windows, target_pointer_width="64")))]
-mulo!(__muloti4: i128);
+    #[unadjusted_on_win64]
+    pub extern "C" fn __muloti4(a: i128, b: i128, oflow: &mut i32) -> i128 {
+        a.mulo(b, oflow)
+    }
+}

+ 66 - 0
src/macros.rs

@@ -1,5 +1,10 @@
 macro_rules! intrinsics {
     () => ();
+
+    // Anything which has a `not(feature = "c")` we'll generate a shim function
+    // which calls out to the C function if the `c` feature is enabled.
+    // Otherwise if the `c` feature isn't enabled then we'll just have a normal
+    // intrinsic.
     (
         #[cfg(not(all(feature = "c", $($cfg_clause:tt)*)))]
         $(#[$attr:meta])*
@@ -32,6 +37,67 @@ macro_rules! intrinsics {
         intrinsics!($($rest)*);
     );
 
+    // We recognize the `#[aapcs_only_on_arm]` attribute here and generate the
+    // same intrinsic but force it to have the `"aapcs"` calling convention on
+    // ARM and `"C"` elsewhere.
+    (
+        #[aapcs_on_arm]
+        $(#[$attr:meta])*
+        pub extern $abi:tt fn $name:ident( $($argname:ident:  $ty:ty),* ) -> $ret:ty {
+            $($body:tt)*
+        }
+
+        $($rest:tt)*
+    ) => (
+        #[cfg(target_arch = "arm")]
+        intrinsics! {
+            $(#[$attr])*
+            pub extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        #[cfg(not(target_arch = "arm"))]
+        intrinsics! {
+            $(#[$attr])*
+            pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        intrinsics!($($rest)*);
+    );
+
+    // Like aapcs above we recognize an attribute for the "unadjusted" abi on
+    // win64 for some methods.
+    (
+        #[unadjusted_on_win64]
+        $(#[$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 "unadjusted" fn $name( $($argname: $ty),* ) -> $ret {
+                $($body)*
+            }
+        }
+
+        #[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 {