Port mul intrinsics to traits
Also add a few features to the `intrinsics!` macro
This commit is contained in:
parent
eeb44abacf
commit
275d1032b5
|
@ -20,11 +20,19 @@ pub mod udiv;
|
||||||
/// Trait for some basic operations on integers
|
/// Trait for some basic operations on integers
|
||||||
pub trait Int:
|
pub trait Int:
|
||||||
Copy +
|
Copy +
|
||||||
PartialEq +
|
PartialEq +
|
||||||
|
PartialOrd +
|
||||||
|
ops::AddAssign +
|
||||||
|
ops::Add<Output = Self> +
|
||||||
|
ops::Sub<Output = Self> +
|
||||||
|
ops::Div<Output = Self> +
|
||||||
ops::Shl<u32, Output = Self> +
|
ops::Shl<u32, Output = Self> +
|
||||||
ops::Shr<u32, Output = Self> +
|
ops::Shr<u32, Output = Self> +
|
||||||
ops::BitOr<Output = Self> +
|
ops::BitOr<Output = Self> +
|
||||||
// ops::BitAnd<Output = Self> +
|
ops::BitXor<Output = Self> +
|
||||||
|
ops::BitAnd<Output = Self> +
|
||||||
|
ops::BitAndAssign +
|
||||||
|
ops::Not<Output = Self> +
|
||||||
{
|
{
|
||||||
/// Type with the same width but other signedness
|
/// Type with the same width but other signedness
|
||||||
type OtherSign: Int;
|
type OtherSign: Int;
|
||||||
|
@ -34,8 +42,8 @@ pub trait Int:
|
||||||
/// Returns the bitwidth of the int type
|
/// Returns the bitwidth of the int type
|
||||||
fn bits() -> u32;
|
fn bits() -> u32;
|
||||||
|
|
||||||
/// Returns the zero representation of this number
|
|
||||||
fn zero() -> Self;
|
fn zero() -> Self;
|
||||||
|
fn one() -> Self;
|
||||||
|
|
||||||
/// Extracts the sign from self and returns a tuple.
|
/// Extracts the sign from self and returns a tuple.
|
||||||
///
|
///
|
||||||
|
@ -51,6 +59,12 @@ pub trait Int:
|
||||||
|
|
||||||
/// Convert to a signed representation
|
/// Convert to a signed representation
|
||||||
fn unsigned(self) -> Self::UnsignedInt;
|
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 {
|
macro_rules! int_impl {
|
||||||
|
@ -63,6 +77,10 @@ macro_rules! int_impl {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
fn bits() -> u32 {
|
fn bits() -> u32 {
|
||||||
$bits
|
$bits
|
||||||
}
|
}
|
||||||
|
@ -74,6 +92,22 @@ macro_rules! int_impl {
|
||||||
fn unsigned(self) -> $uty {
|
fn unsigned(self) -> $uty {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn max_value() -> Self {
|
||||||
|
<Self>::max_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min_value() -> Self {
|
||||||
|
<Self>::min_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapping_add(self, other: Self) -> Self {
|
||||||
|
<Self>::wrapping_add(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapping_mul(self, other: Self) -> Self {
|
||||||
|
<Self>::wrapping_mul(self, other)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Int for $ity {
|
impl Int for $ity {
|
||||||
|
@ -88,6 +122,10 @@ macro_rules! int_impl {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn one() -> Self {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_sign(self) -> (bool, $uty) {
|
fn extract_sign(self) -> (bool, $uty) {
|
||||||
if self < 0 {
|
if self < 0 {
|
||||||
(true, (!(self as $uty)).wrapping_add(1))
|
(true, (!(self as $uty)).wrapping_add(1))
|
||||||
|
@ -99,6 +137,22 @@ macro_rules! int_impl {
|
||||||
fn unsigned(self) -> $uty {
|
fn unsigned(self) -> $uty {
|
||||||
self as $uty
|
self as $uty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn max_value() -> Self {
|
||||||
|
<Self>::max_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min_value() -> Self {
|
||||||
|
<Self>::min_value()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapping_add(self, other: Self) -> Self {
|
||||||
|
<Self>::wrapping_add(self, other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrapping_mul(self, other: Self) -> Self {
|
||||||
|
<Self>::wrapping_mul(self, other)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +167,9 @@ pub trait LargeInt: Int {
|
||||||
type HighHalf: Int;
|
type HighHalf: Int;
|
||||||
|
|
||||||
fn low(self) -> Self::LowHalf;
|
fn low(self) -> Self::LowHalf;
|
||||||
|
fn low_as_high(low: Self::LowHalf) -> Self::HighHalf;
|
||||||
fn high(self) -> 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;
|
fn from_parts(low: Self::LowHalf, high: Self::HighHalf) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,9 +182,15 @@ macro_rules! large_int {
|
||||||
fn low(self) -> $tylow {
|
fn low(self) -> $tylow {
|
||||||
self as $tylow
|
self as $tylow
|
||||||
}
|
}
|
||||||
|
fn low_as_high(low: $tylow) -> $tyhigh {
|
||||||
|
low as $tyhigh
|
||||||
|
}
|
||||||
fn high(self) -> $tyhigh {
|
fn high(self) -> $tyhigh {
|
||||||
(self >> $halfbits) as $tyhigh
|
(self >> $halfbits) as $tyhigh
|
||||||
}
|
}
|
||||||
|
fn high_as_low(high: $tyhigh) -> $tylow {
|
||||||
|
high as $tylow
|
||||||
|
}
|
||||||
fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
|
fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
|
||||||
low as $ty | ((high as $ty) << $halfbits)
|
low as $ty | ((high as $ty) << $halfbits)
|
||||||
}
|
}
|
||||||
|
|
159
src/int/mul.rs
159
src/int/mul.rs
|
@ -1,95 +1,96 @@
|
||||||
|
use core::ops;
|
||||||
|
|
||||||
use int::LargeInt;
|
use int::LargeInt;
|
||||||
use int::Int;
|
use int::Int;
|
||||||
|
|
||||||
macro_rules! mul {
|
trait Mul: LargeInt {
|
||||||
($(#[$attr:meta])+ |
|
fn mul(self, other: Self) -> Self {
|
||||||
$abi:tt, $intrinsic:ident: $ty:ty) => {
|
let half_bits = Self::bits() / 4;
|
||||||
/// Returns `a * b`
|
let lower_mask = !<<Self as LargeInt>::LowHalf>::zero() >> half_bits;
|
||||||
$(#[$attr])+
|
let mut low = (self.low() & lower_mask).wrapping_mul(other.low() & lower_mask);
|
||||||
pub extern $abi fn $intrinsic(a: $ty, b: $ty) -> $ty {
|
let mut t = low >> half_bits;
|
||||||
let half_bits = <$ty>::bits() / 4;
|
low &= lower_mask;
|
||||||
let lower_mask = !0 >> half_bits;
|
t += (self.low() >> half_bits).wrapping_mul(other.low() & lower_mask);
|
||||||
let mut low = (a.low() & lower_mask).wrapping_mul(b.low() & lower_mask);
|
low += (t & lower_mask) << half_bits;
|
||||||
let mut t = low >> half_bits;
|
let mut high = Self::low_as_high(t >> half_bits);
|
||||||
low &= lower_mask;
|
t = low >> half_bits;
|
||||||
t += (a.low() >> half_bits).wrapping_mul(b.low() & lower_mask);
|
low &= lower_mask;
|
||||||
low += (t & lower_mask) << half_bits;
|
t += (other.low() >> half_bits).wrapping_mul(self.low() & lower_mask);
|
||||||
let mut high = (t >> half_bits) as hty!($ty);
|
low += (t & lower_mask) << half_bits;
|
||||||
t = low >> half_bits;
|
high += Self::low_as_high(t >> half_bits);
|
||||||
low &= lower_mask;
|
high += Self::low_as_high((self.low() >> half_bits).wrapping_mul(other.low() >> half_bits));
|
||||||
t += (b.low() >> half_bits).wrapping_mul(a.low() & lower_mask);
|
high = high.wrapping_add(self.high().wrapping_mul(Self::low_as_high(other.low())))
|
||||||
low += (t & lower_mask) << half_bits;
|
.wrapping_add(Self::low_as_high(self.low()).wrapping_mul(other.high()));
|
||||||
high += (t >> half_bits) as hty!($ty);
|
Self::from_parts(low, high)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! mulo {
|
impl Mul for u64 {}
|
||||||
($intrinsic:ident: $ty:ty) => {
|
impl Mul for i128 {}
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sa = a >> (<$ty>::bits() - 1);
|
trait Mulo: Int + ops::Neg<Output = Self> {
|
||||||
let abs_a = (a ^ sa) - sa;
|
fn mulo(self, other: Self, overflow: &mut i32) -> Self {
|
||||||
let sb = b >> (<$ty>::bits() - 1);
|
*overflow = 0;
|
||||||
let abs_b = (b ^ sb) - sb;
|
let result = self.wrapping_mul(other);
|
||||||
if abs_a < 2 || abs_b < 2 {
|
if self == Self::min_value() {
|
||||||
return result;
|
if other != Self::zero() && other != Self::one() {
|
||||||
|
*overflow = 1;
|
||||||
}
|
}
|
||||||
if sa == sb {
|
return result;
|
||||||
if abs_a > <$ty>::max_value() / abs_b {
|
|
||||||
*overflow = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if abs_a > <$ty>::min_value() / -abs_b {
|
|
||||||
*overflow = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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")))]
|
impl Mulo for i32 {}
|
||||||
mul!(#[cfg_attr(all(not(test), not(target_arch = "arm")), no_mangle)]
|
impl Mulo for i64 {}
|
||||||
#[cfg_attr(all(not(test), target_arch = "arm"), inline(always))]
|
impl Mulo for i128 {}
|
||||||
| "C", __muldi3: u64);
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "arm"))]
|
intrinsics! {
|
||||||
mul!(#[cfg_attr(not(test), no_mangle)]
|
#[cfg(not(all(feature = "c", target_arch = "x86")))]
|
||||||
| "C", __multi3: i128);
|
pub extern "C" fn __muldi3(a: u64, b: u64) -> u64 {
|
||||||
|
a.mul(b)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "arm")]
|
#[aapcs_on_arm]
|
||||||
mul!(#[cfg_attr(not(test), no_mangle)]
|
pub extern "C" fn __multi3(a: i128, b: i128) -> i128 {
|
||||||
| "aapcs", __multi3: i128);
|
a.mul(b)
|
||||||
|
}
|
||||||
|
|
||||||
mulo!(__mulosi4: i32);
|
pub extern "C" fn __mulosi4(a: i32, b: i32, oflow: &mut i32) -> i32 {
|
||||||
mulo!(__mulodi4: i64);
|
a.mulo(b, oflow)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(windows, target_pointer_width="64"))]
|
pub extern "C" fn __mulodi4(a: i64, b: i64, oflow: &mut i32) -> i64 {
|
||||||
mulo!(__muloti4: i128, "unadjusted");
|
a.mulo(b, oflow)
|
||||||
#[cfg(not(all(windows, target_pointer_width="64")))]
|
}
|
||||||
mulo!(__muloti4: i128);
|
|
||||||
|
#[unadjusted_on_win64]
|
||||||
|
pub extern "C" fn __muloti4(a: i128, b: i128, oflow: &mut i32) -> i128 {
|
||||||
|
a.mulo(b, oflow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
macro_rules! intrinsics {
|
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)*)))]
|
#[cfg(not(all(feature = "c", $($cfg_clause:tt)*)))]
|
||||||
$(#[$attr:meta])*
|
$(#[$attr:meta])*
|
||||||
|
@ -32,6 +37,67 @@ macro_rules! intrinsics {
|
||||||
intrinsics!($($rest)*);
|
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])*
|
$(#[$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 {
|
||||||
|
|
Loading…
Reference in New Issue