1
Fork 0

Represent PrimVals as "bitbags".

Now instead of holding a native type based on the tag, all PrimVals
store a u64 (the `bits`), along with a `kind` corresponding to the
variant as it would be in the old PrimVal representation.

This commit makes no major optimizations and attempts to not change any
behaviour. There will be commits to follow that make use of this
representation to eliminate unnecessary allocation hacks like in
`value_to_primval`.

A number of places could be even more cleaned up after this commit,
particularly in `cast.rs`.
This commit is contained in:
Scott Olson 2016-10-20 04:42:19 -06:00
parent d6b4e1aba6
commit 330be7766f
7 changed files with 611 additions and 512 deletions

View file

@ -8,96 +8,107 @@ use primval::PrimVal;
use memory::Pointer;
use rustc::ty::Ty;
use syntax::ast::{self, IntTy, UintTy};
use syntax::ast::{FloatTy, IntTy, UintTy};
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match val {
Bool(b) => self.cast_const_int(b as u64, ty, false),
F32(f) => self.cast_const_float(f as f64, ty),
F64(f) => self.cast_const_float(f, ty),
I8(i) => self.cast_signed_int(i as i64, ty),
I16(i) => self.cast_signed_int(i as i64, ty),
I32(i) => self.cast_signed_int(i as i64, ty),
I64(i) => self.cast_signed_int(i, ty),
U8(u) => self.cast_const_int(u as u64, ty, false),
U16(u) => self.cast_const_int(u as u64, ty, false),
U32(u) => self.cast_const_int(u as u64, ty, false),
Char(c) => self.cast_const_int(c as u64, ty, false),
U64(u) => self.cast_const_int(u, ty, false),
FnPtr(ptr) |
Ptr(ptr) => self.cast_ptr(ptr, ty),
}
}
use primval::PrimValKind::*;
match val.kind {
F32 => self.cast_float(val.to_f32() as f64, ty),
F64 => self.cast_float(val.to_f64(), ty),
fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
match ty.sty {
ty::TyRef(..) |
ty::TyRawPtr(_) => Ok(Ptr(ptr)),
ty::TyFnPtr(_) => Ok(FnPtr(ptr)),
ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)),
ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)),
ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)),
ty::TyInt(IntTy::I64) => Ok(I64(ptr.to_int()? as i64)),
ty::TyUint(UintTy::U8) => Ok(U8(ptr.to_int()? as u8)),
ty::TyUint(UintTy::U16) => Ok(U16(ptr.to_int()? as u16)),
ty::TyUint(UintTy::U32) => Ok(U32(ptr.to_int()? as u32)),
ty::TyUint(UintTy::U64) => Ok(U64(ptr.to_int()? as u64)),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
I8 | I16 | I32 | I64 => self.cast_signed_int(val.bits as i64, ty),
Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false),
FnPtr(alloc) | Ptr(alloc) => {
let ptr = Pointer::new(alloc, val.bits as usize);
self.cast_ptr(ptr, ty)
}
}
}
fn cast_signed_int(&self, val: i64, ty: ty::Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
self.cast_const_int(val as u64, ty, val < 0)
self.cast_int(val as u64, ty, val < 0)
}
fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
fn cast_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> {
use primval::PrimValKind::*;
use rustc::ty::TypeVariants::*;
match ty.sty {
ty::TyBool if v == 0 => Ok(Bool(false)),
ty::TyBool if v == 1 => Ok(Bool(true)),
ty::TyBool => Err(EvalError::InvalidBool),
ty::TyInt(ast::IntTy::I8) => Ok(I8(v as i64 as i8)),
ty::TyInt(ast::IntTy::I16) => Ok(I16(v as i64 as i16)),
ty::TyInt(ast::IntTy::I32) => Ok(I32(v as i64 as i32)),
ty::TyInt(ast::IntTy::I64) => Ok(I64(v as i64)),
ty::TyInt(ast::IntTy::Is) => {
TyBool if v == 0 => Ok(PrimVal::from_bool(false)),
TyBool if v == 1 => Ok(PrimVal::from_bool(true)),
TyBool => Err(EvalError::InvalidBool),
TyInt(IntTy::I8) => Ok(PrimVal::new(v as i64 as i8 as u64, I8)),
TyInt(IntTy::I16) => Ok(PrimVal::new(v as i64 as i16 as u64, I16)),
TyInt(IntTy::I32) => Ok(PrimVal::new(v as i64 as i32 as u64, I32)),
TyInt(IntTy::I64) => Ok(PrimVal::new(v as i64 as i64 as u64, I64)),
TyUint(UintTy::U8) => Ok(PrimVal::new(v as u8 as u64, U8)),
TyUint(UintTy::U16) => Ok(PrimVal::new(v as u16 as u64, U16)),
TyUint(UintTy::U32) => Ok(PrimVal::new(v as u32 as u64, U32)),
TyUint(UintTy::U64) => Ok(PrimVal::new(v, U64)),
TyInt(IntTy::Is) => {
let int_ty = self.tcx.sess.target.int_type;
let ty = self.tcx.mk_mach_int(int_ty);
self.cast_const_int(v, ty, negative)
},
ty::TyUint(ast::UintTy::U8) => Ok(U8(v as u8)),
ty::TyUint(ast::UintTy::U16) => Ok(U16(v as u16)),
ty::TyUint(ast::UintTy::U32) => Ok(U32(v as u32)),
ty::TyUint(ast::UintTy::U64) => Ok(U64(v)),
ty::TyUint(ast::UintTy::Us) => {
self.cast_int(v, ty, negative)
}
TyUint(UintTy::Us) => {
let uint_ty = self.tcx.sess.target.uint_type;
let ty = self.tcx.mk_mach_uint(uint_ty);
self.cast_const_int(v, ty, negative)
},
ty::TyFloat(ast::FloatTy::F64) if negative => Ok(F64(v as i64 as f64)),
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)),
ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)),
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)),
ty::TyRawPtr(_) => Ok(Ptr(Pointer::from_int(v as usize))),
ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)),
ty::TyChar => Err(EvalError::InvalidChar(v)),
self.cast_int(v, ty, negative)
}
TyFloat(FloatTy::F64) if negative => Ok(PrimVal::from_f64(v as i64 as f64)),
TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(v as f64)),
TyFloat(FloatTy::F32) if negative => Ok(PrimVal::from_f32(v as i64 as f32)),
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(v as f32)),
TyChar if v as u8 as u64 == v => Ok(PrimVal::new(v, Char)),
TyChar => Err(EvalError::InvalidChar(v)),
TyRawPtr(_) => Ok(PrimVal::from_ptr(Pointer::from_int(v as usize))),
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
}
}
fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimVal::*;
fn cast_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
match ty.sty {
// casting negative floats to unsigned integers yields zero
ty::TyUint(_) if val < 0.0 => self.cast_const_int(0, ty, false),
ty::TyInt(_) if val < 0.0 => self.cast_const_int(val as i64 as u64, ty, true),
ty::TyInt(_) | ty::TyUint(_) => self.cast_const_int(val as u64, ty, false),
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(val)),
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(val as f32)),
// Casting negative floats to unsigned integers yields zero.
TyUint(_) if val < 0.0 => self.cast_int(0, ty, false),
TyInt(_) if val < 0.0 => self.cast_int(val as i64 as u64, ty, true),
TyInt(_) | ty::TyUint(_) => self.cast_int(val as u64, ty, false),
TyFloat(FloatTy::F64) => Ok(PrimVal::from_f64(val)),
TyFloat(FloatTy::F32) => Ok(PrimVal::from_f32(val as f32)),
_ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))),
}
}
fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use primval::PrimValKind::*;
use rustc::ty::TypeVariants::*;
match ty.sty {
TyRef(..) | TyRawPtr(_) => Ok(PrimVal::from_ptr(ptr)),
TyFnPtr(_) => Ok(PrimVal::from_fn_ptr(ptr)),
TyInt(IntTy::I8) => Ok(PrimVal::new(ptr.to_int()? as u64, I8)),
TyInt(IntTy::I16) => Ok(PrimVal::new(ptr.to_int()? as u64, I16)),
TyInt(IntTy::I32) => Ok(PrimVal::new(ptr.to_int()? as u64, I32)),
TyInt(IntTy::I64) => Ok(PrimVal::new(ptr.to_int()? as u64, I64)),
TyUint(UintTy::U8) => Ok(PrimVal::new(ptr.to_int()? as u64, U8)),
TyUint(UintTy::U16) => Ok(PrimVal::new(ptr.to_int()? as u64, U16)),
TyUint(UintTy::U32) => Ok(PrimVal::new(ptr.to_int()? as u64, U32)),
TyUint(UintTy::U64) => Ok(PrimVal::new(ptr.to_int()? as u64, U64)),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
}
}
}

View file

@ -16,7 +16,7 @@ use syntax::codemap::{self, DUMMY_SP};
use error::{EvalError, EvalResult};
use memory::{Memory, Pointer, AllocId};
use primval::{self, PrimVal};
use primval::{self, PrimVal, PrimValKind};
use self::value::Value;
use std::collections::HashMap;
@ -202,24 +202,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&self.stack
}
fn isize_primval(&self, n: i64) -> PrimVal {
match self.memory.pointer_size() {
1 => PrimVal::I8(n as i8),
2 => PrimVal::I16(n as i16),
4 => PrimVal::I32(n as i32),
8 => PrimVal::I64(n as i64),
p => bug!("unsupported target pointer size: {}", p),
}
fn usize_primval(&self, n: u64) -> PrimVal {
PrimVal::from_uint_with_size(n, self.memory.pointer_size())
}
fn usize_primval(&self, n: u64) -> PrimVal {
match self.memory.pointer_size() {
1 => PrimVal::U8(n as u8),
2 => PrimVal::U16(n as u16),
4 => PrimVal::U32(n as u32),
8 => PrimVal::U64(n as u64),
p => bug!("unsupported target pointer size: {}", p),
}
fn isize_primval(&self, n: i64) -> PrimVal {
PrimVal::from_int_with_size(n, self.memory.pointer_size())
}
fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
@ -227,32 +215,43 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ptr = self.memory.allocate(s.len(), 1)?;
self.memory.write_bytes(ptr, s.as_bytes())?;
self.memory.freeze(ptr.alloc_id)?;
Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.usize_primval(s.len() as u64)))
Ok(Value::ByValPair(PrimVal::from_ptr(ptr), self.usize_primval(s.len() as u64)))
}
fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
use rustc_const_math::ConstFloat;
let primval = match *const_val {
Integral(ConstInt::I8(i)) => PrimVal::I8(i),
Integral(ConstInt::U8(i)) => PrimVal::U8(i),
Integral(ConstInt::Isize(ConstIsize::Is16(i))) |
Integral(ConstInt::I16(i)) => PrimVal::I16(i),
Integral(ConstInt::Usize(ConstUsize::Us16(i))) |
Integral(ConstInt::U16(i)) => PrimVal::U16(i),
Integral(ConstInt::Isize(ConstIsize::Is32(i))) |
Integral(ConstInt::I32(i)) => PrimVal::I32(i),
Integral(ConstInt::Usize(ConstUsize::Us32(i))) |
Integral(ConstInt::U32(i)) => PrimVal::U32(i),
Integral(ConstInt::Isize(ConstIsize::Is64(i))) |
Integral(ConstInt::I64(i)) => PrimVal::I64(i),
Integral(ConstInt::Usize(ConstUsize::Us64(i))) |
Integral(ConstInt::U64(i)) => PrimVal::U64(i),
Float(ConstFloat::F32(f)) => PrimVal::F32(f),
Float(ConstFloat::F64(f)) => PrimVal::F64(f),
Bool(b) => PrimVal::Bool(b),
Char(c) => PrimVal::Char(c),
Integral(const_int) => {
use rustc_const_math::ConstInt::*;
use rustc_const_math::ConstIsize::*;
use rustc_const_math::ConstUsize::*;
let kind = match const_int {
I8(_) => PrimValKind::I8,
I16(_) | Isize(Is16(_)) => PrimValKind::I16,
I32(_) | Isize(Is32(_)) => PrimValKind::I32,
I64(_) | Isize(Is64(_)) => PrimValKind::I64,
U8(_) => PrimValKind::U8,
U16(_) | Usize(Us16(_)) => PrimValKind::U16,
U32(_) | Usize(Us32(_)) => PrimValKind::U32,
U64(_) | Usize(Us64(_)) => PrimValKind::U64,
Infer(_) | InferSigned(_) =>
bug!("uninferred constants only exist before typeck"),
};
PrimVal::new(const_int.to_u64_unchecked(), kind)
}
Float(ConstFloat::F32(f)) => PrimVal::from_f32(f),
Float(ConstFloat::F64(f)) => PrimVal::from_f64(f),
Float(ConstFloat::FInfer { .. }) =>
bug!("uninferred constants only exist before typeck"),
Bool(b) => PrimVal::from_bool(b),
Char(c) => PrimVal::from_char(c),
Str(ref s) => return self.str_to_value(s),
@ -260,7 +259,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ptr = self.memory.allocate(bs.len(), 1)?;
self.memory.write_bytes(ptr, bs)?;
self.memory.freeze(ptr.alloc_id)?;
PrimVal::Ptr(ptr)
PrimVal::from_ptr(ptr)
}
Struct(_) => unimplemented!(),
@ -269,11 +268,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Array(_, _) => unimplemented!(),
Repeat(_, _) => unimplemented!(),
Dummy => unimplemented!(),
Float(ConstFloat::FInfer{..}) |
Integral(ConstInt::Infer(_)) |
Integral(ConstInt::InferSigned(_)) =>
bug!("uninferred constants only exist before typeck"),
};
Ok(Value::ByVal(primval))
@ -416,7 +410,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, ()> {
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
let val = Value::ByValPair(val, PrimVal::Bool(overflowed));
let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
self.write_value(val, dest, dest_ty)
}
@ -572,9 +566,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let size = discr.size().bytes() as usize;
let val = if signed {
PrimVal::int_with_size(n as i64, size)
PrimVal::from_int_with_size(n as i64, size)
} else {
PrimVal::uint_with_size(n, size)
PrimVal::from_uint_with_size(n, size)
};
self.write_primval(dest, val)?;
@ -615,12 +609,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ref(_, _, ref lvalue) => {
let src = self.eval_lvalue(lvalue)?;
let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
let ptr = PrimVal::Ptr(raw_ptr);
let ptr = PrimVal::from_ptr(raw_ptr);
let val = match extra {
LvalueExtra::None => Value::ByVal(ptr),
LvalueExtra::Length(len) => Value::ByValPair(ptr, self.usize_primval(len)),
LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)),
LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::from_ptr(vtable)),
LvalueExtra::DowncastVariant(..) =>
bug!("attempted to take a reference to an enum downcast lvalue"),
};
@ -630,7 +624,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Box(ty) => {
let ptr = self.alloc_ptr(ty)?;
self.write_primval(dest, PrimVal::Ptr(ptr))?;
self.write_primval(dest, PrimVal::from_ptr(ptr))?;
}
Cast(kind, ref operand, cast_ty) => {
@ -699,6 +693,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
InlineAsm { .. } => return Err(EvalError::InlineAsm),
}
if log_enabled!(::log::LogLevel::Trace) {
self.dump_local(dest);
}
Ok(())
}
@ -974,7 +972,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Deref => {
use primval::PrimVal::*;
use primval::PrimValKind::*;
use interpreter::value::Value::*;
let val = match self.eval_and_read_lvalue(&proj.base)? {
@ -983,10 +981,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
};
match val {
ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)),
ByValPair(Ptr(ptr), n) =>
(ptr, LvalueExtra::Length(n.expect_uint("slice length"))),
ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None),
ByValPair(
PrimVal { kind: Ptr(ptr_alloc), bits: ptr_offset },
PrimVal { kind: Ptr(vtable_alloc), bits: vtable_offset },
) => {
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
(ptr, LvalueExtra::Vtable(vtable))
}
ByValPair(PrimVal { kind: Ptr(alloc), bits: offset }, n) => {
let ptr = Pointer::new(alloc, offset as usize);
(ptr, LvalueExtra::Length(n.expect_uint("slice length")))
}
ByVal(PrimVal { kind: Ptr(alloc), bits: offset }) => {
let ptr = Pointer::new(alloc, offset as usize);
(ptr, LvalueExtra::None)
}
_ => bug!("can't deref non pointer types"),
}
}
@ -1243,11 +1256,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
use syntax::ast::FloatTy;
let val = match ty.sty {
ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?),
ty::TyBool => PrimVal::from_bool(self.memory.read_bool(ptr)?),
ty::TyChar => {
let c = self.memory.read_uint(ptr, 4)? as u32;
match ::std::char::from_u32(c) {
Some(ch) => PrimVal::Char(ch),
Some(ch) => PrimVal::from_char(ch),
None => return Err(EvalError::InvalidChar(c as u64)),
}
}
@ -1262,7 +1275,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Is => self.memory.pointer_size(),
};
let n = self.memory.read_int(ptr, size)?;
PrimVal::int_with_size(n, size)
PrimVal::from_int_with_size(n, size)
}
ty::TyUint(uint_ty) => {
@ -1275,32 +1288,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Us => self.memory.pointer_size(),
};
let n = self.memory.read_uint(ptr, size)?;
PrimVal::uint_with_size(n, size)
PrimVal::from_uint_with_size(n, size)
}
ty::TyFloat(FloatTy::F32) => PrimVal::F32(self.memory.read_f32(ptr)?),
ty::TyFloat(FloatTy::F64) => PrimVal::F64(self.memory.read_f64(ptr)?),
ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?),
ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?),
ty::TyFnDef(def_id, substs, fn_ty) => {
PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty))
PrimVal::from_fn_ptr(self.memory.create_fn_ptr(def_id, substs, fn_ty))
},
ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?,
ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::from_fn_ptr)?,
ty::TyBox(ty) |
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
let p = self.memory.read_ptr(ptr)?;
if self.type_is_sized(ty) {
PrimVal::Ptr(p)
PrimVal::from_ptr(p)
} else {
// FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>`
let extra = ptr.offset(self.memory.pointer_size() as isize);
let extra = match self.tcx.struct_tail(ty).sty {
ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?),
ty::TyTrait(..) => PrimVal::from_ptr(self.memory.read_ptr(extra)?),
ty::TySlice(..) |
ty::TyStr => self.usize_primval(self.memory.read_usize(extra)?),
_ => bug!("unsized primval ptr read from {:?}", ty),
};
return Ok(Value::ByValPair(PrimVal::Ptr(p), extra));
return Ok(Value::ByValPair(PrimVal::from_ptr(p), extra));
}
}
@ -1310,10 +1323,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let size = discr.size().bytes() as usize;
if signed {
let n = self.memory.read_int(ptr, size)?;
PrimVal::int_with_size(n, size)
PrimVal::from_int_with_size(n, size)
} else {
let n = self.memory.read_uint(ptr, size)?;
PrimVal::uint_with_size(n, size)
PrimVal::from_uint_with_size(n, size)
}
} else {
bug!("primitive read of non-clike enum: {:?}", ty);

View file

@ -7,7 +7,7 @@ use rustc::ty::{self, Ty};
use error::{EvalError, EvalResult};
use interpreter::value::Value;
use interpreter::{EvalContext, Lvalue};
use primval::{self, PrimVal};
use primval::{self, PrimVal, PrimValKind};
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn call_intrinsic(
@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let offset = self.value_to_primval(args_ptrs[1], isize)?
.expect_int("arith_offset second arg not isize");
let new_ptr = ptr.offset(offset as isize);
self.write_primval(dest, PrimVal::Ptr(new_ptr))?;
self.write_primval(dest, PrimVal::from_ptr(new_ptr))?;
}
"assume" => {
@ -99,19 +99,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ty = substs.type_at(0);
let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?;
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
self.write_primval(dest, PrimVal::U64(discr_val))?;
self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?;
}
"fabsf32" => {
let f = self.value_to_primval(args_ptrs[2], f32)?
.expect_f32("fabsf32 read non f32");
self.write_primval(dest, PrimVal::F32(f.abs()))?;
self.write_primval(dest, PrimVal::from_f32(f.abs()))?;
}
"fabsf64" => {
let f = self.value_to_primval(args_ptrs[2], f64)?
.expect_f64("fabsf64 read non f64");
self.write_primval(dest, PrimVal::F64(f.abs()))?;
self.write_primval(dest, PrimVal::from_f64(f.abs()))?;
}
"fadd_fast" => {
@ -159,7 +159,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ty = substs.type_at(0);
let env = self.tcx.empty_parameter_environment();
let needs_drop = self.tcx.type_needs_drop_given_env(ty, &env);
self.write_primval(dest, PrimVal::Bool(needs_drop))?;
self.write_primval(dest, PrimVal::from_bool(needs_drop))?;
}
"offset" => {
@ -170,7 +170,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
let result_ptr = ptr.offset(offset as isize * pointee_size);
self.write_primval(dest, PrimVal::Ptr(result_ptr))?;
self.write_primval(dest, PrimVal::from_ptr(result_ptr))?;
}
"overflowing_sub" => {
@ -190,7 +190,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
.expect_f32("powif32 first arg not f32");
let i = self.value_to_primval(args_ptrs[1], i32)?
.expect_int("powif32 second arg not i32");
self.write_primval(dest, PrimVal::F32(f.powi(i as i32)))?;
self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?;
}
"powif64" => {
@ -198,19 +198,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
.expect_f64("powif64 first arg not f64");
let i = self.value_to_primval(args_ptrs[1], i32)?
.expect_int("powif64 second arg not i32");
self.write_primval(dest, PrimVal::F64(f.powi(i as i32)))?;
self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?;
}
"sqrtf32" => {
let f = self.value_to_primval(args_ptrs[0], f32)?
.expect_f32("sqrtf32 first arg not f32");
self.write_primval(dest, PrimVal::F32(f.sqrt()))?;
self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?;
}
"sqrtf64" => {
let f = self.value_to_primval(args_ptrs[0], f64)?
.expect_f64("sqrtf64 first arg not f64");
self.write_primval(dest, PrimVal::F64(f.sqrt()))?;
self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?;
}
"size_of" => {
@ -235,7 +235,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"type_id" => {
let ty = substs.type_at(0);
let n = self.tcx.type_id_hash(ty);
self.write_primval(dest, PrimVal::U64(n))?;
self.write_primval(dest, PrimVal::new(n, PrimValKind::U64))?;
}
"transmute" => {
@ -362,53 +362,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
macro_rules! integer_intrinsic {
($name:expr, $val:expr, $method:ident) => ({
let val = $val;
use primval::PrimValKind::*;
let bits = match val.kind {
I8 => (val.bits as i8).$method() as u64,
U8 => (val.bits as u8).$method() as u64,
I16 => (val.bits as i16).$method() as u64,
U16 => (val.bits as u16).$method() as u64,
I32 => (val.bits as i32).$method() as u64,
U32 => (val.bits as u32).$method() as u64,
I64 => (val.bits as i64).$method() as u64,
U64 => (val.bits as u64).$method() as u64,
_ => bug!("invalid `{}` argument: {:?}", $name, val),
};
PrimVal::new(bits, val.kind)
});
}
fn numeric_intrinsic(name: &str, val: PrimVal) -> PrimVal {
use primval::PrimVal::*;
match name {
"ctpop" => match val {
I8(i) => I8(i.count_ones() as i8),
U8(i) => U8(i.count_ones() as u8),
I16(i) => I16(i.count_ones() as i16),
U16(i) => U16(i.count_ones() as u16),
I32(i) => I32(i.count_ones() as i32),
U32(i) => U32(i.count_ones() as u32),
I64(i) => I64(i.count_ones() as i64),
U64(i) => U64(i.count_ones() as u64),
other => bug!("invalid `ctpop` argument: {:?}", other),
},
"cttz" => match val {
I8(i) => I8(i.trailing_zeros() as i8),
U8(i) => U8(i.trailing_zeros() as u8),
I16(i) => I16(i.trailing_zeros() as i16),
U16(i) => U16(i.trailing_zeros() as u16),
I32(i) => I32(i.trailing_zeros() as i32),
U32(i) => U32(i.trailing_zeros() as u32),
I64(i) => I64(i.trailing_zeros() as i64),
U64(i) => U64(i.trailing_zeros() as u64),
other => bug!("invalid `cttz` argument: {:?}", other),
},
"ctlz" => match val {
I8(i) => I8(i.leading_zeros() as i8),
U8(i) => U8(i.leading_zeros() as u8),
I16(i) => I16(i.leading_zeros() as i16),
U16(i) => U16(i.leading_zeros() as u16),
I32(i) => I32(i.leading_zeros() as i32),
U32(i) => U32(i.leading_zeros() as u32),
I64(i) => I64(i.leading_zeros() as i64),
U64(i) => U64(i.leading_zeros() as u64),
other => bug!("invalid `ctlz` argument: {:?}", other),
},
"bswap" => match val {
I8(i) => I8(i.swap_bytes() as i8),
U8(i) => U8(i.swap_bytes() as u8),
I16(i) => I16(i.swap_bytes() as i16),
U16(i) => U16(i.swap_bytes() as u16),
I32(i) => I32(i.swap_bytes() as i32),
U32(i) => U32(i.swap_bytes() as u32),
I64(i) => I64(i.swap_bytes() as i64),
U64(i) => U64(i.swap_bytes() as u64),
other => bug!("invalid `bswap` argument: {:?}", other),
},
_ => bug!("not a numeric intrinsic: {}", name),
"bswap" => integer_intrinsic!("bswap", val, swap_bytes),
"ctlz" => integer_intrinsic!("ctlz", val, leading_zeros),
"ctpop" => integer_intrinsic!("ctpop", val, count_ones),
"cttz" => integer_intrinsic!("cttz", val, trailing_zeros),
_ => bug!("not a numeric intrinsic: {}", name),
}
}

View file

@ -87,7 +87,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
let fn_ptr = self.eval_operand_to_primval(func)?
.expect_fn_ptr("TyFnPtr callee did not evaluate to PrimVal::FnPtr");
.expect_fn_ptr("TyFnPtr callee did not evaluate to FnPtr");
let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?;
if fn_ty != bare_fn_ty {
return Err(EvalError::FunctionPointerTyMismatch(fn_ty, bare_fn_ty));
@ -293,7 +293,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let align = self.value_to_primval(args[1], usize)?
.expect_uint("__rust_allocate second arg not usize");
let ptr = self.memory.allocate(size as usize, align as usize)?;
self.write_primval(dest, PrimVal::Ptr(ptr))?;
self.write_primval(dest, PrimVal::from_ptr(ptr))?;
}
"__rust_reallocate" => {
@ -301,7 +301,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize");
let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize");
let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?;
self.write_primval(dest, PrimVal::Ptr(new_ptr))?;
self.write_primval(dest, PrimVal::from_ptr(new_ptr))?;
}
"memcmp" => {
@ -321,7 +321,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
};
self.write_primval(dest, PrimVal::int_with_size(result, dest_size))?;
self.write_primval(dest, PrimVal::from_int_with_size(result, dest_size))?;
}
_ => {
@ -430,7 +430,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// FIXME: this is a memory leak, should probably add the pointer to the
// current stack.
let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?;
args[0].0 = Value::ByVal(PrimVal::Ptr(first));
args[0].0 = Value::ByVal(PrimVal::from_ptr(first));
args[0].1 = self.tcx.mk_mut_ptr(args[0].1);
}
@ -453,7 +453,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let idx = self.tcx.get_vtable_index_of_object_method(data, def_id);
if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) {
let (self_ptr, vtable) = first_arg.expect_ptr_vtable_pair(&self.memory)?;
*first_arg = Value::ByVal(PrimVal::Ptr(self_ptr));
*first_arg = Value::ByVal(PrimVal::from_ptr(self_ptr));
let idx = idx + 3;
let offset = idx * self.memory.pointer_size();
let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?;

View file

@ -1,6 +1,6 @@
use error::EvalResult;
use memory::{Memory, Pointer};
use primval::PrimVal;
use primval::{PrimVal, PrimValKind};
/// A `Value` represents a single self-contained Rust value.
///
@ -22,8 +22,13 @@ impl<'a, 'tcx: 'a> Value {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_ptr(ptr),
ByVal(PrimVal::Ptr(ptr)) |
ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr),
ByVal(PrimVal { kind: PrimValKind::Ptr(alloc), bits: offset }) |
ByVal(PrimVal { kind: PrimValKind::FnPtr(alloc), bits: offset }) => {
let ptr = Pointer::new(alloc, offset as usize);
Ok(ptr)
}
ByValPair(..) => unimplemented!(),
ByVal(_other) => unimplemented!(),
}
@ -40,7 +45,16 @@ impl<'a, 'tcx: 'a> Value {
let vtable = mem.read_ptr(ptr.offset(mem.pointer_size() as isize))?;
Ok((ptr, vtable))
}
ByValPair(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)) => Ok((ptr, vtable)),
ByValPair(
PrimVal { kind: PrimValKind::Ptr(ptr_alloc), bits: ptr_offset },
PrimVal { kind: PrimValKind::Ptr(vtable_alloc), bits: vtable_offset },
) => {
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
Ok((ptr, vtable))
}
_ => bug!("expected ptr and vtable, got {:?}", self),
}
}
@ -49,10 +63,7 @@ impl<'a, 'tcx: 'a> Value {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)),
ByValPair(_, PrimVal::U8(len)) => Ok(len as u64),
ByValPair(_, PrimVal::U16(len)) => Ok(len as u64),
ByValPair(_, PrimVal::U32(len)) => Ok(len as u64),
ByValPair(_, PrimVal::U64(len)) => Ok(len),
ByValPair(_, val) if val.kind.is_int() => Ok(val.bits),
_ => unimplemented!(),
}
}

View file

@ -49,8 +49,13 @@ pub struct Pointer {
}
impl Pointer {
pub fn new(alloc_id: AllocId, offset: usize) -> Self {
Pointer { alloc_id: alloc_id, offset: offset }
}
pub fn offset(self, i: isize) -> Self {
Pointer { offset: (self.offset as isize + i) as usize, ..self }
let new_offset = (self.offset as isize + i) as usize;
Pointer::new(self.alloc_id, new_offset)
}
pub fn points_to_zst(&self) -> bool {
@ -65,25 +70,18 @@ impl Pointer {
}
}
// FIXME(solson): Integer pointers should use u64, not usize. Target pointers can be larger
// than host usize.
pub fn from_int(i: usize) -> Self {
Pointer {
alloc_id: ZST_ALLOC_ID,
offset: i,
}
Pointer::new(ZST_ALLOC_ID, i)
}
pub fn zst_ptr() -> Self {
Pointer {
alloc_id: ZST_ALLOC_ID,
offset: 0,
}
Pointer::new(ZST_ALLOC_ID, 0)
}
pub fn never_ptr() -> Self {
Pointer {
alloc_id: NEVER_ALLOC_ID,
offset: 0,
}
Pointer::new(NEVER_ALLOC_ID, 0)
}
}
@ -167,20 +165,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
fn create_fn_alloc(&mut self, def: FunctionDefinition<'tcx>) -> Pointer {
if let Some(&alloc_id) = self.function_alloc_cache.get(&def) {
return Pointer {
alloc_id: alloc_id,
offset: 0,
};
return Pointer::new(alloc_id, 0);
}
let id = self.next_id;
debug!("creating fn ptr: {}", id);
self.next_id.0 += 1;
self.functions.insert(id, def.clone());
self.function_alloc_cache.insert(def, id);
Pointer {
alloc_id: id,
offset: 0,
}
Pointer::new(id, 0)
}
pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
@ -207,10 +199,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
let id = self.next_id;
self.next_id.0 += 1;
self.alloc_map.insert(id, alloc);
Ok(Pointer {
alloc_id: id,
offset: 0,
})
Ok(Pointer::new(id, 0))
}
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
@ -241,10 +230,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
alloc.undef_mask.truncate(new_size);
}
Ok(Pointer {
alloc_id: ptr.alloc_id,
offset: 0,
})
Ok(Pointer::new(ptr.alloc_id, 0))
}
// TODO(solson): See comment on `reallocate`.
@ -535,7 +521,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
let offset = read_target_uint(endianess, bytes).unwrap() as usize;
let alloc = self.get(ptr.alloc_id)?;
match alloc.relocations.get(&ptr.offset) {
Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }),
Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)),
None => Ok(Pointer::from_int(offset)),
}
}
@ -547,22 +533,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
match val {
PrimVal::Bool(b) => self.write_bool(ptr, b),
PrimVal::I8(n) => self.write_int(ptr, n as i64, 1),
PrimVal::I16(n) => self.write_int(ptr, n as i64, 2),
PrimVal::I32(n) => self.write_int(ptr, n as i64, 4),
PrimVal::I64(n) => self.write_int(ptr, n as i64, 8),
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::Char(c) => self.write_uint(ptr, c as u64, 4),
PrimVal::F32(f) => self.write_f32(ptr, f),
PrimVal::F64(f) => self.write_f64(ptr, f),
PrimVal::FnPtr(p) |
PrimVal::Ptr(p) => self.write_ptr(ptr, p),
use primval::PrimValKind::*;
match val.kind {
FnPtr(alloc_id) | Ptr(alloc_id) => {
let p = Pointer::new(alloc_id, val.bits as usize);
return self.write_ptr(ptr, p);
}
_ => {}
}
let (size, bits) = match val.kind {
I8 | U8 | Bool => (1, val.bits as u8 as u64),
I16 | U16 => (2, val.bits as u16 as u64),
I32 | U32 | F32 | Char => (4, val.bits as u32 as u64),
I64 | U64 | F64 => (8, val.bits),
FnPtr(_) | Ptr(_) => bug!("handled above"),
};
self.write_uint(ptr, bits, size)
}
pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> {

View file

@ -1,306 +1,402 @@
#![allow(unknown_lints)]
#![allow(float_cmp)]
use std::mem::transmute;
use rustc::mir::repr as mir;
use error::{EvalError, EvalResult};
use memory::Pointer;
use memory::{AllocId, 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),
Ptr(Pointer),
FnPtr(Pointer),
Char(char),
F32(f32), F64(f64),
fn bits_to_f32(bits: u64) -> f32 {
unsafe { transmute::<u32, f32>(bits as u32) }
}
macro_rules! declare_expect_fn {
($name:ident, $variant:ident, $t:ty) => (
pub fn $name(self, error_msg: &str) -> $t {
match self {
PrimVal::$variant(x) => x,
_ => bug!("{}", error_msg),
}
fn bits_to_f64(bits: u64) -> f64 {
unsafe { transmute::<u64, f64>(bits) }
}
fn f32_to_bits(f: f32) -> u64 {
unsafe { transmute::<f32, u32>(f) as u64 }
}
fn f64_to_bits(f: f64) -> u64 {
unsafe { transmute::<f64, u64>(f) }
}
fn bits_to_bool(n: u64) -> bool {
// FIXME(solson): Can we reach here due to user error?
debug_assert!(n == 0 || n == 1, "bits interpreted as bool were {}", n);
n & 1 == 1
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PrimVal {
pub bits: u64,
pub kind: PrimValKind,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrimValKind {
I8, I16, I32, I64,
U8, U16, U32, U64,
F32, F64,
Bool,
Char,
Ptr(AllocId),
FnPtr(AllocId),
}
impl PrimValKind {
pub fn is_int(self) -> bool {
use self::PrimValKind::*;
match self {
I8 | I16 | I32 | I64 | U8 | U16 | U32 | U64 => true,
_ => false,
}
);
}
}
impl PrimVal {
declare_expect_fn!(expect_bool, Bool, bool);
declare_expect_fn!(expect_f32, F32, f32);
declare_expect_fn!(expect_f64, F64, f64);
declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer);
declare_expect_fn!(expect_ptr, Ptr, Pointer);
pub fn new(bits: u64, kind: PrimValKind) -> Self {
PrimVal { bits: bits, kind: kind }
}
pub fn from_ptr(ptr: Pointer) -> Self {
PrimVal::new(ptr.offset as u64, PrimValKind::Ptr(ptr.alloc_id))
}
pub fn from_fn_ptr(ptr: Pointer) -> Self {
PrimVal::new(ptr.offset as u64, PrimValKind::FnPtr(ptr.alloc_id))
}
pub fn from_bool(b: bool) -> Self {
PrimVal::new(b as u64, PrimValKind::Bool)
}
pub fn from_char(c: char) -> Self {
PrimVal::new(c as u64, PrimValKind::Char)
}
pub fn from_f32(f: f32) -> Self {
PrimVal::new(f32_to_bits(f), PrimValKind::F32)
}
pub fn from_f64(f: f64) -> Self {
PrimVal::new(f64_to_bits(f), PrimValKind::F64)
}
pub fn from_uint_with_size(n: u64, size: usize) -> Self {
let kind = match size {
1 => PrimValKind::U8,
2 => PrimValKind::U16,
4 => PrimValKind::U32,
8 => PrimValKind::U64,
_ => bug!("can't make uint ({}) with size {}", n, size),
};
PrimVal::new(n, kind)
}
pub fn from_int_with_size(n: i64, size: usize) -> Self {
let kind = match size {
1 => PrimValKind::I8,
2 => PrimValKind::I16,
4 => PrimValKind::I32,
8 => PrimValKind::I64,
_ => bug!("can't make int ({}) with size {}", n, size),
};
PrimVal::new(n as u64, kind)
}
pub fn to_f32(self) -> f32 {
bits_to_f32(self.bits)
}
pub fn to_f64(self) -> f64 {
bits_to_f64(self.bits)
}
pub fn expect_uint(self, error_msg: &str) -> u64 {
use self::PrimVal::*;
match self {
U8(u) => u as u64,
U16(u) => u as u64,
U32(u) => u as u64,
U64(u) => u,
Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as u64,
use self::PrimValKind::*;
match self.kind {
U8 | U16 | U32 | U64 => self.bits,
Ptr(alloc_id) => {
let ptr = Pointer::new(alloc_id, self.bits as usize);
ptr.to_int().expect("non abstract ptr") as u64
}
_ => bug!("{}", error_msg),
}
}
pub fn expect_int(self, error_msg: &str) -> i64 {
use self::PrimVal::*;
match self {
I8(i) => i as i64,
I16(i) => i as i64,
I32(i) => i as i64,
I64(i) => i,
Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as i64,
use self::PrimValKind::*;
match self.kind {
I8 | I16 | I32 | I64 => self.bits as i64,
Ptr(alloc_id) => {
let ptr = Pointer::new(alloc_id, self.bits as usize);
ptr.to_int().expect("non abstract ptr") as i64
}
_ => bug!("{}", error_msg),
}
}
pub fn uint_with_size(n: u64, size: usize) -> Self {
use self::PrimVal::*;
match size {
1 => U8(n as u8),
2 => U16(n as u16),
4 => U32(n as u32),
8 => U64(n),
_ => bug!("can't make uint ({}) with size {}", n, size),
pub fn expect_bool(self, error_msg: &str) -> bool {
match (self.kind, self.bits) {
(PrimValKind::Bool, 0) => false,
(PrimValKind::Bool, 1) => true,
_ => bug!("{}", error_msg),
}
}
pub fn int_with_size(n: i64, size: usize) -> Self {
use self::PrimVal::*;
match size {
1 => I8(n as i8),
2 => I16(n as i16),
4 => I32(n as i32),
8 => I64(n),
_ => bug!("can't make int ({}) with size {}", n, size),
pub fn expect_f32(self, error_msg: &str) -> f32 {
match self.kind {
PrimValKind::F32 => bits_to_f32(self.bits),
_ => bug!("{}", error_msg),
}
}
pub fn expect_f64(self, error_msg: &str) -> f64 {
match self.kind {
PrimValKind::F32 => bits_to_f64(self.bits),
_ => bug!("{}", error_msg),
}
}
pub fn expect_ptr(self, error_msg: &str) -> Pointer {
match self.kind {
PrimValKind::Ptr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
_ => bug!("{}", error_msg),
}
}
pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer {
match self.kind {
PrimValKind::FnPtr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
_ => bug!("{}", error_msg),
}
}
}
/// returns the result of the operation and whether the operation overflowed
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> {
////////////////////////////////////////////////////////////////////////////////
// MIR operator evaluation
////////////////////////////////////////////////////////////////////////////////
macro_rules! overflow {
($kind:expr, $op:ident, $l:expr, $r:expr) => ({
let (val, overflowed) = $l.$op($r);
let primval = PrimVal::new(val as u64, $kind);
Ok((primval, overflowed))
})
}
macro_rules! int_arithmetic {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
match $kind {
I8 => overflow!(I8, $int_op, l as i8, r as i8),
I16 => overflow!(I16, $int_op, l as i16, r as i16),
I32 => overflow!(I32, $int_op, l as i32, r as i32),
I64 => overflow!(I64, $int_op, l as i64, r as i64),
U8 => overflow!(U8, $int_op, l as u8, r as u8),
U16 => overflow!(U16, $int_op, l as u16, r as u16),
U32 => overflow!(U32, $int_op, l as u32, r as u32),
U64 => overflow!(U64, $int_op, l as u64, r as u64),
_ => bug!("int_arithmetic should only be called on int primvals"),
}
})
}
macro_rules! int_shift {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
match $kind {
I8 => overflow!(I8, $int_op, l as i8, r),
I16 => overflow!(I16, $int_op, l as i16, r),
I32 => overflow!(I32, $int_op, l as i32, r),
I64 => overflow!(I64, $int_op, l as i64, r),
U8 => overflow!(U8, $int_op, l as u8, r),
U16 => overflow!(U16, $int_op, l as u16, r),
U32 => overflow!(U32, $int_op, l as u32, r),
U64 => overflow!(U64, $int_op, l as u64, r),
_ => bug!("int_shift should only be called on int primvals"),
}
})
}
macro_rules! float_arithmetic {
($kind:expr, $from_bits:ident, $to_bits:ident, $float_op:tt, $l:expr, $r:expr) => ({
let l = $from_bits($l);
let r = $from_bits($r);
let bits = $to_bits(l $float_op r);
PrimVal::new(bits, $kind)
})
}
macro_rules! f32_arithmetic {
($float_op:tt, $l:expr, $r:expr) => (
float_arithmetic!(F32, bits_to_f32, f32_to_bits, $float_op, $l, $r)
)
}
macro_rules! f64_arithmetic {
($float_op:tt, $l:expr, $r:expr) => (
float_arithmetic!(F64, bits_to_f64, f64_to_bits, $float_op, $l, $r)
)
}
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op<'tcx>(
bin_op: mir::BinOp,
left: PrimVal,
right: PrimVal
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::repr::BinOp::*;
use self::PrimVal::*;
macro_rules! overflow {
($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
let (val, of) = $l.$op($r);
if of {
return Ok(($v(val), true));
} else {
$v(val)
}
})
}
macro_rules! int_binops {
($v:ident, $l:ident, $r:ident) => ({
match bin_op {
Add => overflow!($v, $v, $l, overflowing_add, $r),
Sub => overflow!($v, $v, $l, overflowing_sub, $r),
Mul => overflow!($v, $v, $l, overflowing_mul, $r),
Div => overflow!($v, $v, $l, overflowing_div, $r),
Rem => overflow!($v, $v, $l, overflowing_rem, $r),
BitXor => $v($l ^ $r),
BitAnd => $v($l & $r),
BitOr => $v($l | $r),
// these have already been handled
Shl | Shr => bug!("`{}` operation should already have been handled", bin_op.to_hir_binop().as_str()),
Eq => Bool($l == $r),
Ne => Bool($l != $r),
Lt => Bool($l < $r),
Le => Bool($l <= $r),
Gt => Bool($l > $r),
Ge => Bool($l >= $r),
}
})
}
macro_rules! float_binops {
($v:ident, $l:ident, $r:ident) => ({
match bin_op {
Add => $v($l + $r),
Sub => $v($l - $r),
Mul => $v($l * $r),
Div => $v($l / $r),
Rem => $v($l % $r),
// invalid float ops
BitXor | BitAnd | BitOr |
Shl | Shr => bug!("`{}` is not a valid operation on floats", bin_op.to_hir_binop().as_str()),
Eq => Bool($l == $r),
Ne => Bool($l != $r),
Lt => Bool($l < $r),
Le => Bool($l <= $r),
Gt => Bool($l > $r),
Ge => Bool($l >= $r),
}
})
}
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, 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!(),
}
}
match bin_op {
// can have rhs with a different numeric type
Shl | Shr => {
// these numbers are the maximum number a bitshift rhs could possibly have
// e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range
let type_bits: u32 = match left {
I8(_) | U8(_) => 8,
I16(_) | U16(_) => 16,
I32(_) | U32(_) => 32,
I64(_) | U64(_) => 64,
_ => bug!("bad MIR: bitshift lhs is not integral"),
};
assert!(type_bits.is_power_of_two());
// turn into `u32` because `overflowing_sh{l,r}` only take `u32`
let r = match right {
I8(i) => i as u32,
I16(i) => i as u32,
I32(i) => i as u32,
I64(i) => i as u32,
U8(i) => i as u32,
U16(i) => i as u32,
U32(i) => i as u32,
U64(i) => i as u32,
_ => bug!("bad MIR: bitshift rhs is not integral"),
};
// apply mask
let r = r & (type_bits - 1);
macro_rules! shift {
($v:ident, $l:ident, $r:ident) => ({
match bin_op {
Shl => overflow!($v, U32, $l, overflowing_shl, $r),
Shr => overflow!($v, U32, $l, overflowing_shr, $r),
_ => bug!("it has already been checked that this is a shift op"),
}
})
}
let val = match left {
I8(l) => shift!(I8, l, r),
I16(l) => shift!(I16, l, r),
I32(l) => shift!(I32, l, r),
I64(l) => shift!(I64, l, r),
U8(l) => shift!(U8, l, r),
U16(l) => shift!(U16, l, r),
U32(l) => shift!(U32, l, r),
U64(l) => shift!(U64, l, r),
_ => bug!("bad MIR: bitshift lhs is not integral (should already have been checked)"),
};
return Ok((val, false));
},
_ => {},
}
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),
(I64(l), I64(r)) => int_binops!(I64, l, r),
(U8(l), U8(r)) => int_binops!(U8, l, r),
(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),
(F32(l), F32(r)) => float_binops!(F32, l, r),
(F64(l), F64(r)) => float_binops!(F64, l, r),
(Char(l), Char(r)) => 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),
_ => bug!("invalid char op: {:?}", bin_op),
},
(Bool(l), Bool(r)) => {
Bool(match bin_op {
Eq => l == r,
Ne => l != r,
Lt => l < r,
Le => l <= r,
Gt => l > r,
Ge => l >= r,
BitOr => l | r,
BitXor => l ^ r,
BitAnd => l & r,
Add | Sub | Mul | Div | Rem | Shl | Shr => return Err(EvalError::InvalidBoolOp(bin_op)),
})
}
use self::PrimValKind::*;
match (left.kind, right.kind) {
(FnPtr(_), Ptr(_)) |
(Ptr(_), FnPtr(_)) =>
unrelated_ptr_ops(bin_op)?,
(Ptr(_), FnPtr(_)) => return Ok((unrelated_ptr_ops(bin_op)?, false)),
(FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
Eq => Bool(l_ptr == r_ptr),
Ne => Bool(l_ptr != r_ptr),
_ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))),
},
(Ptr(l_ptr), Ptr(r_ptr)) => {
if l_ptr.alloc_id != r_ptr.alloc_id {
return Ok((unrelated_ptr_ops(bin_op)?, false));
}
let l = l_ptr.offset;
let r = r_ptr.offset;
(Ptr(l_alloc), Ptr(r_alloc)) if l_alloc != r_alloc
=> return Ok((unrelated_ptr_ops(bin_op)?, false)),
(FnPtr(l_alloc), FnPtr(r_alloc)) => {
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),
_ => return Err(EvalError::Unimplemented(format!("unimplemented ptr op: {:?}", bin_op))),
Eq => return Ok((PrimVal::from_bool(l_alloc == r_alloc), false)),
Ne => return Ok((PrimVal::from_bool(l_alloc != r_alloc), false)),
_ => {}
}
}
(l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))),
_ => {}
}
let (l, r) = (left.bits, right.bits);
// These ops can have an RHS with a different numeric type.
if bin_op == Shl || bin_op == Shr {
// These are the maximum values a bitshift RHS could possibly have. For example, u16
// can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in
// that range.
let type_bits: u32 = match left.kind {
I8 | U8 => 8,
I16 | U16 => 16,
I32 | U32 => 32,
I64 | U64 => 64,
_ => bug!("bad MIR: bitshift lhs is not integral"),
};
// Cast to `u32` because `overflowing_sh{l,r}` only take `u32`, then apply the bitmask
// to ensure it's within the valid shift value range.
let r = (right.bits as u32) & (type_bits - 1);
return match bin_op {
Shl => int_shift!(left.kind, overflowing_shl, l, r),
Shr => int_shift!(left.kind, overflowing_shr, l, r),
_ => bug!("it has already been checked that this is a shift op"),
};
}
if left.kind != right.kind {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
return Err(EvalError::Unimplemented(msg));
}
let val = match (bin_op, left.kind) {
(Eq, F32) => PrimVal::from_bool(bits_to_f32(l) == bits_to_f32(r)),
(Ne, F32) => PrimVal::from_bool(bits_to_f32(l) != bits_to_f32(r)),
(Lt, F32) => PrimVal::from_bool(bits_to_f32(l) < bits_to_f32(r)),
(Le, F32) => PrimVal::from_bool(bits_to_f32(l) <= bits_to_f32(r)),
(Gt, F32) => PrimVal::from_bool(bits_to_f32(l) > bits_to_f32(r)),
(Ge, F32) => PrimVal::from_bool(bits_to_f32(l) >= bits_to_f32(r)),
(Eq, F64) => PrimVal::from_bool(bits_to_f64(l) == bits_to_f64(r)),
(Ne, F64) => PrimVal::from_bool(bits_to_f64(l) != bits_to_f64(r)),
(Lt, F64) => PrimVal::from_bool(bits_to_f64(l) < bits_to_f64(r)),
(Le, F64) => PrimVal::from_bool(bits_to_f64(l) <= bits_to_f64(r)),
(Gt, F64) => PrimVal::from_bool(bits_to_f64(l) > bits_to_f64(r)),
(Ge, F64) => PrimVal::from_bool(bits_to_f64(l) >= bits_to_f64(r)),
(Add, F32) => f32_arithmetic!(+, l, r),
(Sub, F32) => f32_arithmetic!(-, l, r),
(Mul, F32) => f32_arithmetic!(*, l, r),
(Div, F32) => f32_arithmetic!(/, l, r),
(Rem, F32) => f32_arithmetic!(%, l, r),
(Add, F64) => f64_arithmetic!(+, l, r),
(Sub, F64) => f64_arithmetic!(-, l, r),
(Mul, F64) => f64_arithmetic!(*, l, r),
(Div, F64) => f64_arithmetic!(/, l, r),
(Rem, F64) => f64_arithmetic!(%, l, r),
(Eq, _) => PrimVal::from_bool(l == r),
(Ne, _) => PrimVal::from_bool(l != r),
(Lt, _) => PrimVal::from_bool(l < r),
(Le, _) => PrimVal::from_bool(l <= r),
(Gt, _) => PrimVal::from_bool(l > r),
(Ge, _) => PrimVal::from_bool(l >= r),
(BitOr, k) => PrimVal::new(l | r, k),
(BitAnd, k) => PrimVal::new(l & r, k),
(BitXor, k) => PrimVal::new(l ^ r, k),
(Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
(Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
(Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
(Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
_ => {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
return Err(EvalError::Unimplemented(msg));
}
};
Ok((val, false))
}
pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::repr::UnOp::*;
use self::PrimVal::*;
match (un_op, val) {
(Not, Bool(b)) => Ok(Bool(!b)),
(Not, I8(n)) => Ok(I8(!n)),
(Neg, I8(n)) => Ok(I8(-n)),
(Not, I16(n)) => Ok(I16(!n)),
(Neg, I16(n)) => Ok(I16(-n)),
(Not, I32(n)) => Ok(I32(!n)),
(Neg, I32(n)) => Ok(I32(-n)),
(Not, I64(n)) => Ok(I64(!n)),
(Neg, I64(n)) => Ok(I64(-n)),
(Not, U8(n)) => Ok(U8(!n)),
(Not, U16(n)) => Ok(U16(!n)),
(Not, U32(n)) => Ok(U32(!n)),
(Not, U64(n)) => Ok(U64(!n)),
(Neg, F64(n)) => Ok(F64(-n)),
(Neg, F32(n)) => Ok(F32(-n)),
_ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::repr::BinOp::*;
match bin_op {
Eq => Ok(PrimVal::from_bool(false)),
Ne => Ok(PrimVal::from_bool(true)),
Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
_ => bug!(),
}
}
pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::repr::UnOp::*;
use self::PrimValKind::*;
let bits = match (un_op, val.kind) {
(Not, Bool) => !bits_to_bool(val.bits) as u64,
(Not, U8) => !(val.bits as u8) as u64,
(Not, U16) => !(val.bits as u16) as u64,
(Not, U32) => !(val.bits as u32) as u64,
(Not, U64) => !val.bits,
(Not, I8) => !(val.bits as i8) as u64,
(Not, I16) => !(val.bits as i16) as u64,
(Not, I32) => !(val.bits as i32) as u64,
(Not, I64) => !(val.bits as i64) as u64,
(Neg, I8) => -(val.bits as i8) as u64,
(Neg, I16) => -(val.bits as i16) as u64,
(Neg, I32) => -(val.bits as i32) as u64,
(Neg, I64) => -(val.bits as i64) as u64,
(Neg, F32) => f32_to_bits(-bits_to_f32(val.bits)),
(Neg, F64) => f64_to_bits(-bits_to_f64(val.bits)),
_ => {
let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
return Err(EvalError::Unimplemented(msg));
}
};
Ok(PrimVal::new(bits, val.kind))
}