Skip to content

Commit 03bb054

Browse files
authored
Unrolled build for rust-lang#133663
Rollup merge of rust-lang#133663 - scottmcm:carrying_mul_add, r=Amanieu Add a compiler intrinsic to back `bigint_helper_methods` cc rust-lang#85532 This adds a new `carrying_mul_add` intrinsic, to implement `wide_mul` and `carrying_mul`. It has fallback MIR for all types -- including `u128`, which isn't currently supported on nightly -- so that it'll continue to work on all backends, including CTFE. Then it's overridden in `cg_llvm` to use wider intermediate types, including `i256` for `u128::carrying_mul`.
2 parents e5f0d6f + 4669c0d commit 03bb054

File tree

12 files changed

+501
-135
lines changed

12 files changed

+501
-135
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

+31
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,37 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
340340
self.const_i32(cache_type),
341341
])
342342
}
343+
sym::carrying_mul_add => {
344+
let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
345+
346+
let wide_llty = self.type_ix(size.bits() * 2);
347+
let args = args.as_array().unwrap();
348+
let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
349+
350+
let wide = if signed {
351+
let prod = self.unchecked_smul(a, b);
352+
let acc = self.unchecked_sadd(prod, c);
353+
self.unchecked_sadd(acc, d)
354+
} else {
355+
let prod = self.unchecked_umul(a, b);
356+
let acc = self.unchecked_uadd(prod, c);
357+
self.unchecked_uadd(acc, d)
358+
};
359+
360+
let narrow_llty = self.type_ix(size.bits());
361+
let low = self.trunc(wide, narrow_llty);
362+
let bits_const = self.const_uint(wide_llty, size.bits());
363+
// No need for ashr when signed; LLVM changes it to lshr anyway.
364+
let high = self.lshr(wide, bits_const);
365+
// FIXME: could be `trunc nuw`, even for signed.
366+
let high = self.trunc(high, narrow_llty);
367+
368+
let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
369+
let pair = self.const_poison(pair_llty);
370+
let pair = self.insert_value(pair, low, 0);
371+
let pair = self.insert_value(pair, high, 1);
372+
pair
373+
}
343374
sym::ctlz
344375
| sym::ctlz_nonzero
345376
| sym::cttz

compiler/rustc_codegen_llvm/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![feature(iter_intersperse)]
1818
#![feature(let_chains)]
1919
#![feature(rustdoc_internals)]
20+
#![feature(slice_as_array)]
2021
#![feature(try_blocks)]
2122
#![warn(unreachable_pub)]
2223
// tidy-alphabetical-end

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
9494
| sym::add_with_overflow
9595
| sym::sub_with_overflow
9696
| sym::mul_with_overflow
97+
| sym::carrying_mul_add
9798
| sym::wrapping_add
9899
| sym::wrapping_sub
99100
| sym::wrapping_mul
@@ -436,6 +437,10 @@ pub fn check_intrinsic_type(
436437
(1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
437438
}
438439

440+
sym::carrying_mul_add => {
441+
(2, 0, vec![param(0); 4], Ty::new_tup(tcx, &[param(1), param(0)]))
442+
}
443+
439444
sym::ptr_guaranteed_cmp => (
440445
1,
441446
0,

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ symbols! {
555555
call_ref_future,
556556
caller_location,
557557
capture_disjoint_fields,
558+
carrying_mul_add,
558559
catch_unwind,
559560
cause,
560561
cdylib,
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
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+
}

library/core/src/intrinsics/mod.rs

+29
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ use crate::marker::{DiscriminantKind, Tuple};
6868
use crate::mem::SizedTypeProperties;
6969
use crate::{ptr, ub_checks};
7070

71+
pub mod fallback;
7172
pub mod mir;
7273
pub mod simd;
7374

@@ -3305,6 +3306,34 @@ pub const fn mul_with_overflow<T: Copy>(_x: T, _y: T) -> (T, bool) {
33053306
unimplemented!()
33063307
}
33073308

3309+
/// Performs full-width multiplication and addition with a carry:
3310+
/// `multiplier * multiplicand + addend + carry`.
3311+
///
3312+
/// This is possible without any overflow. For `uN`:
3313+
/// MAX * MAX + MAX + MAX
3314+
/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1)
3315+
/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2)
3316+
/// => 2²ⁿ - 1
3317+
///
3318+
/// For `iN`, the upper bound is MIN * MIN + MAX + MAX => 2²ⁿ⁻² + 2ⁿ - 2,
3319+
/// and the lower bound is MAX * MIN + MIN + MIN => -2²ⁿ⁻² - 2ⁿ + 2ⁿ⁺¹.
3320+
///
3321+
/// This currently supports unsigned integers *only*, no signed ones.
3322+
/// The stabilized versions of this intrinsic are available on integers.
3323+
#[unstable(feature = "core_intrinsics", issue = "none")]
3324+
#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")]
3325+
#[rustc_nounwind]
3326+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
3327+
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
3328+
pub const fn carrying_mul_add<T: ~const fallback::CarryingMulAdd<Unsigned = U>, U>(
3329+
multiplier: T,
3330+
multiplicand: T,
3331+
addend: T,
3332+
carry: T,
3333+
) -> (U, T) {
3334+
multiplier.carrying_mul_add(multiplicand, addend, carry)
3335+
}
3336+
33083337
/// Performs an exact division, resulting in undefined behavior where
33093338
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
33103339
///

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
#![cfg_attr(bootstrap, feature(do_not_recommend))]
111111
#![feature(array_ptr_get)]
112112
#![feature(asm_experimental_arch)]
113+
#![feature(const_carrying_mul_add)]
113114
#![feature(const_eval_select)]
114115
#![feature(const_typed_swap)]
115116
#![feature(core_intrinsics)]

0 commit comments

Comments
 (0)