6 #![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
14 extern crate rustc_data_structures;
15 extern crate rustc_mir;
16 extern crate rustc_target;
19 use rustc::ty::{self, TyCtxt};
20 use rustc::ty::layout::{TyLayout, LayoutOf, Size};
21 use rustc::ty::subst::Subst;
22 use rustc::hir::def_id::DefId;
25 use rustc_data_structures::fx::FxHasher;
27 use syntax::ast::Mutability;
28 use syntax::codemap::Span;
30 use std::marker::PhantomData;
31 use std::collections::{HashMap, BTreeMap};
32 use std::hash::{Hash, Hasher};
34 pub use rustc::mir::interpret::*;
35 pub use rustc_mir::interpret::*;
46 use fn_call::EvalContextExt as MissingFnsEvalContextExt;
47 use operator::EvalContextExt as OperatorEvalContextExt;
48 use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
49 use tls::EvalContextExt as TlsEvalContextExt;
51 use range_map::RangeMap;
52 use helpers::{ScalarExt, FalibleScalarExt};
54 pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
55 tcx: TyCtxt<'a, 'tcx, 'tcx>,
57 start_wrapper: Option<DefId>,
58 ) -> EvalResult<'tcx, (EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>, Option<Pointer>)> {
59 let mut ecx = EvalContext::new(
60 tcx.at(syntax::codemap::DUMMY_SP),
61 ty::ParamEnv::reveal_all(),
66 let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
67 let main_mir = ecx.load_mir(main_instance.def)?;
68 let mut cleanup_ptr = None; // Scalar to be deallocated when we are done
70 if !main_mir.return_ty().is_nil() || main_mir.arg_count != 0 {
71 return err!(Unimplemented(
72 "miri does not support main functions without `fn()` type signatures"
76 let ptr_size = ecx.memory.pointer_size();
78 if let Some(start_id) = start_wrapper {
79 let main_ret_ty = ecx.tcx.fn_sig(main_id).output();
80 let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
81 let start_instance = ty::Instance::resolve(
83 ty::ParamEnv::reveal_all(),
86 ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
88 let start_mir = ecx.load_mir(start_instance.def)?;
90 if start_mir.arg_count != 3 {
91 return err!(AbiViolation(format!(
92 "'start' lang item should have three arguments, but has {}",
98 let size = ecx.tcx.data_layout.pointer_size;
99 let align = ecx.tcx.data_layout.pointer_align;
100 let ret_ptr = ecx.memory_mut().allocate(size, align, MemoryKind::Stack)?;
101 cleanup_ptr = Some(ret_ptr);
103 // Push our stack frame
104 ecx.push_stack_frame(
108 Place::from_ptr(ret_ptr, align),
109 StackPopCleanup::None,
112 let mut args = ecx.frame().mir.args_iter();
114 // First argument: pointer to main()
115 let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
116 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
117 ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
119 // Second argument (argc): 1
120 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
121 ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
123 // FIXME: extract main source file path
124 // Third argument (argv): &[b"foo"]
125 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
126 let foo = ecx.memory.allocate_bytes(b"foo\0");
127 let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
128 let foo_layout = ecx.layout_of(foo_ty)?;
129 let foo_place = ecx.allocate(foo_layout, MemoryKind::Stack)?;
130 ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
131 ecx.memory.mark_static_initialized(foo_place.to_ptr()?.alloc_id, Mutability::Immutable)?;
132 ecx.write_scalar(foo_place.ptr, dest)?;
134 assert!(args.next().is_none(), "start lang item has more arguments than expected");
136 ecx.push_stack_frame(
140 Place::from_scalar_ptr(Scalar::from_int(1, ptr_size).into(), ty::layout::Align::from_bytes(1, 1).unwrap()),
141 StackPopCleanup::None,
145 let mut args = ecx.frame().mir.args_iter();
146 assert!(args.next().is_none(), "main function must not have arguments");
149 Ok((ecx, cleanup_ptr))
152 pub fn eval_main<'a, 'tcx: 'a>(
153 tcx: TyCtxt<'a, 'tcx, 'tcx>,
155 start_wrapper: Option<DefId>,
157 let (mut ecx, cleanup_ptr) = create_ecx(tcx, main_id, start_wrapper).expect("Couldn't create ecx");
159 let res: EvalResult = do catch {
161 ecx.run_tls_dtors()?;
162 if let Some(cleanup_ptr) = cleanup_ptr {
163 ecx.memory_mut().deallocate(
173 let leaks = ecx.memory().leak_report();
175 // TODO: Prevent leaks which aren't supposed to be there
176 //tcx.sess.err("the evaluated program leaked memory");
180 if let Some(frame) = ecx.stack().last() {
181 let block = &frame.mir.basic_blocks()[frame.block];
182 let span = if frame.stmt < block.statements.len() {
183 block.statements[frame.stmt].source_info.span
185 block.terminator().source_info.span
188 let e = e.to_string();
189 let msg = format!("constant evaluation error: {}", e);
190 let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
191 let (frames, span) = ecx.generate_stacktrace(None);
192 err.span_label(span, e);
193 for FrameInfo { span, location, .. } in frames {
194 err.span_note(span, &format!("inside call to `{}`", location));
198 ecx.tcx.sess.err(&e.to_string());
201 for (i, frame) in ecx.stack().iter().enumerate() {
202 trace!("-------------------");
203 trace!("Frame {}", i);
204 trace!(" return: {:#?}", frame.return_place);
205 for (i, local) in frame.locals.iter().enumerate() {
206 if let Ok(local) = local.access() {
207 trace!(" local {}: {:?}", i, local);
215 #[derive(Clone, Default, PartialEq, Eq)]
216 pub struct Evaluator<'tcx> {
217 /// Environment variables set by `setenv`
218 /// Miri does not expose env vars from the host to the emulated program
219 pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
222 _dummy : PhantomData<&'tcx ()>,
225 impl<'tcx> Hash for Evaluator<'tcx> {
226 fn hash<H: Hasher>(&self, state: &mut H) {
234 let mut h = FxHasher::default();
239 .fold(0u64, |acc, hash| acc.wrapping_add(hash))
244 pub type TlsKey = u128;
246 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
247 pub struct TlsEntry<'tcx> {
248 data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
249 dtor: Option<ty::Instance<'tcx>>,
252 #[derive(Clone, PartialEq, Eq)]
253 pub struct MemoryData<'tcx> {
254 /// The Key to use for the next thread-local allocation.
255 next_thread_local: TlsKey,
257 /// pthreads-style thread-local storage.
258 thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
260 /// Memory regions that are locked by some function
262 /// Only mutable (static mut, heap, stack) allocations have an entry in this map.
263 /// The entry is created when allocating the memory and deleted after deallocation.
264 locks: HashMap<AllocId, RangeMap<LockInfo<'tcx>>>,
266 statics: HashMap<GlobalId<'tcx>, AllocId>,
269 impl<'tcx> MemoryData<'tcx> {
272 next_thread_local: 1, // start with 1 as we must not use 0 on Windows
273 thread_local: BTreeMap::new(),
274 locks: HashMap::new(),
275 statics: HashMap::new(),
280 impl<'tcx> Hash for MemoryData<'tcx> {
281 fn hash<H: Hasher>(&self, state: &mut H) {
283 next_thread_local: _,
289 thread_local.hash(state);
293 impl<'mir, 'tcx: 'mir> Machine<'mir, 'tcx> for Evaluator<'tcx> {
294 type MemoryData = MemoryData<'tcx>;
295 type MemoryKinds = memory::MemoryKind;
297 /// Returns Ok() when the function was handled, fail otherwise
299 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
300 instance: ty::Instance<'tcx>,
301 destination: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
304 ) -> EvalResult<'tcx, bool> {
305 ecx.eval_fn_call(instance, destination, args, span)
308 fn call_intrinsic<'a>(
309 ecx: &mut rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
310 instance: ty::Instance<'tcx>,
313 target: mir::BasicBlock,
314 ) -> EvalResult<'tcx> {
315 ecx.call_intrinsic(instance, args, dest, target)
319 ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
322 left_layout: TyLayout<'tcx>,
324 right_layout: TyLayout<'tcx>,
325 ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
326 ecx.ptr_op(bin_op, left, left_layout, right, right_layout)
329 fn mark_static_initialized<'a>(
330 mem: &mut Memory<'a, 'mir, 'tcx, Self>,
332 _mutability: Mutability,
333 ) -> EvalResult<'tcx, bool> {
334 use memory::MemoryKind::*;
335 match mem.get_alloc_kind(id) {
336 // FIXME: This could be allowed, but not for env vars set during miri execution
337 Some(MemoryKind::Machine(Env)) => err!(Unimplemented("statics can't refer to env vars".to_owned())),
338 _ => Ok(false), // TODO: What does the bool mean?
343 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
345 ) -> EvalResult<'tcx, AllocId> {
346 // Step 1: If the static has already been evaluated return the cached version
347 if let Some(alloc_id) = ecx.memory.data.statics.get(&cid) {
348 return Ok(*alloc_id);
351 let tcx = ecx.tcx.tcx;
354 let mut mir = ecx.load_mir(cid.instance.def)?;
355 if let Some(index) = cid.promoted {
356 mir = &mir.promoted[index];
358 assert!(mir.arg_count == 0);
360 // Step 3: Allocate storage
361 let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
362 assert!(!layout.is_unsized());
363 let ptr = ecx.memory.allocate(
369 // Step 4: Cache allocation id for recursive statics
370 assert!(ecx.memory.data.statics.insert(cid, ptr.alloc_id).is_none());
372 // Step 5: Push stackframe to evaluate static
373 let cleanup = StackPopCleanup::None;
374 ecx.push_stack_frame(
378 Place::from_ptr(ptr, layout.align),
382 // Step 6: Step until static has been initialized
383 let call_stackframe = ecx.stack().len();
384 while ecx.step()? && ecx.stack().len() >= call_stackframe {
385 if ecx.stack().len() == call_stackframe {
387 let frame = ecx.frame();
388 let bb = &frame.mir.basic_blocks()[frame.block];
389 bb.statements.len() == frame.stmt && !bb.is_cleanup &&
390 if let ::rustc::mir::TerminatorKind::Return = bb.terminator().kind { true } else { false }
393 for (local, _local_decl) in mir.local_decls.iter_enumerated().skip(1) {
394 // Don't deallocate locals, because the return value might reference them
395 ecx.storage_dead(local);
401 // TODO: Freeze immutable statics without copying them to the global static cache
403 // Step 7: Return the alloc
408 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
410 ) -> EvalResult<'tcx> {
411 trace!("box_alloc for {:?}", dest.layout.ty);
412 // Call the `exchange_malloc` lang item
413 let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
414 let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
415 let malloc_mir = ecx.load_mir(malloc.def)?;
416 ecx.push_stack_frame(
421 // Don't do anything when we are done. The statement() function will increment
422 // the old stack frame's stmt counter to the next statement, which means that when
423 // exchange_malloc returns, we go on evaluating exactly where we want to be.
424 StackPopCleanup::None,
427 let mut args = ecx.frame().mir.args_iter();
428 let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
430 // First argument: size
431 // (0 is allowed here, this is expected to be handled by the lang item)
432 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
433 let size = layout.size.bytes();
434 ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
436 // Second argument: align
437 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
438 let align = layout.align.abi();
439 ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
442 assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
446 fn global_item_with_linkage<'a>(
447 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
448 _instance: ty::Instance<'tcx>,
449 _mutability: Mutability,
450 ) -> EvalResult<'tcx> {
451 panic!("remove this function from rustc");
455 _mem: &Memory<'a, 'mir, 'tcx, Self>,
459 ) -> EvalResult<'tcx> {
464 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
469 _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
472 ) -> EvalResult<'tcx> {
477 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
478 _reg: Option<::rustc::middle::region::Scope>,
479 ) -> EvalResult<'tcx> {
483 fn validation_op<'a>(
484 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
485 _op: ::rustc::mir::ValidationOp,
486 _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
487 ) -> EvalResult<'tcx> {
488 // FIXME: prevent this from ICEing
489 //ecx.validation_op(op, operand)