Port sdiv to traits + intrinsics!
Enhance `intrinsics!` along the way!
This commit is contained in:
parent
275d1032b5
commit
7886ae275b
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
159
src/int/sdiv.rs
159
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<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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -99,6 +99,10 @@ fn sconv(i: i128) -> U64x2 {
|
||||
#[cfg(test)]
|
||||
extern crate core;
|
||||
|
||||
fn abort() -> ! {
|
||||
unsafe { core::intrinsics::abort() }
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user