Skip to content

Commit

Permalink
Auto merge of #30417 - alexcrichton:better-detect-elf-tls, r=alexcric…
Browse files Browse the repository at this point in the history
…hton

Currently a compiler can be built with the `--disable-elf-tls` option for compatibility with OSX 10.6 which doesn't have ELF TLS. This is unfortunate, however, as a whole new compiler must be generated which can take some time. These commits add a new (feature gated) `cfg(target_thread_local)` annotation set by the compiler which indicates whether `#[thread_local]` is available for use. The compiler now interprets `MACOSX_DEPLOYMENT_TARGET` (a standard environment variable) to set this flag on OSX. With this we may want to start compiling our OSX nightlies with `MACOSX_DEPLOYMENT_TARGET` set to 10.6 which would allow the compiler out-of-the-box to generate 10.6-compatible binaries.

For now the compiler still by default targets OSX 10.7 by allowing ELF TLS by default (e.g. if `MACOSX_DEPLOYMENT_TARGET` isn't set).
  • Loading branch information
bors committed Dec 22, 2015
2 parents 5178449 + cd74364 commit 42c3ef8
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 84 deletions.
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 @@ -218,6 +218,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

0 comments on commit 42c3ef8

Please sign in to comment.