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

Improve ELF TLS detection on OSX #30417

Merged
merged 3 commits into from
Dec 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
"windows" | "unix" => ret.push(attr::mk_word_item(fam)),
_ => (),
}
if sess.target.target.options.has_elf_tls {
ret.push(attr::mk_word_item(InternedString::new("target_thread_local")));
}
if sess.opts.debug_assertions {
ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/aarch64_unknown_linux_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
use target::Target;

pub fn target() -> Target {
let base = super::linux_base::opts();
let mut base = super::linux_base::opts();
base.has_elf_tls = false;
Target {
llvm_target: "aarch64-unknown-linux-gnu".to_string(),
target_endian: "little".to_string(),
Expand Down
23 changes: 22 additions & 1 deletion src/librustc_back/target/apple_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,30 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env;

use target::TargetOptions;
use std::default::Default;

pub fn opts() -> TargetOptions {
// ELF TLS is only available in OSX 10.7+. If you try to compile for 10.6
// either the linker will complain if it is used or the binary will end up
// segfaulting at runtime when run on 10.6. Rust by default supports OSX
// 10.7+, but there is a standard environment variable,
// MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
// versions of OSX. For example compiling on 10.10 with
// MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
// warnings about the usage of ELF TLS.
//
// Here we detect what version is being requested, defaulting to 10.7. ELF
// TLS is flagged as enabled if it looks to be supported.
let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok();
let version = deployment_target.as_ref().and_then(|s| {
let mut i = s.splitn(2, ".");
i.next().and_then(|a| i.next().map(|b| (a, b)))
}).and_then(|(a, b)| {
a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()
}).unwrap_or((10, 7));

TargetOptions {
// OSX has -dead_strip, which doesn't rely on ffunction_sections
function_sections: false,
Expand All @@ -25,6 +45,7 @@ pub fn opts() -> TargetOptions {
archive_format: "bsd".to_string(),
pre_link_args: Vec::new(),
exe_allocation_crate: super::maybe_jemalloc(),
has_elf_tls: version >= (10, 7),
.. Default::default()
}
}
1 change: 1 addition & 0 deletions src/librustc_back/target/apple_ios_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub fn opts(arch: Arch) -> TargetOptions {
dynamic_linking: false,
executables: true,
pre_link_args: pre_link_args(arch),
has_elf_tls: false,
.. super::apple_base::opts()
}
}
1 change: 1 addition & 0 deletions src/librustc_back/target/arm_linux_androideabi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use target::Target;
pub fn target() -> Target {
let mut base = super::android_base::opts();
base.features = "+v7".to_string();
base.has_elf_tls = false;

Target {
llvm_target: "arm-linux-androideabi".to_string(),
Expand Down
1 change: 1 addition & 0 deletions src/librustc_back/target/linux_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub fn opts() -> TargetOptions {
position_independent_executables: true,
archive_format: "gnu".to_string(),
exe_allocation_crate: super::maybe_jemalloc(),
has_elf_tls: true,
.. Default::default()
}
}
5 changes: 5 additions & 0 deletions src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ pub struct TargetOptions {
/// Default crate for allocation symbols to link against
pub lib_allocation_crate: String,
pub exe_allocation_crate: String,

/// Flag indicating whether ELF TLS (e.g. #[thread_local]) is available for
/// this target.
pub has_elf_tls: bool,
}

impl Default for TargetOptions {
Expand Down Expand Up @@ -240,6 +244,7 @@ impl Default for TargetOptions {
lib_allocation_crate: "alloc_system".to_string(),
exe_allocation_crate: "alloc_system".to_string(),
allow_asm: true,
has_elf_tls: false,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
feature_gated_cfgs.sort();
feature_gated_cfgs.dedup();
for cfg in &feature_gated_cfgs {
cfg.check_and_emit(sess.diagnostic(), &features);
cfg.check_and_emit(sess.diagnostic(), &features, sess.codemap());
}
});

Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
#![feature(borrow_state)]
#![feature(box_syntax)]
#![feature(cfg_target_vendor)]
#![feature(cfg_target_thread_local)]
#![feature(char_internals)]
#![feature(clone_from_slice)]
#![feature(collections)]
Expand Down
120 changes: 44 additions & 76 deletions src/libstd/thread/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
use cell::UnsafeCell;
use mem;

// Sure wish we had macro hygiene, no?
#[doc(hidden)]
pub use self::imp::Key as __KeyInner;

/// A thread local storage key which owns its contents.
///
/// This key uses the fastest possible implementation available to it for the
Expand Down Expand Up @@ -61,78 +57,42 @@ pub use self::imp::Key as __KeyInner;
/// });
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct LocalKey<T:'static> {
// The key itself may be tagged with #[thread_local], and this `Key` is
// stored as a `static`, and it's not valid for a static to reference the
// address of another thread_local static. For this reason we kinda wonkily
// work around this by generating a shim function which will give us the
// address of the inner TLS key at runtime.
pub struct LocalKey<T: 'static> {
// This outer `LocalKey<T>` type is what's going to be stored in statics,
// but actual data inside will sometimes be tagged with #[thread_local].
// It's not valid for a true static to reference a #[thread_local] static,
// so we get around that by exposing an accessor through a layer of function
// indirection (this thunk).
//
// Note that the thunk is itself unsafe because the returned lifetime of the
// slot where data lives, `'static`, is not actually valid. The lifetime
// here is actually `'thread`!
//
// This is trivially devirtualizable by LLVM because we never store anything
// to this field and rustc can declare the `static` as constant as well.
inner: fn() -> &'static __KeyInner<T>,
// Although this is an extra layer of indirection, it should in theory be
// trivially devirtualizable by LLVM because the value of `inner` never
// changes and the constant should be readonly within a crate. This mainly
// only runs into problems when TLS statics are exported across crates.
inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,

// initialization routine to invoke to create a value
init: fn() -> T,
}

// Macro pain #4586:
//
// When cross compiling, rustc will load plugins and macros from the *host*
// platform before search for macros from the target platform. This is primarily
// done to detect, for example, plugins. Ideally the macro below would be
// defined once per module below, but unfortunately this means we have the
// following situation:
//
// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro
// will inject #[thread_local] statics.
// 2. We then try to compile a program for arm-linux-androideabi
// 3. The compiler has a host of linux and a target of android, so it loads
// macros from the *linux* libstd.
// 4. The macro generates a #[thread_local] field, but the android libstd does
// not use #[thread_local]
// 5. Compile error about structs with wrong fields.
//
// To get around this, we're forced to inject the #[cfg] logic into the macro
// itself. Woohoo.

/// Declare a new thread local storage key of type `std::thread::LocalKey`.
///
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
/// information.
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable]
#[cfg(not(no_elf_tls))]
macro_rules! thread_local {
(static $name:ident: $t:ty = $init:expr) => (
static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init,
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
not(target_arch = "aarch64")),
thread_local)]);
);
(pub static $name:ident: $t:ty = $init:expr) => (
pub static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init,
#[cfg_attr(all(any(target_os = "macos", target_os = "linux"),
not(target_arch = "aarch64")),
thread_local)]);
);
}

#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable]
#[cfg(no_elf_tls)]
macro_rules! thread_local {
(static $name:ident: $t:ty = $init:expr) => (
static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init, #[]);
__thread_local_inner!($t, $init);
);
(pub static $name:ident: $t:ty = $init:expr) => (
pub static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init, #[]);
__thread_local_inner!($t, $init);
);
}

Expand All @@ -143,12 +103,25 @@ macro_rules! thread_local {
#[macro_export]
#[allow_internal_unstable]
macro_rules! __thread_local_inner {
($t:ty, $init:expr, #[$($attr:meta),*]) => {{
$(#[$attr])*
static __KEY: $crate::thread::__LocalKeyInner<$t> =
$crate::thread::__LocalKeyInner::new();
($t:ty, $init:expr) => {{
fn __init() -> $t { $init }
fn __getit() -> &'static $crate::thread::__LocalKeyInner<$t> { &__KEY }

unsafe fn __getit() -> $crate::option::Option<
&'static $crate::cell::UnsafeCell<
$crate::option::Option<$t>>>
{
#[thread_local]
#[cfg(target_thread_local)]
static __KEY: $crate::thread::__ElfLocalKeyInner<$t> =
$crate::thread::__ElfLocalKeyInner::new();

#[cfg(not(target_thread_local))]
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
$crate::thread::__OsLocalKeyInner::new();

__KEY.get()
}

$crate::thread::LocalKey::new(__getit, __init)
}}
}
Expand Down Expand Up @@ -190,11 +163,11 @@ impl<T: 'static> LocalKey<T> {
#[unstable(feature = "thread_local_internals",
reason = "recently added to create a key",
issue = "0")]
pub const fn new(inner: fn() -> &'static __KeyInner<T>,
pub const fn new(inner: unsafe fn() -> Option<&'static UnsafeCell<Option<T>>>,
init: fn() -> T) -> LocalKey<T> {
LocalKey {
inner: inner,
init: init
init: init,
}
}

Expand All @@ -211,10 +184,10 @@ impl<T: 'static> LocalKey<T> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with<F, R>(&'static self, f: F) -> R
where F: FnOnce(&T) -> R {
let slot = (self.inner)();
unsafe {
let slot = slot.get().expect("cannot access a TLS value during or \
after it is destroyed");
let slot = (self.inner)();
let slot = slot.expect("cannot access a TLS value during or \
after it is destroyed");
f(match *slot.get() {
Some(ref inner) => inner,
None => self.init(slot),
Expand Down Expand Up @@ -270,7 +243,7 @@ impl<T: 'static> LocalKey<T> {
issue = "27716")]
pub fn state(&'static self) -> LocalKeyState {
unsafe {
match (self.inner)().get() {
match (self.inner)() {
Some(cell) => {
match *cell.get() {
Some(..) => LocalKeyState::Valid,
Expand All @@ -283,11 +256,9 @@ impl<T: 'static> LocalKey<T> {
}
}

#[cfg(all(any(target_os = "macos", target_os = "linux"),
not(target_arch = "aarch64"),
not(no_elf_tls)))]
#[cfg(target_thread_local)]
#[doc(hidden)]
mod imp {
pub mod elf {
use cell::{Cell, UnsafeCell};
use intrinsics;
use ptr;
Expand Down Expand Up @@ -431,11 +402,8 @@ mod imp {
}
}

#[cfg(any(not(any(target_os = "macos", target_os = "linux")),
target_arch = "aarch64",
no_elf_tls))]
#[doc(hidden)]
mod imp {
pub mod os {
use prelude::v1::*;

use cell::{Cell, UnsafeCell};
Expand Down
5 changes: 4 additions & 1 deletion src/libstd/thread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,10 @@ pub use self::local::{LocalKey, LocalKeyState};
pub use self::scoped_tls::ScopedKey;

#[unstable(feature = "libstd_thread_internals", issue = "0")]
#[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner;
#[cfg(target_thread_local)]
#[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "0")]
#[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner;
#[unstable(feature = "libstd_thread_internals", issue = "0")]
#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;

Expand Down
Loading