From 51c93553d4344472f2e291b3a4f110f884062a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 3 Feb 2023 08:05:59 +0100 Subject: [PATCH 1/4] Test that TLS access works outside of the dylib it's defined in --- tests/ui/thread-local/auxiliary/tls-export.rs | 16 ++++++++++++++++ tests/ui/thread-local/auxiliary/tls-rlib.rs | 14 ++++++++++++++ tests/ui/thread-local/tls-dylib-access.rs | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/ui/thread-local/auxiliary/tls-export.rs create mode 100644 tests/ui/thread-local/auxiliary/tls-rlib.rs create mode 100644 tests/ui/thread-local/tls-dylib-access.rs diff --git a/tests/ui/thread-local/auxiliary/tls-export.rs b/tests/ui/thread-local/auxiliary/tls-export.rs new file mode 100644 index 0000000000000..aab48f871c9b8 --- /dev/null +++ b/tests/ui/thread-local/auxiliary/tls-export.rs @@ -0,0 +1,16 @@ +#![crate_type = "dylib"] +#![feature(thread_local)] +#![feature(cfg_target_thread_local)] +#![cfg(target_thread_local)] + +extern crate tls_rlib; + +pub use tls_rlib::*; + +#[thread_local] +pub static FOO: bool = true; + +#[inline(never)] +pub fn foo_addr() -> usize { + &FOO as *const bool as usize +} diff --git a/tests/ui/thread-local/auxiliary/tls-rlib.rs b/tests/ui/thread-local/auxiliary/tls-rlib.rs new file mode 100644 index 0000000000000..0fbcf387b5fa9 --- /dev/null +++ b/tests/ui/thread-local/auxiliary/tls-rlib.rs @@ -0,0 +1,14 @@ +// no-prefer-dynamic + +#![crate_type = "rlib"] +#![feature(thread_local)] +#![feature(cfg_target_thread_local)] +#![cfg(target_thread_local)] + +#[thread_local] +pub static BAR: bool = true; + +#[inline(never)] +pub fn bar_addr() -> usize { + &BAR as *const bool as usize +} diff --git a/tests/ui/thread-local/tls-dylib-access.rs b/tests/ui/thread-local/tls-dylib-access.rs new file mode 100644 index 0000000000000..12c46113cead1 --- /dev/null +++ b/tests/ui/thread-local/tls-dylib-access.rs @@ -0,0 +1,19 @@ +// aux-build: tls-rlib.rs +// aux-build: tls-export.rs +// run-pass + +#![feature(cfg_target_thread_local)] + +#[cfg(target_thread_local)] +extern crate tls_export; + +fn main() { + #[cfg(target_thread_local)] + { + // Check that we get the real address of the `FOO` TLS in the dylib + assert_eq!(&tls_export::FOO as *const bool as usize, tls_export::foo_addr()); + + // Check that we get the real address of the `BAR` TLS in the rlib linked into the dylib + assert_eq!(&tls_export::BAR as *const bool as usize, tls_export::bar_addr()); + } +} From 0d89c6a2d44b78d052280d7faecfcb79e6f3d4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 3 Feb 2023 09:04:12 +0100 Subject: [PATCH 2/4] Support TLS access into dylibs on Windows --- .../rustc_codegen_cranelift/src/constant.rs | 22 +++++++--- .../src/back/symbol_export.rs | 43 ++++++++++++++++--- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 14 +++++- .../src/interpret/terminator.rs | 1 + .../src/middle/exported_symbols.rs | 5 +++ compiler/rustc_middle/src/mir/mono.rs | 1 + compiler/rustc_middle/src/mir/tcx.rs | 12 +----- compiler/rustc_middle/src/mir/visit.rs | 1 + compiler/rustc_middle/src/ty/instance.rs | 17 +++++++- compiler/rustc_middle/src/ty/mod.rs | 1 + compiler/rustc_middle/src/ty/util.rs | 22 ++++++++++ compiler/rustc_mir_transform/src/inline.rs | 1 + .../rustc_mir_transform/src/inline/cycle.rs | 1 + compiler/rustc_mir_transform/src/shim.rs | 29 +++++++++++++ compiler/rustc_monomorphize/src/collector.rs | 24 ++++++++--- .../src/partitioning/default.rs | 35 +++++++++------ compiler/rustc_symbol_mangling/src/legacy.rs | 4 ++ compiler/rustc_symbol_mangling/src/v0.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 5 +++ compiler/rustc_target/src/spec/msvc_base.rs | 1 + .../rustc_target/src/spec/windows_gnu_base.rs | 1 + .../src/spec/windows_gnullvm_base.rs | 1 + compiler/rustc_ty_utils/src/abi.rs | 12 +++++- 23 files changed, 207 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 31278f810e911..ebb4de33f990a 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -54,12 +54,22 @@ pub(crate) fn codegen_tls_ref<'tcx>( def_id: DefId, layout: TyAndLayout<'tcx>, ) -> CValue<'tcx> { - let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); - let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); - if fx.clif_comments.enabled() { - fx.add_comment(local_data_id, format!("tls {:?}", def_id)); - } - let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id); + let tls_ptr = if !def_id.is_local() && fx.tcx.needs_thread_local_shim(def_id) { + let instance = ty::Instance { + def: ty::InstanceDef::ThreadLocalShim(def_id), + substs: ty::InternalSubsts::empty(), + }; + let func_ref = fx.get_function_ref(instance); + let call = fx.bcx.ins().call(func_ref, &[]); + fx.bcx.func.dfg.first_result(call) + } else { + let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false); + let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + if fx.clif_comments.enabled() { + fx.add_comment(local_data_id, format!("tls {:?}", def_id)); + } + fx.bcx.ins().tls_value(fx.pointer_type, local_data_id) + }; CValue::by_val(tls_ptr, layout) } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index e403a1fd8ae78..d0fd3cd766674 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -177,14 +177,29 @@ fn exported_symbols_provider_local( // FIXME: Sorting this is unnecessary since we are sorting later anyway. // Can we skip the later sorting? - let mut symbols: Vec<_> = tcx.with_stable_hashing_context(|hcx| { - tcx.reachable_non_generics(LOCAL_CRATE) - .to_sorted(&hcx, true) - .into_iter() - .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)) - .collect() + let sorted = tcx.with_stable_hashing_context(|hcx| { + tcx.reachable_non_generics(LOCAL_CRATE).to_sorted(&hcx, true) }); + let mut symbols: Vec<_> = + sorted.iter().map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)).collect(); + + // Export TLS shims + if !tcx.sess.target.dll_tls_export { + symbols.extend(sorted.iter().filter_map(|(&def_id, &info)| { + tcx.needs_thread_local_shim(def_id).then(|| { + ( + ExportedSymbol::ThreadLocalShim(def_id), + SymbolExportInfo { + level: info.level, + kind: SymbolExportKind::Text, + used: info.used, + }, + ) + }) + })) + } + if tcx.entry_fn(()).is_some() { let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref())); @@ -380,7 +395,9 @@ fn upstream_monomorphizations_provider( continue; } } - ExportedSymbol::NonGeneric(..) | ExportedSymbol::NoDefId(..) => { + ExportedSymbol::NonGeneric(..) + | ExportedSymbol::ThreadLocalShim(..) + | ExportedSymbol::NoDefId(..) => { // These are no monomorphizations continue; } @@ -500,6 +517,16 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( instantiating_crate, ) } + ExportedSymbol::ThreadLocalShim(def_id) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + ty::Instance { + def: ty::InstanceDef::ThreadLocalShim(def_id), + substs: ty::InternalSubsts::empty(), + }, + instantiating_crate, + ) + } ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate( tcx, Instance::resolve_drop_in_place(tcx, ty), @@ -548,6 +575,8 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( ExportedSymbol::DropGlue(..) => None, // NoDefId always follow the target's default symbol decoration scheme. ExportedSymbol::NoDefId(..) => None, + // ThreadLocalShim always follow the target's default symbol decoration scheme. + ExportedSymbol::ThreadLocalShim(..) => None, }; let (conv, args) = instance diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index d867d6b0cd48a..0a59fabdc17fe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -516,8 +516,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::ThreadLocalRef(def_id) => { assert!(bx.cx().tcx().is_static(def_id)); - let static_ = bx.get_static(def_id); let layout = bx.layout_of(bx.cx().tcx().static_ptr_ty(def_id)); + let static_ = if !def_id.is_local() && bx.cx().tcx().needs_thread_local_shim(def_id) + { + let instance = ty::Instance { + def: ty::InstanceDef::ThreadLocalShim(def_id), + substs: ty::InternalSubsts::empty(), + }; + let fn_ptr = bx.get_fn_addr(instance); + let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); + let fn_ty = bx.fn_decl_backend_type(&fn_abi); + bx.call(fn_ty, Some(fn_abi), fn_ptr, &[], None) + } else { + bx.get_static(def_id) + }; OperandRef { val: OperandValue::Immediate(static_), layout } } mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand), diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 54d67bd1f29dc..2d9fee9852cc6 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -383,6 +383,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::FnPtrAddrShim(..) + | ty::InstanceDef::ThreadLocalShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn let Some((body, instance)) = diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 631fd09ec4cf6..c0c0fd07b6e06 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> { NonGeneric(DefId), Generic(DefId, SubstsRef<'tcx>), DropGlue(Ty<'tcx>), + ThreadLocalShim(DefId), NoDefId(ty::SymbolName<'tcx>), } @@ -58,6 +59,10 @@ impl<'tcx> ExportedSymbol<'tcx> { ExportedSymbol::DropGlue(ty) => { tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty)) } + ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { + def: ty::InstanceDef::ThreadLocalShim(def_id), + substs: ty::InternalSubsts::empty(), + }), ExportedSymbol::NoDefId(symbol_name) => symbol_name, } } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 90397f01bc814..f592f1515c110 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -382,6 +382,7 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) + | InstanceDef::ThreadLocalShim(..) | InstanceDef::FnPtrAddrShim(..) => None, } } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 0aa2c500f51fb..ee439df8b6b7c 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -164,17 +164,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::Repeat(ref operand, count) => { tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count) } - Rvalue::ThreadLocalRef(did) => { - let static_ty = tcx.type_of(did).subst_identity(); - if tcx.is_mutable_static(did) { - tcx.mk_mut_ptr(static_ty) - } else if tcx.is_foreign_item(did) { - tcx.mk_imm_ptr(static_ty) - } else { - // FIXME: These things don't *really* have 'static lifetime. - tcx.mk_imm_ref(tcx.lifetimes.re_static, static_ty) - } - } + Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did), Rvalue::Ref(reg, bk, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() }) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 9b31ad783fc35..b39fc3aaaff88 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -335,6 +335,7 @@ macro_rules! make_mir_visitor { ty::InstanceDef::VTableShim(_def_id) | ty::InstanceDef::ReifyShim(_def_id) | ty::InstanceDef::Virtual(_def_id, _) | + ty::InstanceDef::ThreadLocalShim(_def_id) | ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } | ty::InstanceDef::DropGlue(_def_id, None) => {} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 036b447767971..e73225f70ccca 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -82,6 +82,11 @@ pub enum InstanceDef<'tcx> { /// The `DefId` is the ID of the `call_once` method in `FnOnce`. ClosureOnceShim { call_once: DefId, track_caller: bool }, + /// Compiler-generated accessor for thread locals which returns a reference to the thread local + /// the `DefId` defines. This is used to export thread locals from dylibs on platforms lacking + /// native support. + ThreadLocalShim(DefId), + /// `core::ptr::drop_in_place::`. /// /// The `DefId` is for `core::ptr::drop_in_place`. @@ -156,6 +161,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::FnPtrShim(def_id, _) | InstanceDef::Virtual(def_id, _) | InstanceDef::Intrinsic(def_id) + | InstanceDef::ThreadLocalShim(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) @@ -167,7 +173,9 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { ty::InstanceDef::Item(def) => Some(def.did), - ty::InstanceDef::DropGlue(def_id, Some(_)) => Some(def_id), + ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => { + Some(def_id) + } InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) @@ -192,6 +200,7 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) + | InstanceDef::ThreadLocalShim(def_id) | InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id), } } @@ -215,6 +224,7 @@ impl<'tcx> InstanceDef<'tcx> { let def_id = match *self { ty::InstanceDef::Item(def) => def.did, ty::InstanceDef::DropGlue(_, Some(_)) => return false, + ty::InstanceDef::ThreadLocalShim(_) => return false, _ => return true, }; matches!( @@ -255,6 +265,9 @@ impl<'tcx> InstanceDef<'tcx> { ) }); } + if let ty::InstanceDef::ThreadLocalShim(..) = *self { + return false; + } tcx.codegen_fn_attrs(self.def_id()).requests_inline() } @@ -278,6 +291,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn has_polymorphic_mir_body(&self) -> bool { match *self { InstanceDef::CloneShim(..) + | InstanceDef::ThreadLocalShim(..) | InstanceDef::FnPtrAddrShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::DropGlue(_, Some(_)) => false, @@ -310,6 +324,7 @@ fn fmt_instance( InstanceDef::Item(_) => Ok(()), InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"), InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), + InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty), diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c9dd3e499a8c0..7df90fab56f04 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2386,6 +2386,7 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::ThreadLocalShim(..) | ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance), } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index dcd9743196e1d..e9c0552812b07 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -597,6 +597,28 @@ impl<'tcx> TyCtxt<'tcx> { self.static_mutability(def_id) == Some(hir::Mutability::Mut) } + /// Returns `true` if the item pointed to by `def_id` is a thread local which needs a + /// thread local shim generated. + #[inline] + pub fn needs_thread_local_shim(self, def_id: DefId) -> bool { + !self.sess.target.dll_tls_export + && self.is_thread_local_static(def_id) + && !self.is_foreign_item(def_id) + } + + /// Returns the type a reference to the thread local takes in MIR. + pub fn thread_local_ptr_ty(self, def_id: DefId) -> Ty<'tcx> { + let static_ty = self.type_of(def_id).subst_identity(); + if self.is_mutable_static(def_id) { + self.mk_mut_ptr(static_ty) + } else if self.is_foreign_item(def_id) { + self.mk_imm_ptr(static_ty) + } else { + // FIXME: These things don't *really* have 'static lifetime. + self.mk_imm_ref(self.lifetimes.re_static, static_ty) + } + } + /// Get the type of the pointer to the static that we use in MIR. pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> { // Make sure that any constants in the static's type are evaluated. diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index b69186c9451e4..5c7415192b936 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -271,6 +271,7 @@ impl<'tcx> Inliner<'tcx> { | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) + | InstanceDef::ThreadLocalShim(..) | InstanceDef::FnPtrAddrShim(..) => return Ok(()), } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index faf404c77715b..8aa3c23d01917 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -83,6 +83,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( | InstanceDef::ReifyShim(_) | InstanceDef::FnPtrShim(..) | InstanceDef::ClosureOnceShim { .. } + | InstanceDef::ThreadLocalShim { .. } | InstanceDef::CloneShim(..) => {} // This shim does not call any other functions, thus there can be no recursion. diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 06a6deeee43e4..2ef5c1062fe28 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -76,6 +76,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' build_drop_shim(tcx, def_id, ty) } + ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), ty::InstanceDef::Virtual(..) => { @@ -322,6 +323,34 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } +fn build_thread_local_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'tcx> { + let def_id = instance.def_id(); + + let span = tcx.def_span(def_id); + let source_info = SourceInfo::outermost(span); + + let mut blocks = IndexVec::with_capacity(1); + blocks.push(BasicBlockData { + statements: vec![Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::ThreadLocalRef(def_id), + ))), + }], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), + is_cleanup: false, + }); + + new_body( + MirSource::from_instance(instance), + blocks, + IndexVec::from_raw(vec![LocalDecl::new(tcx.thread_local_ptr_ty(def_id), span)]), + 0, + span, + ) +} + /// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`. fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> { debug!("build_clone_shim(def_id={:?})", def_id); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 98265d58a0a59..af0222c8172d2 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -190,7 +190,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::query::TyCtxtAt; use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts}; use rustc_middle::ty::{ - self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, VtblEntry, + self, GenericParamDefKind, Instance, InstanceDef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, + VtblEntry, }; use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext}; use rustc_session::config::EntryFnType; @@ -462,6 +463,16 @@ fn collect_items_rec<'tcx>( collect_miri(tcx, id, &mut neighbors); } } + + if tcx.needs_thread_local_shim(def_id) { + neighbors.push(respan( + starting_point.span, + MonoItem::Fn(Instance { + def: InstanceDef::ThreadLocalShim(def_id), + substs: InternalSubsts::empty(), + }), + )); + } } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally @@ -962,6 +973,9 @@ fn visit_instance_use<'tcx>( bug!("{:?} being reified", instance); } } + ty::InstanceDef::ThreadLocalShim(..) => { + bug!("{:?} being reified", instance); + } ty::InstanceDef::DropGlue(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { @@ -1210,11 +1224,9 @@ impl<'v> RootCollector<'_, 'v> { self.output.push(dummy_spanned(MonoItem::GlobalAsm(id))); } DefKind::Static(..) => { - debug!( - "RootCollector: ItemKind::Static({})", - self.tcx.def_path_str(id.owner_id.to_def_id()) - ); - self.output.push(dummy_spanned(MonoItem::Static(id.owner_id.to_def_id()))); + let def_id = id.owner_id.to_def_id(); + debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); + self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { // const items only generate mono items if they are diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs index 64968a76ab554..50bcc3336d60d 100644 --- a/compiler/rustc_monomorphize/src/partitioning/default.rs +++ b/compiler/rustc_monomorphize/src/partitioning/default.rs @@ -279,6 +279,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::ThreadLocalShim(..) | ty::InstanceDef::FnPtrAddrShim(..) => return None, }; @@ -392,6 +393,19 @@ fn mono_item_linkage_and_visibility<'tcx>( type CguNameCache = FxHashMap<(DefId, bool), Symbol>; +fn static_visibility<'tcx>( + tcx: TyCtxt<'tcx>, + can_be_internalized: &mut bool, + def_id: DefId, +) -> Visibility { + if tcx.is_reachable_non_generic(def_id) { + *can_be_internalized = false; + default_visibility(tcx, def_id, false) + } else { + Visibility::Hidden + } +} + fn mono_item_visibility<'tcx>( tcx: TyCtxt<'tcx>, mono_item: &MonoItem<'tcx>, @@ -403,21 +417,9 @@ fn mono_item_visibility<'tcx>( MonoItem::Fn(instance) => instance, // Misc handling for generics and such, but otherwise: - MonoItem::Static(def_id) => { - return if tcx.is_reachable_non_generic(*def_id) { - *can_be_internalized = false; - default_visibility(tcx, *def_id, false) - } else { - Visibility::Hidden - }; - } + MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id), MonoItem::GlobalAsm(item_id) => { - return if tcx.is_reachable_non_generic(item_id.owner_id) { - *can_be_internalized = false; - default_visibility(tcx, item_id.owner_id.to_def_id(), false) - } else { - Visibility::Hidden - }; + return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id()); } }; @@ -425,6 +427,11 @@ fn mono_item_visibility<'tcx>( InstanceDef::Item(def) => def.did, InstanceDef::DropGlue(def_id, Some(_)) => def_id, + // We match the visiblity of statics here + InstanceDef::ThreadLocalShim(def_id) => { + return static_visibility(tcx, can_be_internalized, def_id); + } + // These are all compiler glue and such, never exported, always hidden. InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 2368468c89123..5cbca81926b9a 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -65,6 +65,10 @@ pub(super) fn mangle<'tcx>( ) .unwrap(); + if let ty::InstanceDef::ThreadLocalShim(..) = instance.def { + let _ = printer.write_str("{{tls-shim}}"); + } + if let ty::InstanceDef::VTableShim(..) = instance.def { let _ = printer.write_str("{{vtable-shim}}"); } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 2f20d42139c8d..cac7ff72267db 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -42,6 +42,7 @@ pub(super) fn mangle<'tcx>( // Append `::{shim:...#0}` to shims that can coexist with a non-shim instance. let shim_kind = match instance.def { + ty::InstanceDef::ThreadLocalShim(_) => Some("tls"), ty::InstanceDef::VTableShim(_) => Some("vtable"), ty::InstanceDef::ReifyShim(_) => Some("reify"), diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2553b11d8789b..b53cd2e7df19f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1468,6 +1468,8 @@ pub struct TargetOptions { pub features: StaticCow, /// Whether dynamic linking is available on this target. Defaults to false. pub dynamic_linking: bool, + /// Whether dynamic linking can export TLS globals. Defaults to true. + pub dll_tls_export: bool, /// If dynamic linking is available, whether only cdylibs are supported. pub only_cdylib: bool, /// Whether executables are available on this target. Defaults to true. @@ -1859,6 +1861,7 @@ impl Default for TargetOptions { cpu: "generic".into(), features: "".into(), dynamic_linking: false, + dll_tls_export: true, only_cdylib: false, executables: true, relocation_model: RelocModel::Pic, @@ -2530,6 +2533,7 @@ impl Target { key!(cpu); key!(features); key!(dynamic_linking, bool); + key!(dll_tls_export, bool); key!(only_cdylib, bool); key!(executables, bool); key!(relocation_model, RelocModel)?; @@ -2783,6 +2787,7 @@ impl ToJson for Target { target_option_val!(cpu); target_option_val!(features); target_option_val!(dynamic_linking); + target_option_val!(dll_tls_export); target_option_val!(only_cdylib); target_option_val!(executables); target_option_val!(relocation_model); diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index 1dad9133ea3d2..efe949a4e9074 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions { TargetOptions { linker_flavor: LinkerFlavor::Msvc(Lld::No), + dll_tls_export: false, is_like_windows: true, is_like_msvc: true, pre_link_args, diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index a32ca469b2f58..2231983f07126 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -78,6 +78,7 @@ pub fn opts() -> TargetOptions { function_sections: false, linker: Some("gcc".into()), dynamic_linking: true, + dll_tls_export: false, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs index cada28652f98a..b1d8e2be5a61f 100644 --- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs @@ -23,6 +23,7 @@ pub fn opts() -> TargetOptions { abi: "llvm".into(), linker: Some("clang".into()), dynamic_linking: true, + dll_tls_export: false, dll_prefix: "".into(), dll_suffix: ".dll".into(), exe_suffix: ".exe".into(), diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index ee5a7909ba3dc..271284b2d8115 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -3,7 +3,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::ty::layout::{ fn_can_unwind, FnAbiError, HasParamEnv, HasTyCtxt, LayoutCx, LayoutOf, TyAndLayout, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt}; use rustc_session::config::OptLevel; use rustc_span::def_id::DefId; use rustc_target::abi::call::{ @@ -29,6 +29,16 @@ fn fn_sig_for_fn_abi<'tcx>( instance: ty::Instance<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> ty::PolyFnSig<'tcx> { + if let InstanceDef::ThreadLocalShim(..) = instance.def { + return ty::Binder::dummy(tcx.mk_fn_sig( + [], + tcx.thread_local_ptr_ty(instance.def_id()), + false, + hir::Unsafety::Normal, + rustc_target::spec::abi::Abi::Unadjusted, + )); + } + let ty = instance.ty(tcx, param_env); match *ty.kind() { ty::FnDef(..) => { From 3019a341f328f1c5e4a671df2b61171ae925b034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 15 Feb 2023 16:50:28 +0100 Subject: [PATCH 3/4] Use #[inline] on Windows for thread local access --- .../src/sys/common/thread_local/fast_local.rs | 26 ++----------------- .../src/sys/common/thread_local/os_local.rs | 26 ++----------------- 2 files changed, 4 insertions(+), 48 deletions(-) diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index 2addcc4a759d0..e229eb16aa139 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -10,7 +10,7 @@ macro_rules! __thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals (@key $t:ty, const $init:expr) => {{ - #[cfg_attr(not(windows), inline)] // see comments below + #[cfg_attr(not(bootstrap), inline)] #[deny(unsafe_op_in_unsafe_fn)] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, @@ -77,29 +77,7 @@ macro_rules! __thread_local_inner { #[inline] fn __init() -> $t { $init } - // When reading this function you might ask "why is this inlined - // everywhere other than Windows?", and that's a very reasonable - // question to ask. The short story is that it segfaults rustc if - // this function is inlined. The longer story is that Windows looks - // to not support `extern` references to thread locals across DLL - // boundaries. This appears to at least not be supported in the ABI - // that LLVM implements. - // - // Because of this we never inline on Windows, but we do inline on - // other platforms (where external references to thread locals - // across DLLs are supported). A better fix for this would be to - // inline this function on Windows, but only for "statically linked" - // components. For example if two separately compiled rlibs end up - // getting linked into a DLL then it's fine to inline this function - // across that boundary. It's only not fine to inline this function - // across a DLL boundary. Unfortunately rustc doesn't currently - // have this sort of logic available in an attribute, and it's not - // clear that rustc is even equipped to answer this (it's more of a - // Cargo question kinda). This means that, unfortunately, Windows - // gets the pessimistic path for now where it's never inlined. - // - // The issue of "should enable on Windows sometimes" is #84933 - #[cfg_attr(not(windows), inline)] + #[cfg_attr(not(bootstrap), inline)] unsafe fn __getit( init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs index 6f6560c4aa949..ce74ad3486e10 100644 --- a/library/std/src/sys/common/thread_local/os_local.rs +++ b/library/std/src/sys/common/thread_local/os_local.rs @@ -10,7 +10,7 @@ macro_rules! __thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals (@key $t:ty, const $init:expr) => {{ - #[cfg_attr(not(windows), inline)] // see comments below + #[cfg_attr(not(bootstrap), inline)] #[deny(unsafe_op_in_unsafe_fn)] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, @@ -49,29 +49,7 @@ macro_rules! __thread_local_inner { #[inline] fn __init() -> $t { $init } - // When reading this function you might ask "why is this inlined - // everywhere other than Windows?", and that's a very reasonable - // question to ask. The short story is that it segfaults rustc if - // this function is inlined. The longer story is that Windows looks - // to not support `extern` references to thread locals across DLL - // boundaries. This appears to at least not be supported in the ABI - // that LLVM implements. - // - // Because of this we never inline on Windows, but we do inline on - // other platforms (where external references to thread locals - // across DLLs are supported). A better fix for this would be to - // inline this function on Windows, but only for "statically linked" - // components. For example if two separately compiled rlibs end up - // getting linked into a DLL then it's fine to inline this function - // across that boundary. It's only not fine to inline this function - // across a DLL boundary. Unfortunately rustc doesn't currently - // have this sort of logic available in an attribute, and it's not - // clear that rustc is even equipped to answer this (it's more of a - // Cargo question kinda). This means that, unfortunately, Windows - // gets the pessimistic path for now where it's never inlined. - // - // The issue of "should enable on Windows sometimes" is #84933 - #[cfg_attr(not(windows), inline)] + #[cfg_attr(not(bootstrap), inline)] unsafe fn __getit( init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { From d499bbb99d72c991f1d1691f83ffe96bcfafc80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 29 Mar 2023 14:50:10 +0200 Subject: [PATCH 4/4] Use #[cfg(target_thread_local)] on items --- tests/ui/thread-local/auxiliary/tls-export.rs | 3 ++- tests/ui/thread-local/auxiliary/tls-rlib.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ui/thread-local/auxiliary/tls-export.rs b/tests/ui/thread-local/auxiliary/tls-export.rs index aab48f871c9b8..027213bc03349 100644 --- a/tests/ui/thread-local/auxiliary/tls-export.rs +++ b/tests/ui/thread-local/auxiliary/tls-export.rs @@ -1,15 +1,16 @@ #![crate_type = "dylib"] #![feature(thread_local)] #![feature(cfg_target_thread_local)] -#![cfg(target_thread_local)] extern crate tls_rlib; pub use tls_rlib::*; +#[cfg(target_thread_local)] #[thread_local] pub static FOO: bool = true; +#[cfg(target_thread_local)] #[inline(never)] pub fn foo_addr() -> usize { &FOO as *const bool as usize diff --git a/tests/ui/thread-local/auxiliary/tls-rlib.rs b/tests/ui/thread-local/auxiliary/tls-rlib.rs index 0fbcf387b5fa9..20bc998ec11d8 100644 --- a/tests/ui/thread-local/auxiliary/tls-rlib.rs +++ b/tests/ui/thread-local/auxiliary/tls-rlib.rs @@ -3,11 +3,12 @@ #![crate_type = "rlib"] #![feature(thread_local)] #![feature(cfg_target_thread_local)] -#![cfg(target_thread_local)] +#[cfg(target_thread_local)] #[thread_local] pub static BAR: bool = true; +#[cfg(target_thread_local)] #[inline(never)] pub fn bar_addr() -> usize { &BAR as *const bool as usize