Merge pull request #458 from RalfJung/tls

Move TLS data to machine data
This commit is contained in:
Eduard-Mihai Burtescu 2018-09-20 02:31:34 +03:00 committed by GitHub
commit c95923d602
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 116 deletions

View file

@ -8,10 +8,6 @@ use std::mem;
use super::*;
use tls::MemoryExt;
use super::memory::MemoryKind;
pub trait EvalContextExt<'tcx, 'mir> {
/// Emulate calling a foreign item, fail if the item is not supported.
/// This function will handle `goto_block` if needed.
@ -129,7 +125,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
self.write_null(dest)?;
} else {
let align = self.tcx.data_layout.pointer_align;
let ptr = self.memory.allocate(Size::from_bytes(size), align, MemoryKind::C.into())?;
let ptr = self.memory.allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into())?;
self.write_scalar(Scalar::Ptr(ptr), dest)?;
}
}
@ -140,7 +136,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
self.memory.deallocate(
ptr.to_ptr()?,
None,
MemoryKind::C.into(),
MiriMemoryKind::C.into(),
)?;
}
}
@ -156,7 +152,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
}
let ptr = self.memory.allocate(Size::from_bytes(size),
Align::from_bytes(align, align).unwrap(),
MemoryKind::Rust.into())?;
MiriMemoryKind::Rust.into())?;
self.write_scalar(Scalar::Ptr(ptr), dest)?;
}
"__rust_alloc_zeroed" => {
@ -170,7 +166,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
}
let ptr = self.memory.allocate(Size::from_bytes(size),
Align::from_bytes(align, align).unwrap(),
MemoryKind::Rust.into())?;
MiriMemoryKind::Rust.into())?;
self.memory.write_repeat(ptr.into(), 0, Size::from_bytes(size))?;
self.write_scalar(Scalar::Ptr(ptr), dest)?;
}
@ -187,7 +183,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
self.memory.deallocate(
ptr,
Some((Size::from_bytes(old_size), Align::from_bytes(align, align).unwrap())),
MemoryKind::Rust.into(),
MiriMemoryKind::Rust.into(),
)?;
}
"__rust_realloc" => {
@ -207,7 +203,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
Align::from_bytes(align, align).unwrap(),
Size::from_bytes(new_size),
Align::from_bytes(align, align).unwrap(),
MemoryKind::Rust.into(),
MiriMemoryKind::Rust.into(),
)?;
self.write_scalar(Scalar::Ptr(new_ptr), dest)?;
}
@ -365,7 +361,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
}
if let Some(old) = success {
if let Some(var) = old {
self.memory.deallocate(var, None, MemoryKind::Env.into())?;
self.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
}
self.write_null(dest)?;
} else {
@ -391,7 +387,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
let value_copy = self.memory.allocate(
Size::from_bytes((value.len() + 1) as u64),
Align::from_bytes(1, 1).unwrap(),
MemoryKind::Env.into(),
MiriMemoryKind::Env.into(),
)?;
self.memory.write_bytes(value_copy.into(), &value)?;
let trailing_zero_ptr = value_copy.offset(Size::from_bytes(value.len() as u64), &self)?.into();
@ -401,7 +397,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
value_copy,
)
{
self.memory.deallocate(var, None, MemoryKind::Env.into())?;
self.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
}
self.write_null(dest)?;
} else {
@ -504,7 +500,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
let key_layout = self.layout_of(key_type)?;
// Create key and write it into the memory where key_ptr wants it
let key = self.memory.create_tls_key(dtor) as u128;
let key = self.machine.tls.create_tls_key(dtor, *self.tcx) as u128;
if key_layout.size.bits() < 128 && key >= (1u128 << key_layout.size.bits() as u128) {
return err!(OutOfTls);
}
@ -520,19 +516,19 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
}
"pthread_key_delete" => {
let key = self.read_scalar(args[0])?.to_bytes()?;
self.memory.delete_tls_key(key)?;
self.machine.tls.delete_tls_key(key)?;
// Return success (0)
self.write_null(dest)?;
}
"pthread_getspecific" => {
let key = self.read_scalar(args[0])?.to_bytes()?;
let ptr = self.memory.load_tls(key)?;
let ptr = self.machine.tls.load_tls(key)?;
self.write_scalar(ptr, dest)?;
}
"pthread_setspecific" => {
let key = self.read_scalar(args[0])?.to_bytes()?;
let new_ptr = self.read_scalar(args[1])?.not_undef()?;
self.memory.store_tls(key, new_ptr)?;
self.machine.tls.store_tls(key, new_ptr)?;
// Return success (0)
self.write_null(dest)?;
@ -607,7 +603,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
// This just creates a key; Windows does not natively support TLS dtors.
// Create key and return it
let key = self.memory.create_tls_key(None) as u128;
let key = self.machine.tls.create_tls_key(None, *self.tcx) as u128;
// Figure out how large a TLS key actually is. This is c::DWORD.
if dest.layout.size.bits() < 128 && key >= (1u128 << dest.layout.size.bits() as u128) {
@ -617,13 +613,13 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, '
}
"TlsGetValue" => {
let key = self.read_scalar(args[0])?.to_bytes()?;
let ptr = self.memory.load_tls(key)?;
let ptr = self.machine.tls.load_tls(key)?;
self.write_scalar(ptr, dest)?;
}
"TlsSetValue" => {
let key = self.read_scalar(args[0])?.to_bytes()?;
let new_ptr = self.read_scalar(args[1])?.not_undef()?;
self.memory.store_tls(key, new_ptr)?;
self.machine.tls.store_tls(key, new_ptr)?;
// Return success (1)
self.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;

View file

@ -21,17 +21,16 @@ use rustc::mir;
use syntax::ast::Mutability;
use syntax::attr;
use std::marker::PhantomData;
use std::collections::HashMap;
pub use rustc::mir::interpret::*;
pub use rustc_mir::interpret::*;
pub use rustc_mir::interpret;
mod fn_call;
mod operator;
mod intrinsic;
mod helpers;
mod memory;
mod tls;
mod locks;
mod range_map;
@ -39,9 +38,7 @@ mod range_map;
use fn_call::EvalContextExt as MissingFnsEvalContextExt;
use operator::EvalContextExt as OperatorEvalContextExt;
use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
use tls::EvalContextExt as TlsEvalContextExt;
use memory::{MemoryKind as MiriMemoryKind, TlsKey, TlsEntry, MemoryData};
use locks::LockInfo;
use tls::{EvalContextExt as TlsEvalContextExt, TlsData};
use range_map::RangeMap;
use helpers::FalibleScalarExt;
@ -54,7 +51,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
tcx.at(syntax::source_map::DUMMY_SP),
ty::ParamEnv::reveal_all(),
Default::default(),
MemoryData::new()
Default::default(),
);
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
@ -201,21 +198,41 @@ pub fn eval_main<'a, 'tcx: 'a>(
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MiriMemoryKind {
/// `__rust_alloc` memory
Rust,
/// `malloc` memory
C,
/// Part of env var emulation
Env,
/// mutable statics
MutStatic,
}
impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
fn into(self) -> MemoryKind<MiriMemoryKind> {
MemoryKind::Machine(self)
}
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Evaluator<'tcx> {
/// Environment variables set by `setenv`
/// Miri does not expose env vars from the host to the emulated program
pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
/// Use the lifetime
_dummy : PhantomData<&'tcx ()>,
/// TLS state
pub(crate) tls: TlsData<'tcx>,
}
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
type MemoryData = memory::MemoryData<'tcx>;
type MemoryKinds = memory::MemoryKind;
type MemoryData = ();
type MemoryKinds = MiriMemoryKind;
const MUT_STATIC_KIND: Option<memory::MemoryKind> = Some(memory::MemoryKind::MutStatic);
const MUT_STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
const DETECT_LOOPS: bool = false;
/// Returns Ok() when the function was handled, fail otherwise

View file

@ -1,57 +0,0 @@
use std::collections::{HashMap, BTreeMap};
use rustc::ty;
use super::{AllocId, Scalar, LockInfo, RangeMap};
pub type TlsKey = u128;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TlsEntry<'tcx> {
pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
pub(crate) dtor: Option<ty::Instance<'tcx>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MemoryData<'tcx> {
/// The Key to use for the next thread-local allocation.
pub(crate) next_thread_local: TlsKey,
/// pthreads-style thread-local storage.
pub(crate) thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
/// Memory regions that are locked by some function
///
/// Only mutable (static mut, heap, stack) allocations have an entry in this map.
/// The entry is created when allocating the memory and deleted after deallocation.
pub(crate) locks: HashMap<AllocId, RangeMap<LockInfo<'tcx>>>,
}
impl<'tcx> MemoryData<'tcx> {
pub(crate) fn new() -> Self {
MemoryData {
next_thread_local: 1, // start with 1 as we must not use 0 on Windows
thread_local: BTreeMap::new(),
locks: HashMap::new(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MemoryKind {
/// `__rust_alloc` memory
Rust,
/// `malloc` memory
C,
/// Part of env var emulation
Env,
/// mutable statics
MutStatic,
}
impl Into<::rustc_mir::interpret::MemoryKind<MemoryKind>> for MemoryKind {
fn into(self) -> ::rustc_mir::interpret::MemoryKind<MemoryKind> {
::rustc_mir::interpret::MemoryKind::Machine(self)
}
}

View file

@ -1,31 +1,52 @@
use rustc::{ty, mir};
use std::collections::BTreeMap;
use super::{TlsKey, TlsEntry, EvalResult, EvalErrorKind, Scalar, Memory, Evaluator,
use rustc::{ty, ty::layout::HasDataLayout, mir};
use super::{EvalResult, EvalErrorKind, Scalar, Evaluator,
Place, StackPopCleanup, EvalContext};
pub trait MemoryExt<'tcx> {
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey;
fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx>;
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar>;
fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx>;
fn fetch_tls_dtor(
&mut self,
key: Option<TlsKey>,
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)>;
pub type TlsKey = u128;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TlsEntry<'tcx> {
pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
pub(crate) dtor: Option<ty::Instance<'tcx>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TlsData<'tcx> {
/// The Key to use for the next thread-local allocation.
pub(crate) next_key: TlsKey,
/// pthreads-style thread-local storage.
pub(crate) keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
}
impl<'tcx> Default for TlsData<'tcx> {
fn default() -> Self {
TlsData {
next_key: 1, // start with 1 as we must not use 0 on Windows
keys: Default::default(),
}
}
}
pub trait EvalContextExt<'tcx> {
fn run_tls_dtors(&mut self) -> EvalResult<'tcx>;
}
impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evaluator<'tcx>> {
fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
let new_key = self.data.next_thread_local;
self.data.next_thread_local += 1;
self.data.thread_local.insert(
impl<'tcx> TlsData<'tcx> {
pub fn create_tls_key(
&mut self,
dtor: Option<ty::Instance<'tcx>>,
cx: impl HasDataLayout,
) -> TlsKey {
let new_key = self.next_key;
self.next_key += 1;
self.keys.insert(
new_key,
TlsEntry {
data: Scalar::ptr_null(*self.tcx).into(),
data: Scalar::ptr_null(cx).into(),
dtor,
},
);
@ -33,8 +54,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
new_key
}
fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
match self.data.thread_local.remove(&key) {
pub fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
match self.keys.remove(&key) {
Some(_) => {
trace!("TLS key {} removed", key);
Ok(())
@ -43,8 +64,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
}
}
fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> {
match self.data.thread_local.get(&key) {
pub fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> {
match self.keys.get(&key) {
Some(&TlsEntry { data, .. }) => {
trace!("TLS key {} loaded: {:?}", key, data);
Ok(data)
@ -53,8 +74,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
}
}
fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> {
match self.data.thread_local.get_mut(&key) {
pub fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> {
match self.keys.get_mut(&key) {
Some(&mut TlsEntry { ref mut data, .. }) => {
trace!("TLS key {} stored: {:?}", key, new_data);
*data = new_data;
@ -85,10 +106,11 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
fn fetch_tls_dtor(
&mut self,
key: Option<TlsKey>,
cx: impl HasDataLayout,
) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {
use std::collections::Bound::*;
let thread_local = &mut self.data.thread_local;
let thread_local = &mut self.keys;
let start = match key {
Some(key) => Excluded(key),
None => Unbounded,
@ -99,7 +121,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
if !data.is_null() {
if let Some(dtor) = dtor {
let ret = Some((dtor, *data, key));
*data = Scalar::ptr_null(*self.tcx);
*data = Scalar::ptr_null(cx);
return ret;
}
}
@ -110,7 +132,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> MemoryExt<'tcx> for Memory<'a, 'mir, 'tcx, Evalu
impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>> {
fn run_tls_dtors(&mut self) -> EvalResult<'tcx> {
let mut dtor = self.memory.fetch_tls_dtor(None);
let mut dtor = self.machine.tls.fetch_tls_dtor(None, *self.tcx);
// FIXME: replace loop by some structure that works with stepping
while let Some((instance, ptr, key)) = dtor {
trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
@ -134,9 +156,9 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx> for EvalContext<'a, 'mir, '
// step until out of stackframes
self.run()?;
dtor = match self.memory.fetch_tls_dtor(Some(key)) {
dtor = match self.machine.tls.fetch_tls_dtor(Some(key), *self.tcx) {
dtor @ Some(_) => dtor,
None => self.memory.fetch_tls_dtor(None),
None => self.machine.tls.fetch_tls_dtor(None, *self.tcx),
};
}
// FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.