Skip to content

Commit 537c448

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

File tree

16 files changed

+684
-164
lines changed

16 files changed

+684
-164
lines changed

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 76 additions & 1 deletion
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_const_eval/src/interpret/intrinsics.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
178178
)?;
179179
self.write_scalar(val, dest)?;
180180
}
181+
sym::add_with_carry | sym::sub_with_carry => {
182+
let l = self.read_immediate(&args[0])?;
183+
let r = self.read_immediate(&args[1])?;
184+
let c = self.read_immediate(&args[2])?;
185+
let (val, overflowed) = self.carrying_arith(
186+
if intrinsic_name == sym::add_with_carry { BinOp::Add } else { BinOp::Sub },
187+
&l,
188+
&r,
189+
&c,
190+
)?;
191+
self.write_scalar_pair(val, overflowed, dest)?;
192+
}
181193
sym::discriminant_value => {
182194
let place = self.deref_pointer(&args[0])?;
183195
let variant = self.read_discriminant(&place)?;
@@ -573,6 +585,39 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
573585
})
574586
}
575587

588+
pub fn carrying_arith(
589+
&self,
590+
mir_op: BinOp,
591+
l: &ImmTy<'tcx, M::Provenance>,
592+
r: &ImmTy<'tcx, M::Provenance>,
593+
c: &ImmTy<'tcx, M::Provenance>,
594+
) -> InterpResult<'tcx, (Scalar<M::Provenance>, Scalar<M::Provenance>)> {
595+
assert_eq!(l.layout.ty, r.layout.ty);
596+
assert_matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..));
597+
assert_matches!(c.layout.ty.kind(), ty::Bool);
598+
assert_matches!(mir_op, BinOp::Add | BinOp::Sub);
599+
600+
let mir_op = mir_op.wrapping_to_overflowing().unwrap();
601+
602+
let (val, overflowed1) = self.binary_op(mir_op, l, r)?.to_scalar_pair();
603+
604+
let val = ImmTy::from_scalar(val, l.layout);
605+
let c = ImmTy::from_scalar(c.to_scalar(), l.layout);
606+
607+
let (val, overflowed2) = self.binary_op(mir_op, &val, &c)?.to_scalar_pair();
608+
609+
let overflowed1 = overflowed1.to_bool()?;
610+
let overflowed2 = overflowed2.to_bool()?;
611+
612+
let overflowed = Scalar::from_bool(if l.layout.abi.is_signed() {
613+
overflowed1 != overflowed2
614+
} else {
615+
overflowed1 | overflowed2
616+
});
617+
618+
interp_ok((val, overflowed))
619+
}
620+
576621
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
577622
/// allocation.
578623
pub fn ptr_offset_inbounds(

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,17 @@ where
626626
self.write_immediate(Immediate::Scalar(val.into()), dest)
627627
}
628628

629+
/// Write a scalar pair to a place
630+
#[inline(always)]
631+
pub fn write_scalar_pair(
632+
&mut self,
633+
val1: impl Into<Scalar<M::Provenance>>,
634+
val2: impl Into<Scalar<M::Provenance>>,
635+
dest: &impl Writeable<'tcx, M::Provenance>,
636+
) -> InterpResult<'tcx> {
637+
self.write_immediate(Immediate::ScalarPair(val1.into(), val2.into()), dest)
638+
}
639+
629640
/// Write a pointer to a place
630641
#[inline(always)]
631642
pub fn write_pointer(

compiler/rustc_hir_analysis/src/check/intrinsic.rs

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

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

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