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);
/// 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)
}
}
}
}

View File

@ -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)
}

View File

@ -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<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);
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))
}
}

View File

@ -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)
}

View File

@ -99,6 +99,10 @@ fn sconv(i: i128) -> U64x2 {
#[cfg(test)]
extern crate core;
fn abort() -> ! {
unsafe { core::intrinsics::abort() }
}
#[macro_use]
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
// 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())
}
}
}