Generate thread local allocations in eval_maybe_thread_local_static_const.
This commit is contained in:
parent
963e9698f9
commit
51b16e56cd
@ -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,
|
||||
|
108
src/threads.rs
108
src/threads.rs
@ -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()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user