Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some cleanups around AllocId management #56461

Merged
merged 11 commits into from
Dec 13, 2018
2 changes: 1 addition & 1 deletion src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ impl_stable_hash_for!(
);

impl_stable_hash_for!(
impl<'tcx, M> for enum mir::interpret::AllocType<'tcx, M> [ mir::interpret::AllocType ] {
impl<'tcx> for enum mir::interpret::AllocType<'tcx> [ mir::interpret::AllocType ] {
Function(instance),
Static(def_id),
Memory(mem),
Expand Down
62 changes: 41 additions & 21 deletions src/librustc/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ use ty::{self, TyCtxt, Instance};
use ty::layout::{self, Size};
use middle::region;
use std::io;
use std::hash::Hash;
use rustc_serialize::{Encoder, Decodable, Encodable};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{Lock as Mutex, HashMapExt};
Expand Down Expand Up @@ -104,7 +103,7 @@ pub fn specialized_encode_alloc_id<
tcx: TyCtxt<'a, 'tcx, 'tcx>,
alloc_id: AllocId,
) -> Result<(), E::Error> {
let alloc_type: AllocType<'tcx, &'tcx Allocation> =
let alloc_type: AllocType<'tcx> =
tcx.alloc_map.lock().get(alloc_id).expect("no value for AllocId");
match alloc_type {
AllocType::Memory(alloc) => {
Expand Down Expand Up @@ -292,29 +291,29 @@ impl fmt::Display for AllocId {
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable)]
pub enum AllocType<'tcx, M> {
pub enum AllocType<'tcx> {
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
/// The alloc id is used as a function pointer
Function(Instance<'tcx>),
/// The alloc id points to a "lazy" static variable that did not get computed (yet).
/// This is also used to break the cycle in recursive statics.
Static(DefId),
/// The alloc id points to memory
Memory(M)
Memory(&'tcx Allocation),
}

pub struct AllocMap<'tcx, M> {
pub struct AllocMap<'tcx> {
/// Lets you know what an AllocId refers to
id_to_type: FxHashMap<AllocId, AllocType<'tcx, M>>,
id_to_type: FxHashMap<AllocId, AllocType<'tcx>>,

/// Used to ensure that functions and statics only get one associated AllocId
type_interner: FxHashMap<AllocType<'tcx, M>, AllocId>,
/// Used to ensure that statics only get one associated AllocId
type_interner: FxHashMap<AllocType<'tcx>, AllocId>,

/// The AllocId to assign to the next requested id.
/// Always incremented, never gets smaller.
next_id: AllocId,
}

impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
impl<'tcx> AllocMap<'tcx> {
pub fn new() -> Self {
AllocMap {
id_to_type: Default::default(),
Expand All @@ -323,8 +322,11 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
}
}

/// obtains a new allocation ID that can be referenced but does not
/// Obtains a new allocation ID that can be referenced but does not
/// yet have an allocation backing it.
///
/// Make sure to call `set_id_memory` or `set_id_same_memory` before returning such an
/// `AllocId` from a query.
pub fn reserve(
&mut self,
) -> AllocId {
Expand All @@ -337,7 +339,7 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
next
}

fn intern(&mut self, alloc_type: AllocType<'tcx, M>) -> AllocId {
fn intern(&mut self, alloc_type: AllocType<'tcx>) -> AllocId {
if let Some(&alloc_id) = self.type_interner.get(&alloc_type) {
return alloc_id;
}
Expand All @@ -348,42 +350,60 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
id
}

// FIXME: Check if functions have identity. If not, we should not intern these,
// but instead create a new id per use.
// Alternatively we could just make comparing function pointers an error.
/// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
/// by the linker and functions can be duplicated across crates.
/// We thus generate a new `AllocId` for every mention of a function. This means that
/// `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> AllocId {
self.intern(AllocType::Function(instance))
let id = self.reserve();
self.id_to_type.insert(id, AllocType::Function(instance));
id
}

pub fn get(&self, id: AllocId) -> Option<AllocType<'tcx, M>> {
/// Returns `None` in case the `AllocId` is dangling.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Dangling" for the global memory only, right? It could still be allocated in the local (EvalContext) memory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the local memory will never hit the global one if it has that AllocId locally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I just think the comment should explain what "dangling" means here.

/// This function exists to allow const eval to detect the difference between evaluation-
/// local dangling pointers and allocations in constants/statics.
pub fn get(&self, id: AllocId) -> Option<AllocType<'tcx>> {
self.id_to_type.get(&id).cloned()
}

pub fn unwrap_memory(&self, id: AllocId) -> M {
/// Panics if the `AllocId` does not refer to an `Allocation`
pub fn unwrap_memory(&self, id: AllocId) -> &'tcx Allocation {
match self.get(id) {
Some(AllocType::Memory(mem)) => mem,
_ => bug!("expected allocation id {} to point to memory", id),
}
}

/// Generate an `AllocId` for a static or return a cached one in case this function has been
/// called on the same static before.
pub fn intern_static(&mut self, static_id: DefId) -> AllocId {
self.intern(AllocType::Static(static_id))
}

pub fn allocate(&mut self, mem: M) -> AllocId {
/// Intern the `Allocation` and return a new `AllocId`, even if there's already an identical
/// `Allocation` with a different `AllocId`.
// FIXME: is this really necessary? Can we ensure `FOO` and `BAR` being different after codegen
// in `static FOO: u32 = 42; static BAR: u32 = 42;` even if they reuse the same allocation
// inside rustc?
pub fn allocate(&mut self, mem: &'tcx Allocation) -> AllocId {
let id = self.reserve();
self.set_id_memory(id, mem);
id
}

pub fn set_id_memory(&mut self, id: AllocId, mem: M) {
/// Freeze an `AllocId` created with `reserve` by pointing it at an `Allocation`. Trying to
/// call this function twice, even with the same `Allocation` will ICE the compiler.
pub fn set_id_memory(&mut self, id: AllocId, mem: &'tcx Allocation) {
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
if let Some(old) = self.id_to_type.insert(id, AllocType::Memory(mem)) {
bug!("tried to set allocation id {}, but it was already existing as {:#?}", id, old);
}
}

pub fn set_id_same_memory(&mut self, id: AllocId, mem: M) {
self.id_to_type.insert_same(id, AllocType::Memory(mem));
/// Freeze an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called
/// twice for the same `(AllocId, Allocation)` pair.
pub fn set_id_same_memory(&mut self, id: AllocId, mem: &'tcx Allocation) {
self.id_to_type.insert_same(id, AllocType::Memory(mem));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ pub struct GlobalCtxt<'tcx> {
/// Stores the value of constants (and deduplicates the actual memory)
allocation_interner: Lock<FxHashMap<&'tcx Allocation, ()>>,

pub alloc_map: Lock<interpret::AllocMap<'tcx, &'tcx Allocation>>,
pub alloc_map: Lock<interpret::AllocMap<'tcx>>,

layout_interner: Lock<FxHashMap<&'tcx LayoutDetails, ()>>,

Expand Down