Implement pointer primvals and comparison ops on them.
This commit is contained in:
parent
81f49ed1c0
commit
dbc9913b7d
4 changed files with 116 additions and 44 deletions
11
src/error.rs
11
src/error.rs
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
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::*;
|
||||
use self::PrimVal::*;
|
||||
match bin_op {
|
||||
Add => $v($l + $r),
|
||||
Sub => $v($l - $r),
|
||||
Mul => $v($l * $r),
|
||||
Div => $v($l / $r),
|
||||
Rem => $v($l % $r),
|
||||
Add => $v($l + $r),
|
||||
Sub => $v($l - $r),
|
||||
Mul => $v($l * $r),
|
||||
Div => $v($l / $r),
|
||||
Rem => $v($l % $r),
|
||||
BitXor => $v($l ^ $r),
|
||||
BitAnd => $v($l & $r),
|
||||
BitOr => $v($l | $r),
|
||||
BitOr => $v($l | $r),
|
||||
|
||||
// TODO(tsion): Can have differently-typed RHS.
|
||||
Shl => $v($l << $r),
|
||||
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue