Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7dbd9ee

Browse files
committedDec 2, 2024
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 5e1440a commit 7dbd9ee

File tree

9 files changed

+297
-135
lines changed

9 files changed

+297
-135
lines changed
 

‎compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 5 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ symbols! {
532532
call_ref_future,
533533
caller_location,
534534
capture_disjoint_fields,
535+
carrying_mul_add,
535536
catch_unwind,
536537
cause,
537538
cdylib,
Lines changed: 111 additions & 0 deletions
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

Lines changed: 26 additions & 0 deletions
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

@@ -3293,6 +3294,31 @@ pub const fn mul_with_overflow<T: Copy>(_x: T, _y: T) -> (T, bool) {
32933294
unimplemented!()
32943295
}
32953296

3297+
/// Performs full-width multiplication and addition with a carry:
3298+
/// `multiplier * multiplicand + addend + carry`.
3299+
///
3300+
/// This is possible without any overflow:
3301+
/// MAX * MAX + MAX + MAX
3302+
/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1)
3303+
/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2)
3304+
/// => 2²ⁿ - 1
3305+
///
3306+
/// This currently supports unsigned integers *only*, no signed ones.
3307+
/// The stabilized versions of this intrinsic are available on integers.
3308+
#[unstable(feature = "core_intrinsics", issue = "none")]
3309+
#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")]
3310+
#[rustc_nounwind]
3311+
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
3312+
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
3313+
pub const fn carrying_mul_add<T: Copy + 'static>(
3314+
multiplier: T,
3315+
multiplicand: T,
3316+
addend: T,
3317+
carry: T,
3318+
) -> (T, T) {
3319+
fallback::carrying_mul_add(multiplier, multiplicand, addend, carry)
3320+
}
3321+
32963322
/// Performs an exact division, resulting in undefined behavior where
32973323
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
32983324
///

‎library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 0 additions & 135 deletions
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

‎library/core/src/num/uint_macros.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,6 +3348,122 @@ macro_rules! uint_impl {
33483348
unsafe { mem::transmute(bytes) }
33493349
}
33503350

3351+
/// Calculates the complete product `self * rhs` without the possibility to overflow.
3352+
///
3353+
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
3354+
/// of the result as two separate values, in that order.
3355+
///
3356+
/// If you also need to add a carry to the wide result, then you want
3357+
/// [`Self::carrying_mul`] instead.
3358+
///
3359+
/// # Examples
3360+
///
3361+
/// Basic usage:
3362+
///
3363+
/// Please note that this example is shared between integer types.
3364+
/// Which explains why `u32` is used here.
3365+
///
3366+
/// ```
3367+
/// #![feature(bigint_helper_methods)]
3368+
/// assert_eq!(5u32.widening_mul(2), (10, 0));
3369+
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
3370+
/// ```
3371+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
3372+
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
3373+
#[must_use = "this returns the result of the operation, \
3374+
without modifying the original"]
3375+
#[inline]
3376+
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
3377+
Self::carrying_mul(self, rhs, 0)
3378+
}
3379+
3380+
/// Calculates the "full multiplication" `self * rhs + carry`
3381+
/// without the possibility to overflow.
3382+
///
3383+
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
3384+
/// of the result as two separate values, in that order.
3385+
///
3386+
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
3387+
/// additional amount of overflow. This allows for chaining together multiple
3388+
/// multiplications to create "big integers" which represent larger values.
3389+
///
3390+
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
3391+
///
3392+
/// # Examples
3393+
///
3394+
/// Basic usage:
3395+
///
3396+
/// Please note that this example is shared between integer types.
3397+
/// Which explains why `u32` is used here.
3398+
///
3399+
/// ```
3400+
/// #![feature(bigint_helper_methods)]
3401+
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
3402+
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
3403+
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
3404+
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
3405+
#[doc = concat!("assert_eq!(",
3406+
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
3407+
"(0, ", stringify!($SelfT), "::MAX));"
3408+
)]
3409+
/// ```
3410+
///
3411+
/// This is the core operation needed for scalar multiplication when
3412+
/// implementing it for wider-than-native types.
3413+
///
3414+
/// ```
3415+
/// #![feature(bigint_helper_methods)]
3416+
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
3417+
/// let mut carry = 0;
3418+
/// for d in little_endian_digits.iter_mut() {
3419+
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
3420+
/// }
3421+
/// if carry != 0 {
3422+
/// little_endian_digits.push(carry);
3423+
/// }
3424+
/// }
3425+
///
3426+
/// let mut v = vec![10, 20];
3427+
/// scalar_mul_eq(&mut v, 3);
3428+
/// assert_eq!(v, [30, 60]);
3429+
///
3430+
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
3431+
/// let mut v = vec![0x4321, 0x8765];
3432+
/// scalar_mul_eq(&mut v, 0xFEED);
3433+
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
3434+
/// ```
3435+
///
3436+
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
3437+
/// except that it gives the value of the overflow instead of just whether one happened:
3438+
///
3439+
/// ```
3440+
/// #![feature(bigint_helper_methods)]
3441+
/// let r = u8::carrying_mul(7, 13, 0);
3442+
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
3443+
/// let r = u8::carrying_mul(13, 42, 0);
3444+
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
3445+
/// ```
3446+
///
3447+
/// The value of the first field in the returned tuple matches what you'd get
3448+
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
3449+
/// [`wrapping_add`](Self::wrapping_add) methods:
3450+
///
3451+
/// ```
3452+
/// #![feature(bigint_helper_methods)]
3453+
/// assert_eq!(
3454+
/// 789_u16.carrying_mul(456, 123).0,
3455+
/// 789_u16.wrapping_mul(456).wrapping_add(123),
3456+
/// );
3457+
/// ```
3458+
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
3459+
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
3460+
#[must_use = "this returns the result of the operation, \
3461+
without modifying the original"]
3462+
#[inline]
3463+
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
3464+
intrinsics::carrying_mul_add(self, rhs, 0, carry)
3465+
}
3466+
33513467
/// New code should prefer to use
33523468
#[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
33533469
///

‎library/core/tests/intrinsics.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,39 @@ fn test_three_way_compare_in_const_contexts() {
125125
assert_eq!(SIGNED_EQUAL, Equal);
126126
assert_eq!(SIGNED_GREATER, Greater);
127127
}
128+
129+
#[test]
130+
fn carrying_mul_add_fallback_u32() {
131+
use core::intrinsics::fallback::carrying_mul_add;
132+
let r = carrying_mul_add::<u32>(0x9e37_79b9, 0x7f4a_7c15, 0xf39c_c060, 0x5ced_c834);
133+
assert_eq!(r, (0x2087_20c1, 0x4eab_8e1d));
134+
let r = carrying_mul_add::<u32>(0x1082_276b, 0xf3a2_7251, 0xf86c_6a11, 0xd0c1_8e95);
135+
assert_eq!(r, (0x7aa0_1781, 0x0fb6_0528));
136+
}
137+
138+
#[test]
139+
fn carrying_mul_add_fallback_u128() {
140+
use core::intrinsics::fallback::carrying_mul_add;
141+
142+
assert_eq!(carrying_mul_add::<u128>(1, 1, 1, 1), (3, 0));
143+
assert_eq!(carrying_mul_add::<u128>(0, 0, u128::MAX, u128::MAX), (u128::MAX - 1, 1));
144+
assert_eq!(
145+
(const { carrying_mul_add::<u128>(u128::MAX, u128::MAX, u128::MAX, u128::MAX) }),
146+
(u128::MAX, u128::MAX),
147+
);
148+
149+
let r = carrying_mul_add::<u128>(
150+
0x243f6a8885a308d313198a2e03707344,
151+
0xa4093822299f31d0082efa98ec4e6c89,
152+
0x452821e638d01377be5466cf34e90c6c,
153+
0xc0ac29b7c97c50dd3f84d5b5b5470917,
154+
);
155+
assert_eq!(r, (0x8050ec20ed554e40338d277e00b674e7, 0x1739ee6cea07da409182d003859b59d8));
156+
let r = carrying_mul_add::<u128>(
157+
0x9216d5d98979fb1bd1310ba698dfb5ac,
158+
0x2ffd72dbd01adfb7b8e1afed6a267e96,
159+
0xba7c9045f12c7f9924a19947b3916cf7,
160+
0x0801f2e2858efc16636920d871574e69,
161+
);
162+
assert_eq!(r, (0x185525545fdb2fefb502a3a602efd628, 0x1b62d35fe3bff6b566f99667ef7ebfd6));
163+
}

‎library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#![feature(const_swap)]
2222
#![feature(const_trait_impl)]
2323
#![feature(core_intrinsics)]
24+
#![feature(core_intrinsics_fallbacks)]
2425
#![feature(core_io_borrowed_buf)]
2526
#![feature(core_private_bignum)]
2627
#![feature(core_private_diy_float)]

0 commit comments

Comments
 (0)
Please sign in to comment.