From 0f177fdecf07ae2a23c17452a6b00b2ba2c5b36c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 7 Sep 2016 18:34:59 +0200 Subject: [PATCH 1/3] implement more casts --- src/error.rs | 2 +- src/interpreter/cast.rs | 96 +++++++++++++++++++++++++++++ src/interpreter/mod.rs | 113 +++++++++++++++++++++++++++------- src/interpreter/terminator.rs | 2 +- src/memory.rs | 4 +- 5 files changed, 190 insertions(+), 27 deletions(-) create mode 100644 src/interpreter/cast.rs diff --git a/src/error.rs b/src/error.rs index 5b39399070..726a4dcfa3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,7 +28,7 @@ pub enum EvalError<'tcx> { ExecuteMemory, ArrayIndexOutOfBounds(Span, u64, u64), Math(Span, ConstMathErr), - InvalidChar(u32), + InvalidChar(u64), OutOfMemory { allocation_size: usize, memory_size: usize, diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs new file mode 100644 index 0000000000..580f957b44 --- /dev/null +++ b/src/interpreter/cast.rs @@ -0,0 +1,96 @@ + +use super::{ + EvalContext, +}; +use error::{EvalResult, EvalError}; +use rustc::ty; +use primval::PrimVal; +use memory::Pointer; + +use rustc::ty::Ty; +use syntax::ast; + +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) | + IntegerPtr(u) => self.cast_const_int(u, ty, false), + FnPtr(ptr) | + AbstractPtr(ptr) => self.cast_ptr(ptr, ty), + } + } + + fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + match ty.sty { + ty::TyRef(..) | + ty::TyRawPtr(_) => Ok(AbstractPtr(ptr)), + ty::TyFnPtr(_) => Ok(FnPtr(ptr)), + _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", 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) + } + + fn cast_const_int(&self, v: u64, ty: ty::Ty<'tcx>, negative: bool) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + 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) => { + 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) => { + 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(IntegerPtr(v)), + ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)), + ty::TyChar => Err(EvalError::InvalidChar(v)), + _ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))), + } + } + + fn cast_const_float(&self, val: f64, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + use primval::PrimVal::*; + 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)), + _ => Err(EvalError::Unimplemented(format!("float to {:?} cast", ty))), + } + } +} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a8d29f7875..bbdde3729c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -22,6 +22,7 @@ use std::collections::HashMap; mod step; mod terminator; +mod cast; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -211,9 +212,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len(), 1)?; let ptr = self.memory.allocate(psize * 2, psize)?; + let (ptr, extra) = self.get_fat_ptr(ptr); self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; - self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?; + self.memory.write_usize(extra, s.len() as u64)?; Ok(ptr) } ByteStr(ref bs) => { @@ -244,6 +246,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + // generics are weird, don't run this function on a generic + debug_assert_eq!(self.monomorphize(ty, self.substs()), ty); ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } @@ -558,12 +562,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let lv = self.eval_lvalue(lvalue)?; - self.memory.write_ptr(dest, lv.ptr)?; + let (ptr, extra) = self.get_fat_ptr(dest); + self.memory.write_ptr(ptr, lv.ptr)?; match lv.extra { LvalueExtra::None => {}, LvalueExtra::Length(len) => { - let len_ptr = dest.offset(self.memory.pointer_size() as isize); - self.memory.write_usize(len_ptr, len)?; + self.memory.write_usize(extra, len)?; } LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -583,14 +587,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Unsize => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - self.move_(src, dest, src_ty)?; + let (ptr, extra) = self.get_fat_ptr(dest); + self.move_(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - let len_ptr = dest.offset(self.memory.pointer_size() as isize); - self.memory.write_usize(len_ptr, length as u64)?; + self.memory.write_usize(extra, length as u64)?; } _ => return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))), @@ -600,20 +604,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - // FIXME(solson): Wrong for almost everything. - warn!("misc cast from {:?} to {:?}", src_ty, dest_ty); - let dest_size = self.type_size(dest_ty); - let src_size = self.type_size(src_ty); - let dest_align = self.type_align(dest_ty); - - // Hack to support fat pointer -> thin pointer casts to keep tests for - // other things passing for now. - let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty)); - - if dest_size == src_size || is_fat_ptr_cast { - self.memory.copy(src, dest, dest_size, dest_align)?; + if self.type_is_immediate(src_ty) { + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); + assert!(self.type_is_immediate(dest_ty)); + let src_val = self.read_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, dest_ty)?; + self.memory.write_primval(dest, dest_val)?; } else { - return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue))); + // Casts from a fat-ptr. + assert!(self.type_is_fat_ptr(src_ty)); + let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); + let ptr_size = self.memory.pointer_size(); + let dest_ty = self.monomorphize(dest_ty, self.substs()); + if self.type_is_fat_ptr(dest_ty) { + // FIXME: add assertion that the extra part of the src_ty and + // dest_ty is of the same type + self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?; + } else { // cast to thin-ptr + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and + // pointer-cast of that pointer to desired pointer type. + self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; + } } } @@ -644,6 +656,35 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } + /// equivalent to rustc_trans::common::type_is_immediate + fn type_is_immediate(&self, ty: Ty<'tcx>) -> bool { + let simple = ty.is_scalar() || + ty.is_unique() || ty.is_region_ptr() || + ty.is_simd(); + if simple && !self.type_is_fat_ptr(ty) { + return true; + } + if !self.type_is_sized(ty) { + return false; + } + match ty.sty { + ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) | + ty::TyClosure(..) => { + self.type_size(ty) < self.memory.pointer_size() + } + _ => self.type_size(ty) == 0 + } + } + + fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty::TypeAndMut{ty, ..}) | + ty::TyBox(ty) => !self.type_is_sized(ty), + _ => false, + } + } + fn nonnull_offset(&self, ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32]) -> EvalResult<'tcx, Size> { // Skip the constant 0 at the start meant for LLVM GEP. let mut path = discrfield.iter().skip(1).map(|&i| i as usize); @@ -809,11 +850,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Deref => { let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); + self.memory.dump(base.ptr.alloc_id); let ptr = self.memory.read_ptr(base.ptr)?; let extra = match pointee_ty.sty { ty::TySlice(_) | ty::TyStr => { - let len_ptr = base.ptr.offset(self.memory.pointer_size() as isize); - let len = self.memory.read_usize(len_ptr)?; + let (_, extra) = self.get_fat_ptr(base.ptr); + let len = self.memory.read_usize(extra)?; LvalueExtra::Length(len) } ty::TyTrait(_) => unimplemented!(), @@ -842,6 +884,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } + fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + (ptr, ptr.offset(self.memory.pointer_size() as isize)) + } + fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } @@ -865,7 +913,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let c = self.memory.read_uint(ptr, 4)? as u32; match ::std::char::from_u32(c) { Some(ch) => PrimVal::Char(ch), - None => return Err(EvalError::InvalidChar(c)), + None => return Err(EvalError::InvalidChar(c as u64)), } } (_, &ty::TyInt(IntTy::I8)) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), @@ -905,6 +953,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + (_, &ty::TyEnum(..)) => { + use rustc::ty::layout::Layout::*; + if let CEnum { discr, signed, .. } = *self.type_layout(ty) { + match (discr.size().bytes(), signed) { + (1, true) => PrimVal::I8(self.memory.read_int(ptr, 1)? as i8), + (2, true) => PrimVal::I16(self.memory.read_int(ptr, 2)? as i16), + (4, true) => PrimVal::I32(self.memory.read_int(ptr, 4)? as i32), + (8, true) => PrimVal::I64(self.memory.read_int(ptr, 8)? as i64), + (1, false) => PrimVal::U8(self.memory.read_uint(ptr, 1)? as u8), + (2, false) => PrimVal::U16(self.memory.read_uint(ptr, 2)? as u16), + (4, false) => PrimVal::U32(self.memory.read_uint(ptr, 4)? as u32), + (8, false) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64), + (size, _) => bug!("CEnum discr size {}", size), + } + } else { + bug!("primitive read of non-clike enum: {:?}", ty); + } + }, + _ => bug!("primitive read of non-primitive type: {:?}", ty), }; Ok(val) diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index 36e2c05f4f..afba23fac5 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -47,7 +47,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { - return Err(EvalError::InvalidChar(discr_val as u32)); + return Err(EvalError::InvalidChar(discr_val as u64)); } } diff --git a/src/memory.rs b/src/memory.rs index 3cf0bc9755..75cbb16122 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -455,8 +455,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size), PrimVal::F32(f) => self.write_f32(ptr, f), PrimVal::F64(f) => self.write_f64(ptr, f), - PrimVal::FnPtr(_p) | - PrimVal::AbstractPtr(_p) => unimplemented!(), + PrimVal::FnPtr(p) | + PrimVal::AbstractPtr(p) => self.write_ptr(ptr, p), } } From d627cc749f9d504751e0636da3be620f01520088 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 8 Sep 2016 10:25:45 +0200 Subject: [PATCH 2/3] use cheap assertions instead of expensive debug assertions --- src/interpreter/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bbdde3729c..ef6318a35a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -5,7 +5,7 @@ use rustc::mir::repr as mir; use rustc::traits::Reveal; use rustc::ty::layout::{self, Layout, Size}; use rustc::ty::subst::{self, Subst, Substs}; -use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::util::nodemap::DefIdMap; use rustc_data_structures::indexed_vec::Idx; use std::cell::RefCell; @@ -247,7 +247,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { // generics are weird, don't run this function on a generic - debug_assert_eq!(self.monomorphize(ty, self.substs()), ty); + assert!(!ty.needs_subst()); ty.is_sized(self.tcx, &self.tcx.empty_parameter_environment(), DUMMY_SP) } From 168d9e77456b09d062f84d808c0f42336c03acb1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 8 Sep 2016 10:26:33 +0200 Subject: [PATCH 3/3] don't use `type_is_immediate` for finding fat ptr casts --- src/interpreter/mod.rs | 37 +++++++------------------------------ 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index ef6318a35a..e77ec942bb 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -604,16 +604,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Misc => { let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); - if self.type_is_immediate(src_ty) { - // FIXME: dest_ty should already be monomorphized - let dest_ty = self.monomorphize(dest_ty, self.substs()); - assert!(self.type_is_immediate(dest_ty)); - let src_val = self.read_primval(src, src_ty)?; - let dest_val = self.cast_primval(src_val, dest_ty)?; - self.memory.write_primval(dest, dest_val)?; - } else { - // Casts from a fat-ptr. - assert!(self.type_is_fat_ptr(src_ty)); + if self.type_is_fat_ptr(src_ty) { let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); let ptr_size = self.memory.pointer_size(); let dest_ty = self.monomorphize(dest_ty, self.substs()); @@ -626,6 +617,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // pointer-cast of that pointer to desired pointer type. self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; } + } else { + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); + let src_val = self.read_primval(src, src_ty)?; + let dest_val = self.cast_primval(src_val, dest_ty)?; + self.memory.write_primval(dest, dest_val)?; } } @@ -656,26 +653,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(()) } - /// equivalent to rustc_trans::common::type_is_immediate - fn type_is_immediate(&self, ty: Ty<'tcx>) -> bool { - let simple = ty.is_scalar() || - ty.is_unique() || ty.is_region_ptr() || - ty.is_simd(); - if simple && !self.type_is_fat_ptr(ty) { - return true; - } - if !self.type_is_sized(ty) { - return false; - } - match ty.sty { - ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) | - ty::TyClosure(..) => { - self.type_size(ty) < self.memory.pointer_size() - } - _ => self.type_size(ty) == 0 - } - } - fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { match ty.sty { ty::TyRawPtr(ty::TypeAndMut{ty, ..}) |