From fdef27acf5a233220272776cd565378a4fc59459 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 28 Jun 2017 13:59:16 +0200 Subject: [PATCH 1/3] Copy `path_to_def` from clippy --- src/terminator/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index dc6610a2213b..450cae3b49cb 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,4 +1,5 @@ -use rustc::hir::def_id::DefId; +use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc::hir::def::Def; use rustc::mir; use rustc::ty::{self, TypeVariants, Ty}; use rustc::ty::layout::Layout; @@ -13,6 +14,8 @@ use memory::{MemoryPointer, TlsKey}; use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; +use std::mem; + mod drop; mod intrinsic; @@ -933,4 +936,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(dest_block); Ok(()) } + /// Get the definition associated to a path. + fn path_to_def(&self, path: &[&str]) -> Option { + let cstore = &self.tcx.sess.cstore; + + let crates = cstore.crates(); + crates.iter() + .find(|&&krate| cstore.crate_name(krate) == path[0]) + .and_then(|krate| { + let krate = DefId { + krate: *krate, + index: CRATE_DEF_INDEX, + }; + let mut items = cstore.item_children(krate, self.tcx.sess); + let mut path_it = path.iter().skip(1).peekable(); + + while let Some(segment) = path_it.next() { + for item in &mem::replace(&mut items, vec![]) { + if item.ident.name == *segment { + if path_it.peek().is_none() { + return Some(item.def); + } + + items = cstore.item_children(item.def.def_id(), self.tcx.sess); + break; + } + } + } + None + }) + } } From f78d6a0d9719d6d63b06e0fb0a4648a86bf317b8 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 12 Jul 2017 10:36:14 +0200 Subject: [PATCH 2/3] Don't use magic numbers for synconf names instead read them from the `libc` crate if available. fixes #216 --- src/error.rs | 5 ++++ src/terminator/mod.rs | 64 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/error.rs b/src/error.rs index 801ea5c7da48..3e1155e0b874 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,6 +69,7 @@ pub enum EvalError<'tcx> { NeedsRfc(String), NotConst(String), ReadFromReturnPointer, + PathNotFound(Vec), } pub type EvalResult<'tcx, T = ()> = Result>; @@ -175,6 +176,8 @@ impl<'tcx> Error for EvalError<'tcx> { "this feature is not compatible with constant evaluation", ReadFromReturnPointer => "tried to read from the return pointer", + EvalError::PathNotFound(_) => + "a path could not be resolved, maybe the crate is not loaded", } } @@ -215,6 +218,8 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), NotConst(ref msg) => write!(f, "Cannot evaluate within constants: \"{}\"", msg), + EvalError::PathNotFound(ref path) => + write!(f, "Cannot find path {:?}", path), _ => write!(f, "{}", self.description()), } } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 450cae3b49cb..c15b7b9162d2 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,5 +1,4 @@ use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; -use rustc::hir::def::Def; use rustc::mir; use rustc::ty::{self, TypeVariants, Ty}; use rustc::ty::layout::Layout; @@ -856,12 +855,50 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "sysconf" => { let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); - let result = match name { - 30 => PrimVal::Bytes(4096), // _SC_PAGESIZE - 70 => PrimVal::from_i128(-1), // _SC_GETPW_R_SIZE_MAX - _ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))) - }; - self.write_primval(dest, result, dest_ty)?; + // cache the sysconf integers + let paths = &[ + (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), + (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), + ]; + let mut result = None; + for &(path, path_value) in paths { + if let Ok(instance) = self.resolve_path(path) { + use lvalue::{Global, GlobalId}; + let cid = GlobalId { instance, promoted: None }; + let val = match self.globals.get(&cid).map(|glob| glob.value) { + Some(value) => value, + None => { + let mir = self.load_mir(instance.def)?; + self.globals.insert(cid, Global::uninitialized(mir.return_ty)); + let cleanup = StackPopCleanup::MarkStatic(false); + let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + trace!("pushing stack frame for global: {}", name); + let frame = self.stack.len(); + self.push_stack_frame( + instance, + mir.span, + mir, + Lvalue::Global(cid), + cleanup, + )?; + while self.stack.len() != frame { + self.step()?; + } + self.globals.get(&cid).expect("we just computed the global").value + } + }; + let val = self.value_to_primval(val, usize)?.to_u64()?; + if val == name { + result = Some(path_value); + break; + } + } + } + if let Some(result) = result { + self.write_primval(dest, result, dest_ty)?; + } else { + return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))); + } } // Hook pthread calls that go to the thread-local storage memory subsystem @@ -936,8 +973,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.goto_block(dest_block); Ok(()) } - /// Get the definition associated to a path. - fn path_to_def(&self, path: &[&str]) -> Option { + + /// Get an instance for a path. + fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> { let cstore = &self.tcx.sess.cstore; let crates = cstore.crates(); @@ -955,7 +993,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { for item in &mem::replace(&mut items, vec![]) { if item.ident.name == *segment { if path_it.peek().is_none() { - return Some(item.def); + return Some(ty::Instance::mono(self.tcx, item.def.def_id())); } items = cstore.item_children(item.def.def_id(), self.tcx.sess); @@ -965,5 +1003,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } None }) + .ok_or_else(|| { + let path = path.iter() + .map(|&s| s.to_owned()) + .collect(); + EvalError::PathNotFound(path) + }) } } From f8757aa092ef296ca02a33bb0994f27a60572950 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 13 Jul 2017 16:49:55 +0200 Subject: [PATCH 3/3] Reuse the `const_eval` method for syscall name resolution --- src/const_eval.rs | 28 +++++++++++++++++----------- src/terminator/mod.rs | 28 +++++----------------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/const_eval.rs b/src/const_eval.rs index ff80f68e2c3f..771b740b3755 100644 --- a/src/const_eval.rs +++ b/src/const_eval.rs @@ -1,22 +1,20 @@ -use rustc::hir::def_id::DefId; use rustc::traits::Reveal; -use rustc::ty::subst::Substs; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, TyCtxt, Ty, Instance}; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue}; +use value::PrimVal; use rustc_const_math::ConstInt; use eval_context::{EvalContext, StackPopCleanup}; -pub fn eval_body_as_integer<'a, 'tcx>( +pub fn eval_body_as_primval<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, - (def_id, substs): (DefId, &'tcx Substs<'tcx>), -) -> EvalResult<'tcx, ConstInt> { + instance: Instance<'tcx>, +) -> EvalResult<'tcx, (PrimVal, Ty<'tcx>)> { let limits = ::ResourceLimits::default(); let mut ecx = EvalContext::new(tcx, limits); - let instance = ecx.resolve_associated_const(def_id, substs); let cid = GlobalId { instance, promoted: None }; - if ecx.tcx.has_attr(def_id, "linkage") { + if ecx.tcx.has_attr(instance.def_id(), "linkage") { return Err(EvalError::NotConst("extern global".to_string())); } @@ -28,7 +26,7 @@ pub fn eval_body_as_integer<'a, 'tcx>( ty::ParamEnv::empty(Reveal::All), mir.span); let cleanup = StackPopCleanup::MarkStatic(mutable); - let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); + let name = ty::tls::with(|tcx| tcx.item_path_str(instance.def_id())); trace!("pushing stack frame for global: {}", name); ecx.push_stack_frame( instance, @@ -41,11 +39,19 @@ pub fn eval_body_as_integer<'a, 'tcx>( while ecx.step()? {} } let value = ecx.globals.get(&cid).expect("global not cached").value; - let prim = ecx.value_to_primval(value, mir.return_ty)?.to_bytes()?; + Ok((ecx.value_to_primval(value, mir.return_ty)?, mir.return_ty)) +} + +pub fn eval_body_as_integer<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + instance: Instance<'tcx>, +) -> EvalResult<'tcx, ConstInt> { + let (prim, ty) = eval_body_as_primval(tcx, instance)?; + let prim = prim.to_bytes()?; use syntax::ast::{IntTy, UintTy}; use rustc::ty::TypeVariants::*; use rustc_const_math::{ConstIsize, ConstUsize}; - Ok(match mir.return_ty.sty { + Ok(match ty.sty { TyInt(IntTy::I8) => ConstInt::I8(prim as i128 as i8), TyInt(IntTy::I16) => ConstInt::I16(prim as i128 as i16), TyInt(IntTy::I32) => ConstInt::I32(prim as i128 as i32), diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c15b7b9162d2..643df3608f9c 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -855,7 +855,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "sysconf" => { let name = self.value_to_primval(args[0], usize)?.to_u64()?; trace!("sysconf() called with name {}", name); - // cache the sysconf integers + // cache the sysconf integers via miri's global cache let paths = &[ (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), @@ -863,31 +863,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let mut result = None; for &(path, path_value) in paths { if let Ok(instance) = self.resolve_path(path) { - use lvalue::{Global, GlobalId}; + use lvalue::GlobalId; let cid = GlobalId { instance, promoted: None }; + // compute global if not cached let val = match self.globals.get(&cid).map(|glob| glob.value) { - Some(value) => value, - None => { - let mir = self.load_mir(instance.def)?; - self.globals.insert(cid, Global::uninitialized(mir.return_ty)); - let cleanup = StackPopCleanup::MarkStatic(false); - let name = ty::tls::with(|tcx| tcx.item_path_str(def_id)); - trace!("pushing stack frame for global: {}", name); - let frame = self.stack.len(); - self.push_stack_frame( - instance, - mir.span, - mir, - Lvalue::Global(cid), - cleanup, - )?; - while self.stack.len() != frame { - self.step()?; - } - self.globals.get(&cid).expect("we just computed the global").value - } + Some(value) => self.value_to_primval(value, usize)?.to_u64()?, + None => ::const_eval::eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, }; - let val = self.value_to_primval(val, usize)?.to_u64()?; if val == name { result = Some(path_value); break;