Skip to content

Commit c8d0720

Browse files
committed
Add intrinsics for bigint helper methods
1 parent 68e4d96 commit c8d0720

File tree

14 files changed

+625
-164
lines changed

14 files changed

+625
-164
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

+76-1
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
347347
| sym::rotate_left
348348
| sym::rotate_right
349349
| sym::saturating_add
350-
| sym::saturating_sub => {
350+
| sym::saturating_sub
351+
| sym::add_with_carry
352+
| sym::sub_with_carry
353+
| sym::mul_double
354+
| sym::mul_double_add
355+
| sym::mul_double_add2 => {
351356
let ty = arg_tys[0];
352357
match int_type_width_signed(ty, self) {
353358
Some((width, signed)) => match name {
@@ -417,6 +422,76 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
417422
);
418423
self.call_intrinsic(llvm_name, &[lhs, rhs])
419424
}
425+
sym::add_with_carry | sym::sub_with_carry => {
426+
let llty = self.type_ix(width);
427+
let is_add = name == sym::add_with_carry;
428+
let lhs = args[0].immediate();
429+
let rhs = args[1].immediate();
430+
431+
// sign-extending the carry would treat it as -1, not 1
432+
let carry = self.intcast(args[2].immediate(), llty, false);
433+
434+
let llvm_name = &format!(
435+
"llvm.{}{}.with.overflow.i{}",
436+
if signed { 's' } else { 'u' },
437+
if is_add { "add" } else { "sub" },
438+
width,
439+
);
440+
441+
let ret = self.call_intrinsic(llvm_name, &[lhs, rhs]);
442+
let agg = self.extract_value(ret, 0);
443+
let overflow1 = self.extract_value(ret, 1);
444+
445+
let ret = self.call_intrinsic(llvm_name, &[agg, carry]);
446+
let agg = self.extract_value(ret, 0);
447+
let overflow2 = self.extract_value(ret, 1);
448+
449+
let overflow = if signed {
450+
self.icmp(IntPredicate::IntNE, overflow1, overflow2)
451+
} else {
452+
self.or(overflow1, overflow2)
453+
};
454+
455+
let holder = self.const_struct(
456+
&[self.const_undef(llty), self.const_undef(self.type_i1())],
457+
false,
458+
);
459+
let holder = self.insert_value(holder, agg, 0);
460+
let holder = self.insert_value(holder, overflow, 1);
461+
holder
462+
}
463+
sym::mul_double | sym::mul_double_add | sym::mul_double_add2 => {
464+
let single_ty = self.type_ix(width);
465+
let double_ty = self.type_ix(width * 2);
466+
let lhs = self.intcast(args[0].immediate(), double_ty, signed);
467+
let rhs = self.intcast(args[1].immediate(), double_ty, signed);
468+
let mut ret = self.mul(lhs, rhs);
469+
if name == sym::mul_double_add || name == sym::mul_double_add2 {
470+
let carry = self.intcast(args[2].immediate(), double_ty, signed);
471+
ret = self.add(ret, carry)
472+
}
473+
if name == sym::mul_double_add2 {
474+
let carry2 = self.intcast(args[3].immediate(), double_ty, signed);
475+
ret = self.add(ret, carry2);
476+
}
477+
478+
// note: insignificant part is always treated as unsigned, even if we
479+
// coerce it to signed in the final result to make the intrinsic
480+
// signature simpler
481+
let lo = self.intcast(ret, single_ty, signed);
482+
483+
let bits = self.const_uint(double_ty, width);
484+
let hi = self.ashr(ret, bits);
485+
let hi = self.intcast(hi, single_ty, signed);
486+
487+
let holder = self.const_struct(
488+
&[self.const_undef(single_ty), self.const_undef(single_ty)],
489+
false,
490+
);
491+
let holder = self.insert_value(holder, lo, 0);
492+
let holder = self.insert_value(holder, hi, 1);
493+
holder
494+
}
420495
_ => bug!(),
421496
},
422497
None => {

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+27
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
103103
| sym::add_with_overflow
104104
| sym::sub_with_overflow
105105
| sym::mul_with_overflow
106+
| sym::add_with_carry
107+
| sym::sub_with_carry
108+
| sym::mul_double
109+
| sym::mul_double_add
110+
| sym::mul_double_add2
106111
| sym::wrapping_add
107112
| sym::wrapping_sub
108113
| sym::wrapping_mul
@@ -433,6 +438,28 @@ pub fn check_intrinsic_type(
433438
(1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
434439
}
435440

441+
sym::add_with_carry | sym::sub_with_carry => (
442+
1,
443+
0,
444+
vec![param(0), param(0), tcx.types.bool],
445+
Ty::new_tup(tcx, &[param(0), tcx.types.bool]),
446+
),
447+
448+
sym::mul_double => {
449+
(1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), param(0)]))
450+
}
451+
452+
sym::mul_double_add => {
453+
(1, 0, vec![param(0), param(0), param(0)], Ty::new_tup(tcx, &[param(0), param(0)]))
454+
}
455+
456+
sym::mul_double_add2 => (
457+
1,
458+
0,
459+
vec![param(0), param(0), param(0), param(0)],
460+
Ty::new_tup(tcx, &[param(0), param(0)]),
461+
),
462+
436463
sym::ptr_guaranteed_cmp => (
437464
1,
438465
0,

compiler/rustc_span/src/symbol.rs

+5
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ symbols! {
377377
abort,
378378
add,
379379
add_assign,
380+
add_with_carry,
380381
add_with_overflow,
381382
address,
382383
adt_const_params,
@@ -1276,6 +1277,9 @@ symbols! {
12761277
move_size_limit,
12771278
mul,
12781279
mul_assign,
1280+
mul_double,
1281+
mul_double_add,
1282+
mul_double_add2,
12791283
mul_with_overflow,
12801284
multiple_supertrait_upcastable,
12811285
must_not_suspend,
@@ -1918,6 +1922,7 @@ symbols! {
19181922
structural_peq,
19191923
sub,
19201924
sub_assign,
1925+
sub_with_carry,
19211926
sub_with_overflow,
19221927
suggestion,
19231928
surface_async_drop_in_place,

library/core/src/intrinsics.rs

+74
Original file line numberDiff line numberDiff line change
@@ -2444,6 +2444,80 @@ extern "rust-intrinsic" {
24442444
#[rustc_nounwind]
24452445
pub fn mul_with_overflow<T: Copy>(x: T, y: T) -> (T, bool);
24462446

2447+
/// Performs integer addition with an explicit carry flag.
2448+
///
2449+
/// Note that, unlike most intrinsics, this is safe to call;
2450+
/// it does not require an `unsafe` block.
2451+
/// Therefore, implementations must not require the user to uphold
2452+
/// any safety invariants.
2453+
///
2454+
/// This intrinsic does not have a stable counterpart.
2455+
#[cfg(not(bootstrap))]
2456+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
2457+
#[unstable(feature = "core_intrinsics", issue = "none")]
2458+
#[rustc_safe_intrinsic]
2459+
#[rustc_nounwind]
2460+
pub fn add_with_carry<T: Copy>(x: T, y: T, z: bool) -> (T, bool);
2461+
2462+
/// Performs integer subtraction with an explicit carry flag.
2463+
///
2464+
/// Note that, unlike most intrinsics, this is safe to call;
2465+
/// it does not require an `unsafe` block.
2466+
/// Therefore, implementations must not require the user to uphold
2467+
/// any safety invariants.
2468+
///
2469+
/// This intrinsic does not have a stable counterpart.
2470+
#[cfg(not(bootstrap))]
2471+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
2472+
#[unstable(feature = "core_intrinsics", issue = "none")]
2473+
#[rustc_safe_intrinsic]
2474+
#[rustc_nounwind]
2475+
pub fn sub_with_carry<T: Copy>(x: T, y: T, z: bool) -> (T, bool);
2476+
2477+
/// Performs double-wide integer multiplication.
2478+
///
2479+
/// Note that, unlike most intrinsics, this is safe to call;
2480+
/// it does not require an `unsafe` block.
2481+
/// Therefore, implementations must not require the user to uphold
2482+
/// any safety invariants.
2483+
///
2484+
/// This intrinsic does not have a stable counterpart.
2485+
#[cfg(not(bootstrap))]
2486+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
2487+
#[unstable(feature = "core_intrinsics", issue = "none")]
2488+
#[rustc_safe_intrinsic]
2489+
#[rustc_nounwind]
2490+
pub fn mul_double<T: Copy>(x: T, y: T) -> (T, T);
2491+
2492+
/// Performs double-wide integer multiplication with an extra addition.
2493+
///
2494+
/// Note that, unlike most intrinsics, this is safe to call;
2495+
/// it does not require an `unsafe` block.
2496+
/// Therefore, implementations must not require the user to uphold
2497+
/// any safety invariants.
2498+
///
2499+
/// This intrinsic does not have a stable counterpart.
2500+
#[cfg(not(bootstrap))]
2501+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
2502+
#[unstable(feature = "core_intrinsics", issue = "none")]
2503+
#[rustc_safe_intrinsic]
2504+
#[rustc_nounwind]
2505+
pub fn mul_double_add<T: Copy>(x: T, y: T, z: T) -> (T, T);
2506+
/// Performs double-wide integer multiplication with an extra two additions.
2507+
///
2508+
/// Note that, unlike most intrinsics, this is safe to call;
2509+
/// it does not require an `unsafe` block.
2510+
/// Therefore, implementations must not require the user to uphold
2511+
/// any safety invariants.
2512+
///
2513+
/// This intrinsic does not have a stable counterpart.
2514+
#[cfg(not(bootstrap))]
2515+
#[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")]
2516+
#[unstable(feature = "core_intrinsics", issue = "none")]
2517+
#[rustc_safe_intrinsic]
2518+
#[rustc_nounwind]
2519+
pub fn mul_double_add2<T: Copy>(x: T, y: T, z: T, w: T) -> (T, T);
2520+
24472521
/// Performs an exact division, resulting in undefined behavior where
24482522
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
24492523
///

0 commit comments

Comments
 (0)