Skip to content

Commit 4013392

Browse files
committed
Move {widening, carrying}_mul to an intrinsic with fallback MIR
Including implementing it for `u128`, so it can be defined in `uint_impl!`. This way it works for all backends, including CTFE.
1 parent 76f3ff6 commit 4013392

File tree

9 files changed

+297
-135
lines changed

9 files changed

+297
-135
lines changed

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+
(1, 0, vec![param(0); 4], Ty::new_tup(tcx, &[param(0), 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
@@ -530,6 +530,7 @@ symbols! {
530530
call_ref_future,
531531
caller_location,
532532
capture_disjoint_fields,
533+
carrying_mul_add,
533534
catch_unwind,
534535
cause,
535536
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+
use crate::panicking::panic_nounwind;
11+
12+
/// Ideally we'd do fallbacks using ordinary trait impls, but that doesn't work
13+
/// for const (yet™) so we're stuck with hacky workarounds.
14+
#[inline]
15+
const fn try_as<T: 'static, F: Copy + 'static>(val: F) -> Option<T> {
16+
if const { super::type_id::<T>() == super::type_id::<F>() } {
17+
// SAFETY: just checked it's the same type
18+
Some(unsafe { super::transmute_unchecked(val) })
19+
} else {
20+
None
21+
}
22+
}
23+
24+
macro_rules! if_the_types_work {
25+
($f:ident ( $a:expr )) => {
26+
if let Some(arg) = try_as($a) {
27+
if let Some(ret) = try_as($f(arg)) {
28+
return ret;
29+
}
30+
}
31+
};
32+
}
33+
34+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
35+
const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) {
36+
const fn to_low_high(x: u128) -> [u64; 2] {
37+
[x as u64, (x >> 64) as u64]
38+
}
39+
const fn from_low_high(x: [u64; 2]) -> u128 {
40+
(x[0] as u128) | ((x[1] as u128) << 64)
41+
}
42+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
43+
const fn scalar_mul(low_high: [u64; 2], k: u64) -> [u64; 3] {
44+
let (x, c) = u64::widening_mul(k, low_high[0]);
45+
let (y, z) = u64::carrying_mul(k, low_high[1], c);
46+
[x, y, z]
47+
}
48+
let a = to_low_high(a);
49+
let b = to_low_high(b);
50+
let low = scalar_mul(a, b[0]);
51+
let high = scalar_mul(a, b[1]);
52+
let r0 = low[0];
53+
let (r1, c) = u64::overflowing_add(low[1], high[0]);
54+
let (r2, c) = u64::carrying_add(low[2], high[1], c);
55+
let r3 = high[2] + (c as u64);
56+
(from_low_high([r0, r1]), from_low_high([r2, r3]))
57+
}
58+
59+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
60+
#[inline]
61+
pub const fn carrying_mul_add<T: Copy + 'static>(a: T, b: T, c: T, d: T) -> (T, T) {
62+
let args = (a, b, c, d);
63+
macro_rules! via_wider_type {
64+
($narrow:ty => $wide:ty) => {{
65+
#[inline]
66+
const fn doit(
67+
(a, b, c, d): ($narrow, $narrow, $narrow, $narrow),
68+
) -> ($narrow, $narrow) {
69+
let (a, b, c, d) = (a as $wide, b as $wide, c as $wide, d as $wide);
70+
let full = a * b + c + d;
71+
(full as $narrow, (full >> <$narrow>::BITS) as $narrow)
72+
}
73+
if_the_types_work!(doit(args));
74+
}};
75+
}
76+
via_wider_type!(u8 => u16);
77+
via_wider_type!(u16 => u32);
78+
via_wider_type!(u32 => u64);
79+
via_wider_type!(u64 => u128);
80+
81+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
82+
#[inline]
83+
const fn for_usize((a, b, c, d): (usize, usize, usize, usize)) -> (usize, usize) {
84+
#[cfg(target_pointer_width = "16")]
85+
type T = u16;
86+
#[cfg(target_pointer_width = "32")]
87+
type T = u32;
88+
#[cfg(target_pointer_width = "64")]
89+
type T = u64;
90+
91+
let (x, y) = carrying_mul_add(a as T, b as T, c as T, d as T);
92+
(x as usize, y as usize)
93+
}
94+
if_the_types_work!(for_usize(args));
95+
96+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
97+
#[inline]
98+
const fn carrying_mul_add_u128((a, b, c1, c2): (u128, u128, u128, u128)) -> (u128, u128) {
99+
let (mut r1, mut r2) = wide_mul_u128(a, b);
100+
let c;
101+
(r1, c) = u128::overflowing_add(r1, c1);
102+
r2 += c as u128;
103+
let c;
104+
(r1, c) = u128::overflowing_add(r1, c2);
105+
r2 += c as u128;
106+
(r1, r2)
107+
}
108+
if_the_types_work!(carrying_mul_add_u128(args));
109+
110+
panic_nounwind("Not supported for this generic type")
111+
}

library/core/src/intrinsics/mod.rs

+26
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

@@ -2939,6 +2940,31 @@ pub const fn mul_with_overflow<T: Copy>(_x: T, _y: T) -> (T, bool) {
29392940
unimplemented!()
29402941
}
29412942

2943+
/// Performs full-width multiplication and addition with a carry:
2944+
/// `multiplier * multiplicand + addend + carry`.
2945+
///
2946+
/// This is possible without any overflow:
2947+
/// MAX * MAX + MAX + MAX
2948+
/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1)
2949+
/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2)
2950+
/// => 2²ⁿ - 1
2951+
///
2952+
/// This currently supports unsigned integers *only*, no signed ones.
2953+
/// The stabilized versions of this intrinsic are available on integers.
2954+
#[unstable(feature = "core_intrinsics", issue = "none")]
2955+
#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")]
2956+
#[rustc_nounwind]
2957+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
2958+
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
2959+
pub const fn carrying_mul_add<T: Copy + 'static>(
2960+
multiplier: T,
2961+
multiplicand: T,
2962+
addend: T,
2963+
carry: T,
2964+
) -> (T, T) {
2965+
fallback::carrying_mul_add(multiplier, multiplicand, addend, carry)
2966+
}
2967+
29422968
/// Performs an exact division, resulting in undefined behavior where
29432969
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
29442970
///

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
#![feature(const_align_of_val_raw)]
114114
#![feature(const_alloc_layout)]
115115
#![feature(const_black_box)]
116+
#![feature(const_carrying_mul_add)]
116117
#![feature(const_eq_ignore_ascii_case)]
117118
#![feature(const_eval_select)]
118119
#![feature(const_heap)]

library/core/src/num/mod.rs

-135
Original file line numberDiff line numberDiff line change
@@ -203,134 +203,6 @@ macro_rules! midpoint_impl {
203203
};
204204
}
205205

206-
macro_rules! widening_impl {
207-
($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => {
208-
/// Calculates the complete product `self * rhs` without the possibility to overflow.
209-
///
210-
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
211-
/// of the result as two separate values, in that order.
212-
///
213-
/// If you also need to add a carry to the wide result, then you want
214-
/// [`Self::carrying_mul`] instead.
215-
///
216-
/// # Examples
217-
///
218-
/// Basic usage:
219-
///
220-
/// Please note that this example is shared between integer types.
221-
/// Which explains why `u32` is used here.
222-
///
223-
/// ```
224-
/// #![feature(bigint_helper_methods)]
225-
/// assert_eq!(5u32.widening_mul(2), (10, 0));
226-
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
227-
/// ```
228-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
229-
#[must_use = "this returns the result of the operation, \
230-
without modifying the original"]
231-
#[inline]
232-
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
233-
// note: longer-term this should be done via an intrinsic,
234-
// but for now we can deal without an impl for u128/i128
235-
// SAFETY: overflow will be contained within the wider types
236-
let wide = unsafe { (self as $WideT).unchecked_mul(rhs as $WideT) };
237-
(wide as $SelfT, (wide >> $BITS) as $SelfT)
238-
}
239-
240-
/// Calculates the "full multiplication" `self * rhs + carry`
241-
/// without the possibility to overflow.
242-
///
243-
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
244-
/// of the result as two separate values, in that order.
245-
///
246-
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
247-
/// additional amount of overflow. This allows for chaining together multiple
248-
/// multiplications to create "big integers" which represent larger values.
249-
///
250-
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
251-
///
252-
/// # Examples
253-
///
254-
/// Basic usage:
255-
///
256-
/// Please note that this example is shared between integer types.
257-
/// Which explains why `u32` is used here.
258-
///
259-
/// ```
260-
/// #![feature(bigint_helper_methods)]
261-
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
262-
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
263-
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
264-
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
265-
#[doc = concat!("assert_eq!(",
266-
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
267-
"(0, ", stringify!($SelfT), "::MAX));"
268-
)]
269-
/// ```
270-
///
271-
/// This is the core operation needed for scalar multiplication when
272-
/// implementing it for wider-than-native types.
273-
///
274-
/// ```
275-
/// #![feature(bigint_helper_methods)]
276-
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
277-
/// let mut carry = 0;
278-
/// for d in little_endian_digits.iter_mut() {
279-
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
280-
/// }
281-
/// if carry != 0 {
282-
/// little_endian_digits.push(carry);
283-
/// }
284-
/// }
285-
///
286-
/// let mut v = vec![10, 20];
287-
/// scalar_mul_eq(&mut v, 3);
288-
/// assert_eq!(v, [30, 60]);
289-
///
290-
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
291-
/// let mut v = vec![0x4321, 0x8765];
292-
/// scalar_mul_eq(&mut v, 0xFEED);
293-
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
294-
/// ```
295-
///
296-
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
297-
/// except that it gives the value of the overflow instead of just whether one happened:
298-
///
299-
/// ```
300-
/// #![feature(bigint_helper_methods)]
301-
/// let r = u8::carrying_mul(7, 13, 0);
302-
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
303-
/// let r = u8::carrying_mul(13, 42, 0);
304-
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
305-
/// ```
306-
///
307-
/// The value of the first field in the returned tuple matches what you'd get
308-
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
309-
/// [`wrapping_add`](Self::wrapping_add) methods:
310-
///
311-
/// ```
312-
/// #![feature(bigint_helper_methods)]
313-
/// assert_eq!(
314-
/// 789_u16.carrying_mul(456, 123).0,
315-
/// 789_u16.wrapping_mul(456).wrapping_add(123),
316-
/// );
317-
/// ```
318-
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
319-
#[must_use = "this returns the result of the operation, \
320-
without modifying the original"]
321-
#[inline]
322-
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
323-
// note: longer-term this should be done via an intrinsic,
324-
// but for now we can deal without an impl for u128/i128
325-
// SAFETY: overflow will be contained within the wider types
326-
let wide = unsafe {
327-
(self as $WideT).unchecked_mul(rhs as $WideT).unchecked_add(carry as $WideT)
328-
};
329-
(wide as $SelfT, (wide >> $BITS) as $SelfT)
330-
}
331-
};
332-
}
333-
334206
impl i8 {
335207
int_impl! {
336208
Self = i8,
@@ -551,7 +423,6 @@ impl u8 {
551423
from_xe_bytes_doc = "",
552424
bound_condition = "",
553425
}
554-
widening_impl! { u8, u16, 8, unsigned }
555426
midpoint_impl! { u8, u16, unsigned }
556427

557428
/// Checks if the value is within the ASCII range.
@@ -1167,7 +1038,6 @@ impl u16 {
11671038
from_xe_bytes_doc = "",
11681039
bound_condition = "",
11691040
}
1170-
widening_impl! { u16, u32, 16, unsigned }
11711041
midpoint_impl! { u16, u32, unsigned }
11721042

11731043
/// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`].
@@ -1215,7 +1085,6 @@ impl u32 {
12151085
from_xe_bytes_doc = "",
12161086
bound_condition = "",
12171087
}
1218-
widening_impl! { u32, u64, 32, unsigned }
12191088
midpoint_impl! { u32, u64, unsigned }
12201089
}
12211090

@@ -1239,7 +1108,6 @@ impl u64 {
12391108
from_xe_bytes_doc = "",
12401109
bound_condition = "",
12411110
}
1242-
widening_impl! { u64, u128, 64, unsigned }
12431111
midpoint_impl! { u64, u128, unsigned }
12441112
}
12451113

@@ -1289,7 +1157,6 @@ impl usize {
12891157
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
12901158
bound_condition = " on 16-bit targets",
12911159
}
1292-
widening_impl! { usize, u32, 16, unsigned }
12931160
midpoint_impl! { usize, u32, unsigned }
12941161
}
12951162

@@ -1314,7 +1181,6 @@ impl usize {
13141181
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
13151182
bound_condition = " on 32-bit targets",
13161183
}
1317-
widening_impl! { usize, u64, 32, unsigned }
13181184
midpoint_impl! { usize, u64, unsigned }
13191185
}
13201186

@@ -1339,7 +1205,6 @@ impl usize {
13391205
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
13401206
bound_condition = " on 64-bit targets",
13411207
}
1342-
widening_impl! { usize, u128, 64, unsigned }
13431208
midpoint_impl! { usize, u128, unsigned }
13441209
}
13451210

0 commit comments

Comments
 (0)