Skip to content

Commit

Permalink
Auto merge of #108089 - Zoxc:windows-tls, r=bjorn3
Browse files Browse the repository at this point in the history
Support TLS access into dylibs on Windows

This allows access to `#[thread_local]`  in upstream dylibs on Windows by introducing a MIR shim to return the address of the thread local. Accesses that go into an upstream dylib will call the MIR shim to get the address of it.

`convert_tls_rvalues` is introduced in `rustc_codegen_ssa` which rewrites MIR TLS accesses to dummy calls which are replaced with calls to the MIR shims when the dummy calls are lowered to backend calls.

A new `dll_tls_export` target option enables this behavior with a `false` value which is set for Windows platforms.

This fixes #84933.
  • Loading branch information
bors committed Mar 29, 2023
2 parents 8679208 + d499bbb commit f98598c
Show file tree
Hide file tree
Showing 28 changed files with 262 additions and 95 deletions.
22 changes: 16 additions & 6 deletions compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
43 changes: 36 additions & 7 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down Expand Up @@ -380,7 +395,9 @@ fn upstream_monomorphizations_provider(
continue;
}
}
ExportedSymbol::NonGeneric(..) | ExportedSymbol::NoDefId(..) => {
ExportedSymbol::NonGeneric(..)
| ExportedSymbol::ThreadLocalShim(..)
| ExportedSymbol::NoDefId(..) => {
// These are no monomorphizations
continue;
}
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_ssa/src/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)) =
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/middle/exported_symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> {
NonGeneric(DefId),
Generic(DefId, SubstsRef<'tcx>),
DropGlue(Ty<'tcx>),
ThreadLocalShim(DefId),
NoDefId(ty::SymbolName<'tcx>),
}

Expand All @@ -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,
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ impl<'tcx> CodegenUnit<'tcx> {
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)
| InstanceDef::FnPtrAddrShim(..) => None,
}
}
Expand Down
12 changes: 1 addition & 11 deletions compiler/rustc_middle/src/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() })
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {}

Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<T>`.
///
/// The `DefId` is for `core::ptr::drop_in_place`.
Expand Down Expand Up @@ -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, _)
Expand All @@ -167,7 +173,9 @@ impl<'tcx> InstanceDef<'tcx> {
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
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(..)
Expand All @@ -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),
}
}
Expand All @@ -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!(
Expand Down Expand Up @@ -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()
}

Expand All @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ impl<'tcx> Inliner<'tcx> {
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/inline/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 29 additions & 0 deletions compiler/rustc_mir_transform/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) => {
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit f98598c

Please sign in to comment.