Skip to content

Commit f7294c6

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

File tree

18 files changed

+775
-164
lines changed

18 files changed

+775
-164
lines changed

compiler/rustc_codegen_gcc/src/intrinsic/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
192192
| sym::prefetch_write_instruction => {
193193
unimplemented!();
194194
}
195+
#[cfg(not(bootstrap))]
196+
sym::add_with_carry
197+
| sym::sub_with_carry
198+
| sym::mul_double
199+
| sym::mul_double_add
200+
| sym::mul_double_add2 => unimplemented!(),
195201
sym::ctlz
196202
| sym::ctlz_nonzero
197203
| sym::cttz

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_const_eval/src/interpret/intrinsics.rs

+128
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,34 @@ 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+
}
193+
sym::mul_double | sym::mul_double_add | sym::mul_double_add2 => {
194+
let l = self.read_immediate(&args[0])?;
195+
let r = self.read_immediate(&args[1])?;
196+
let c1 = if intrinsic_name != sym::mul_double {
197+
Some(self.read_immediate(&args[2])?)
198+
} else {
199+
None
200+
};
201+
let c2 = if intrinsic_name == sym::mul_double_add2 {
202+
Some(self.read_immediate(&args[3])?)
203+
} else {
204+
None
205+
};
206+
let (lo, hi) = self.mul_double_add2(&l, &r, c1.as_ref(), c2.as_ref())?;
207+
self.write_scalar_pair(lo, hi, dest)?;
208+
}
181209
sym::discriminant_value => {
182210
let place = self.deref_pointer(&args[0])?;
183211
let variant = self.read_discriminant(&place)?;
@@ -573,6 +601,106 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
573601
})
574602
}
575603

604+
pub fn carrying_arith(
605+
&self,
606+
mir_op: BinOp,
607+
l: &ImmTy<'tcx, M::Provenance>,
608+
r: &ImmTy<'tcx, M::Provenance>,
609+
c: &ImmTy<'tcx, M::Provenance>,
610+
) -> InterpResult<'tcx, (Scalar<M::Provenance>, Scalar<M::Provenance>)> {
611+
assert_eq!(l.layout.ty, r.layout.ty);
612+
assert_matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..));
613+
assert_matches!(c.layout.ty.kind(), ty::Bool);
614+
assert_matches!(mir_op, BinOp::Add | BinOp::Sub);
615+
616+
let mir_op = mir_op.wrapping_to_overflowing().unwrap();
617+
618+
let (val, overflowed1) = self.binary_op(mir_op, l, r)?.to_scalar_pair();
619+
620+
let val = ImmTy::from_scalar(val, l.layout);
621+
let c = ImmTy::from_scalar(c.to_scalar(), l.layout);
622+
623+
let (val, overflowed2) = self.binary_op(mir_op, &val, &c)?.to_scalar_pair();
624+
625+
let overflowed1 = overflowed1.to_bool()?;
626+
let overflowed2 = overflowed2.to_bool()?;
627+
628+
let overflowed = Scalar::from_bool(if l.layout.abi.is_signed() {
629+
overflowed1 != overflowed2
630+
} else {
631+
overflowed1 | overflowed2
632+
});
633+
634+
interp_ok((val, overflowed))
635+
}
636+
637+
pub fn mul_double_add2(
638+
&self,
639+
l: &ImmTy<'tcx, M::Provenance>,
640+
r: &ImmTy<'tcx, M::Provenance>,
641+
c1: Option<&ImmTy<'tcx, M::Provenance>>,
642+
c2: Option<&ImmTy<'tcx, M::Provenance>>,
643+
) -> InterpResult<'tcx, (Scalar<M::Provenance>, Scalar<M::Provenance>)> {
644+
assert_eq!(l.layout.ty, r.layout.ty);
645+
assert_matches!(l.layout.ty.kind(), ty::Int(..) | ty::Uint(..));
646+
647+
let is_signed = l.layout.abi.is_signed();
648+
let size = l.layout.size;
649+
let bits = size.bits();
650+
let l = l.to_scalar_int()?;
651+
let r = r.to_scalar_int()?;
652+
653+
interp_ok(if is_signed {
654+
let l = l.to_int(size);
655+
let r = r.to_int(size);
656+
let c1 = c1.map_or(interp_ok(0), |c1| interp_ok(c1.to_scalar_int()?.to_int(size)))?;
657+
let c2 = c2.map_or(interp_ok(0), |c2| interp_ok(c2.to_scalar_int()?.to_int(size)))?;
658+
if bits == 128 {
659+
#[cfg(bootstrap)]
660+
{
661+
let _ = (l, r, c1, c2);
662+
unimplemented!()
663+
}
664+
#[cfg(not(bootstrap))]
665+
{
666+
let (lo, hi) = l.carrying2_mul(r, c1, c2);
667+
let lo = Scalar::from_uint(lo, size);
668+
let hi = Scalar::from_int(hi, size);
669+
(lo, hi)
670+
}
671+
} else {
672+
let prod = l * r + c1 + c2;
673+
let lo = Scalar::from_int(prod, size);
674+
let hi = Scalar::from_int(prod >> size.bits(), size);
675+
(lo, hi)
676+
}
677+
} else {
678+
let l = l.to_uint(size);
679+
let r = r.to_uint(size);
680+
let c1 = c1.map_or(interp_ok(0), |c1| interp_ok(c1.to_scalar_int()?.to_uint(size)))?;
681+
let c2 = c2.map_or(interp_ok(0), |c2| interp_ok(c2.to_scalar_int()?.to_uint(size)))?;
682+
if bits == 128 {
683+
#[cfg(bootstrap)]
684+
{
685+
let _ = (l, r, c1, c2);
686+
unimplemented!()
687+
}
688+
#[cfg(not(bootstrap))]
689+
{
690+
let (lo, hi) = l.carrying2_mul(r, c1, c2);
691+
let lo = Scalar::from_uint(lo, size);
692+
let hi = Scalar::from_uint(hi, size);
693+
(lo, hi)
694+
}
695+
} else {
696+
let prod = l * r + c1 + c2;
697+
let lo = Scalar::from_uint(prod, size);
698+
let hi = Scalar::from_uint(prod >> size.bits(), size);
699+
(lo, hi)
700+
}
701+
})
702+
}
703+
576704
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
577705
/// allocation.
578706
pub fn ptr_offset_inbounds(

compiler/rustc_const_eval/src/interpret/place.rs

+11
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_const_eval/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![cfg_attr(not(bootstrap), warn(unqualified_local_imports))]
66
#![doc(rust_logo)]
77
#![feature(assert_matches)]
8+
#![feature(bigint_helper_methods)]
89
#![feature(box_patterns)]
910
#![feature(decl_macro)]
1011
#![feature(if_let_guard)]

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,

0 commit comments

Comments
 (0)