diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 44c30d22a9e9c..44765a15dc1d8 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -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> { assert!(args_cstr_buff.len() > 0); @@ -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(), ) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bfa9e8b82a033..c4254935797e2 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -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; @@ -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 = { @@ -302,6 +304,7 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, + enable_tlsdesc, &args_cstr_buff, ) }) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index b85d28a2f1f71..d828bf9d5bc5a 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -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, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8fc586d2c8ffc..a052253e74209 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -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; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index e2585c023883a..ba19a45c4b18a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -837,6 +837,7 @@ fn print_crate_info( RelocationModels | CodeModels | TlsModels + | TlsDialect | TargetCPUs | StackProtectorStrategies | TargetFeatures => { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3e906f89c15c3..d93ac3fcf33ea 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -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); @@ -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 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d733e32f209db..52713756574b6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -825,6 +825,7 @@ pub enum PrintKind { RelocationModels, CodeModels, TlsModels, + TlsDialect, TargetSpec, AllTargetSpecs, NativeStaticLibs, @@ -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 ]; @@ -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::{ @@ -3083,6 +3085,7 @@ pub(crate) mod dep_tracking { FramePointer, RelocModel, CodeModel, + TlsDialect, TlsModel, InstrumentCoverage, CoverageOptions, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 54a4621db2462..7c2889e4498ce 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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::*; @@ -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`"; @@ -1244,6 +1246,13 @@ mod parse { } true } + pub(crate) fn parse_tls_dialect(slot: &mut Option, 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 { @@ -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 = (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 = (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], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 45434534c75ea..feae0e4ab556a 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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; @@ -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 { self.opts .unstable_opts diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3e7b42d3d1c13..40e72fddc5aab 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -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 { + 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 { @@ -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`. @@ -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, diff --git a/tests/assembly/auxiliary/tlsdesc_aux.rs b/tests/assembly/auxiliary/tlsdesc_aux.rs new file mode 100644 index 0000000000000..3a590f4c0844d --- /dev/null +++ b/tests/assembly/auxiliary/tlsdesc_aux.rs @@ -0,0 +1,8 @@ +#![crate_type = "lib"] + +use std::cell::Cell; + +thread_local! { +#[no_mangle] +pub static A: Cell = const { Cell::new(0) }; +} diff --git a/tests/assembly/tlsdesc.rs b/tests/assembly/tlsdesc.rs new file mode 100644 index 0000000000000..65588f69a89e8 --- /dev/null +++ b/tests/assembly/tlsdesc.rs @@ -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()) +} diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index 70b4a394dd022..2d54727656735 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -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`