From 4d38f8dffbeca5c5f81c3c72d42d4287c0acb1fc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 22 Jul 2017 11:28:14 -0700 Subject: [PATCH] move pointer truncation to a common method in memory.rs --- src/librustc_mir/interpret/cast.rs | 4 +- src/librustc_mir/interpret/eval_context.rs | 5 +- src/librustc_mir/interpret/memory.rs | 107 ++++++++++++++++--- src/librustc_mir/interpret/terminator/mod.rs | 3 +- src/librustc_mir/interpret/traits.rs | 7 +- src/librustc_mir/interpret/value.rs | 57 ++-------- 6 files changed, 113 insertions(+), 70 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index f33fe3ac889..aff358f815c 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -4,7 +4,7 @@ use error::{EvalResult, EvalError}; use eval_context::EvalContext; use value::PrimVal; -use memory::MemoryPointer; +use memory::{MemoryPointer, HasDataLayout}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval( @@ -78,7 +78,7 @@ fn cast_from_int(&self, v: u128, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult TyChar => Err(EvalError::InvalidChar(v)), // No alignment check needed for raw pointers. But we have to truncate to target ptr size. - TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1u128 << self.memory.layout.pointer_size.bits()))), + TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 49de56fa5b9..3c38f318e4a 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -1226,8 +1226,9 @@ pub(super) fn write_pair_to_ptr( let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?.into(), a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?.into(), b, field_1_size)?; + let layout = self.memory.layout; + self.memory.write_primval(ptr.offset(field_0, layout)?.into(), a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1, layout)?.into(), b, field_1_size)?; Ok(()) } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 481744b11c4..00b29d26e97 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -7,7 +7,7 @@ use syntax::ast::Mutability; use error::{EvalError, EvalResult}; -use value::{PrimVal, self, Pointer}; +use value::{PrimVal, Pointer}; use eval_context::EvalContext; //////////////////////////////////////////////////////////////////////////////// @@ -73,26 +73,26 @@ pub fn new(alloc_id: AllocId, offset: u64) -> Self { MemoryPointer { alloc_id, offset } } - pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { - MemoryPointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) + pub(crate) fn wrapping_signed_offset<'a, L: HasDataLayout<'a>>(self, i: i64, l: L) -> Self { + MemoryPointer::new(self.alloc_id, l.wrapping_signed_offset(self.offset, i)) } - pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) { - let (res, over) = value::overflowing_signed_offset(self.offset, i, layout); + pub(crate) fn overflowing_signed_offset<'a, L: HasDataLayout<'a>>(self, i: i128, l: L) -> (Self, bool) { + let (res, over) = l.overflowing_signed_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } - pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(MemoryPointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) + pub(crate) fn signed_offset<'a, 'tcx, L: HasDataLayout<'a>>(self, i: i64, l: L) -> EvalResult<'tcx, Self> { + Ok(MemoryPointer::new(self.alloc_id, l.signed_offset(self.offset, i)?)) } - pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) { - let (res, over) = value::overflowing_offset(self.offset, i, layout); + pub(crate) fn overflowing_offset<'a, L: HasDataLayout<'a>>(self, i: u64, l: L) -> (Self, bool) { + let (res, over) = l.overflowing_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } - pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { - Ok(MemoryPointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) + pub(crate) fn offset<'a, 'tcx, L: HasDataLayout<'a>>(self, i: u64, l: L) -> EvalResult<'tcx, Self> { + Ok(MemoryPointer::new(self.alloc_id, l.offset(self.offset, i)?)) } } @@ -540,7 +540,7 @@ fn get_bytes_unchecked(&self, ptr: MemoryPointer, size: u64, align: u64) -> Eval if size == 0 { return Ok(&[]); } - self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -1131,6 +1131,7 @@ fn bit_index(bits: u64) -> (usize, usize) { pub(crate) trait HasMemory<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx>; + fn memory(&self) -> &Memory<'a, 'tcx>; // These are not supposed to be overriden. fn read_maybe_aligned(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> @@ -1159,6 +1160,11 @@ impl<'a, 'tcx> HasMemory<'a, 'tcx> for Memory<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { self } + + #[inline] + fn memory(&self) -> &Memory<'a, 'tcx> { + self + } } impl<'a, 'tcx> HasMemory<'a, 'tcx> for EvalContext<'a, 'tcx> { @@ -1166,4 +1172,81 @@ impl<'a, 'tcx> HasMemory<'a, 'tcx> for EvalContext<'a, 'tcx> { fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx> { &mut self.memory } + + #[inline] + fn memory(&self) -> &Memory<'a, 'tcx> { + &self.memory + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Pointer arithmetic +//////////////////////////////////////////////////////////////////////////////// + +pub(crate) trait HasDataLayout<'a> : Copy { + fn data_layout(self) -> &'a TargetDataLayout; + + // These are not supposed to be overriden. + + //// Trunace the given value to the pointer size; also return whether there was an overflow + fn truncate_to_ptr(self, val: u128) -> (u64, bool) { + let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits(); + ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) + } + + // Overflow checking only works properly on the range from -u64 to +u64. + fn overflowing_signed_offset(self, val: u64, i: i128) -> (u64, bool) { + // FIXME: is it possible to over/underflow here? + if i < 0 { + // trickery to ensure that i64::min_value() works fine + // this formula only works for true negative values, it panics for zero! + let n = u64::max_value() - (i as u64) + 1; + val.overflowing_sub(n) + } else { + self.overflowing_offset(val, i as u64) + } + } + + fn overflowing_offset(self, val: u64, i: u64) -> (u64, bool) { + let (res, over1) = val.overflowing_add(i); + let (res, over2) = self.truncate_to_ptr(res as u128); + (res, over1 || over2) + } + + fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_signed_offset(val, i as i128); + if over { + Err(EvalError::OverflowingMath) + } else { + Ok(res) + } + } + + fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> { + let (res, over) = self.overflowing_offset(val, i); + if over { + Err(EvalError::OverflowingMath) + } else { + Ok(res) + } + } + + fn wrapping_signed_offset(self, val: u64, i: i64) -> u64 { + self.overflowing_signed_offset(val, i as i128).0 + } +} + +impl<'a> HasDataLayout<'a> for &'a TargetDataLayout { + #[inline] + fn data_layout(self) -> &'a TargetDataLayout { + self + } +} + +impl<'a, 'b, 'tcx, T> HasDataLayout<'a> for &'b T + where T: HasMemory<'a, 'tcx> { + #[inline] + fn data_layout(self) -> &'a TargetDataLayout { + self.memory().layout + } } diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 73511c38341..2db79b03d00 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -814,8 +814,9 @@ fn call_c_abi( if let Some((name, value)) = new { // +1 for the null terminator let value_copy = self.memory.allocate((value.len() + 1) as u64, 1, Kind::Env)?; + let layout = self.memory.layout; self.memory.write_bytes(value_copy.into(), &value)?; - self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?.into(), &[0])?; + self.memory.write_bytes(value_copy.offset(value.len() as u64, layout)?.into(), &[0])?; if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) { self.memory.deallocate(var, None, Kind::Env)?; } diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 615ea214595..98059afb9e9 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -57,14 +57,15 @@ pub fn get_vtable(&mut self, ty: Ty<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; - self.memory.write_usize(vtable.offset(ptr_size, self.memory.layout)?, size)?; - self.memory.write_usize(vtable.offset(ptr_size * 2, self.memory.layout)?, align)?; + let layout = self.memory.layout; + self.memory.write_usize(vtable.offset(ptr_size, layout)?, size)?; + self.memory.write_usize(vtable.offset(ptr_size * 2, layout)?, align)?; for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { let instance = eval_context::resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), layout)?, fn_ptr)?; } } diff --git a/src/librustc_mir/interpret/value.rs b/src/librustc_mir/interpret/value.rs index 606a3516485..7420ae1256a 100644 --- a/src/librustc_mir/interpret/value.rs +++ b/src/librustc_mir/interpret/value.rs @@ -1,10 +1,8 @@ #![allow(unknown_lints)] #![allow(float_cmp)] -use rustc::ty::layout::TargetDataLayout; - use error::{EvalError, EvalResult}; -use memory::{Memory, MemoryPointer, HasMemory}; +use memory::{Memory, MemoryPointer, HasMemory, HasDataLayout}; pub(super) fn bytes_to_f32(bytes: u128) -> f32 { f32::from_bits(bytes as u32) @@ -61,33 +59,33 @@ pub fn into_inner_primval(self) -> PrimVal { self.primval } - pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn signed_offset<'a, L: HasDataLayout<'a>>(self, i: i64, layout: L) -> EvalResult<'tcx, Self> { match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.signed_offset(b as u64, i)? as u128))) }, PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(Pointer::from), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } - pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn offset<'a, L: HasDataLayout<'a>>(self, i: u64, layout: L) -> EvalResult<'tcx, Self> { match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(offset(b as u64, i, layout)? as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.offset(b as u64, i)? as u128))) }, PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(Pointer::from), PrimVal::Undef => Err(EvalError::ReadUndefBytes), } } - pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn wrapping_signed_offset<'a, L: HasDataLayout<'a>>(self, i: i64, layout: L) -> EvalResult<'tcx, Self> { match self.primval { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); - Ok(Pointer::from(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128))) + Ok(Pointer::from(PrimVal::Bytes(layout.wrapping_signed_offset(b as u64, i) as u128))) }, PrimVal::Ptr(ptr) => Ok(Pointer::from(ptr.wrapping_signed_offset(i, layout))), PrimVal::Undef => Err(EvalError::ReadUndefBytes), @@ -323,47 +321,6 @@ pub fn to_bool(self) -> EvalResult<'tcx, bool> { } } -// Overflow checking only works properly on the range from -u64 to +u64. -pub fn overflowing_signed_offset<'tcx>(val: u64, i: i128, layout: &TargetDataLayout) -> (u64, bool) { - // FIXME: is it possible to over/underflow here? - if i < 0 { - // trickery to ensure that i64::min_value() works fine - // this formula only works for true negative values, it panics for zero! - let n = u64::max_value() - (i as u64) + 1; - val.overflowing_sub(n) - } else { - overflowing_offset(val, i as u64, layout) - } -} - -pub fn overflowing_offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> (u64, bool) { - let (res, over) = val.overflowing_add(i); - ((res as u128 % (1u128 << layout.pointer_size.bits())) as u64, - over || res as u128 >= (1u128 << layout.pointer_size.bits())) -} - -pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let (res, over) = overflowing_signed_offset(val, i as i128, layout); - if over { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } -} - -pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let (res, over) = overflowing_offset(val, i, layout); - if over { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } -} - -pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 { - overflowing_signed_offset(val, i as i128, layout).0 -} - impl PrimValKind { pub fn is_int(self) -> bool { use self::PrimValKind::*; -- GitLab