1
Fork 0

Implement pointer primvals and comparison ops on them.

This commit is contained in:
Scott Olson 2016-03-18 23:03:46 -06:00
parent 81f49ed1c0
commit dbc9913b7d
4 changed files with 116 additions and 44 deletions

View file

@ -6,8 +6,9 @@ pub enum EvalError {
DanglingPointerDeref,
InvalidBool,
PointerOutOfBounds,
InvalidPointerAccess,
ReadPointerAsBytes,
ReadBytesAsPointer,
InvalidPointerMath,
}
pub type EvalResult<T> = Result<T, EvalError>;
@ -18,10 +19,12 @@ impl Error for EvalError {
EvalError::DanglingPointerDeref => "dangling pointer was dereferenced",
EvalError::InvalidBool => "invalid boolean value read",
EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation",
EvalError::InvalidPointerAccess =>
"a raw memory access tried to access part of a pointer value as bytes",
EvalError::ReadPointerAsBytes =>
"a raw memory access tried to access part of a pointer value as raw bytes",
EvalError::ReadBytesAsPointer =>
"attempted to read some raw bytes as a pointer address",
"attempted to interpret some raw bytes as a pointer address",
EvalError::InvalidPointerMath =>
"attempted to do math or a comparison on pointers into different allocations",
}
}

View file

@ -16,9 +16,9 @@ use std::rc::Rc;
use syntax::ast;
use syntax::codemap::DUMMY_SP;
use error::EvalResult;
use error::{EvalError, EvalResult};
use memory::{self, FieldRepr, Memory, Pointer, Repr};
use primval;
use primval::{self, PrimVal};
const TRACE_EXECUTION: bool = true;
@ -404,19 +404,20 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
BinaryOp(bin_op, ref left, ref right) => {
let left_ptr = try!(self.eval_operand(left));
let left_ty = self.operand_ty(left);
let left_val = try!(self.memory.read_primval(left_ptr, left_ty));
let left_val = try!(self.read_primval(left_ptr, left_ty));
let right_ptr = try!(self.eval_operand(right));
let right_ty = self.operand_ty(right);
let right_val = try!(self.memory.read_primval(right_ptr, right_ty));
let right_val = try!(self.read_primval(right_ptr, right_ty));
self.memory.write_primval(dest, primval::binary_op(bin_op, left_val, right_val))
let val = try!(primval::binary_op(bin_op, left_val, right_val));
self.memory.write_primval(dest, val)
}
UnaryOp(un_op, ref operand) => {
let ptr = try!(self.eval_operand(operand));
let ty = self.operand_ty(operand);
let val = try!(self.memory.read_primval(ptr, ty));
let val = try!(self.read_primval(ptr, ty));
self.memory.write_primval(dest, primval::unary_op(un_op, val))
}
@ -627,6 +628,10 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
infer::normalize_associated_type(self.tcx, &substituted)
}
fn type_is_sized(&self, ty: ty::Ty<'tcx>) -> bool {
ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP)
}
fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize {
self.ty_to_repr(ty).size()
}
@ -671,7 +676,7 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
ty::TyBox(ty) => {
if ty.is_sized(&self.tcx.empty_parameter_environment(), DUMMY_SP) {
if self.type_is_sized(ty) {
Repr::Primitive { size: self.memory.pointer_size }
} else {
Repr::Primitive { size: self.memory.pointer_size * 2 }
@ -725,6 +730,46 @@ impl<'a, 'tcx: 'a, 'arena> Interpreter<'a, 'tcx, 'arena> {
}
pub fn read_primval(&mut self, ptr: Pointer, ty: ty::Ty<'tcx>) -> EvalResult<PrimVal> {
use syntax::ast::{IntTy, UintTy};
let val = match ty.sty {
ty::TyBool => PrimVal::Bool(try!(self.memory.read_bool(ptr))),
ty::TyInt(IntTy::I8) => PrimVal::I8(try!(self.memory.read_int(ptr, 1)) as i8),
ty::TyInt(IntTy::I16) => PrimVal::I16(try!(self.memory.read_int(ptr, 2)) as i16),
ty::TyInt(IntTy::I32) => PrimVal::I32(try!(self.memory.read_int(ptr, 4)) as i32),
ty::TyInt(IntTy::I64) => PrimVal::I64(try!(self.memory.read_int(ptr, 8)) as i64),
ty::TyUint(UintTy::U8) => PrimVal::U8(try!(self.memory.read_uint(ptr, 1)) as u8),
ty::TyUint(UintTy::U16) => PrimVal::U16(try!(self.memory.read_uint(ptr, 2)) as u16),
ty::TyUint(UintTy::U32) => PrimVal::U32(try!(self.memory.read_uint(ptr, 4)) as u32),
ty::TyUint(UintTy::U64) => PrimVal::U64(try!(self.memory.read_uint(ptr, 8)) as u64),
// TODO(tsion): Pick the PrimVal dynamically.
ty::TyInt(IntTy::Is) =>
PrimVal::I64(try!(self.memory.read_int(ptr, self.memory.pointer_size))),
ty::TyUint(UintTy::Us) =>
PrimVal::U64(try!(self.memory.read_uint(ptr, self.memory.pointer_size))),
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
if self.type_is_sized(ty) {
match self.memory.read_ptr(ptr) {
Ok(p) => PrimVal::AbstractPtr(p),
Err(EvalError::ReadBytesAsPointer) => {
let n = try!(self.memory.read_uint(ptr, self.memory.pointer_size));
PrimVal::IntegerPtr(n)
}
Err(e) => return Err(e),
}
} else {
panic!("unimplemented: primitive read of fat pointer type: {:?}", ty);
}
}
_ => panic!("primitive read of non-primitive type: {:?}", ty),
};
Ok(val)
}
fn current_frame(&self) -> &Frame<'a, 'tcx> {
self.stack.last().expect("no call frames exist")
}

View file

@ -1,5 +1,4 @@
use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt};
use rustc::middle::ty;
use std::collections::{BTreeMap, HashMap};
use std::collections::Bound::{Included, Excluded};
use std::mem;
@ -169,26 +168,6 @@ impl Memory {
Ok(())
}
pub fn read_primval(&self, ptr: Pointer, ty: ty::Ty) -> EvalResult<PrimVal> {
use syntax::ast::{IntTy, UintTy};
match ty.sty {
ty::TyBool => self.read_bool(ptr).map(PrimVal::Bool),
ty::TyInt(IntTy::I8) => self.read_int(ptr, 1).map(|n| PrimVal::I8(n as i8)),
ty::TyInt(IntTy::I16) => self.read_int(ptr, 2).map(|n| PrimVal::I16(n as i16)),
ty::TyInt(IntTy::I32) => self.read_int(ptr, 4).map(|n| PrimVal::I32(n as i32)),
ty::TyInt(IntTy::I64) => self.read_int(ptr, 8).map(|n| PrimVal::I64(n as i64)),
ty::TyUint(UintTy::U8) => self.read_uint(ptr, 1).map(|n| PrimVal::U8(n as u8)),
ty::TyUint(UintTy::U16) => self.read_uint(ptr, 2).map(|n| PrimVal::U16(n as u16)),
ty::TyUint(UintTy::U32) => self.read_uint(ptr, 4).map(|n| PrimVal::U32(n as u32)),
ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)),
// TODO(tsion): Pick the PrimVal dynamically.
ty::TyInt(IntTy::Is) => self.read_int(ptr, self.pointer_size).map(PrimVal::I64),
ty::TyUint(UintTy::Us) => self.read_uint(ptr, self.pointer_size).map(PrimVal::U64),
_ => panic!("primitive read of non-primitive type: {:?}", ty),
}
}
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> {
match val {
PrimVal::Bool(b) => self.write_bool(ptr, b),
@ -199,7 +178,8 @@ impl Memory {
PrimVal::U8(n) => self.write_uint(ptr, n as u64, 1),
PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2),
PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4),
PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8),
PrimVal::U64(n) | PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, 8),
PrimVal::AbstractPtr(_p) => unimplemented!(),
}
}
@ -258,7 +238,7 @@ impl Allocation {
if n == 0 {
Ok(())
} else {
Err(EvalError::InvalidPointerAccess)
Err(EvalError::ReadPointerAsBytes)
}
}
@ -267,7 +247,7 @@ impl Allocation {
if self.count_overlapping_relocations(start, end) == 0 {
Ok(())
} else {
Err(EvalError::InvalidPointerAccess)
Err(EvalError::ReadPointerAsBytes)
}
}
}

View file

@ -1,13 +1,19 @@
use rustc::mir::repr as mir;
use error::{EvalError, EvalResult};
use memory::Pointer;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimVal {
Bool(bool),
I8(i8), I16(i16), I32(i32), I64(i64),
U8(u8), U16(u16), U32(u32), U64(u64),
AbstractPtr(Pointer),
IntegerPtr(u64),
}
pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal {
pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<PrimVal> {
macro_rules! int_binops {
($v:ident, $l:ident, $r:ident) => ({
use rustc::mir::repr::BinOp::*;
@ -36,8 +42,18 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal {
})
}
fn unrelated_ptr_ops(bin_op: mir::BinOp) -> EvalResult<PrimVal> {
use rustc::mir::repr::BinOp::*;
match bin_op {
Eq => Ok(Bool(false)),
Ne => Ok(Bool(true)),
Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
_ => unimplemented!(),
}
}
use self::PrimVal::*;
match (left, right) {
let val = match (left, right) {
(I8(l), I8(r)) => int_binops!(I8, l, r),
(I16(l), I16(r)) => int_binops!(I16, l, r),
(I32(l), I32(r)) => int_binops!(I32, l, r),
@ -46,8 +62,36 @@ pub fn binary_op(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> PrimVal {
(U16(l), U16(r)) => int_binops!(U16, l, r),
(U32(l), U32(r)) => int_binops!(U32, l, r),
(U64(l), U64(r)) => int_binops!(U64, l, r),
(IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),
(AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) =>
return unrelated_ptr_ops(bin_op),
(AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => {
if l_ptr.alloc_id != r_ptr.alloc_id {
return unrelated_ptr_ops(bin_op);
}
let l = l_ptr.offset;
let r = r_ptr.offset;
use rustc::mir::repr::BinOp::*;
match bin_op {
Eq => Bool(l == r),
Ne => Bool(l != r),
Lt => Bool(l < r),
Le => Bool(l <= r),
Gt => Bool(l > r),
Ge => Bool(l >= r),
_ => unimplemented!(),
}
}
_ => unimplemented!(),
};
Ok(val)
}
pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal {