-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Some cleanups around AllocId
management
#56461
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
Changes from 4 commits
a5e57c8
3074fcb
f6b3eb0
3d278e9
a1e83a9
2e00d64
d625019
66b2353
760df2c
982a966
7181aa1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}; | ||
|
@@ -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) => { | ||
|
@@ -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> { | ||
/// 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(), | ||
|
@@ -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 { | ||
|
@@ -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; | ||
} | ||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ( There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
} | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.