|
| 1 | +#![unstable( |
| 2 | + feature = "core_intrinsics_fallbacks", |
| 3 | + reason = "The fallbacks will never be stable, as they exist only to be called \ |
| 4 | + by the fallback MIR, but they're exported so they can be tested on \ |
| 5 | + platforms where the fallback MIR isn't actually used", |
| 6 | + issue = "none" |
| 7 | +)] |
| 8 | +#![allow(missing_docs)] |
| 9 | + |
| 10 | +#[const_trait] |
| 11 | +pub trait CarryingMulAdd: Copy + 'static { |
| 12 | + type Unsigned: Copy + 'static; |
| 13 | + fn carrying_mul_add( |
| 14 | + self, |
| 15 | + multiplicand: Self, |
| 16 | + addend: Self, |
| 17 | + carry: Self, |
| 18 | + ) -> (Self::Unsigned, Self); |
| 19 | +} |
| 20 | + |
| 21 | +macro_rules! impl_carrying_mul_add_by_widening { |
| 22 | + ($($t:ident $u:ident $w:ident,)+) => {$( |
| 23 | + #[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] |
| 24 | + impl const CarryingMulAdd for $t { |
| 25 | + type Unsigned = $u; |
| 26 | + #[inline] |
| 27 | + fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) { |
| 28 | + let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w); |
| 29 | + (wide as _, (wide >> Self::BITS) as _) |
| 30 | + } |
| 31 | + } |
| 32 | + )+}; |
| 33 | +} |
| 34 | +impl_carrying_mul_add_by_widening! { |
| 35 | + u8 u8 u16, |
| 36 | + u16 u16 u32, |
| 37 | + u32 u32 u64, |
| 38 | + u64 u64 u128, |
| 39 | + usize usize UDoubleSize, |
| 40 | + i8 u8 i16, |
| 41 | + i16 u16 i32, |
| 42 | + i32 u32 i64, |
| 43 | + i64 u64 i128, |
| 44 | + isize usize UDoubleSize, |
| 45 | +} |
| 46 | + |
| 47 | +#[cfg(target_pointer_width = "16")] |
| 48 | +type UDoubleSize = u32; |
| 49 | +#[cfg(target_pointer_width = "32")] |
| 50 | +type UDoubleSize = u64; |
| 51 | +#[cfg(target_pointer_width = "64")] |
| 52 | +type UDoubleSize = u128; |
| 53 | + |
| 54 | +#[inline] |
| 55 | +const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) { |
| 56 | + #[inline] |
| 57 | + const fn to_low_high(x: u128) -> [u128; 2] { |
| 58 | + const MASK: u128 = u64::MAX as _; |
| 59 | + [x & MASK, x >> 64] |
| 60 | + } |
| 61 | + #[inline] |
| 62 | + const fn from_low_high(x: [u128; 2]) -> u128 { |
| 63 | + x[0] | (x[1] << 64) |
| 64 | + } |
| 65 | + #[inline] |
| 66 | + const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] { |
| 67 | + let [x, c] = to_low_high(k * low_high[0]); |
| 68 | + let [y, z] = to_low_high(k * low_high[1] + c); |
| 69 | + [x, y, z] |
| 70 | + } |
| 71 | + let a = to_low_high(a); |
| 72 | + let b = to_low_high(b); |
| 73 | + let low = scalar_mul(a, b[0]); |
| 74 | + let high = scalar_mul(a, b[1]); |
| 75 | + let r0 = low[0]; |
| 76 | + let [r1, c] = to_low_high(low[1] + high[0]); |
| 77 | + let [r2, c] = to_low_high(low[2] + high[1] + c); |
| 78 | + let r3 = high[2] + c; |
| 79 | + (from_low_high([r0, r1]), from_low_high([r2, r3])) |
| 80 | +} |
| 81 | + |
| 82 | +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] |
| 83 | +impl const CarryingMulAdd for u128 { |
| 84 | + type Unsigned = u128; |
| 85 | + #[inline] |
| 86 | + fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) { |
| 87 | + let (low, mut high) = wide_mul_u128(self, b); |
| 88 | + let (low, carry) = u128::overflowing_add(low, c); |
| 89 | + high += carry as u128; |
| 90 | + let (low, carry) = u128::overflowing_add(low, d); |
| 91 | + high += carry as u128; |
| 92 | + (low, high) |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")] |
| 97 | +impl const CarryingMulAdd for i128 { |
| 98 | + type Unsigned = u128; |
| 99 | + #[inline] |
| 100 | + fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) { |
| 101 | + let (low, high) = wide_mul_u128(self as u128, b as u128); |
| 102 | + let mut high = high as i128; |
| 103 | + high = high.wrapping_add(i128::wrapping_mul(self >> 127, b)); |
| 104 | + high = high.wrapping_add(i128::wrapping_mul(self, b >> 127)); |
| 105 | + let (low, carry) = u128::overflowing_add(low, c as u128); |
| 106 | + high = high.wrapping_add((carry as i128) + (c >> 127)); |
| 107 | + let (low, carry) = u128::overflowing_add(low, d as u128); |
| 108 | + high = high.wrapping_add((carry as i128) + (d >> 127)); |
| 109 | + (low, high) |
| 110 | + } |
| 111 | +} |
0 commit comments