miri: equip unary_op with overflow detection

This commit is contained in:
Ralf Jung 2020-02-08 23:27:29 +01:00
parent 28f85c6ffa
commit 7d2f6ae001

View file

@ -342,7 +342,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
/// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows.
/// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows.
#[inline]
pub fn binary_op(
&self,
@ -354,11 +354,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
}
pub fn unary_op(
/// Returns the result of the specified operation, whether it overflowed, and
/// the result type.
pub fn overflowing_unary_op(
&self,
un_op: mir::UnOp,
val: ImmTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> {
use rustc::mir::UnOp::*;
let layout = val.layout;
@ -372,7 +374,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Not => !val,
_ => bug!("Invalid bool op {:?}", un_op),
};
Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?))
Ok((Scalar::from_bool(res), false, self.tcx.types.bool))
}
ty::Float(fty) => {
let res = match (un_op, fty) {
@ -380,21 +382,37 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
_ => bug!("Invalid float op {:?}", un_op),
};
Ok(ImmTy::from_scalar(res, layout))
Ok((res, false, layout.ty))
}
_ => {
assert!(layout.ty.is_integral());
let val = self.force_bits(val, layout.size)?;
let res = match un_op {
Not => !val,
let (res, overflow) = match un_op {
Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate
Neg => {
// arithmetic negation
assert!(layout.abi.is_signed());
(-(val as i128)) as u128
let val = self.sign_extend(val, layout) as i128;
let (res, overflow) = val.overflowing_neg();
let res = res as u128;
// Truncate to target type.
// If that truncation loses any information, we have an overflow.
let truncated = self.truncate(res, layout);
(truncated, overflow || self.sign_extend(truncated, layout) != res)
}
};
// res needs tuncating
Ok(ImmTy::from_uint(self.truncate(res, layout), layout))
Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty))
}
}
}
pub fn unary_op(
&self,
un_op: mir::UnOp,
val: ImmTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?;
Ok(ImmTy::from_scalar(val, self.layout_of(ty)?))
}
}