Generate thread local allocations in eval_maybe_thread_local_static_const.

This commit is contained in:
Vytautas Astrauskas 2020-04-15 14:34:34 -07:00
parent 963e9698f9
commit 51b16e56cd
2 changed files with 78 additions and 94 deletions

View File

@ -18,6 +18,7 @@
mir,
ty::{
self,
Instance,
layout::{LayoutCx, LayoutError, TyAndLayout},
TyCtxt,
},
@ -27,7 +28,7 @@
use crate::*;
pub use crate::threads::{ThreadId, ThreadManager, ThreadState, ThreadLocalStorage};
pub use crate::threads::{ThreadId, ThreadManager, ThreadState};
// Some global facts about the emulated machine.
pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
@ -110,7 +111,6 @@ pub struct AllocExtra {
pub struct MemoryExtra {
pub stacked_borrows: Option<stacked_borrows::MemoryExtra>,
pub intptrcast: intptrcast::MemoryExtra,
pub tls: ThreadLocalStorage,
/// Mapping extern static names to their canonical allocation.
extern_statics: FxHashMap<Symbol, AllocId>,
@ -147,7 +147,6 @@ pub fn new(
rng: RefCell::new(rng),
tracked_alloc_id,
check_alignment,
tls: Default::default(),
}
}
@ -423,24 +422,58 @@ fn box_alloc(
fn eval_maybe_thread_local_static_const(
ecx: &InterpCx<'mir, 'tcx, Self>,
mut val: mir::interpret::ConstValue<'tcx>
)-> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
mut val: mir::interpret::ConstValue<'tcx>,
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
match &mut val {
mir::interpret::ConstValue::Scalar(Scalar::Ptr(ptr)) => {
let alloc_id = ptr.alloc_id;
let alloc = ecx.tcx.alloc_map.lock().get(alloc_id);
match alloc {
Some(GlobalAlloc::Static(def_id))
if ecx.tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) => {
if ecx
.tcx
.codegen_fn_attrs(def_id)
.flags
.contains(CodegenFnAttrFlags::THREAD_LOCAL) =>
{
// We have a thread-local static.
let new_alloc_id = ecx.memory.extra.tls.get_or_register_allocation(
*ecx.memory.tcx, alloc_id);
let new_alloc_id = if let Some(new_alloc_id) =
ecx.get_thread_local_alloc_id(alloc_id)
{
new_alloc_id
} else {
if ecx.tcx.is_foreign_item(def_id) {
throw_unsup_format!(
"Foreign thread-local statics are not supported."
)
}
let instance = Instance::mono(ecx.tcx.tcx, def_id);
let gid = GlobalId { instance, promoted: None };
let raw_const = ecx
.tcx
.const_eval_raw(ty::ParamEnv::reveal_all().and(gid))
.map_err(|err| {
// no need to report anything, the const_eval call takes care of that
// for statics
assert!(ecx.tcx.is_static(def_id));
match err {
ErrorHandled::Reported => err_inval!(ReferencedConstant),
ErrorHandled::TooGeneric => err_inval!(TooGeneric),
}
})?;
let id = raw_const.alloc_id;
let mut alloc_map = ecx.tcx.alloc_map.lock();
let allocation = alloc_map.unwrap_memory(id);
let new_alloc_id = alloc_map.create_memory_alloc(allocation);
ecx.set_thread_local_alloc_id(alloc_id, new_alloc_id);
new_alloc_id
};
ptr.alloc_id = new_alloc_id;
},
_ => {},
}
_ => {}
}
}
_ => {},
_ => {}
}
Ok(val)
}
@ -470,15 +503,6 @@ fn canonical_alloc_id(mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
}
}
#[inline(always)]
fn resolve_maybe_global_alloc(
tcx: ty::query::TyCtxtAt<'tcx>,
extra: &Self::MemoryExtra,
id: AllocId,
) -> Option<mir::interpret::GlobalAlloc<'tcx>> {
extra.tls.resolve_allocation(*tcx, id)
}
fn init_allocation_extra<'b>(
memory_extra: &MemoryExtra,
id: AllocId,

View File

@ -1,15 +1,12 @@
//! Implements threads.
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::convert::TryFrom;
use log::trace;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir;
use rustc_middle::ty;
use crate::*;
@ -129,17 +126,41 @@ pub struct ThreadManager<'mir, 'tcx> {
threads: IndexVec<ThreadId, Thread<'mir, 'tcx>>,
/// A counter used to generate unique identifiers for blocksets.
blockset_counter: u32,
/// A mapping from an allocation id of a thread-local static to an
/// allocation id of a thread specific allocation.
thread_local_alloc_ids: RefCell<FxHashMap<(AllocId, ThreadId), AllocId>>,
}
impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
fn default() -> Self {
let mut threads = IndexVec::new();
threads.push(Default::default());
Self { active_thread: ThreadId::new(0), threads: threads, blockset_counter: 0 }
Self {
active_thread: ThreadId::new(0),
threads: threads,
blockset_counter: 0,
thread_local_alloc_ids: Default::default(),
}
}
}
impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
/// Check if we have an allocation for the given thread local static for the
/// active thread.
pub fn get_thread_local_alloc_id(&self, static_alloc_id: AllocId) -> Option<AllocId> {
self.thread_local_alloc_ids.borrow().get(&(static_alloc_id, self.active_thread)).cloned()
}
/// Set the allocation id as the allocation id of the given thread local
/// static for the active thread.
pub fn set_thread_local_alloc_id(&self, static_alloc_id: AllocId, new_alloc_id: AllocId) {
assert!(
self.thread_local_alloc_ids
.borrow_mut()
.insert((static_alloc_id, self.active_thread), new_alloc_id)
.is_none(),
"Bug: a thread local initialized twice for the same thread."
);
}
/// Borrow the stack of the active thread.
fn active_thread_stack(&self) -> &[Frame<'mir, 'tcx, Tag, FrameData<'tcx>>] {
&self.threads[self.active_thread].stack
@ -251,69 +272,16 @@ fn schedule(&mut self) -> InterpResult<'tcx, bool> {
}
}
/// In Rust, a thread local variable is just a specially marked static. To
/// ensure a property that each memory allocation has a globally unique
/// allocation identifier, we create a fresh allocation id for each thread. This
/// data structure keeps the track of the created allocation identifiers and
/// their relation to the original static allocations.
#[derive(Clone, Debug, Default)]
pub struct ThreadLocalStorage {
/// A map from a thread local allocation identifier to the static from which
/// it was created.
thread_local_origin: RefCell<FxHashMap<AllocId, AllocId>>,
/// A map from a thread local static and thread id to the unique thread
/// local allocation.
thread_local_allocations: RefCell<FxHashMap<(AllocId, ThreadId), AllocId>>,
/// The currently active thread.
active_thread: Option<ThreadId>,
}
impl ThreadLocalStorage {
/// For static allocation identifier `original_id` get a thread local
/// allocation identifier. If it is not allocated yet, allocate.
pub fn get_or_register_allocation(&self, tcx: ty::TyCtxt<'_>, original_id: AllocId) -> AllocId {
match self
.thread_local_allocations
.borrow_mut()
.entry((original_id, self.active_thread.unwrap()))
{
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let fresh_id = tcx.alloc_map.lock().reserve();
entry.insert(fresh_id);
self.thread_local_origin.borrow_mut().insert(fresh_id, original_id);
trace!(
"get_or_register_allocation(original_id={:?}) -> {:?}",
original_id,
fresh_id
);
fresh_id
}
}
}
/// For thread local allocation identifier `alloc_id`, retrieve the original
/// static allocation identifier from which it was created.
pub fn resolve_allocation<'tcx>(
&self,
tcx: ty::TyCtxt<'tcx>,
alloc_id: AllocId,
) -> Option<mir::interpret::GlobalAlloc<'tcx>> {
trace!("resolve_allocation(alloc_id: {:?})", alloc_id);
if let Some(original_id) = self.thread_local_origin.borrow().get(&alloc_id) {
trace!("resolve_allocation(alloc_id: {:?}) -> {:?}", alloc_id, original_id);
tcx.alloc_map.lock().get(*original_id)
} else {
tcx.alloc_map.lock().get(alloc_id)
}
}
/// Set which thread is currently active.
fn set_active_thread(&mut self, active_thread: ThreadId) {
self.active_thread = Some(active_thread);
}
}
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
fn get_thread_local_alloc_id(&self, static_alloc_id: AllocId) -> Option<AllocId> {
let this = self.eval_context_ref();
this.machine.threads.get_thread_local_alloc_id(static_alloc_id)
}
fn set_thread_local_alloc_id(&self, static_alloc_id: AllocId, thread_local_alloc_id: AllocId) {
let this = self.eval_context_ref();
this.machine.threads.set_thread_local_alloc_id(static_alloc_id, thread_local_alloc_id)
}
fn create_thread(&mut self) -> InterpResult<'tcx, ThreadId> {
let this = self.eval_context_mut();
Ok(this.machine.threads.create_thread())
@ -330,7 +298,6 @@ fn join_thread(&mut self, joined_thread_id: ThreadId) -> InterpResult<'tcx> {
}
fn set_active_thread(&mut self, thread_id: ThreadId) -> InterpResult<'tcx, ThreadId> {
let this = self.eval_context_mut();
this.memory.extra.tls.set_active_thread(thread_id);
Ok(this.machine.threads.set_active_thread_id(thread_id))
}
fn get_active_thread(&self) -> InterpResult<'tcx, ThreadId> {
@ -370,13 +337,6 @@ fn unblock_random_thread(&mut self, set: BlockSetId) -> InterpResult<'tcx, Optio
/// Returns `false` if all threads terminated.
fn schedule(&mut self) -> InterpResult<'tcx, bool> {
let this = self.eval_context_mut();
// Find the next thread to run.
if this.machine.threads.schedule()? {
let active_thread = this.machine.threads.get_active_thread_id();
this.memory.extra.tls.set_active_thread(active_thread);
Ok(true)
} else {
Ok(false)
}
this.machine.threads.schedule()
}
}