diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index f53d22699ca..427b8aed0f1 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -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))), + } + } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index d5d7cdb6576..463b9bf0063 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -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); diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index f352a4110ca..c3327dd76f9 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -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), } } diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 1555f22efb2..be6147f8b74 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -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))?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index b89210c065d..09b0f8345d8 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -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!(), } } diff --git a/src/memory.rs b/src/memory.rs index a212f25fedd..6979d7ef339 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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> { diff --git a/src/primval.rs b/src/primval.rs index 26c1d715c4c..5008aa24a2a 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -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::(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::(bits) } +} + +fn f32_to_bits(f: f32) -> u64 { + unsafe { transmute::(f) as u64 } +} + +fn f64_to_bits(f: f64) -> u64 { + unsafe { transmute::(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)) +}