This commit tweaks the implementation of the synthetic `#[use_c_shim_if]` attribute, renaming it to `#[maybe_use_optimized_c_shim]` in the process. This no longer requires specifying a `#[cfg]` clause indicating when the optimized intrinsic should be used, but rather this is inferred and printed from the build script. The build script will now print out appropriate `#[cfg]` directives for rustc to indicate what intrinsics it's compiling. This should remove the need for us to keep the build script and the source in sync, but rather the build script can simply take care of everything.
369 lines
9.2 KiB
Rust
369 lines
9.2 KiB
Rust
use core::ops;
|
|
|
|
macro_rules! hty {
|
|
($ty:ty) => {
|
|
<$ty as LargeInt>::HighHalf
|
|
};
|
|
}
|
|
|
|
macro_rules! os_ty {
|
|
($ty:ty) => {
|
|
<$ty as Int>::OtherSign
|
|
};
|
|
}
|
|
|
|
pub mod addsub;
|
|
pub mod mul;
|
|
pub mod sdiv;
|
|
pub mod shift;
|
|
pub mod udiv;
|
|
|
|
/// Trait for some basic operations on integers
|
|
pub trait Int:
|
|
Copy
|
|
+ PartialEq
|
|
+ PartialOrd
|
|
+ ops::AddAssign
|
|
+ ops::BitAndAssign
|
|
+ ops::BitOrAssign
|
|
+ ops::ShlAssign<i32>
|
|
+ ops::ShrAssign<u32>
|
|
+ ops::Add<Output = Self>
|
|
+ ops::Sub<Output = Self>
|
|
+ ops::Div<Output = Self>
|
|
+ ops::Shl<u32, Output = Self>
|
|
+ ops::Shr<u32, Output = Self>
|
|
+ ops::BitOr<Output = Self>
|
|
+ ops::BitXor<Output = Self>
|
|
+ ops::BitAnd<Output = Self>
|
|
+ ops::Not<Output = Self>
|
|
{
|
|
/// Type with the same width but other signedness
|
|
type OtherSign: Int;
|
|
/// Unsigned version of Self
|
|
type UnsignedInt: Int;
|
|
|
|
/// The bitwidth of the int type
|
|
const BITS: u32;
|
|
|
|
const ZERO: Self;
|
|
const ONE: Self;
|
|
|
|
/// Extracts the sign from self and returns a tuple.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust,ignore
|
|
/// let i = -25_i32;
|
|
/// let (sign, u) = i.extract_sign();
|
|
/// assert_eq!(sign, true);
|
|
/// assert_eq!(u, 25_u32);
|
|
/// ```
|
|
fn extract_sign(self) -> (bool, Self::UnsignedInt);
|
|
|
|
fn unsigned(self) -> Self::UnsignedInt;
|
|
fn from_unsigned(unsigned: Self::UnsignedInt) -> Self;
|
|
|
|
fn from_bool(b: bool) -> 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 wrapping_shl(self, other: u32) -> Self;
|
|
fn overflowing_add(self, other: Self) -> (Self, bool);
|
|
fn aborting_div(self, other: Self) -> Self;
|
|
fn aborting_rem(self, other: Self) -> Self;
|
|
fn leading_zeros(self) -> u32;
|
|
}
|
|
|
|
fn unwrap<T>(t: Option<T>) -> T {
|
|
match t {
|
|
Some(t) => t,
|
|
None => ::abort(),
|
|
}
|
|
}
|
|
|
|
macro_rules! int_impl_common {
|
|
($ty:ty, $bits:expr) => {
|
|
const BITS: u32 = $bits;
|
|
|
|
const ZERO: Self = 0;
|
|
const ONE: Self = 1;
|
|
|
|
fn from_bool(b: bool) -> Self {
|
|
b as $ty
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
fn wrapping_sub(self, other: Self) -> Self {
|
|
<Self>::wrapping_sub(self, other)
|
|
}
|
|
|
|
fn wrapping_shl(self, other: u32) -> Self {
|
|
<Self>::wrapping_shl(self, other)
|
|
}
|
|
|
|
fn overflowing_add(self, other: Self) -> (Self, bool) {
|
|
<Self>::overflowing_add(self, other)
|
|
}
|
|
|
|
fn aborting_div(self, other: Self) -> Self {
|
|
unwrap(<Self>::checked_div(self, other))
|
|
}
|
|
|
|
fn aborting_rem(self, other: Self) -> Self {
|
|
unwrap(<Self>::checked_rem(self, other))
|
|
}
|
|
|
|
fn leading_zeros(self) -> u32 {
|
|
<Self>::leading_zeros(self)
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! int_impl {
|
|
($ity:ty, $uty:ty, $bits:expr) => {
|
|
impl Int for $uty {
|
|
type OtherSign = $ity;
|
|
type UnsignedInt = $uty;
|
|
|
|
fn extract_sign(self) -> (bool, $uty) {
|
|
(false, self)
|
|
}
|
|
|
|
fn unsigned(self) -> $uty {
|
|
self
|
|
}
|
|
|
|
fn from_unsigned(me: $uty) -> Self {
|
|
me
|
|
}
|
|
|
|
int_impl_common!($uty, $bits);
|
|
}
|
|
|
|
impl Int for $ity {
|
|
type OtherSign = $uty;
|
|
type UnsignedInt = $uty;
|
|
|
|
fn extract_sign(self) -> (bool, $uty) {
|
|
if self < 0 {
|
|
(true, (!(self as $uty)).wrapping_add(1))
|
|
} else {
|
|
(false, self as $uty)
|
|
}
|
|
}
|
|
|
|
fn unsigned(self) -> $uty {
|
|
self as $uty
|
|
}
|
|
|
|
fn from_unsigned(me: $uty) -> Self {
|
|
me as $ity
|
|
}
|
|
|
|
int_impl_common!($ity, $bits);
|
|
}
|
|
};
|
|
}
|
|
|
|
int_impl!(i32, u32, 32);
|
|
int_impl!(i64, u64, 64);
|
|
int_impl!(i128, u128, 128);
|
|
|
|
/// Trait to convert an integer to/from smaller parts
|
|
pub trait LargeInt: Int {
|
|
type LowHalf: Int;
|
|
type HighHalf: Int;
|
|
|
|
fn low(self) -> Self::LowHalf;
|
|
fn low_as_high(low: Self::LowHalf) -> 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;
|
|
}
|
|
|
|
macro_rules! large_int {
|
|
($ty:ty, $tylow:ty, $tyhigh:ty, $halfbits:expr) => {
|
|
impl LargeInt for $ty {
|
|
type LowHalf = $tylow;
|
|
type HighHalf = $tyhigh;
|
|
|
|
fn low(self) -> $tylow {
|
|
self as $tylow
|
|
}
|
|
fn low_as_high(low: $tylow) -> $tyhigh {
|
|
low as $tyhigh
|
|
}
|
|
fn high(self) -> $tyhigh {
|
|
(self >> $halfbits) as $tyhigh
|
|
}
|
|
fn high_as_low(high: $tyhigh) -> $tylow {
|
|
high as $tylow
|
|
}
|
|
fn from_parts(low: $tylow, high: $tyhigh) -> $ty {
|
|
low as $ty | ((high as $ty) << $halfbits)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
large_int!(u64, u32, u32, 32);
|
|
large_int!(i64, u32, i32, 32);
|
|
large_int!(u128, u64, u64, 64);
|
|
large_int!(i128, u64, i64, 64);
|
|
|
|
/// Trait to express (possibly lossy) casting of integers
|
|
pub trait CastInto<T: Copy>: Copy {
|
|
fn cast(self) -> T;
|
|
}
|
|
|
|
macro_rules! cast_into {
|
|
($ty:ty) => {
|
|
cast_into!($ty; usize, isize, u32, i32, u64, i64, u128, i128);
|
|
};
|
|
($ty:ty; $($into:ty),*) => {$(
|
|
impl CastInto<$into> for $ty {
|
|
fn cast(self) -> $into {
|
|
self as $into
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
|
|
cast_into!(u32);
|
|
cast_into!(i32);
|
|
cast_into!(u64);
|
|
cast_into!(i64);
|
|
cast_into!(u128);
|
|
cast_into!(i128);
|
|
|
|
pub trait WideInt: Int {
|
|
type Output: Int;
|
|
|
|
fn wide_mul(self, other: Self) -> (Self, Self);
|
|
fn wide_shift_left(&mut self, low: &mut Self, count: i32);
|
|
fn wide_shift_right_with_sticky(&mut self, low: &mut Self, count: i32);
|
|
}
|
|
|
|
macro_rules! impl_wide_int {
|
|
($ty:ty, $tywide:ty, $bits:expr) => {
|
|
impl WideInt for $ty {
|
|
type Output = $ty;
|
|
|
|
fn wide_mul(self, other: Self) -> (Self, Self) {
|
|
let product = (self as $tywide).wrapping_mul(other as $tywide);
|
|
((product >> ($bits as $ty)) as $ty, product as $ty)
|
|
}
|
|
|
|
fn wide_shift_left(&mut self, low: &mut Self, count: i32) {
|
|
*self = (*self << count) | (*low >> ($bits - count));
|
|
*low = *low << count;
|
|
}
|
|
|
|
fn wide_shift_right_with_sticky(&mut self, low: &mut Self, count: i32) {
|
|
if count < $bits {
|
|
let sticky = *low << ($bits - count);
|
|
*low = *self << ($bits - count) | *low >> count | sticky;
|
|
*self = *self >> count;
|
|
} else if count < 2 * $bits {
|
|
let sticky = *self << (2 * $bits - count) | *low;
|
|
*low = *self >> (count - $bits) | sticky;
|
|
*self = 0;
|
|
} else {
|
|
let sticky = *self | *low;
|
|
*self = sticky;
|
|
*self = 0;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_wide_int!(u32, u64, 32);
|
|
impl_wide_int!(u64, u128, 64);
|
|
|
|
intrinsics! {
|
|
#[maybe_use_optimized_c_shim]
|
|
#[cfg(any(
|
|
target_pointer_width = "16",
|
|
target_pointer_width = "32",
|
|
target_pointer_width = "64"
|
|
))]
|
|
pub extern "C" fn __clzsi2(x: usize) -> usize {
|
|
// TODO: const this? Would require const-if
|
|
// Note(Lokathor): the `intrinsics!` macro can't process mut inputs
|
|
let mut x = x;
|
|
let mut y: usize;
|
|
let mut n: usize = {
|
|
#[cfg(target_pointer_width = "64")]
|
|
{
|
|
64
|
|
}
|
|
#[cfg(target_pointer_width = "32")]
|
|
{
|
|
32
|
|
}
|
|
#[cfg(target_pointer_width = "16")]
|
|
{
|
|
16
|
|
}
|
|
};
|
|
#[cfg(target_pointer_width = "64")]
|
|
{
|
|
y = x >> 32;
|
|
if y != 0 {
|
|
n -= 32;
|
|
x = y;
|
|
}
|
|
}
|
|
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
|
|
{
|
|
y = x >> 16;
|
|
if y != 0 {
|
|
n -= 16;
|
|
x = y;
|
|
}
|
|
}
|
|
y = x >> 8;
|
|
if y != 0 {
|
|
n -= 8;
|
|
x = y;
|
|
}
|
|
y = x >> 4;
|
|
if y != 0 {
|
|
n -= 4;
|
|
x = y;
|
|
}
|
|
y = x >> 2;
|
|
if y != 0 {
|
|
n -= 2;
|
|
x = y;
|
|
}
|
|
y = x >> 1;
|
|
if y != 0 {
|
|
n - 2
|
|
} else {
|
|
n - x
|
|
}
|
|
}
|
|
}
|