Skip to content

Commit

Permalink
Add emulated TLS support
Browse files Browse the repository at this point in the history
Currently LLVM uses emutls by default
for some targets (such as android, openbsd),
but rust does not use it, because `has_thread_local` is false.

This commit has some changes to allow users to enable emutls:

1. add `-Zhas_thread_local` flag to specify
    that std uses `#[thread_local]` instead of pthread key.
2. when using emutls, decorate symbol names
    to find thread local symbol correctly.
3. change `-Zforce_emulated_tls` to `-Ztls-model=emulated`
    to explicitly specify whether to generate emutls.
  • Loading branch information
quininer committed Dec 6, 2023
1 parent 8a7b203 commit cc16abe
Show file tree
Hide file tree
Showing 17 changed files with 71 additions and 28 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_codegen_gcc/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,5 +569,6 @@ fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel {
TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic,
TlsModel::InitialExec => gccjit::TlsModel::InitialExec,
TlsModel::LocalExec => gccjit::TlsModel::LocalExec,
TlsModel::Emulated => gccjit::TlsModel::GlobalDynamic,
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl OwnedTargetMachine {
split_dwarf_file: &CStr,
output_obj_file: &CStr,
debug_info_compression: &CStr,
force_emulated_tls: bool,
use_emulated_tls: bool,
args_cstr_buff: &[u8],
) -> Result<Self, LlvmError<'static>> {
assert!(args_cstr_buff.len() > 0);
Expand Down Expand Up @@ -71,7 +71,7 @@ impl OwnedTargetMachine {
split_dwarf_file.as_ptr(),
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
force_emulated_tls,
use_emulated_tls,
args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.len(),
)
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, Switc
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};

use crate::llvm::diagnostic::OptimizationDiagnosticKind;
use libc::{c_char, c_int, c_uint, c_void, size_t};
Expand Down Expand Up @@ -223,7 +223,7 @@ pub fn target_machine_factory(

let path_mapping = sess.source_map().path_mapping().clone();

let force_emulated_tls = sess.target.force_emulated_tls;
let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated);

// copy the exe path, followed by path all into one buffer
// null terminating them so we can use them as null terminated strings
Expand Down Expand Up @@ -297,7 +297,7 @@ pub fn target_machine_factory(
&split_dwarf_file,
&output_obj_file,
&debuginfo_compression,
force_emulated_tls,
use_emulated_tls,
&args_cstr_buff,
)
})
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode {
TlsModel::LocalDynamic => llvm::ThreadLocalMode::LocalDynamic,
TlsModel::InitialExec => llvm::ThreadLocalMode::InitialExec,
TlsModel::LocalExec => llvm::ThreadLocalMode::LocalExec,
TlsModel::Emulated => llvm::ThreadLocalMode::GeneralDynamic,
}
}

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,9 @@ impl CodegenBackend for LlvmCodegenBackend {
}
PrintKind::TlsModels => {
writeln!(out, "Available TLS models:");
for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] {
for name in
&["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"]
{
writeln!(out, " {name}");
}
writeln!(out);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2159,7 +2159,7 @@ extern "C" {
SplitDwarfFile: *const c_char,
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
ForceEmulatedTls: bool,
UseEmulatedTls: bool,
ArgsCstrBuff: *const c_char,
ArgsCstrBuffLen: usize,
) -> *mut TargetMachine;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,9 @@ fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) {
symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
symbols.push(symbol_export::exporting_symbol_name_for_instance_in_crate(
tcx, symbol, cnum,
));
}
});

Expand Down
34 changes: 33 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
use rustc_middle::util::Providers;
use rustc_session::config::{CrateType, OomStrategy};
use rustc_target::spec::SanitizerSet;
use rustc_target::spec::{SanitizerSet, TlsModel};

pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel {
crates_export_threshold(tcx.crate_types())
Expand Down Expand Up @@ -552,6 +552,12 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(

let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);

// thread local will not be a function call,
// so it is safe to return before windows symbol decoration check.
if let Some(name) = maybe_emutls_symbol_name(tcx, symbol, &undecorated) {
return name;
}

let target = &tcx.sess.target;
if !target.is_like_windows {
// Mach-O has a global "_" suffix and `object` crate will handle it.
Expand Down Expand Up @@ -612,6 +618,32 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
}

pub fn exporting_symbol_name_for_instance_in_crate<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
cnum: CrateNum,
) -> String {
let undecorated = symbol_name_for_instance_in_crate(tcx, symbol, cnum);
maybe_emutls_symbol_name(tcx, symbol, &undecorated).unwrap_or(undecorated)
}

fn maybe_emutls_symbol_name<'tcx>(
tcx: TyCtxt<'tcx>,
symbol: ExportedSymbol<'tcx>,
undecorated: &str,
) -> Option<String> {
if matches!(tcx.sess.tls_model(), TlsModel::Emulated)
&& let ExportedSymbol::NonGeneric(def_id) = symbol
&& tcx.is_thread_local_static(def_id)
{
// When using emutls, LLVM will add the `__emutls_v.` prefix to thread local symbols,
// and exported symbol name need to match this.
Some(format!("__emutls_v.{undecorated}"))
} else {
None
}
}

fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
// Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about
Expand Down
10 changes: 3 additions & 7 deletions compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
const char *SplitDwarfFile,
const char *OutputObjFile,
const char *DebugInfoCompression,
bool ForceEmulatedTls,
bool UseEmulatedTls,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {

auto OptLevel = fromRust(RustOptLevel);
Expand Down Expand Up @@ -456,13 +456,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
Options.UseInitArray = UseInitArray;

#if LLVM_VERSION_LT(17, 0)
if (ForceEmulatedTls) {
Options.ExplicitEmulatedTLS = true;
Options.EmulatedTLS = true;
}
#else
Options.EmulatedTLS = ForceEmulatedTls || Trip.hasDefaultEmulatedTLS();
Options.ExplicitEmulatedTLS = true;
#endif
Options.EmulatedTLS = UseEmulatedTls;

if (TrapUnreachable) {
// Tell LLVM to codegen `unreachable` into an explicit trap instruction.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1283,7 +1283,7 @@ fn default_configuration(sess: &Session) -> Cfg {
ret.insert((sym::relocation_model, Some(relocation_model)));
}
ret.insert((sym::target_vendor, Some(Symbol::intern(vendor))));
if sess.target.has_thread_local {
if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) {
ret.insert((sym::target_thread_local, None));
}
let mut has_atomic = false;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,8 @@ options! {
graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED],
"use the given `fontname` in graphviz output; can be overridden by setting \
environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"),
has_thread_local: Option<bool> = (None, parse_opt_bool, [TRACKED],
"explicitly enable the `cfg(target_thread_local)` directive"),
hir_stats: bool = (false, parse_bool, [UNTRACKED],
"print some statistics about AST and HIR (default: no)"),
human_readable_cgu_names: bool = (false, parse_bool, [TRACKED],
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/spec/base/android.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::spec::{base, SanitizerSet, TargetOptions};
use crate::spec::{base, SanitizerSet, TargetOptions, TlsModel};

pub fn opts() -> TargetOptions {
let mut base = base::linux::opts();
base.os = "android".into();
base.is_like_android = true;
base.default_dwarf_version = 2;
base.tls_model = TlsModel::Emulated;
base.has_thread_local = false;
base.supported_sanitizers = SanitizerSet::ADDRESS;
// This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/base/linux_ohos.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::spec::{base, TargetOptions};
use crate::spec::{base, TargetOptions, TlsModel};

pub fn opts() -> TargetOptions {
let mut base = base::linux::opts();

base.env = "ohos".into();
base.crt_static_default = false;
base.force_emulated_tls = true;
base.tls_model = TlsModel::Emulated;
base.has_thread_local = false;

base
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/spec/base/openbsd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions};
use crate::spec::{cvs, FramePointer, RelroLevel, TargetOptions, TlsModel};

pub fn opts() -> TargetOptions {
TargetOptions {
Expand All @@ -11,6 +11,7 @@ pub fn opts() -> TargetOptions {
frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
relro_level: RelroLevel::Full,
default_dwarf_version: 2,
tls_model: TlsModel::Emulated,
..Default::default()
}
}
9 changes: 3 additions & 6 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,7 @@ pub enum TlsModel {
LocalDynamic,
InitialExec,
LocalExec,
Emulated,
}

impl FromStr for TlsModel {
Expand All @@ -942,6 +943,7 @@ impl FromStr for TlsModel {
"local-dynamic" => TlsModel::LocalDynamic,
"initial-exec" => TlsModel::InitialExec,
"local-exec" => TlsModel::LocalExec,
"emulated" => TlsModel::Emulated,
_ => return Err(()),
})
}
Expand All @@ -954,6 +956,7 @@ impl ToJson for TlsModel {
TlsModel::LocalDynamic => "local-dynamic",
TlsModel::InitialExec => "initial-exec",
TlsModel::LocalExec => "local-exec",
TlsModel::Emulated => "emulated",
}
.to_json()
}
Expand Down Expand Up @@ -2190,9 +2193,6 @@ pub struct TargetOptions {

/// Whether the target supports XRay instrumentation.
pub supports_xray: bool,

/// Forces the use of emulated TLS (__emutls_get_address)
pub force_emulated_tls: bool,
}

/// Add arguments for the given flavor and also for its "twin" flavors
Expand Down Expand Up @@ -2408,7 +2408,6 @@ impl Default for TargetOptions {
entry_name: "main".into(),
entry_abi: Conv::C,
supports_xray: false,
force_emulated_tls: false,
}
}
}
Expand Down Expand Up @@ -3112,7 +3111,6 @@ impl Target {
key!(entry_name);
key!(entry_abi, Conv)?;
key!(supports_xray, bool);
key!(force_emulated_tls, bool);

if base.is_builtin {
// This can cause unfortunate ICEs later down the line.
Expand Down Expand Up @@ -3368,7 +3366,6 @@ impl ToJson for Target {
target_option_val!(entry_name);
target_option_val!(entry_abi);
target_option_val!(supports_xray);
target_option_val!(force_emulated_tls);

if let Some(abi) = self.default_adjusted_cabi {
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
Expand Down
8 changes: 7 additions & 1 deletion library/std/src/sys/unix/thread_local_dtor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
#[allow(unexpected_cfgs)]
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))]
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox",
target_os = "hurd"
))]
// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
#[no_sanitize(cfi, kcfi)]
Expand Down
2 changes: 2 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/tls-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ loaded at program startup.
The TLS data must not be in a library loaded after startup (via `dlopen`).
- `local-exec` - model usable only if the TLS data is defined directly in the executable,
but not in a shared library, and is accessed only from that executable.
- `emulated` - Uses thread-specific data keys to implement emulated TLS.
It is like using a general-dynamic TLS model for all modes.

`rustc` and LLVM may use a more optimized model than specified if they know that we are producing
an executable rather than a library, or that the `static` item is private enough.

0 comments on commit cc16abe

Please sign in to comment.