_ => {
// Do it in memory
let mplace = self.force_allocation(dest)?;
- assert!(mplace.extra.is_none());
+ assert!(mplace.meta.is_none());
self.memory.write_repeat(mplace.ptr, 0, dest.layout.size)?;
}
}
_ => {
// Do it in memory
let mplace = self.force_allocation(dest)?;
- assert!(mplace.extra.is_none());
+ assert!(mplace.meta.is_none());
self.memory.mark_definedness(mplace.ptr.to_ptr()?, dest.layout.size, false)?;
}
}
extern crate rustc_target;
extern crate syntax;
+use std::collections::HashMap;
+use std::borrow::Cow;
+
use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
use rustc::ty::layout::{TyLayout, LayoutOf, Size};
use rustc::hir::def_id::DefId;
use syntax::ast::Mutability;
use syntax::attr;
-use std::collections::HashMap;
pub use rustc::mir::interpret::*;
pub use rustc_mir::interpret::*;
-pub use rustc_mir::interpret;
+pub use rustc_mir::interpret::{self, AllocMap}; // resolve ambiguity
mod fn_call;
mod operator;
mod tls;
mod locks;
mod range_map;
+mod mono_hash_map;
use fn_call::EvalContextExt as MissingFnsEvalContextExt;
use operator::EvalContextExt as OperatorEvalContextExt;
use tls::{EvalContextExt as TlsEvalContextExt, TlsData};
use range_map::RangeMap;
use helpers::FalibleScalarExt;
+use mono_hash_map::MonoHashMap;
pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
type MemoryData = ();
type MemoryKinds = MiriMemoryKind;
+ type PointerTag = (); // still WIP
- const MUT_STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
+ type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<()>)>;
+
+ const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
const ENFORCE_VALIDITY: bool = false; // this is still WIP
/// Returns Ok() when the function was handled, fail otherwise
fn find_foreign_static(
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
def_id: DefId,
- ) -> EvalResult<'tcx, &'tcx Allocation> {
+ ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> {
let attrs = tcx.get_attrs(def_id);
let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
Some(name) => name.as_str(),
"__cxa_thread_atexit_impl" => {
// This should be all-zero, pointer-sized
let data = vec![0; tcx.data_layout.pointer_size.bytes() as usize];
- let alloc = Allocation::from_bytes(&data[..], tcx.data_layout.pointer_align);
- tcx.intern_const_alloc(alloc)
+ Allocation::from_bytes(&data[..], tcx.data_layout.pointer_align)
}
_ => return err!(Unimplemented(
format!("can't access foreign static: {}", link_name),
)),
};
- Ok(alloc)
+ Ok(Cow::Owned(alloc))
}
fn validation_op(
// We are not interested in detecting loops
Ok(())
}
+
+ fn static_with_default_tag(
+ alloc: &'_ Allocation
+ ) -> Cow<'_, Allocation<Self::PointerTag>> {
+ let alloc = alloc.clone();
+ Cow::Owned(alloc)
+ }
}
--- /dev/null
+//! This is a "monotonic HashMap": A HashMap that, when shared, can be pushed to but not
+//! otherwise mutated. We also Box items in the map. This means we can safely provide
+//! shared references into existing items in the HashMap, because they will not be dropped
+//! (from being removed) or moved (because they are boxed).
+//! The API is is completely tailored to what `memory.rs` needs. It is still in
+//! a separate file to minimize the amount of code that has to care about the unsafety.
+
+use std::collections::hash_map::Entry;
+use std::cell::RefCell;
+use std::hash::Hash;
+use std::borrow::Borrow;
+
+use rustc_data_structures::fx::FxHashMap;
+
+use super::AllocMap;
+
+#[derive(Debug, Clone)]
+pub struct MonoHashMap<K: Hash + Eq, V>(RefCell<FxHashMap<K, Box<V>>>);
+
+impl<K: Hash + Eq, V> Default for MonoHashMap<K, V> {
+ fn default() -> Self {
+ MonoHashMap(RefCell::new(Default::default()))
+ }
+}
+
+impl<K: Hash + Eq, V> AllocMap<K, V> for MonoHashMap<K, V> {
+ #[inline(always)]
+ fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
+ where K: Borrow<Q>
+ {
+ self.0.get_mut().contains_key(k)
+ }
+
+ #[inline(always)]
+ fn insert(&mut self, k: K, v: V) -> Option<V>
+ {
+ self.0.get_mut().insert(k, Box::new(v)).map(|x| *x)
+ }
+
+ #[inline(always)]
+ fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
+ where K: Borrow<Q>
+ {
+ self.0.get_mut().remove(k).map(|x| *x)
+ }
+
+ #[inline(always)]
+ fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
+ self.0.borrow()
+ .iter()
+ .filter_map(move |(k, v)| f(k, &*v))
+ .collect()
+ }
+
+ /// The most interesting method: Providing a shared ref without
+ /// holding the `RefCell` open, and inserting new data if the key
+ /// is not used yet.
+ /// `vacant` is called if the key is not found in the map;
+ /// if it returns a reference, that is used directly, if it
+ /// returns owned data, that is put into the map and returned.
+ #[inline(always)]
+ fn get_or<E>(
+ &self,
+ k: K,
+ vacant: impl FnOnce() -> Result<V, E>
+ ) -> Result<&V, E> {
+ let val: *const V = match self.0.borrow_mut().entry(k) {
+ Entry::Occupied(entry) => &**entry.get(),
+ Entry::Vacant(entry) => &**entry.insert(Box::new(vacant()?)),
+ };
+ // This is safe because `val` points into a `Box`, that we know will not move and
+ // will also not be dropped as long as the shared reference `self` is live.
+ unsafe { Ok(&*val) }
+ }
+
+ #[inline(always)]
+ fn get_mut_or<E>(
+ &mut self,
+ k: K,
+ vacant: impl FnOnce() -> Result<V, E>
+ ) -> Result<&mut V, E>
+ {
+ match self.0.get_mut().entry(k) {
+ Entry::Occupied(e) => Ok(e.into_mut()),
+ Entry::Vacant(e) => {
+ let v = vacant()?;
+ Ok(e.insert(Box::new(v)))
+ }
+ }
+ }
+}