//! An interpreter for MIR used in CTFE and by miri #[macro_export] macro_rules! err { ($($tt:tt)*) => { Err($crate::mir::interpret::EvalErrorKind::$($tt)*.into()) }; } mod error; mod value; pub use self::error::{EvalError, EvalResult, EvalErrorKind}; pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64}; use std::collections::BTreeMap; use std::fmt; use mir; use ty; use ty::layout::{self, Align, HasDataLayout}; use middle::region; use std::iter; #[derive(Clone, Debug, PartialEq)] pub enum Lock { NoLock, WriteLock(DynamicLifetime), /// This should never be empty -- that would be a read lock held and nobody there to release it... ReadLock(Vec), } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct DynamicLifetime { pub frame: usize, pub region: Option, // "None" indicates "until the function ends" } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AccessKind { Read, Write, } /// Uniquely identifies a specific constant or static. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct GlobalId<'tcx> { /// For a constant or static, the `Instance` of the item itself. /// For a promoted global, the `Instance` of the function they belong to. pub instance: ty::Instance<'tcx>, /// The index for promoted globals within their function's `Mir`. pub promoted: Option, } //////////////////////////////////////////////////////////////////////////////// // Pointer arithmetic //////////////////////////////////////////////////////////////////////////////// pub trait PointerArithmetic: layout::HasDataLayout { // These are not supposed to be overriden. //// Trunace the given value to the pointer size; also return whether there was an overflow fn truncate_to_ptr(self, val: u128) -> (u64, bool) { let max_ptr_plus_1 = 1u128 << self.data_layout().pointer_size.bits(); ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) } // Overflow checking only works properly on the range from -u64 to +u64. fn overflowing_signed_offset(self, val: u64, i: i128) -> (u64, bool) { // FIXME: is it possible to over/underflow here? if i < 0 { // trickery to ensure that i64::min_value() works fine // this formula only works for true negative values, it panics for zero! let n = u64::max_value() - (i as u64) + 1; val.overflowing_sub(n) } else { self.overflowing_offset(val, i as u64) } } fn overflowing_offset(self, val: u64, i: u64) -> (u64, bool) { let (res, over1) = val.overflowing_add(i); let (res, over2) = self.truncate_to_ptr(res as u128); (res, over1 || over2) } fn signed_offset<'tcx>(self, val: u64, i: i64) -> EvalResult<'tcx, u64> { let (res, over) = self.overflowing_signed_offset(val, i as i128); if over { err!(OverflowingMath) } else { Ok(res) } } fn offset<'tcx>(self, val: u64, i: u64) -> EvalResult<'tcx, u64> { let (res, over) = self.overflowing_offset(val, i); if over { err!(OverflowingMath) } else { Ok(res) } } fn wrapping_signed_offset(self, val: u64, i: i64) -> u64 { self.overflowing_signed_offset(val, i as i128).0 } } impl PointerArithmetic for T {} #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct MemoryPointer { pub alloc_id: AllocId, pub offset: u64, } impl<'tcx> MemoryPointer { pub fn new(alloc_id: AllocId, offset: u64) -> Self { MemoryPointer { alloc_id, offset } } pub(crate) fn wrapping_signed_offset(self, i: i64, cx: C) -> Self { MemoryPointer::new( self.alloc_id, cx.data_layout().wrapping_signed_offset(self.offset, i), ) } pub fn overflowing_signed_offset(self, i: i128, cx: C) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } pub(crate) fn signed_offset(self, i: i64, cx: C) -> EvalResult<'tcx, Self> { Ok(MemoryPointer::new( self.alloc_id, cx.data_layout().signed_offset(self.offset, i)?, )) } pub fn overflowing_offset(self, i: u64, cx: C) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } pub fn offset(self, i: u64, cx: C) -> EvalResult<'tcx, Self> { Ok(MemoryPointer::new( self.alloc_id, cx.data_layout().offset(self.offset, i)?, )) } } #[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] pub struct AllocId(pub u64); impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Debug, Eq, PartialEq, Hash)] pub struct Allocation { /// The actual bytes of the allocation. /// Note that the bytes of a pointer represent the offset of the pointer pub bytes: Vec, /// Maps from byte addresses to allocations. /// Only the first byte of a pointer is inserted into the map. pub relocations: BTreeMap, /// Denotes undefined memory. Reading from undefined memory is forbidden in miri pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, } impl Allocation { pub fn from_bytes(slice: &[u8]) -> Self { let mut undef_mask = UndefMask::new(0); undef_mask.grow(slice.len() as u64, true); Self { bytes: slice.to_owned(), relocations: BTreeMap::new(), undef_mask, align: Align::from_bytes(1, 1).unwrap(), } } } //////////////////////////////////////////////////////////////////////////////// // Undefined byte tracking //////////////////////////////////////////////////////////////////////////////// type Block = u64; const BLOCK_SIZE: u64 = 64; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct UndefMask { blocks: Vec, len: u64, } impl UndefMask { pub fn new(size: u64) -> Self { let mut m = UndefMask { blocks: vec![], len: 0, }; m.grow(size, false); m } /// Check whether the range `start..end` (end-exclusive) is entirely defined. pub fn is_range_defined(&self, start: u64, end: u64) -> bool { if end > self.len { return false; } for i in start..end { if !self.get(i) { return false; } } true } pub fn set_range(&mut self, start: u64, end: u64, new_state: bool) { let len = self.len; if end > len { self.grow(end - len, new_state); } self.set_range_inbounds(start, end, new_state); } pub fn set_range_inbounds(&mut self, start: u64, end: u64, new_state: bool) { for i in start..end { self.set(i, new_state); } } pub fn get(&self, i: u64) -> bool { let (block, bit) = bit_index(i); (self.blocks[block] & 1 << bit) != 0 } pub fn set(&mut self, i: u64, new_state: bool) { let (block, bit) = bit_index(i); if new_state { self.blocks[block] |= 1 << bit; } else { self.blocks[block] &= !(1 << bit); } } pub fn grow(&mut self, amount: u64, new_state: bool) { let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len; if amount > unused_trailing_bits { let additional_blocks = amount / BLOCK_SIZE + 1; assert_eq!(additional_blocks as usize as u64, additional_blocks); self.blocks.extend( iter::repeat(0).take(additional_blocks as usize), ); } let start = self.len; self.len += amount; self.set_range_inbounds(start, start + amount, new_state); } } fn bit_index(bits: u64) -> (usize, usize) { let a = bits / BLOCK_SIZE; let b = bits % BLOCK_SIZE; assert_eq!(a as usize as u64, a); assert_eq!(b as usize as u64, b); (a as usize, b as usize) }