From 275d1032b59455ee0fabc97e2e51d650f8390780 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Jun 2017 22:36:37 -0700 Subject: [PATCH] Port mul intrinsics to traits Also add a few features to the `intrinsics!` macro --- src/int/mod.rs | 68 ++++++++++++++++++++- src/int/mul.rs | 159 +++++++++++++++++++++++++------------------------ src/macros.rs | 66 ++++++++++++++++++++ 3 files changed, 211 insertions(+), 82 deletions(-) diff --git a/src/int/mod.rs b/src/int/mod.rs index 11a31ac..02e3412 100644 --- a/src/int/mod.rs +++ b/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 + + ops::Sub + + ops::Div + ops::Shl + ops::Shr + ops::BitOr + - // ops::BitAnd + + ops::BitXor + + ops::BitAnd + + ops::BitAndAssign + + ops::Not + { /// 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 { + ::max_value() + } + + fn min_value() -> Self { + ::min_value() + } + + fn wrapping_add(self, other: Self) -> Self { + ::wrapping_add(self, other) + } + + fn wrapping_mul(self, other: 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 { + ::max_value() + } + + fn min_value() -> Self { + ::min_value() + } + + fn wrapping_add(self, other: Self) -> Self { + ::wrapping_add(self, other) + } + + fn wrapping_mul(self, other: 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) } diff --git a/src/int/mul.rs b/src/int/mul.rs index 5381edd..73f95a3 100644 --- a/src/int/mul.rs +++ b/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 = !<::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; - } - if b == <$ty>::min_value() { - if a != 0 && a != 1 { - *overflow = 1; - } - return result; - } +impl Mul for u64 {} +impl Mul for i128 {} - 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; +trait Mulo: Int + ops::Neg { + 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 sa == sb { - if abs_a > <$ty>::max_value() / abs_b { - *overflow = 1; - } - } else { - if abs_a > <$ty>::min_value() / -abs_b { - *overflow = 1; - } - } - result + return result; } + if other == Self::min_value() { + if self != Self::zero() && self != Self::one() { + *overflow = 1; + } + 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; + } + } else { + if abs_a > Self::min_value() / -abs_b { + *overflow = 1; + } + } + 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) + } -mulo!(__mulosi4: i32); -mulo!(__mulodi4: i64); + pub extern "C" fn __mulosi4(a: i32, b: i32, oflow: &mut i32) -> i32 { + 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); + pub extern "C" fn __mulodi4(a: i64, b: i64, oflow: &mut i32) -> i64 { + a.mulo(b, oflow) + } + + #[unadjusted_on_win64] + pub extern "C" fn __muloti4(a: i128, b: i128, oflow: &mut i32) -> i128 { + a.mulo(b, oflow) + } +} diff --git a/src/macros.rs b/src/macros.rs index a5a535b..c0c94b7 100644 --- a/src/macros.rs +++ b/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 {