refactor pointer arithmetic handling

This commit is contained in:
Ralf Jung
2017-06-21 22:59:47 -07:00
parent 6eafb10b87
commit 894306e47d
+29 -28
View File
@@ -3,6 +3,7 @@
use error::{EvalError, EvalResult};
use eval_context::EvalContext;
use memory::Pointer;
use lvalue::Lvalue;
use value::{
PrimVal,
@@ -130,19 +131,6 @@ macro_rules! f64_arithmetic {
)
}
macro_rules! ptr_add {
($signed:expr, $ptr:expr, $int:expr, $layout:expr) => ({
let ptr = $ptr;
let int = $int;
let (res, over) = if $signed {
ptr.overflowing_signed_offset(int as i128, $layout)
} else {
ptr.overflowing_offset(int as u64, $layout)
};
(PrimVal::Ptr(res), over)
})
}
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op(
@@ -202,27 +190,19 @@ pub fn binary_op(
}
}
// These work if one operand is a pointer, the other an integer
Sub
Add | Sub
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
let left = left.to_ptr()?;
let right = right.to_bytes()? as i128; // this cast is fine as the kind is max. 64bit
let (res, over) = left.overflowing_signed_offset(-right, self.memory.layout);
return Ok((PrimVal::Ptr(res), over))
}
Add
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
let left = left.to_ptr()?;
let right = right.to_bytes()?;
return Ok(ptr_add!(left_kind == isize, left, right, self.memory.layout));
// Cast to i128 is fine as we checked the kind to be ptr-sized
let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?;
return Ok((PrimVal::Ptr(res), over));
}
Add
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_bytes() && right.is_ptr() => {
let left = left.to_bytes()?;
let right = right.to_ptr()?;
return Ok(ptr_add!(left_kind == isize, right, left, self.memory.layout));
// This is a commutative operation, just swap the operands
let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?;
return Ok((PrimVal::Ptr(res), over));
}
_ => {}
}
@@ -300,6 +280,27 @@ pub fn binary_op(
Ok((val, false))
}
fn ptr_int_arithmetic(
&self,
bin_op: mir::BinOp,
left: Pointer,
right: i128,
signed: bool,
) -> EvalResult<'tcx, (Pointer, bool)> {
use rustc::mir::BinOp::*;
Ok(match bin_op {
Sub =>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
left.overflowing_signed_offset(-right, self.memory.layout),
Add if signed =>
left.overflowing_signed_offset(right, self.memory.layout),
Add if !signed =>
left.overflowing_offset(right as u64, self.memory.layout),
_ => bug!("ptr_int_arithmetic called on unsupported operation")
})
}
}
pub fn unary_op<'tcx>(