2024-05-21 12:17:34 +02:00
|
|
|
use either::Either;
|
2023-10-09 07:38:00 +02:00
|
|
|
use rustc_apfloat::{Float, FloatConvert};
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
2023-08-01 17:11:00 +02:00
|
|
|
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
2024-05-21 12:17:34 +02:00
|
|
|
use rustc_middle::ty::{self, FloatTy, ScalarInt};
|
2024-05-08 19:03:14 +10:00
|
|
|
use rustc_middle::{bug, mir, span_bug};
|
2023-06-03 00:41:50 -07:00
|
|
|
use rustc_span::symbol::sym;
|
2024-05-22 14:20:23 +10:00
|
|
|
use tracing::trace;
|
2016-10-20 04:42:19 -06:00
|
|
|
|
2024-08-01 14:38:58 +02:00
|
|
|
use super::{throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
|
2016-12-10 17:03:12 -08:00
|
|
|
|
2024-05-27 08:24:23 +02:00
|
|
|
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
2024-05-21 12:17:34 +02:00
|
|
|
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
|
2023-03-05 20:19:41 -08:00
|
|
|
let res = Ord::cmp(&lhs, &rhs);
|
2024-05-21 12:17:34 +02:00
|
|
|
return ImmTy::from_ordering(res, *self.tcx);
|
2023-03-05 20:19:41 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
fn binary_char_op(&self, bin_op: mir::BinOp, l: char, r: char) -> ImmTy<'tcx, M::Provenance> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::BinOp::*;
|
2016-10-20 04:42:19 -06:00
|
|
|
|
2023-03-05 20:19:41 -08:00
|
|
|
if bin_op == Cmp {
|
|
|
|
|
return self.three_way_compare(l, r);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-26 14:22:59 +02:00
|
|
|
let res = match bin_op {
|
|
|
|
|
Eq => l == r,
|
|
|
|
|
Ne => l != r,
|
|
|
|
|
Lt => l < r,
|
|
|
|
|
Le => l <= r,
|
|
|
|
|
Gt => l > r,
|
|
|
|
|
Ge => l >= r,
|
2020-06-21 16:13:31 +02:00
|
|
|
_ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op),
|
2018-08-26 14:22:59 +02:00
|
|
|
};
|
2024-05-21 12:17:34 +02:00
|
|
|
ImmTy::from_bool(res, *self.tcx)
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
2018-05-24 11:21:23 +02:00
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
fn binary_bool_op(&self, bin_op: mir::BinOp, l: bool, r: bool) -> ImmTy<'tcx, M::Provenance> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::BinOp::*;
|
2018-08-24 18:36:52 +02:00
|
|
|
|
2018-08-26 14:22:59 +02:00
|
|
|
let res = match bin_op {
|
|
|
|
|
Eq => l == r,
|
|
|
|
|
Ne => l != r,
|
|
|
|
|
Lt => l < r,
|
|
|
|
|
Le => l <= r,
|
|
|
|
|
Gt => l > r,
|
|
|
|
|
Ge => l >= r,
|
|
|
|
|
BitAnd => l & r,
|
|
|
|
|
BitOr => l | r,
|
|
|
|
|
BitXor => l ^ r,
|
2020-06-21 16:13:31 +02:00
|
|
|
_ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op),
|
2018-08-26 14:22:59 +02:00
|
|
|
};
|
2024-05-21 12:17:34 +02:00
|
|
|
ImmTy::from_bool(res, *self.tcx)
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-09 07:38:00 +02:00
|
|
|
fn binary_float_op<F: Float + FloatConvert<F> + Into<Scalar<M::Provenance>>>(
|
2018-08-26 14:22:59 +02:00
|
|
|
&self,
|
|
|
|
|
bin_op: mir::BinOp,
|
2023-09-20 21:49:30 +02:00
|
|
|
layout: TyAndLayout<'tcx>,
|
2019-06-09 00:41:20 +02:00
|
|
|
l: F,
|
|
|
|
|
r: F,
|
2024-05-21 12:17:34 +02:00
|
|
|
) -> ImmTy<'tcx, M::Provenance> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::BinOp::*;
|
2018-08-26 14:22:59 +02:00
|
|
|
|
2023-10-08 12:03:01 +02:00
|
|
|
// Performs appropriate non-deterministic adjustments of NaN results.
|
2023-11-13 08:24:55 -05:00
|
|
|
let adjust_nan =
|
|
|
|
|
|f: F| -> F { if f.is_nan() { M::generate_nan(self, &[l, r]) } else { f } };
|
2023-10-08 12:03:01 +02:00
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
match bin_op {
|
2023-09-20 21:49:30 +02:00
|
|
|
Eq => ImmTy::from_bool(l == r, *self.tcx),
|
|
|
|
|
Ne => ImmTy::from_bool(l != r, *self.tcx),
|
|
|
|
|
Lt => ImmTy::from_bool(l < r, *self.tcx),
|
|
|
|
|
Le => ImmTy::from_bool(l <= r, *self.tcx),
|
|
|
|
|
Gt => ImmTy::from_bool(l > r, *self.tcx),
|
|
|
|
|
Ge => ImmTy::from_bool(l >= r, *self.tcx),
|
2023-10-08 12:03:01 +02:00
|
|
|
Add => ImmTy::from_scalar(adjust_nan((l + r).value).into(), layout),
|
|
|
|
|
Sub => ImmTy::from_scalar(adjust_nan((l - r).value).into(), layout),
|
|
|
|
|
Mul => ImmTy::from_scalar(adjust_nan((l * r).value).into(), layout),
|
|
|
|
|
Div => ImmTy::from_scalar(adjust_nan((l / r).value).into(), layout),
|
|
|
|
|
Rem => ImmTy::from_scalar(adjust_nan((l % r).value).into(), layout),
|
2020-06-21 16:13:31 +02:00
|
|
|
_ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op),
|
2024-05-21 12:17:34 +02:00
|
|
|
}
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
2016-10-20 04:42:19 -06:00
|
|
|
|
2018-08-26 14:22:59 +02:00
|
|
|
fn binary_int_op(
|
|
|
|
|
&self,
|
|
|
|
|
bin_op: mir::BinOp,
|
2024-04-18 00:33:46 +02:00
|
|
|
left: &ImmTy<'tcx, M::Provenance>,
|
|
|
|
|
right: &ImmTy<'tcx, M::Provenance>,
|
2024-05-21 12:17:34 +02:00
|
|
|
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::BinOp::*;
|
2018-02-22 14:59:18 +01:00
|
|
|
|
2024-04-18 00:33:46 +02:00
|
|
|
// This checks the size, so that we can just assert it below.
|
|
|
|
|
let l = left.to_scalar_int()?;
|
|
|
|
|
let r = right.to_scalar_int()?;
|
|
|
|
|
// Prepare to convert the values to signed or unsigned form.
|
2024-06-08 16:13:45 +02:00
|
|
|
let l_signed = || l.to_int(left.layout.size);
|
|
|
|
|
let l_unsigned = || l.to_uint(left.layout.size);
|
|
|
|
|
let r_signed = || r.to_int(right.layout.size);
|
|
|
|
|
let r_unsigned = || r.to_uint(right.layout.size);
|
2024-04-18 00:33:46 +02:00
|
|
|
|
2023-06-03 00:41:50 -07:00
|
|
|
let throw_ub_on_overflow = match bin_op {
|
|
|
|
|
AddUnchecked => Some(sym::unchecked_add),
|
|
|
|
|
SubUnchecked => Some(sym::unchecked_sub),
|
|
|
|
|
MulUnchecked => Some(sym::unchecked_mul),
|
|
|
|
|
ShlUnchecked => Some(sym::unchecked_shl),
|
|
|
|
|
ShrUnchecked => Some(sym::unchecked_shr),
|
|
|
|
|
_ => None,
|
|
|
|
|
};
|
2024-05-21 12:17:34 +02:00
|
|
|
let with_overflow = bin_op.is_overflowing();
|
2023-06-03 00:41:50 -07:00
|
|
|
|
2018-08-24 18:36:52 +02:00
|
|
|
// Shift ops can have an RHS with a different numeric type.
|
2023-06-03 00:41:50 -07:00
|
|
|
if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
|
2024-06-14 11:28:18 +02:00
|
|
|
let l_bits = left.layout.size.bits();
|
2024-05-21 12:17:34 +02:00
|
|
|
// Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is
|
|
|
|
|
// the one MIR operator that does *not* directly map to a single LLVM operation.)
|
2024-04-18 00:33:46 +02:00
|
|
|
let (shift_amount, overflow) = if right.layout.abi.is_signed() {
|
|
|
|
|
let shift_amount = r_signed();
|
2024-06-14 11:28:18 +02:00
|
|
|
let rem = shift_amount.rem_euclid(l_bits.into());
|
|
|
|
|
// `rem` is guaranteed positive, so the `unwrap` cannot fail
|
|
|
|
|
(u128::try_from(rem).unwrap(), rem != shift_amount)
|
2023-11-12 12:16:41 +01:00
|
|
|
} else {
|
2024-04-18 00:33:46 +02:00
|
|
|
let shift_amount = r_unsigned();
|
2024-06-14 11:28:18 +02:00
|
|
|
let rem = shift_amount.rem_euclid(l_bits.into());
|
|
|
|
|
(rem, rem != shift_amount)
|
2023-11-12 12:16:41 +01:00
|
|
|
};
|
2024-06-14 11:28:18 +02:00
|
|
|
let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit
|
2023-11-12 12:16:41 +01:00
|
|
|
// Compute the shifted result.
|
2024-04-18 00:33:46 +02:00
|
|
|
let result = if left.layout.abi.is_signed() {
|
|
|
|
|
let l = l_signed();
|
2018-02-22 14:59:18 +01:00
|
|
|
let result = match bin_op {
|
2023-11-12 12:16:41 +01:00
|
|
|
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
|
|
|
|
|
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
|
2022-06-04 11:15:36 -04:00
|
|
|
_ => bug!(),
|
2018-02-22 14:59:18 +01:00
|
|
|
};
|
2024-04-18 00:33:46 +02:00
|
|
|
ScalarInt::truncate_from_int(result, left.layout.size).0
|
2018-02-21 22:02:52 +01:00
|
|
|
} else {
|
2024-04-18 00:33:46 +02:00
|
|
|
let l = l_unsigned();
|
|
|
|
|
let result = match bin_op {
|
2023-11-12 12:16:41 +01:00
|
|
|
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
|
|
|
|
|
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
|
2022-06-04 11:15:36 -04:00
|
|
|
_ => bug!(),
|
2024-04-18 00:33:46 +02:00
|
|
|
};
|
|
|
|
|
ScalarInt::truncate_from_uint(result, left.layout.size).0
|
2018-02-21 22:02:52 +01:00
|
|
|
};
|
2023-06-03 00:41:50 -07:00
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
if overflow && let Some(intrinsic) = throw_ub_on_overflow {
|
|
|
|
|
throw_ub!(ShiftOverflow {
|
|
|
|
|
intrinsic,
|
|
|
|
|
shift_amount: if right.layout.abi.is_signed() {
|
|
|
|
|
Either::Right(r_signed())
|
2023-11-12 12:16:41 +01:00
|
|
|
} else {
|
2024-05-21 12:17:34 +02:00
|
|
|
Either::Left(r_unsigned())
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-06-03 00:41:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
return Ok(ImmTy::from_scalar_int(result, left.layout));
|
2017-06-04 17:26:19 -07:00
|
|
|
}
|
|
|
|
|
|
2018-08-24 18:36:52 +02:00
|
|
|
// For the remaining ops, the types must be the same on both sides
|
2024-04-18 00:33:46 +02:00
|
|
|
if left.layout.ty != right.layout.ty {
|
2020-06-21 16:13:31 +02:00
|
|
|
span_bug!(
|
|
|
|
|
self.cur_span(),
|
2023-09-20 22:25:09 +02:00
|
|
|
"invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
|
2024-04-18 00:33:46 +02:00
|
|
|
l_ty = left.layout.ty,
|
|
|
|
|
r_ty = right.layout.ty,
|
2019-08-02 23:41:24 +02:00
|
|
|
)
|
2016-10-20 04:42:19 -06:00
|
|
|
}
|
2016-03-18 23:03:46 -06:00
|
|
|
|
2024-04-18 00:33:46 +02:00
|
|
|
let size = left.layout.size;
|
2020-02-09 15:23:34 +01:00
|
|
|
|
2018-08-24 18:36:52 +02:00
|
|
|
// Operations that need special treatment for signed integers
|
2024-04-18 00:33:46 +02:00
|
|
|
if left.layout.abi.is_signed() {
|
2018-02-21 22:02:52 +01:00
|
|
|
let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
|
|
|
|
|
Lt => Some(i128::lt),
|
|
|
|
|
Le => Some(i128::le),
|
|
|
|
|
Gt => Some(i128::gt),
|
|
|
|
|
Ge => Some(i128::ge),
|
|
|
|
|
_ => None,
|
|
|
|
|
};
|
|
|
|
|
if let Some(op) = op {
|
2024-05-21 12:17:34 +02:00
|
|
|
return Ok(ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx));
|
2018-02-21 22:02:52 +01:00
|
|
|
}
|
2023-03-05 20:19:41 -08:00
|
|
|
if bin_op == Cmp {
|
2024-04-18 00:33:46 +02:00
|
|
|
return Ok(self.three_way_compare(l_signed(), r_signed()));
|
2023-03-05 20:19:41 -08:00
|
|
|
}
|
2018-02-21 22:02:52 +01:00
|
|
|
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
|
2024-04-18 00:33:46 +02:00
|
|
|
Div if r.is_null() => throw_ub!(DivisionByZero),
|
|
|
|
|
Rem if r.is_null() => throw_ub!(RemainderByZero),
|
2018-02-21 22:02:52 +01:00
|
|
|
Div => Some(i128::overflowing_div),
|
|
|
|
|
Rem => Some(i128::overflowing_rem),
|
2024-05-21 12:17:34 +02:00
|
|
|
Add | AddUnchecked | AddWithOverflow => Some(i128::overflowing_add),
|
|
|
|
|
Sub | SubUnchecked | SubWithOverflow => Some(i128::overflowing_sub),
|
|
|
|
|
Mul | MulUnchecked | MulWithOverflow => Some(i128::overflowing_mul),
|
2018-02-21 22:02:52 +01:00
|
|
|
_ => None,
|
|
|
|
|
};
|
|
|
|
|
if let Some(op) = op {
|
2024-04-18 00:33:46 +02:00
|
|
|
let l = l_signed();
|
|
|
|
|
let r = r_signed();
|
2022-03-01 20:02:59 -05:00
|
|
|
|
|
|
|
|
// We need a special check for overflowing Rem and Div since they are *UB*
|
|
|
|
|
// on overflow, which can happen with "int_min $OP -1".
|
|
|
|
|
if matches!(bin_op, Rem | Div) {
|
|
|
|
|
if l == size.signed_int_min() && r == -1 {
|
|
|
|
|
if bin_op == Rem {
|
|
|
|
|
throw_ub!(RemainderOverflow)
|
|
|
|
|
} else {
|
|
|
|
|
throw_ub!(DivisionOverflow)
|
|
|
|
|
}
|
2020-02-09 15:41:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-09 15:23:34 +01:00
|
|
|
|
2020-03-17 14:11:51 +01:00
|
|
|
let (result, oflo) = op(l, r);
|
2024-04-18 00:33:46 +02:00
|
|
|
// This may be out-of-bounds for the result type, so we have to truncate.
|
2020-02-09 15:23:34 +01:00
|
|
|
// If that truncation loses any information, we have an overflow.
|
2024-04-18 00:33:46 +02:00
|
|
|
let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
|
|
|
|
|
let overflow = oflo || lossy;
|
2024-05-21 12:17:34 +02:00
|
|
|
if overflow && let Some(intrinsic) = throw_ub_on_overflow {
|
|
|
|
|
throw_ub!(ArithOverflow { intrinsic });
|
2023-06-03 00:41:50 -07:00
|
|
|
}
|
2024-05-21 12:17:34 +02:00
|
|
|
let res = ImmTy::from_scalar_int(result, left.layout);
|
|
|
|
|
return Ok(if with_overflow {
|
|
|
|
|
let overflow = ImmTy::from_bool(overflow, *self.tcx);
|
|
|
|
|
ImmTy::from_pair(res, overflow, *self.tcx)
|
|
|
|
|
} else {
|
|
|
|
|
res
|
|
|
|
|
});
|
2018-02-21 22:02:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-04-18 00:33:46 +02:00
|
|
|
// From here on it's okay to treat everything as unsigned.
|
|
|
|
|
let l = l_unsigned();
|
|
|
|
|
let r = r_unsigned();
|
2017-06-04 17:26:19 -07:00
|
|
|
|
2023-03-05 20:19:41 -08:00
|
|
|
if bin_op == Cmp {
|
|
|
|
|
return Ok(self.three_way_compare(l, r));
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
Ok(match bin_op {
|
2023-09-20 21:49:30 +02:00
|
|
|
Eq => ImmTy::from_bool(l == r, *self.tcx),
|
|
|
|
|
Ne => ImmTy::from_bool(l != r, *self.tcx),
|
2018-02-21 22:02:52 +01:00
|
|
|
|
2023-09-20 21:49:30 +02:00
|
|
|
Lt => ImmTy::from_bool(l < r, *self.tcx),
|
|
|
|
|
Le => ImmTy::from_bool(l <= r, *self.tcx),
|
|
|
|
|
Gt => ImmTy::from_bool(l > r, *self.tcx),
|
|
|
|
|
Ge => ImmTy::from_bool(l >= r, *self.tcx),
|
2018-02-21 22:02:52 +01:00
|
|
|
|
2024-04-18 00:33:46 +02:00
|
|
|
BitOr => ImmTy::from_uint(l | r, left.layout),
|
|
|
|
|
BitAnd => ImmTy::from_uint(l & r, left.layout),
|
|
|
|
|
BitXor => ImmTy::from_uint(l ^ r, left.layout),
|
2018-02-21 22:02:52 +01:00
|
|
|
|
2024-05-21 12:17:34 +02:00
|
|
|
_ => {
|
2024-04-18 00:33:46 +02:00
|
|
|
assert!(!left.layout.abi.is_signed());
|
2018-02-21 22:02:52 +01:00
|
|
|
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
|
2024-05-21 12:17:34 +02:00
|
|
|
Add | AddUnchecked | AddWithOverflow => u128::overflowing_add,
|
|
|
|
|
Sub | SubUnchecked | SubWithOverflow => u128::overflowing_sub,
|
|
|
|
|
Mul | MulUnchecked | MulWithOverflow => u128::overflowing_mul,
|
2019-12-01 12:08:05 +01:00
|
|
|
Div if r == 0 => throw_ub!(DivisionByZero),
|
|
|
|
|
Rem if r == 0 => throw_ub!(RemainderByZero),
|
2018-02-21 22:02:52 +01:00
|
|
|
Div => u128::overflowing_div,
|
|
|
|
|
Rem => u128::overflowing_rem,
|
2024-05-21 12:17:34 +02:00
|
|
|
_ => span_bug!(
|
|
|
|
|
self.cur_span(),
|
|
|
|
|
"invalid binary op {:?}: {:?}, {:?} (both {})",
|
|
|
|
|
bin_op,
|
|
|
|
|
left,
|
|
|
|
|
right,
|
|
|
|
|
right.layout.ty,
|
|
|
|
|
),
|
2018-02-21 22:02:52 +01:00
|
|
|
};
|
|
|
|
|
let (result, oflo) = op(l, r);
|
2020-02-09 15:23:34 +01:00
|
|
|
// Truncate to target type.
|
|
|
|
|
// If that truncation loses any information, we have an overflow.
|
2024-04-18 00:33:46 +02:00
|
|
|
let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
|
|
|
|
|
let overflow = oflo || lossy;
|
2024-05-21 12:17:34 +02:00
|
|
|
if overflow && let Some(intrinsic) = throw_ub_on_overflow {
|
|
|
|
|
throw_ub!(ArithOverflow { intrinsic });
|
|
|
|
|
}
|
|
|
|
|
let res = ImmTy::from_scalar_int(result, left.layout);
|
|
|
|
|
if with_overflow {
|
|
|
|
|
let overflow = ImmTy::from_bool(overflow, *self.tcx);
|
|
|
|
|
ImmTy::from_pair(res, overflow, *self.tcx)
|
|
|
|
|
} else {
|
|
|
|
|
res
|
2023-06-03 00:41:50 -07:00
|
|
|
}
|
2018-02-21 22:02:52 +01:00
|
|
|
}
|
2024-05-21 12:17:34 +02:00
|
|
|
})
|
2017-06-04 17:26:19 -07:00
|
|
|
}
|
2016-10-20 04:42:19 -06:00
|
|
|
|
2023-04-28 13:35:50 +02:00
|
|
|
fn binary_ptr_op(
|
|
|
|
|
&self,
|
|
|
|
|
bin_op: mir::BinOp,
|
|
|
|
|
left: &ImmTy<'tcx, M::Provenance>,
|
|
|
|
|
right: &ImmTy<'tcx, M::Provenance>,
|
2024-05-21 12:17:34 +02:00
|
|
|
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
2023-04-28 13:35:50 +02:00
|
|
|
use rustc_middle::mir::BinOp::*;
|
|
|
|
|
|
|
|
|
|
match bin_op {
|
|
|
|
|
// Pointer ops that are always supported.
|
|
|
|
|
Offset => {
|
|
|
|
|
let ptr = left.to_scalar().to_pointer(self)?;
|
2024-05-09 22:45:14 -04:00
|
|
|
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
|
2024-08-01 14:38:58 +02:00
|
|
|
let pointee_layout = self.layout_of(pointee_ty)?;
|
|
|
|
|
assert!(pointee_layout.abi.is_sized());
|
2023-04-28 13:35:50 +02:00
|
|
|
|
2023-08-01 17:11:00 +02:00
|
|
|
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
2024-08-01 14:38:58 +02:00
|
|
|
let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap();
|
|
|
|
|
let pointee_size = ImmTy::from_int(pointee_size, right.layout);
|
|
|
|
|
// Multiply element size and element count.
|
|
|
|
|
let (val, overflowed) = self
|
|
|
|
|
.binary_op(mir::BinOp::MulWithOverflow, right, &pointee_size)?
|
|
|
|
|
.to_scalar_pair();
|
|
|
|
|
// This must not overflow.
|
|
|
|
|
if overflowed.to_bool()? {
|
|
|
|
|
throw_ub!(PointerArithOverflow)
|
|
|
|
|
}
|
2023-08-01 17:11:00 +02:00
|
|
|
|
2024-08-01 14:38:58 +02:00
|
|
|
let offset_bytes = val.to_target_isize(self)?;
|
2023-08-01 17:11:00 +02:00
|
|
|
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
|
2024-05-21 12:17:34 +02:00
|
|
|
Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout))
|
2023-04-28 13:35:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fall back to machine hook so Miri can support more pointer ops.
|
|
|
|
|
_ => M::binary_ptr_op(self, bin_op, left, right),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-22 09:00:07 +02:00
|
|
|
/// Returns the result of the specified operation.
|
|
|
|
|
///
|
|
|
|
|
/// Whether this produces a scalar or a pair depends on the specific `bin_op`.
|
2024-05-21 12:17:34 +02:00
|
|
|
pub fn binary_op(
|
2018-08-28 01:14:29 +02:00
|
|
|
&self,
|
|
|
|
|
bin_op: mir::BinOp,
|
2022-07-18 18:47:31 -04:00
|
|
|
left: &ImmTy<'tcx, M::Provenance>,
|
|
|
|
|
right: &ImmTy<'tcx, M::Provenance>,
|
2024-05-21 12:17:34 +02:00
|
|
|
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
2018-08-26 14:22:59 +02:00
|
|
|
trace!(
|
2023-09-20 22:25:09 +02:00
|
|
|
"Running binary op {:?}: {:?} ({}), {:?} ({})",
|
2024-07-22 22:06:16 -04:00
|
|
|
bin_op, *left, left.layout.ty, *right, right.layout.ty
|
2019-02-08 14:00:52 +01:00
|
|
|
);
|
2018-08-26 14:22:59 +02:00
|
|
|
|
2020-08-03 00:49:11 +02:00
|
|
|
match left.layout.ty.kind() {
|
2018-08-26 14:22:59 +02:00
|
|
|
ty::Char => {
|
2019-02-08 14:00:52 +01:00
|
|
|
assert_eq!(left.layout.ty, right.layout.ty);
|
2022-08-01 19:05:20 -04:00
|
|
|
let left = left.to_scalar();
|
|
|
|
|
let right = right.to_scalar();
|
2019-06-09 00:41:20 +02:00
|
|
|
Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
|
|
|
|
ty::Bool => {
|
2019-02-08 14:00:52 +01:00
|
|
|
assert_eq!(left.layout.ty, right.layout.ty);
|
2022-08-01 19:05:20 -04:00
|
|
|
let left = left.to_scalar();
|
|
|
|
|
let right = right.to_scalar();
|
2019-06-09 00:41:20 +02:00
|
|
|
Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
|
|
|
|
ty::Float(fty) => {
|
2019-02-08 14:00:52 +01:00
|
|
|
assert_eq!(left.layout.ty, right.layout.ty);
|
2023-09-20 21:49:30 +02:00
|
|
|
let layout = left.layout;
|
2022-08-01 19:05:20 -04:00
|
|
|
let left = left.to_scalar();
|
|
|
|
|
let right = right.to_scalar();
|
2019-06-09 00:41:20 +02:00
|
|
|
Ok(match fty {
|
2024-06-13 08:11:16 -05:00
|
|
|
FloatTy::F16 => {
|
|
|
|
|
self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
|
|
|
|
|
}
|
2019-08-10 19:40:56 +02:00
|
|
|
FloatTy::F32 => {
|
2023-09-20 21:49:30 +02:00
|
|
|
self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-08-10 19:40:56 +02:00
|
|
|
FloatTy::F64 => {
|
2023-09-20 21:49:30 +02:00
|
|
|
self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2024-06-13 08:11:16 -05:00
|
|
|
FloatTy::F128 => {
|
|
|
|
|
self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?)
|
|
|
|
|
}
|
2019-06-09 00:41:20 +02:00
|
|
|
})
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
2019-07-24 16:08:50 +02:00
|
|
|
_ if left.layout.ty.is_integral() => {
|
|
|
|
|
// the RHS type can be different, e.g. for shifts -- but it has to be integral, too
|
2019-06-09 12:31:19 +02:00
|
|
|
assert!(
|
2019-07-24 16:08:50 +02:00
|
|
|
right.layout.ty.is_integral(),
|
2023-09-20 22:25:09 +02:00
|
|
|
"Unexpected types for BinOp: {} {:?} {}",
|
2019-07-24 16:08:50 +02:00
|
|
|
left.layout.ty,
|
|
|
|
|
bin_op,
|
|
|
|
|
right.layout.ty
|
|
|
|
|
);
|
2018-08-26 14:22:59 +02:00
|
|
|
|
2024-04-18 00:33:46 +02:00
|
|
|
self.binary_int_op(bin_op, left, right)
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
2019-07-25 01:02:41 +02:00
|
|
|
_ if left.layout.ty.is_any_ptr() => {
|
2021-12-14 00:15:50 +03:00
|
|
|
// The RHS type must be a `pointer` *or an integer type* (for `Offset`).
|
2021-12-14 19:29:29 +03:00
|
|
|
// (Even when both sides are pointers, their type might differ, see issue #91636)
|
2019-07-24 16:08:50 +02:00
|
|
|
assert!(
|
2021-12-13 12:59:31 +03:00
|
|
|
right.layout.ty.is_any_ptr() || right.layout.ty.is_integral(),
|
2023-09-20 22:25:09 +02:00
|
|
|
"Unexpected types for BinOp: {} {:?} {}",
|
2019-07-24 16:08:50 +02:00
|
|
|
left.layout.ty,
|
|
|
|
|
bin_op,
|
|
|
|
|
right.layout.ty
|
|
|
|
|
);
|
|
|
|
|
|
2023-04-28 13:35:50 +02:00
|
|
|
self.binary_ptr_op(bin_op, left, right)
|
2019-07-24 16:08:50 +02:00
|
|
|
}
|
2020-06-21 16:13:31 +02:00
|
|
|
_ => span_bug!(
|
|
|
|
|
self.cur_span(),
|
2023-09-20 22:25:09 +02:00
|
|
|
"Invalid MIR: bad LHS type for binop: {}",
|
2020-06-21 16:13:31 +02:00
|
|
|
left.layout.ty
|
|
|
|
|
),
|
2018-08-26 14:22:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 23:27:29 +01:00
|
|
|
/// Returns the result of the specified operation, whether it overflowed, and
|
|
|
|
|
/// the result type.
|
2024-05-21 12:17:34 +02:00
|
|
|
pub fn unary_op(
|
2018-02-21 22:02:52 +01:00
|
|
|
&self,
|
|
|
|
|
un_op: mir::UnOp,
|
2022-07-18 18:47:31 -04:00
|
|
|
val: &ImmTy<'tcx, M::Provenance>,
|
2024-05-21 12:17:34 +02:00
|
|
|
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::UnOp::*;
|
2016-12-17 03:09:57 -08:00
|
|
|
|
2019-02-08 14:00:52 +01:00
|
|
|
let layout = val.layout;
|
2023-09-20 22:25:09 +02:00
|
|
|
trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty);
|
2016-10-20 04:42:19 -06:00
|
|
|
|
2020-08-03 00:49:11 +02:00
|
|
|
match layout.ty.kind() {
|
2018-08-26 15:13:01 +02:00
|
|
|
ty::Bool => {
|
2024-04-21 16:11:01 -07:00
|
|
|
let val = val.to_scalar();
|
2018-08-26 15:13:01 +02:00
|
|
|
let val = val.to_bool()?;
|
|
|
|
|
let res = match un_op {
|
|
|
|
|
Not => !val,
|
2020-06-21 16:13:31 +02:00
|
|
|
_ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op),
|
2018-08-26 15:13:01 +02:00
|
|
|
};
|
2024-05-21 12:17:34 +02:00
|
|
|
Ok(ImmTy::from_bool(res, *self.tcx))
|
2018-08-26 15:13:01 +02:00
|
|
|
}
|
|
|
|
|
ty::Float(fty) => {
|
2024-04-21 16:11:01 -07:00
|
|
|
let val = val.to_scalar();
|
2024-06-13 08:29:38 -05:00
|
|
|
if un_op != Neg {
|
|
|
|
|
span_bug!(self.cur_span(), "Invalid float op {:?}", un_op);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-08 20:36:25 +02:00
|
|
|
// No NaN adjustment here, `-` is a bitwise operation!
|
2024-06-13 08:29:38 -05:00
|
|
|
let res = match fty {
|
|
|
|
|
FloatTy::F16 => Scalar::from_f16(-val.to_f16()?),
|
|
|
|
|
FloatTy::F32 => Scalar::from_f32(-val.to_f32()?),
|
|
|
|
|
FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
|
|
|
|
|
FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
|
2018-08-26 15:13:01 +02:00
|
|
|
};
|
2024-05-21 12:17:34 +02:00
|
|
|
Ok(ImmTy::from_scalar(res, layout))
|
2018-08-26 15:13:01 +02:00
|
|
|
}
|
2024-06-11 12:12:21 +02:00
|
|
|
ty::Int(..) => {
|
|
|
|
|
let val = val.to_scalar().to_int(layout.size)?;
|
2024-05-21 12:17:34 +02:00
|
|
|
let res = match un_op {
|
2024-06-11 12:12:21 +02:00
|
|
|
Not => !val,
|
|
|
|
|
Neg => val.wrapping_neg(),
|
2024-04-21 16:11:01 -07:00
|
|
|
_ => span_bug!(self.cur_span(), "Invalid integer op {:?}", un_op),
|
2018-08-26 15:13:01 +02:00
|
|
|
};
|
2024-06-11 12:12:21 +02:00
|
|
|
let res = ScalarInt::truncate_from_int(res, layout.size).0;
|
|
|
|
|
Ok(ImmTy::from_scalar(res.into(), layout))
|
|
|
|
|
}
|
|
|
|
|
ty::Uint(..) => {
|
|
|
|
|
let val = val.to_scalar().to_uint(layout.size)?;
|
|
|
|
|
let res = match un_op {
|
|
|
|
|
Not => !val,
|
|
|
|
|
_ => span_bug!(self.cur_span(), "Invalid unsigned integer op {:?}", un_op),
|
|
|
|
|
};
|
|
|
|
|
let res = ScalarInt::truncate_from_uint(res, layout.size).0;
|
|
|
|
|
Ok(ImmTy::from_scalar(res.into(), layout))
|
2018-08-26 15:13:01 +02:00
|
|
|
}
|
2024-06-19 22:14:31 -07:00
|
|
|
ty::RawPtr(..) | ty::Ref(..) => {
|
2024-04-21 16:11:01 -07:00
|
|
|
assert_eq!(un_op, PtrMetadata);
|
|
|
|
|
let (_, meta) = val.to_scalar_and_meta();
|
|
|
|
|
Ok(match meta {
|
|
|
|
|
MemPlaceMeta::Meta(scalar) => {
|
|
|
|
|
let ty = un_op.ty(*self.tcx, val.layout.ty);
|
|
|
|
|
let layout = self.layout_of(ty)?;
|
|
|
|
|
ImmTy::from_scalar(scalar, layout)
|
|
|
|
|
}
|
|
|
|
|
MemPlaceMeta::None => {
|
|
|
|
|
let unit_layout = self.layout_of(self.tcx.types.unit)?;
|
|
|
|
|
ImmTy::uninit(unit_layout)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
bug!("Unexpected unary op argument {val:?}")
|
|
|
|
|
}
|
2018-08-26 15:13:01 +02:00
|
|
|
}
|
2018-02-21 22:02:52 +01:00
|
|
|
}
|
2016-03-13 01:43:28 -06:00
|
|
|
}
|