From 894306e47d1bf5d7c4328d762e0e2c94dbeea2fe Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 22:59:47 -0700 Subject: [PATCH] refactor pointer arithmetic handling --- src/operator.rs | 57 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index adcd237e819f..7cba12594f9b 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -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>(