blob: 932537f2581f89db9b09c918b36a403870d243e5 [file] [log] [blame] [edit]
#![unstable(
feature = "core_intrinsics_fallbacks",
reason = "The fallbacks will never be stable, as they exist only to be called \
by the fallback MIR, but they're exported so they can be tested on \
platforms where the fallback MIR isn't actually used",
issue = "none"
)]
#![allow(missing_docs)]
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub const trait CarryingMulAdd: Copy + 'static {
type Unsigned: Copy + 'static;
fn carrying_mul_add(
self,
multiplicand: Self,
addend: Self,
carry: Self,
) -> (Self::Unsigned, Self);
}
macro_rules! impl_carrying_mul_add_by_widening {
($($t:ident $u:ident $w:ident,)+) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for $t {
type Unsigned = $u;
#[inline]
fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) {
let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w);
(wide as _, (wide >> Self::BITS) as _)
}
}
)+};
}
impl_carrying_mul_add_by_widening! {
u8 u8 u16,
u16 u16 u32,
u32 u32 u64,
u64 u64 u128,
usize usize UDoubleSize,
i8 u8 i16,
i16 u16 i32,
i32 u32 i64,
i64 u64 i128,
isize usize UDoubleSize,
}
#[cfg(target_pointer_width = "16")]
type UDoubleSize = u32;
#[cfg(target_pointer_width = "32")]
type UDoubleSize = u64;
#[cfg(target_pointer_width = "64")]
type UDoubleSize = u128;
#[inline]
const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) {
#[inline]
const fn to_low_high(x: u128) -> [u128; 2] {
const MASK: u128 = u64::MAX as _;
[x & MASK, x >> 64]
}
#[inline]
const fn from_low_high(x: [u128; 2]) -> u128 {
x[0] | (x[1] << 64)
}
#[inline]
const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] {
let [x, c] = to_low_high(k * low_high[0]);
let [y, z] = to_low_high(k * low_high[1] + c);
[x, y, z]
}
let a = to_low_high(a);
let b = to_low_high(b);
let low = scalar_mul(a, b[0]);
let high = scalar_mul(a, b[1]);
let r0 = low[0];
let [r1, c] = to_low_high(low[1] + high[0]);
let [r2, c] = to_low_high(low[2] + high[1] + c);
let r3 = high[2] + c;
(from_low_high([r0, r1]), from_low_high([r2, r3]))
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for u128 {
type Unsigned = u128;
#[inline]
fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) {
let (low, mut high) = wide_mul_u128(self, b);
let (low, carry) = u128::overflowing_add(low, c);
high += carry as u128;
let (low, carry) = u128::overflowing_add(low, d);
high += carry as u128;
(low, high)
}
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const CarryingMulAdd for i128 {
type Unsigned = u128;
#[inline]
fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) {
let (low, high) = wide_mul_u128(self as u128, b as u128);
let mut high = high as i128;
high = high.wrapping_add(i128::wrapping_mul(self >> 127, b));
high = high.wrapping_add(i128::wrapping_mul(self, b >> 127));
let (low, carry) = u128::overflowing_add(low, c as u128);
high = high.wrapping_add((carry as i128) + (c >> 127));
let (low, carry) = u128::overflowing_add(low, d as u128);
high = high.wrapping_add((carry as i128) + (d >> 127));
(low, high)
}
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub const trait DisjointBitOr: Copy + 'static {
/// See [`super::disjoint_bitor`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn disjoint_bitor(self, other: Self) -> Self;
}
macro_rules! zero {
(bool) => {
false
};
($t:ident) => {
0
};
}
macro_rules! impl_disjoint_bitor {
($($t:ident,)+) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const DisjointBitOr for $t {
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn disjoint_bitor(self, other: Self) -> Self {
// Note that the assume here is required for UB detection in Miri!
// SAFETY: our precondition is that there are no bits in common,
// so this is just telling that to the backend.
unsafe { super::assume((self & other) == zero!($t)) };
self | other
}
}
)+};
}
impl_disjoint_bitor! {
bool,
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
}
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
pub const trait FunnelShift: Copy + 'static {
/// See [`super::unchecked_funnel_shl`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self;
/// See [`super::unchecked_funnel_shr`]; we just need the trait indirection to handle
/// different types since calling intrinsics with generics doesn't work.
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self;
}
macro_rules! impl_funnel_shifts {
($($type:ident),*) => {$(
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
impl const FunnelShift for $type {
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn unchecked_funnel_shl(self, rhs: Self, shift: u32) -> Self {
// This implementation is also used by Miri so we have to check the precondition.
// SAFETY: this is guaranteed by the caller
unsafe { super::assume(shift < $type::BITS) };
if shift == 0 {
self
} else {
// SAFETY:
// - `shift < T::BITS`, which satisfies `unchecked_shl`
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
// above), which satisfies `unchecked_shr`
// - because the types are unsigned, the combination are disjoint bits (this is
// not true if they're signed, since SHR will fill in the empty space with a
// sign bit, not zero)
unsafe {
super::disjoint_bitor(
super::unchecked_shl(self, shift),
super::unchecked_shr(rhs, $type::BITS - shift),
)
}
}
}
#[cfg_attr(miri, track_caller)]
#[inline]
unsafe fn unchecked_funnel_shr(self, rhs: Self, shift: u32) -> Self {
// This implementation is also used by Miri so we have to check the precondition.
// SAFETY: this is guaranteed by the caller
unsafe { super::assume(shift < $type::BITS) };
if shift == 0 {
rhs
} else {
// SAFETY:
// - `shift < T::BITS`, which satisfies `unchecked_shr`
// - this also ensures that `T::BITS - shift < T::BITS` (shift = 0 is checked
// above), which satisfies `unchecked_shl`
// - because the types are unsigned, the combination are disjoint bits (this is
// not true if they're signed, since SHR will fill in the empty space with a
// sign bit, not zero)
unsafe {
super::disjoint_bitor(
super::unchecked_shl(self, $type::BITS - shift),
super::unchecked_shr(rhs, shift),
)
}
}
}
}
)*};
}
impl_funnel_shifts! {
u8, u16, u32, u64, u128, usize
}