diff --git a/src/int/mod.rs b/src/int/mod.rs index 02e3412..9ad3d88 100644 --- a/src/int/mod.rs +++ b/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; + fn checked_rem(self, other: Self) -> Option; } macro_rules! int_impl { @@ -93,6 +96,10 @@ macro_rules! int_impl { self } + fn from_unsigned(me: $uty) -> Self { + me + } + fn max_value() -> Self { ::max_value() } @@ -108,6 +115,18 @@ macro_rules! int_impl { fn wrapping_mul(self, other: Self) -> Self { ::wrapping_mul(self, other) } + + fn wrapping_sub(self, other: Self) -> Self { + ::wrapping_sub(self, other) + } + + fn checked_div(self, other: Self) -> Option { + ::checked_div(self, other) + } + + fn checked_rem(self, other: Self) -> Option { + ::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 { ::max_value() } @@ -153,6 +176,18 @@ macro_rules! int_impl { fn wrapping_mul(self, other: Self) -> Self { ::wrapping_mul(self, other) } + + fn wrapping_sub(self, other: Self) -> Self { + ::wrapping_sub(self, other) + } + + fn checked_div(self, other: Self) -> Option { + ::checked_div(self, other) + } + + fn checked_rem(self, other: Self) -> Option { + ::checked_rem(self, other) + } } } } diff --git a/src/int/mul.rs b/src/int/mul.rs index 73f95a3..4b0831d 100644 --- a/src/int/mul.rs +++ b/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) } diff --git a/src/int/sdiv.rs b/src/int/sdiv.rs index 5576898..310eb9f 100644 --- a/src/int/sdiv.rs +++ b/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; +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 = udiv!(a as $uty, b as $uty); - ($conv)((r as $ty ^ s) - s) - } + 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); +impl Div for i32 {} +impl Div for i64 {} +impl Div for i128 {} - let r = urem!(a as $uty, b as $uty); - ($conv)((r as $ty ^ s) - s) - } +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; - } +impl Mod for i32 {} +impl Mod for i64 {} +impl Mod for i128 {} - 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 - } +trait Divmod: Int { + /// Returns `a / b` and sets `*rem = n % d` + fn divmod(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); +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(windows, target_pointer_width="64")))] -div!(__divti3: i128, u128); + #[use_c_shim_if(target_arch = "x86")] + pub extern "C" fn __divdi3(a: i64, b: i64) -> i64 { + a.div(b) + } -#[cfg(all(windows, target_pointer_width="64"))] -div!(__divti3: i128, u128, ::U64x2, ::sconv); + #[win64_128bit_abi_hack] + pub extern "C" fn __divti3(a: i128, b: i128) -> i128 { + a.div(b) + } -#[cfg(not(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))] -mod_!(__modsi3: i32, u32); + #[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(not(all(feature = "c", target_arch = "x86")))] -mod_!(__moddi3: i64, u64); + #[use_c_shim_if(target_arch = "x86")] + pub extern "C" fn __moddi3(a: i64, b: i64) -> i64 { + a.mod_(b) + } -#[cfg(not(all(windows, target_pointer_width="64")))] -mod_!(__modti3: i128, u128); + #[win64_128bit_abi_hack] + pub extern "C" fn __modti3(a: i128, b: i128) -> i128 { + a.mod_(b) + } -#[cfg(all(windows, target_pointer_width="64"))] -mod_!(__modti3: i128, u128, ::U64x2, ::sconv); + #[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(all(feature = "c", target_arch = "arm", not(target_os = "ios"))))] -divmod!("C", __divmodsi4, __divsi3: i32); - -#[cfg(target_arch = "arm")] -divmod!("aapcs", __divmoddi4, __divdi3: i64); - -#[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)) + } +} diff --git a/src/int/shift.rs b/src/int/shift.rs index 23a49ac..746d02d 100644 --- a/src/int/shift.rs +++ b/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) } diff --git a/src/lib.rs b/src/lib.rs index b1e739f..a55752a 100644 --- a/src/lib.rs +++ b/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; diff --git a/src/macros.rs b/src/macros.rs index c0c94b7..5c98774 100644 --- a/src/macros.rs +++ b/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 for U64x2 { + fn from(i: i128) -> U64x2 { + use int::LargeInt; + let j = i as u128; + U64x2(j.low(), j.high()) + } + } + + impl From for U64x2 { + fn from(i: u128) -> U64x2 { + use int::LargeInt; + U64x2(i.low(), i.high()) + } + } +}