enum AllocKind {
Alloc,
Fn,
- ExternStatic,
+ Static,
}
pub fn specialized_encode_alloc_id<
trace!("encoding {:?} with {:#?}", alloc_id, alloc);
AllocKind::Alloc.encode(encoder)?;
alloc.encode(encoder)?;
- // encode whether this allocation is the root allocation of a static
- tcx.interpret_interner
- .get_corresponding_static_def_id(alloc_id)
- .encode(encoder)?;
} else if let Some(fn_instance) = tcx.interpret_interner.get_fn(alloc_id) {
trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
AllocKind::Fn.encode(encoder)?;
fn_instance.encode(encoder)?;
- } else if let Some(did) = tcx.interpret_interner.get_corresponding_static_def_id(alloc_id) {
- // extern "C" statics don't have allocations, just encode its def_id
- AllocKind::ExternStatic.encode(encoder)?;
+ } else if let Some(did) = tcx.interpret_interner.get_static(alloc_id) {
+ // referring to statics doesn't need to know about their allocations, just hash the DefId
+ AllocKind::Static.encode(encoder)?;
did.encode(encoder)?;
} else {
bug!("alloc id without corresponding allocation: {}", alloc_id);
let allocation = tcx.intern_const_alloc(allocation);
tcx.interpret_interner.intern_at_reserved(alloc_id, allocation);
- if let Some(glob) = Option::<DefId>::decode(decoder)? {
- tcx.interpret_interner.cache(glob, alloc_id);
- }
-
Ok(alloc_id)
},
AllocKind::Fn => {
cache(decoder, id);
Ok(id)
},
- AllocKind::ExternStatic => {
+ AllocKind::Static => {
trace!("creating extern static alloc id at");
let did = DefId::decode(decoder)?;
- let alloc_id = tcx.interpret_interner.reserve();
+ let alloc_id = tcx.interpret_interner.cache_static(did);
cache(decoder, alloc_id);
- tcx.interpret_interner.cache(did, alloc_id);
Ok(alloc_id)
},
}
/// Allows obtaining const allocs via a unique identifier
alloc_by_id: FxHashMap<interpret::AllocId, &'tcx interpret::Allocation>,
- /// Reverse map of `alloc_cache`
- global_cache: FxHashMap<interpret::AllocId, DefId>,
+ /// Allows obtaining static def ids via a unique id
+ statics: FxHashMap<interpret::AllocId, DefId>,
/// The AllocId to assign to the next new regular allocation.
/// Always incremented, never gets smaller.
next_id: interpret::AllocId,
- /// Allows checking whether a static already has an allocation
- ///
- /// This is only important for detecting statics referring to themselves
- // FIXME(oli-obk) move it to the EvalContext?
- alloc_cache: FxHashMap<DefId, interpret::AllocId>,
+ /// Inverse map of `statics`
+ /// Used so we don't allocate a new pointer every time we need one
+ static_cache: FxHashMap<DefId, interpret::AllocId>,
/// A cache for basic byte allocations keyed by their contents. This is used to deduplicate
/// allocations for string and bytestring literals.
self.inner.borrow().alloc_by_id.get(&id).cloned()
}
- pub fn get_cached(
- &self,
- static_id: DefId,
- ) -> Option<interpret::AllocId> {
- self.inner.borrow().alloc_cache.get(&static_id).cloned()
- }
-
- pub fn cache(
+ pub fn cache_static(
&self,
static_id: DefId,
- alloc_id: interpret::AllocId,
- ) {
- let mut inner = self.inner.borrow_mut();
- inner.global_cache.insert(alloc_id, static_id);
- if let Some(old) = inner.alloc_cache.insert(static_id, alloc_id) {
- bug!("tried to cache {:?}, but was already existing as {:#?}", static_id, old);
+ ) -> interpret::AllocId {
+ if let Some(alloc_id) = self.inner.borrow().static_cache.get(&static_id).cloned() {
+ return alloc_id;
}
+ let alloc_id = self.reserve();
+ let mut inner = self.inner.borrow_mut();
+ inner.static_cache.insert(static_id, alloc_id);
+ inner.statics.insert(alloc_id, static_id);
+ alloc_id
}
- pub fn get_corresponding_static_def_id(
+ pub fn get_static(
&self,
ptr: interpret::AllocId,
) -> Option<DefId> {
- self.inner.borrow().global_cache.get(&ptr).cloned()
+ self.inner.borrow().statics.get(&ptr).cloned()
}
pub fn intern_at_reserved(
use rustc::ty::{self, TyCtxt, Ty, Instance};
use rustc::ty::layout::{self, LayoutOf};
use rustc::ty::subst::Subst;
-use rustc::util::nodemap::FxHashSet;
use syntax::ast::Mutability;
use syntax::codemap::Span;
}
span = mir.span;
let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
- let alloc = tcx.interpret_interner.get_cached(cid.instance.def_id());
- let is_static = tcx.is_static(cid.instance.def_id()).is_some();
- let alloc = match alloc {
- Some(alloc) => {
- assert!(cid.promoted.is_none());
- assert!(param_env.caller_bounds.is_empty());
- alloc
- },
- None => {
- assert!(!layout.is_unsized());
- let ptr = ecx.memory.allocate(
- layout.size.bytes(),
- layout.align,
- None,
- )?;
- if is_static {
- tcx.interpret_interner.cache(cid.instance.def_id(), ptr.alloc_id);
- }
- let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
- let mutability = tcx.is_static(cid.instance.def_id());
- let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
- Mutability::Mutable
- } else {
- Mutability::Immutable
- };
- let cleanup = StackPopCleanup::MarkStatic(mutability);
- let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
- let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
- trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
- assert!(mir.arg_count == 0);
- ecx.push_stack_frame(
- cid.instance,
- mir.span,
- mir,
- Place::from_ptr(ptr, layout.align),
- cleanup,
- )?;
-
- while ecx.step()? {}
- ptr.alloc_id
- }
+ assert!(!layout.is_unsized());
+ let ptr = ecx.memory.allocate(
+ layout.size.bytes(),
+ layout.align,
+ None,
+ )?;
+ let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
+ let mutability = tcx.is_static(cid.instance.def_id());
+ let mutability = if mutability == Some(hir::Mutability::MutMutable) || internally_mutable {
+ Mutability::Mutable
+ } else {
+ Mutability::Immutable
};
- let ptr = MemoryPointer::new(alloc, 0).into();
+ let cleanup = StackPopCleanup::MarkStatic(mutability);
+ let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
+ let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
+ trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
+ assert!(mir.arg_count == 0);
+ ecx.push_stack_frame(
+ cid.instance,
+ mir.span,
+ mir,
+ Place::from_ptr(ptr, layout.align),
+ cleanup,
+ )?;
+
+ while ecx.step()? {}
+ let ptr = ptr.into();
// always try to read the value and report errors
let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
// if it's a constant (so it needs no address, directly compute its value)
- Some(val) if !is_static => val,
+ Some(val) if tcx.is_static(cid.instance.def_id()).is_none() => val,
// point at the allocation
_ => Value::ByRef(ptr, layout.align),
};
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
cid: GlobalId<'tcx>,
) -> EvalResult<'tcx, AllocId> {
- let alloc = ecx
- .tcx
- .interpret_interner
- .get_cached(cid.instance.def_id());
- // Don't evaluate when already cached to prevent cycles
- if let Some(alloc) = alloc {
- return Ok(alloc)
- }
- // ensure the static is computed
- ecx.const_eval(cid)?;
Ok(ecx
.tcx
.interpret_interner
- .get_cached(cid.instance.def_id())
- .expect("uncached static"))
+ .cache_static(cid.instance.def_id()))
}
fn box_alloc<'a>(
let def_id = cid.instance.def.def_id();
if tcx.is_foreign_item(def_id) {
- let id = tcx.interpret_interner.get_cached(def_id);
- let id = match id {
- // FIXME: due to caches this shouldn't happen, add some assertions
- Some(id) => id,
- None => {
- let id = tcx.interpret_interner.reserve();
- tcx.interpret_interner.cache(def_id, id);
- id
- },
- };
+ let id = tcx.interpret_interner.cache_static(def_id);
let ty = tcx.type_of(def_id);
let layout = tcx.layout_of(key.param_env.and(ty)).unwrap();
let ptr = MemoryPointer::new(id, 0);
};
let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
- res.map(|(miri_value, ptr, miri_ty)| {
- if tcx.is_static(def_id).is_some() {
- if let Ok(ptr) = ptr.primval.to_ptr() {
- let mut seen = FxHashSet::default();
- create_depgraph_edges(tcx, ptr.alloc_id, &mut seen);
- }
- }
+ res.map(|(miri_value, _, miri_ty)| {
tcx.mk_const(ty::Const {
val: ConstVal::Value(miri_value),
ty: miri_ty,
}
})
}
-
-// This function creates dep graph edges from statics to all referred to statics.
-// This is necessary, because the `const_eval` query cannot directly call itself
-// for other statics, because we cannot prevent recursion in queries.
-//
-// see test/incremental/static_refering_to_other_static2/issue.rs for an example
-// where not creating those edges would cause static A, which refers to static B
-// to point to the old allocation of static B, even though B has changed.
-//
-// In the future we will want to remove this funcion in favour of a system that
-// makes sure that statics don't need to have edges to other statics as long as
-// they are only referring by reference and not inspecting the other static's body.
-fn create_depgraph_edges<'a, 'tcx>(
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- alloc_id: AllocId,
- seen: &mut FxHashSet<AllocId>,
-) {
- trace!("create_depgraph_edges: {:?}, {:?}", alloc_id, seen);
- if seen.insert(alloc_id) {
- trace!("seen: {:?}, {:?}", alloc_id, seen);
- if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) {
- trace!("get_alloc: {:?}, {:?}, {:?}", alloc_id, seen, alloc);
- for (_, &reloc) in &alloc.relocations {
- if let Some(did) = tcx.interpret_interner.get_corresponding_static_def_id(reloc) {
- trace!("get_corresponding: {:?}, {:?}, {:?}, {:?}, {:?}", alloc_id, seen, alloc, did, reloc);
- let _ = tcx.maybe_optimized_mir(did);
- }
- create_depgraph_edges(tcx, reloc, seen);
- }
- }
- }
-}