Skip to content

Commit a795f0f

Browse files
authored
Rollup merge of #94685 - RalfJung:saturating, r=oli-obk
interpret: move saturating_add/sub into (pub) helper method I plan to use them for `simd_saturating_add/sub`. The first commit just moves code, the 2nd simplifies it a bit with some helper methods that did not exist yet when the code was originally written.
2 parents 87df3f6 + ac84498 commit a795f0f

File tree

1 file changed

+45
-39
lines changed

1 file changed

+45
-39
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+45-39
Original file line numberDiff line numberDiff line change
@@ -219,48 +219,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
219219
sym::saturating_add | sym::saturating_sub => {
220220
let l = self.read_immediate(&args[0])?;
221221
let r = self.read_immediate(&args[1])?;
222-
let is_add = intrinsic_name == sym::saturating_add;
223-
let (val, overflowed, _ty) = self.overflowing_binary_op(
224-
if is_add { BinOp::Add } else { BinOp::Sub },
222+
let val = self.saturating_arith(
223+
if intrinsic_name == sym::saturating_add { BinOp::Add } else { BinOp::Sub },
225224
&l,
226225
&r,
227226
)?;
228-
let val = if overflowed {
229-
let size = l.layout.size;
230-
let num_bits = size.bits();
231-
if l.layout.abi.is_signed() {
232-
// For signed ints the saturated value depends on the sign of the first
233-
// term since the sign of the second term can be inferred from this and
234-
// the fact that the operation has overflowed (if either is 0 no
235-
// overflow can occur)
236-
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
237-
let first_term_positive = first_term & (1 << (num_bits - 1)) == 0;
238-
if first_term_positive {
239-
// Negative overflow not possible since the positive first term
240-
// can only increase an (in range) negative term for addition
241-
// or corresponding negated positive term for subtraction
242-
Scalar::from_uint(
243-
(1u128 << (num_bits - 1)) - 1, // max positive
244-
Size::from_bits(num_bits),
245-
)
246-
} else {
247-
// Positive overflow not possible for similar reason
248-
// max negative
249-
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
250-
}
251-
} else {
252-
// unsigned
253-
if is_add {
254-
// max unsigned
255-
Scalar::from_uint(size.unsigned_int_max(), Size::from_bits(num_bits))
256-
} else {
257-
// underflow to 0
258-
Scalar::from_uint(0u128, Size::from_bits(num_bits))
259-
}
260-
}
261-
} else {
262-
val
263-
};
264227
self.write_scalar(val, dest)?;
265228
}
266229
sym::discriminant_value => {
@@ -508,6 +471,49 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
508471
self.binop_ignore_overflow(BinOp::Div, &a, &b, dest)
509472
}
510473

474+
pub fn saturating_arith(
475+
&self,
476+
mir_op: BinOp,
477+
l: &ImmTy<'tcx, M::PointerTag>,
478+
r: &ImmTy<'tcx, M::PointerTag>,
479+
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
480+
assert!(matches!(mir_op, BinOp::Add | BinOp::Sub));
481+
let (val, overflowed, _ty) = self.overflowing_binary_op(mir_op, l, r)?;
482+
Ok(if overflowed {
483+
let size = l.layout.size;
484+
let num_bits = size.bits();
485+
if l.layout.abi.is_signed() {
486+
// For signed ints the saturated value depends on the sign of the first
487+
// term since the sign of the second term can be inferred from this and
488+
// the fact that the operation has overflowed (if either is 0 no
489+
// overflow can occur)
490+
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
491+
let first_term_positive = first_term & (1 << (num_bits - 1)) == 0;
492+
if first_term_positive {
493+
// Negative overflow not possible since the positive first term
494+
// can only increase an (in range) negative term for addition
495+
// or corresponding negated positive term for subtraction
496+
Scalar::from_int(size.signed_int_max(), size)
497+
} else {
498+
// Positive overflow not possible for similar reason
499+
// max negative
500+
Scalar::from_int(size.signed_int_min(), size)
501+
}
502+
} else {
503+
// unsigned
504+
if matches!(mir_op, BinOp::Add) {
505+
// max unsigned
506+
Scalar::from_uint(size.unsigned_int_max(), size)
507+
} else {
508+
// underflow to 0
509+
Scalar::from_uint(0u128, size)
510+
}
511+
}
512+
} else {
513+
val
514+
})
515+
}
516+
511517
/// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
512518
/// allocation. For integer pointers, we consider each of them their own tiny allocation of size
513519
/// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value.

0 commit comments

Comments
 (0)