Skip to content

Commit 48d17a9

Browse files
committed
Auto merge of #125359 - RalfJung:interpret-overflowing-ops, r=oli-obk
interpret: make overflowing binops just normal binops Follow-up to rust-lang/rust#125173 (Cc `@scottmcm)`
2 parents 7d350d7 + 309a6d6 commit 48d17a9

23 files changed

+88
-90
lines changed

src/concurrency/data_race.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -648,17 +648,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
648648
place: &MPlaceTy<'tcx, Provenance>,
649649
rhs: &ImmTy<'tcx, Provenance>,
650650
op: mir::BinOp,
651-
neg: bool,
651+
not: bool,
652652
atomic: AtomicRwOrd,
653653
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
654654
let this = self.eval_context_mut();
655655
this.atomic_access_check(place, AtomicAccessType::Rmw)?;
656656

657657
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
658658

659-
// Atomics wrap around on overflow.
660-
let val = this.wrapping_binary_op(op, &old, rhs)?;
661-
let val = if neg { this.wrapping_unary_op(mir::UnOp::Not, &val)? } else { val };
659+
let val = this.binary_op(op, &old, rhs)?;
660+
let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val };
662661
this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
663662

664663
this.validate_atomic_rmw(place, atomic)?;
@@ -700,7 +699,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
700699
this.atomic_access_check(place, AtomicAccessType::Rmw)?;
701700

702701
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
703-
let lt = this.wrapping_binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
702+
let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
704703

705704
#[rustfmt::skip] // rustfmt makes this unreadable
706705
let new_val = if min {
@@ -744,7 +743,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
744743
// Read as immediate for the sake of `binary_op()`
745744
let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
746745
// `binary_op` will bail if either of them is not a scalar.
747-
let eq = this.wrapping_binary_op(mir::BinOp::Eq, &old, expect_old)?;
746+
let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
748747
// If the operation would succeed, but is "weak", fail some portion
749748
// of the time, based on `success_rate`.
750749
let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;

src/intrinsics/atomic.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use crate::*;
44
use helpers::check_arg_count;
55

66
pub enum AtomicOp {
7-
/// The `bool` indicates whether the result of the operation should be negated
8-
/// (must be a boolean-typed operation).
7+
/// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`,
8+
/// must be a boolean-typed operation).
99
MirOp(mir::BinOp, bool),
1010
Max,
1111
Min,
@@ -213,8 +213,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
213213
this.write_immediate(*old, dest)?; // old value is returned
214214
Ok(())
215215
}
216-
AtomicOp::MirOp(op, neg) => {
217-
let old = this.atomic_rmw_op_immediate(&place, &rhs, op, neg, atomic)?;
216+
AtomicOp::MirOp(op, not) => {
217+
let old = this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?;
218218
this.write_immediate(*old, dest)?; // old value is returned
219219
Ok(())
220220
}

src/intrinsics/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
365365
"frem_algebraic" => mir::BinOp::Rem,
366366
_ => bug!(),
367367
};
368-
let res = this.wrapping_binary_op(op, &a, &b)?;
369-
// `wrapping_binary_op` already called `generate_nan` if necessary.
368+
let res = this.binary_op(op, &a, &b)?;
369+
// `binary_op` already called `generate_nan` if necessary.
370370
this.write_immediate(*res, dest)?;
371371
}
372372

@@ -411,12 +411,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
411411
),
412412
_ => {}
413413
}
414-
let res = this.wrapping_binary_op(op, &a, &b)?;
414+
let res = this.binary_op(op, &a, &b)?;
415415
if !float_finite(&res)? {
416416
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
417417
}
418418
// This cannot be a NaN so we also don't have to apply any non-determinism.
419-
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
419+
// (Also, `binary_op` already called `generate_nan` if needed.)
420420
this.write_immediate(*res, dest)?;
421421
}
422422

src/intrinsics/simd.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use either::Either;
2+
13
use rustc_apfloat::{Float, Round};
24
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
35
use rustc_middle::{mir, ty, ty::FloatTy};
@@ -82,7 +84,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
8284
let val = match which {
8385
Op::MirOp(mir_op) => {
8486
// This already does NaN adjustments
85-
this.wrapping_unary_op(mir_op, &op)?.to_scalar()
87+
this.unary_op(mir_op, &op)?.to_scalar()
8688
}
8789
Op::Abs => {
8890
// Works for f32 and f64.
@@ -217,8 +219,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
217219
"mul" => Op::MirOp(BinOp::Mul),
218220
"div" => Op::MirOp(BinOp::Div),
219221
"rem" => Op::MirOp(BinOp::Rem),
220-
"shl" => Op::MirOp(BinOp::Shl),
221-
"shr" => Op::MirOp(BinOp::Shr),
222+
"shl" => Op::MirOp(BinOp::ShlUnchecked),
223+
"shr" => Op::MirOp(BinOp::ShrUnchecked),
222224
"and" => Op::MirOp(BinOp::BitAnd),
223225
"or" => Op::MirOp(BinOp::BitOr),
224226
"xor" => Op::MirOp(BinOp::BitXor),
@@ -243,15 +245,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
243245
let val = match which {
244246
Op::MirOp(mir_op) => {
245247
// This does NaN adjustments.
246-
let (val, overflowed) = this.overflowing_binary_op(mir_op, &left, &right)?;
247-
if matches!(mir_op, BinOp::Shl | BinOp::Shr) {
248-
// Shifts have extra UB as SIMD operations that the MIR binop does not have.
249-
// See <https://github.com/rust-lang/rust/issues/91237>.
250-
if overflowed {
251-
let r_val = right.to_scalar().to_bits(right.layout.size)?;
252-
throw_ub_format!("overflowing shift by {r_val} in `simd_{intrinsic_name}` in SIMD lane {i}");
248+
let val = this.binary_op(mir_op, &left, &right).map_err(|err| {
249+
match err.kind() {
250+
InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ShiftOverflow { shift_amount, .. }) => {
251+
// This resets the interpreter backtrace, but it's not worth avoiding that.
252+
let shift_amount = match shift_amount {
253+
Either::Left(v) => v.to_string(),
254+
Either::Right(v) => v.to_string(),
255+
};
256+
err_ub_format!("overflowing shift by {shift_amount} in `simd_{intrinsic_name}` in lane {i}").into()
257+
}
258+
_ => err
253259
}
254-
}
260+
})?;
255261
if matches!(mir_op, BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge) {
256262
// Special handling for boolean-returning operations
257263
assert_eq!(val.layout.ty, this.tcx.types.bool);
@@ -370,11 +376,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
370376
let op = this.read_immediate(&this.project_index(&op, i)?)?;
371377
res = match which {
372378
Op::MirOp(mir_op) => {
373-
this.wrapping_binary_op(mir_op, &res, &op)?
379+
this.binary_op(mir_op, &res, &op)?
374380
}
375381
Op::MirOpBool(mir_op) => {
376382
let op = imm_from_bool(simd_element_to_bool(op)?);
377-
this.wrapping_binary_op(mir_op, &res, &op)?
383+
this.binary_op(mir_op, &res, &op)?
378384
}
379385
Op::MinMax(mmop) => {
380386
if matches!(res.layout.ty.kind(), ty::Float(_)) {
@@ -385,7 +391,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
385391
MinMax::Min => BinOp::Le,
386392
MinMax::Max => BinOp::Ge,
387393
};
388-
if this.wrapping_binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
394+
if this.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
389395
res
390396
} else {
391397
op
@@ -414,7 +420,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
414420
let mut res = init;
415421
for i in 0..op_len {
416422
let op = this.read_immediate(&this.project_index(&op, i)?)?;
417-
res = this.wrapping_binary_op(mir_op, &res, &op)?;
423+
res = this.binary_op(mir_op, &res, &op)?;
418424
}
419425
this.write_immediate(*res, dest)?;
420426
}

src/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
10251025
bin_op: mir::BinOp,
10261026
left: &ImmTy<'tcx, Provenance>,
10271027
right: &ImmTy<'tcx, Provenance>,
1028-
) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> {
1028+
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
10291029
ecx.binary_ptr_op(bin_op, left, right)
10301030
}
10311031

src/operator.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
1414
bin_op: mir::BinOp,
1515
left: &ImmTy<'tcx, Provenance>,
1616
right: &ImmTy<'tcx, Provenance>,
17-
) -> InterpResult<'tcx, (ImmTy<'tcx, Provenance>, bool)> {
17+
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
1818
use rustc_middle::mir::BinOp::*;
1919

2020
let this = self.eval_context_ref();
@@ -45,7 +45,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
4545
Ge => left >= right,
4646
_ => bug!(),
4747
};
48-
(ImmTy::from_bool(res, *this.tcx), false)
48+
ImmTy::from_bool(res, *this.tcx)
4949
}
5050

5151
// Some more operations are possible with atomics.
@@ -60,16 +60,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
6060
right.to_scalar().to_target_usize(this)?,
6161
this.machine.layouts.usize,
6262
);
63-
let (result, overflowing) = this.overflowing_binary_op(bin_op, &left, &right)?;
63+
let result = this.binary_op(bin_op, &left, &right)?;
6464
// Construct a new pointer with the provenance of `ptr` (the LHS).
6565
let result_ptr = Pointer::new(
6666
ptr.provenance,
6767
Size::from_bytes(result.to_scalar().to_target_usize(this)?),
6868
);
69-
(
70-
ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout),
71-
overflowing,
72-
)
69+
70+
ImmTy::from_scalar(Scalar::from_maybe_pointer(result_ptr, this), left.layout)
7371
}
7472

7573
_ => span_bug!(this.cur_span(), "Invalid operator on pointers: {:?}", bin_op),

src/shims/x86/mod.rs

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,16 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
5050
let a = this.read_immediate(a)?;
5151
let b = this.read_immediate(b)?;
5252

53-
let (sum, overflow1) = this.overflowing_binary_op(mir::BinOp::Add, &a, &b)?;
54-
let (sum, overflow2) = this.overflowing_binary_op(
55-
mir::BinOp::Add,
56-
&sum,
57-
&ImmTy::from_uint(c_in, a.layout),
58-
)?;
59-
let c_out = overflow1 | overflow2;
53+
let (sum, overflow1) =
54+
this.binary_op(mir::BinOp::AddWithOverflow, &a, &b)?.to_pair(this);
55+
let (sum, overflow2) = this
56+
.binary_op(
57+
mir::BinOp::AddWithOverflow,
58+
&sum,
59+
&ImmTy::from_uint(c_in, a.layout),
60+
)?
61+
.to_pair(this);
62+
let c_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
6063

6164
this.write_scalar(Scalar::from_u8(c_out.into()), &this.project_field(dest, 0)?)?;
6265
this.write_immediate(*sum, &this.project_field(dest, 1)?)?;
@@ -76,13 +79,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
7679
let a = this.read_immediate(a)?;
7780
let b = this.read_immediate(b)?;
7881

79-
let (sub, overflow1) = this.overflowing_binary_op(mir::BinOp::Sub, &a, &b)?;
80-
let (sub, overflow2) = this.overflowing_binary_op(
81-
mir::BinOp::Sub,
82-
&sub,
83-
&ImmTy::from_uint(b_in, a.layout),
84-
)?;
85-
let b_out = overflow1 | overflow2;
82+
let (sub, overflow1) = this.binary_op(mir::BinOp::SubWithOverflow, &a, &b)?.to_pair(this);
83+
let (sub, overflow2) = this
84+
.binary_op(mir::BinOp::SubWithOverflow, &sub, &ImmTy::from_uint(b_in, a.layout))?
85+
.to_pair(this);
86+
let b_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
8687

8788
this.write_scalar(Scalar::from_u8(b_out.into()), &this.project_field(dest, 0)?)?;
8889
this.write_immediate(*sub, &this.project_field(dest, 1)?)?;
@@ -245,7 +246,7 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
245246
) -> InterpResult<'tcx, Scalar<Provenance>> {
246247
match which {
247248
FloatBinOp::Arith(which) => {
248-
let res = this.wrapping_binary_op(which, left, right)?;
249+
let res = this.binary_op(which, left, right)?;
249250
Ok(res.to_scalar())
250251
}
251252
FloatBinOp::Cmp { gt, lt, eq, unord } => {
@@ -744,12 +745,9 @@ fn int_abs<'tcx>(
744745
let op = this.read_immediate(&this.project_index(&op, i)?)?;
745746
let dest = this.project_index(&dest, i)?;
746747

747-
let lt_zero = this.wrapping_binary_op(mir::BinOp::Lt, &op, &zero)?;
748-
let res = if lt_zero.to_scalar().to_bool()? {
749-
this.wrapping_unary_op(mir::UnOp::Neg, &op)?
750-
} else {
751-
op
752-
};
748+
let lt_zero = this.binary_op(mir::BinOp::Lt, &op, &zero)?;
749+
let res =
750+
if lt_zero.to_scalar().to_bool()? { this.unary_op(mir::UnOp::Neg, &op)? } else { op };
753751

754752
this.write_immediate(*res, &dest)?;
755753
}
@@ -832,7 +830,7 @@ fn horizontal_bin_op<'tcx>(
832830
let res = if saturating {
833831
Immediate::from(this.saturating_arith(which, &lhs, &rhs)?)
834832
} else {
835-
*this.wrapping_binary_op(which, &lhs, &rhs)?
833+
*this.binary_op(which, &lhs, &rhs)?
836834
};
837835

838836
this.write_immediate(res, &this.project_index(&dest, j)?)?;
@@ -884,8 +882,8 @@ fn conditional_dot_product<'tcx>(
884882
let left = this.read_immediate(&this.project_index(&left, j)?)?;
885883
let right = this.read_immediate(&this.project_index(&right, j)?)?;
886884

887-
let mul = this.wrapping_binary_op(mir::BinOp::Mul, &left, &right)?;
888-
sum = this.wrapping_binary_op(mir::BinOp::Add, &sum, &mul)?;
885+
let mul = this.binary_op(mir::BinOp::Mul, &left, &right)?;
886+
sum = this.binary_op(mir::BinOp::Add, &sum, &mul)?;
889887
}
890888
}
891889

@@ -1276,11 +1274,8 @@ fn psign<'tcx>(
12761274
let left = this.read_immediate(&this.project_index(&left, i)?)?;
12771275
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?;
12781276

1279-
let res = this.wrapping_binary_op(
1280-
mir::BinOp::Mul,
1281-
&left,
1282-
&ImmTy::from_int(right.signum(), dest.layout),
1283-
)?;
1277+
let res =
1278+
this.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
12841279

12851280
this.write_immediate(*res, &dest)?;
12861281
}

tests/fail/intrinsics/simd-shl-too-far.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ fn main() {
1010
unsafe {
1111
let x = i32x2(1, 1);
1212
let y = i32x2(100, 0);
13-
simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in SIMD lane 0
13+
simd_shl(x, y); //~ERROR: overflowing shift by 100 in `simd_shl` in lane 0
1414
}
1515
}

tests/fail/intrinsics/simd-shl-too-far.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in SIMD lane 0
1+
error: Undefined Behavior: overflowing shift by 100 in `simd_shl` in lane 0
22
--> $DIR/simd-shl-too-far.rs:LL:CC
33
|
44
LL | simd_shl(x, y);
5-
| ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in SIMD lane 0
5+
| ^^^^^^^^^^^^^^ overflowing shift by 100 in `simd_shl` in lane 0
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/intrinsics/simd-shr-too-far.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ fn main() {
1010
unsafe {
1111
let x = i32x2(1, 1);
1212
let y = i32x2(20, 40);
13-
simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in SIMD lane 1
13+
simd_shr(x, y); //~ERROR: overflowing shift by 40 in `simd_shr` in lane 1
1414
}
1515
}

tests/fail/intrinsics/simd-shr-too-far.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in SIMD lane 1
1+
error: Undefined Behavior: overflowing shift by 40 in `simd_shr` in lane 1
22
--> $DIR/simd-shr-too-far.rs:LL:CC
33
|
44
LL | simd_shr(x, y);
5-
| ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in SIMD lane 1
5+
| ^^^^^^^^^^^^^^ overflowing shift by 40 in `simd_shr` in lane 1
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
fn main() {
22
// MAX overflow
3-
let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: overflow executing `unchecked_add`
3+
let _val = unsafe { 40000u16.unchecked_add(30000) }; //~ ERROR: arithmetic overflow in `unchecked_add`
44
}

tests/fail/intrinsics/unchecked_add1.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: overflow executing `unchecked_add`
1+
error: Undefined Behavior: arithmetic overflow in `unchecked_add`
22
--> $DIR/unchecked_add1.rs:LL:CC
33
|
44
LL | let _val = unsafe { 40000u16.unchecked_add(30000) };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_add`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ arithmetic overflow in `unchecked_add`
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
fn main() {
22
// MIN overflow
3-
let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: overflow executing `unchecked_add`
3+
let _val = unsafe { (-30000i16).unchecked_add(-8000) }; //~ ERROR: arithmetic overflow in `unchecked_add`
44
}

0 commit comments

Comments
 (0)