Skip to content

Commit

Permalink
Allow specifying the TLS ABI
Browse files Browse the repository at this point in the history
TLSDESC is an alternate method of TLS access. It is part of the ELF TLS
ABI, and is already commonly used in other toolchains. LLVM supports
this for most ELF backends (X86_64, Aarch64, RISC-V). It has always been
the default for Aarch64, but support for RISC-V and X86_64 were added
before the last LLVM release.

More information on TLSDESC can be found in:
https://android.googlesource.com/platform/bionic/+/HEAD/docs/elf-tls.md
or the original RFC:
https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-ARM.txt

This patch adds a new unstable option `-Z tls-dialect`, which matches
the common `-mtls-dialect=` option used in Clang, GCC, and other
toolchains. This option only needs to be passed through to LLVM to
update the code generation. For targets like Aarch64 that always use
TLSDESC, this has no effect. Eventually this flag should probably be
stabilized since it is an important part of a program's ABI.

In the future, if LLVM changes its defaults for TLS to enable TLSDESC
for a particular platform or architecture, users who do not wish to
change can use the new option to select their desired TLS ABI.
  • Loading branch information
ilovepi committed Nov 2, 2024
1 parent 5ca0e9f commit 8a7e831
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 6 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ impl OwnedTargetMachine {
output_obj_file: &CStr,
debug_info_compression: &CStr,
use_emulated_tls: bool,
enable_tlsdesc: bool,
args_cstr_buff: &[u8],
) -> Result<Self, LlvmError<'static>> {
assert!(args_cstr_buff.len() > 0);
Expand Down Expand Up @@ -71,6 +72,7 @@ impl OwnedTargetMachine {
output_obj_file.as_ptr(),
debug_info_compression.as_ptr(),
use_emulated_tls,
enable_tlsdesc,
args_cstr_buff.as_ptr() as *const c_char,
args_cstr_buff.len(),
)
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_llvm/src/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_session::config::{
};
use rustc_span::InnerSpan;
use rustc_span::symbol::sym;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel};
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo, TlsDialect, TlsModel};
use tracing::debug;

use crate::back::lto::ThinBuffer;
Expand Down Expand Up @@ -230,6 +230,8 @@ pub(crate) fn target_machine_factory(

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

let enable_tlsdesc = matches!(sess.tls_dialect(), TlsDialect::Desc);

// copy the exe path, followed by path all into one buffer
// null terminating them so we can use them as null terminated strings
let args_cstr_buff = {
Expand Down Expand Up @@ -302,6 +304,7 @@ pub(crate) fn target_machine_factory(
&output_obj_file,
&debuginfo_compression,
use_emulated_tls,
enable_tlsdesc,
&args_cstr_buff,
)
})
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,17 @@ impl CodegenBackend for LlvmCodegenBackend {
}
writeln!(out).unwrap();
}
PrintKind::TlsDialect => {
writeln!(out, "Available TLS dialects:").unwrap();
for name in
&["trad", "desc"]
{
writeln!(out, " {name}").unwrap();
}
writeln!(out).unwrap();
}


PrintKind::StackProtectorStrategies => {
writeln!(
out,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2230,6 +2230,7 @@ unsafe extern "C" {
OutputObjFile: *const c_char,
DebugInfoCompression: *const c_char,
UseEmulatedTls: bool,
EnableTlsDesc: bool,
ArgsCstrBuff: *const c_char,
ArgsCstrBuffLen: usize,
) -> *mut TargetMachine;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,7 @@ fn print_crate_info(
RelocationModels
| CodeModels
| TlsModels
| TlsDialect
| TargetCPUs
| StackProtectorStrategies
| TargetFeatures => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
bool TrapUnreachable, bool Singlethread, bool VerboseAsm,
bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray,
const char *SplitDwarfFile, const char *OutputObjFile,
const char *DebugInfoCompression, bool UseEmulatedTls,
const char *DebugInfoCompression, bool UseEmulatedTls, bool EnableTLSDESC,
const char *ArgsCstrBuff, size_t ArgsCstrBuffLen) {

auto OptLevel = fromRust(RustOptLevel);
Expand Down Expand Up @@ -456,6 +456,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(

#if LLVM_VERSION_GE(19, 0)
Options.MCOptions.X86RelaxRelocations = RelaxELFRelocations;
Options.EnableTLSDESC = EnableTLSDESC;
#else
Options.RelaxELFRelocations = RelaxELFRelocations;
#endif
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ pub enum PrintKind {
RelocationModels,
CodeModels,
TlsModels,
TlsDialect,
TargetSpec,
AllTargetSpecs,
NativeStaticLibs,
Expand Down Expand Up @@ -1956,6 +1957,7 @@ fn collect_print_requests(
("target-libdir", PrintKind::TargetLibdir),
("target-list", PrintKind::TargetList),
("target-spec-json", PrintKind::TargetSpec),
("tls-dialect", PrintKind::TlsDialect),
("tls-models", PrintKind::TlsModels),
// tidy-alphabetical-end
];
Expand Down Expand Up @@ -3018,7 +3020,7 @@ pub(crate) mod dep_tracking {
use rustc_target::spec::{
CodeModel, FramePointer, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel,
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility, TargetTriple,
TlsModel, WasmCAbi,
TlsDialect, TlsModel, WasmCAbi,
};

use super::{
Expand Down Expand Up @@ -3083,6 +3085,7 @@ pub(crate) mod dep_tracking {
FramePointer,
RelocModel,
CodeModel,
TlsDialect,
TlsModel,
InstrumentCoverage,
CoverageOptions,
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_span::{RealFileName, SourceFileHashAlgorithm};
use rustc_target::spec::{
CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, SymbolVisibility,
TargetTriple, TlsModel, WasmCAbi,
TargetTriple, TlsDialect, TlsModel, WasmCAbi,
};

use crate::config::*;
Expand Down Expand Up @@ -427,6 +427,8 @@ mod desc {
"one of supported code models (`rustc --print code-models`)";
pub(crate) const parse_tls_model: &str =
"one of supported TLS models (`rustc --print tls-models`)";
pub(crate) const parse_tls_dialect: &str =
"one of supported TLS dialects (`rustc --print tls-dialect`)";
pub(crate) const parse_target_feature: &str = parse_string;
pub(crate) const parse_terminal_url: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), or `auto`";
Expand Down Expand Up @@ -1244,6 +1246,13 @@ mod parse {
}
true
}
pub(crate) fn parse_tls_dialect(slot: &mut Option<TlsDialect>, v: Option<&str>) -> bool {
match v.and_then(|s| TlsDialect::from_str(s).ok()) {
Some(tls_dialect) => *slot = Some(tls_dialect),
_ => return false,
}
true
}

pub(crate) fn parse_terminal_url(slot: &mut TerminalUrl, v: Option<&str>) -> bool {
*slot = match v {
Expand Down Expand Up @@ -2114,6 +2123,9 @@ written to standard error output)"),
#[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")]
tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
"choose the TLS model to use (`rustc --print tls-models` for details)"),
#[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")]
tls_dialect: Option<TlsDialect> = (None, parse_tls_dialect, [TRACKED],
"choose the TLS dialect to use (`rustc --print tls-dialect` for details)"),
trace_macros: bool = (false, parse_bool, [UNTRACKED],
"for every macro invocation, print its name and arguments (default: no)"),
track_diagnostics: bool = (false, parse_bool, [UNTRACKED],
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target,
TargetTriple, TlsModel,
TargetTriple, TlsDialect, TlsModel,
};

use crate::code_stats::CodeStats;
Expand Down Expand Up @@ -767,6 +767,10 @@ impl Session {
self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
}

pub fn tls_dialect(&self) -> TlsDialect {
self.opts.unstable_opts.tls_dialect.unwrap_or(self.target.tls_dialect)
}

pub fn direct_access_external_data(&self) -> Option<bool> {
self.opts
.unstable_opts
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,36 @@ impl ToJson for TlsModel {
}
}

#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum TlsDialect {
Trad, // Use traditional TLS (e.g. through tls_get_addr()).
Desc, // Use TLS descriptors (TLSDESC).
}

impl FromStr for TlsDialect {
type Err = ();

fn from_str(s: &str) -> Result<TlsDialect, ()> {
Ok(match s {
// There are only two variants, but the options spelling has been inconsistent across
// architectures in other compilers.
"trad" | "traditional" | "gnu" => TlsDialect::Trad,
"desc" | "descriptor" | "gnu2" => TlsDialect::Desc,
_ => return Err(()),
})
}
}

impl ToJson for TlsDialect {
fn to_json(&self) -> Json {
match *self {
TlsDialect::Trad => "trad",
TlsDialect::Desc => "desc",
}
.to_json()
}
}

/// Everything is flattened to a single enum to make the json encoding/decoding less annoying.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum LinkOutputKind {
Expand Down Expand Up @@ -2226,6 +2256,9 @@ pub struct TargetOptions {
/// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec"
/// and "local-exec". This is similar to the -ftls-model option in GCC/Clang.
pub tls_model: TlsModel,
/// TLS dialect to use. Options are "trad" (default) for traditional TLS access and "desc"
/// for TLSDESC. This is similar to the -mtls-dialect option in GCC/Clang.
pub tls_dialect: TlsDialect,
/// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
pub disable_redzone: bool,
/// Frame pointer mode for this target. Defaults to `MayOmit`.
Expand Down Expand Up @@ -2629,6 +2662,7 @@ impl Default for TargetOptions {
relocation_model: RelocModel::Pic,
code_model: None,
tls_model: TlsModel::GeneralDynamic,
tls_dialect: TlsDialect::Trad,
disable_redzone: false,
frame_pointer: FramePointer::MayOmit,
function_sections: true,
Expand Down
8 changes: 8 additions & 0 deletions tests/assembly/auxiliary/tlsdesc_aux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![crate_type = "lib"]

use std::cell::Cell;

thread_local! {
#[no_mangle]
pub static A: Cell<u64> = const { Cell::new(0) };
}
29 changes: 29 additions & 0 deletions tests/assembly/tlsdesc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Verifies that setting the tls-dialect result sin the correct set of assembly
// instructions.
//
// Note: tls-dialect flags have no changes to LLVM IR, and only affect which
// instruction sequences are emitted by the LLVM backend. Checking the assembly
// output is how we test the lowering in LLVM, and is the only way a frontend
// can determine if its code generation flags are set correctly.
//
//@ revisions: x64 x64-trad x64-desc
//
//@[x64] compile-flags: --target=x86_64-unknown-linux-gnu
//@[x64-trad] compile-flags: --target=x86_64-unknown-linux-gnu -Z tls-dialect=trad
//@[x64-desc] compile-flags: --target=x86_64-unknown-linux-gnu -Z tls-dialect=desc
//
//@ assembly-output: emit-asm
//@ aux-build:tlsdesc_aux.rs

#![crate_type = "lib"]

extern crate tlsdesc_aux as aux;

#[no_mangle]
fn get_aux() -> u64 {
// x64: __tls_get_addr
// x64-trad: __tls_get_addr
// x64-desc: tlsdesc
// x64-desc: tlscall
aux::A.with(|a| a.get())
}
2 changes: 1 addition & 1 deletion tests/ui/invalid-compile-flags/print.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: unknown print request: `yyyy`
|
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models`
= help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-dialect`, `tls-models`

0 comments on commit 8a7e831

Please sign in to comment.