Port sdiv to traits + `intrinsics!`

Enhance `intrinsics!` along the way!
master
Alex Crichton 2017-06-22 23:09:28 -07:00
parent 275d1032b5
commit 7886ae275b
6 changed files with 180 additions and 87 deletions

View File

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

View File

@ -71,7 +71,7 @@ impl Mulo for i64 {}
impl Mulo for i128 {} impl Mulo for i128 {}
intrinsics! { 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 { pub extern "C" fn __muldi3(a: u64, b: u64) -> u64 {
a.mul(b) a.mul(b)
} }

View File

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

View File

@ -65,7 +65,7 @@ impl Lshr for u64 {}
impl Lshr for u128 {} impl Lshr for u128 {}
intrinsics! { 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 { pub extern "C" fn __ashldi3(a: u64, b: u32) -> u64 {
a.ashl(b) a.ashl(b)
} }
@ -74,7 +74,7 @@ intrinsics! {
a.ashl(b) 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 { pub extern "C" fn __ashrdi3(a: i64, b: u32) -> i64 {
a.ashr(b) a.ashr(b)
} }
@ -83,7 +83,7 @@ intrinsics! {
a.ashr(b) 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 { pub extern "C" fn __lshrdi3(a: u64, b: u32) -> u64 {
a.lshr(b) a.lshr(b)
} }

View File

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

View File

@ -6,7 +6,7 @@ macro_rules! intrinsics {
// Otherwise if the `c` feature isn't enabled then we'll just have a normal // Otherwise if the `c` feature isn't enabled then we'll just have a normal
// intrinsic. // intrinsic.
( (
#[cfg(not(all(feature = "c", $($cfg_clause:tt)*)))] #[use_c_shim_if($($cfg_clause:tt)*)]
$(#[$attr:meta])* $(#[$attr:meta])*
pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
$($body:tt)* $($body:tt)*
@ -37,7 +37,7 @@ macro_rules! intrinsics {
intrinsics!($($rest)*); 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 // same intrinsic but force it to have the `"aapcs"` calling convention on
// ARM and `"C"` elsewhere. // ARM and `"C"` elsewhere.
( (
@ -98,6 +98,39 @@ macro_rules! intrinsics {
intrinsics!($($rest)*); 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])* $(#[$attr:meta])*
pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { pub extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
@ -115,3 +148,25 @@ macro_rules! intrinsics {
intrinsics!($($rest)*); 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())
}
}
}