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,
|
DanglingPointerDeref,
|
||||||
InvalidBool,
|
InvalidBool,
|
||||||
PointerOutOfBounds,
|
PointerOutOfBounds,
|
||||||
InvalidPointerAccess,
|
ReadPointerAsBytes,
|
||||||
ReadBytesAsPointer,
|
ReadBytesAsPointer,
|
||||||
|
InvalidPointerMath,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EvalResult<T> = Result<T, EvalError>;
|
pub type EvalResult<T> = Result<T, EvalError>;
|
||||||
|
@ -18,10 +19,12 @@ impl Error for EvalError {
|
||||||
EvalError::DanglingPointerDeref => "dangling pointer was dereferenced",
|
EvalError::DanglingPointerDeref => "dangling pointer was dereferenced",
|
||||||
EvalError::InvalidBool => "invalid boolean value read",
|
EvalError::InvalidBool => "invalid boolean value read",
|
||||||
EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation",
|
EvalError::PointerOutOfBounds => "pointer offset outside bounds of allocation",
|
||||||
EvalError::InvalidPointerAccess =>
|
EvalError::ReadPointerAsBytes =>
|
||||||
"a raw memory access tried to access part of a pointer value as bytes",
|
"a raw memory access tried to access part of a pointer value as raw bytes",
|
||||||
EvalError::ReadBytesAsPointer =>
|
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::ast;
|
||||||
use syntax::codemap::DUMMY_SP;
|
use syntax::codemap::DUMMY_SP;
|
||||||
|
|
||||||
use error::EvalResult;
|
use error::{EvalError, EvalResult};
|
||||||
use memory::{self, FieldRepr, Memory, Pointer, Repr};
|
use memory::{self, FieldRepr, Memory, Pointer, Repr};
|
||||||
use primval;
|
use primval::{self, PrimVal};
|
||||||
|
|
||||||
const TRACE_EXECUTION: bool = true;
|
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) => {
|
BinaryOp(bin_op, ref left, ref right) => {
|
||||||
let left_ptr = try!(self.eval_operand(left));
|
let left_ptr = try!(self.eval_operand(left));
|
||||||
let left_ty = self.operand_ty(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_ptr = try!(self.eval_operand(right));
|
||||||
let right_ty = self.operand_ty(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) => {
|
UnaryOp(un_op, ref operand) => {
|
||||||
let ptr = try!(self.eval_operand(operand));
|
let ptr = try!(self.eval_operand(operand));
|
||||||
let ty = self.operand_ty(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))
|
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)
|
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 {
|
fn ty_size(&self, ty: ty::Ty<'tcx>) -> usize {
|
||||||
self.ty_to_repr(ty).size()
|
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::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||||
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
|
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) |
|
||||||
ty::TyBox(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 }
|
Repr::Primitive { size: self.memory.pointer_size }
|
||||||
} else {
|
} else {
|
||||||
Repr::Primitive { size: self.memory.pointer_size * 2 }
|
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> {
|
fn current_frame(&self) -> &Frame<'a, 'tcx> {
|
||||||
self.stack.last().expect("no call frames exist")
|
self.stack.last().expect("no call frames exist")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use rustc::middle::ty;
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::collections::Bound::{Included, Excluded};
|
use std::collections::Bound::{Included, Excluded};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -169,26 +168,6 @@ impl Memory {
|
||||||
Ok(())
|
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<()> {
|
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> {
|
||||||
match val {
|
match val {
|
||||||
PrimVal::Bool(b) => self.write_bool(ptr, b),
|
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::U8(n) => self.write_uint(ptr, n as u64, 1),
|
||||||
PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2),
|
PrimVal::U16(n) => self.write_uint(ptr, n as u64, 2),
|
||||||
PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4),
|
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 {
|
if n == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(EvalError::InvalidPointerAccess)
|
Err(EvalError::ReadPointerAsBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +247,7 @@ impl Allocation {
|
||||||
if self.count_overlapping_relocations(start, end) == 0 {
|
if self.count_overlapping_relocations(start, end) == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(EvalError::InvalidPointerAccess)
|
Err(EvalError::ReadPointerAsBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,32 @@
|
||||||
use rustc::mir::repr as mir;
|
use rustc::mir::repr as mir;
|
||||||
|
|
||||||
|
use error::{EvalError, EvalResult};
|
||||||
|
use memory::Pointer;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum PrimVal {
|
pub enum PrimVal {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
I8(i8), I16(i16), I32(i32), I64(i64),
|
I8(i8), I16(i16), I32(i32), I64(i64),
|
||||||
U8(u8), U16(u16), U32(u32), U64(u64),
|
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 {
|
macro_rules! int_binops {
|
||||||
($v:ident, $l:ident, $r:ident) => ({
|
($v:ident, $l:ident, $r:ident) => ({
|
||||||
use rustc::mir::repr::BinOp::*;
|
use rustc::mir::repr::BinOp::*;
|
||||||
use self::PrimVal::*;
|
use self::PrimVal::*;
|
||||||
match bin_op {
|
match bin_op {
|
||||||
Add => $v($l + $r),
|
Add => $v($l + $r),
|
||||||
Sub => $v($l - $r),
|
Sub => $v($l - $r),
|
||||||
Mul => $v($l * $r),
|
Mul => $v($l * $r),
|
||||||
Div => $v($l / $r),
|
Div => $v($l / $r),
|
||||||
Rem => $v($l % $r),
|
Rem => $v($l % $r),
|
||||||
BitXor => $v($l ^ $r),
|
BitXor => $v($l ^ $r),
|
||||||
BitAnd => $v($l & $r),
|
BitAnd => $v($l & $r),
|
||||||
BitOr => $v($l | $r),
|
BitOr => $v($l | $r),
|
||||||
|
|
||||||
// TODO(tsion): Can have differently-typed RHS.
|
// TODO(tsion): Can have differently-typed RHS.
|
||||||
Shl => $v($l << $r),
|
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::*;
|
use self::PrimVal::*;
|
||||||
match (left, right) {
|
let val = match (left, right) {
|
||||||
(I8(l), I8(r)) => int_binops!(I8, l, r),
|
(I8(l), I8(r)) => int_binops!(I8, l, r),
|
||||||
(I16(l), I16(r)) => int_binops!(I16, l, r),
|
(I16(l), I16(r)) => int_binops!(I16, l, r),
|
||||||
(I32(l), I32(r)) => int_binops!(I32, 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),
|
(U16(l), U16(r)) => int_binops!(U16, l, r),
|
||||||
(U32(l), U32(r)) => int_binops!(U32, l, r),
|
(U32(l), U32(r)) => int_binops!(U32, l, r),
|
||||||
(U64(l), U64(r)) => int_binops!(U64, 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!(),
|
_ => unimplemented!(),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal {
|
pub fn unary_op(un_op: mir::UnOp, val: PrimVal) -> PrimVal {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue