move pointer truncation to a common method in memory.rs

This commit is contained in:
Ralf Jung 2017-07-22 11:28:14 -07:00
parent 40950b2cd1
commit 4d38f8dffb
6 changed files with 113 additions and 70 deletions

View file

@ -4,7 +4,7 @@ use syntax::ast::{FloatTy, IntTy, UintTy};
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 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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))),
}

View file

@ -1226,8 +1226,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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(())
}

View file

@ -7,7 +7,7 @@ use rustc::ty::layout::{self, TargetDataLayout};
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 @@ impl MemoryPointer {
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 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
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<F, T>(&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
}
}

View file

@ -814,8 +814,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?;
}

View file

@ -57,14 +57,15 @@ impl<'a, 'tcx> EvalContext<'a, '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)?;
}
}

View file

@ -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 @@ impl<'tcx> Pointer {
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 @@ impl<'tcx> PrimVal {
}
}
// 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::*;