Auto merge of #108089 - Zoxc:windows-tls, r=bjorn3
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 https://github.com/rust-lang/rust/issues/84933.
This commit is contained in:
commit
f98598c6cd
28 changed files with 262 additions and 95 deletions
|
@ -54,12 +54,22 @@ pub(crate) fn codegen_tls_ref<'tcx>(
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
) -> CValue<'tcx> {
|
) -> CValue<'tcx> {
|
||||||
|
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 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);
|
let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
|
||||||
if fx.clif_comments.enabled() {
|
if fx.clif_comments.enabled() {
|
||||||
fx.add_comment(local_data_id, format!("tls {:?}", def_id));
|
fx.add_comment(local_data_id, format!("tls {:?}", def_id));
|
||||||
}
|
}
|
||||||
let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id);
|
fx.bcx.ins().tls_value(fx.pointer_type, local_data_id)
|
||||||
|
};
|
||||||
CValue::by_val(tls_ptr, layout)
|
CValue::by_val(tls_ptr, layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,14 +177,29 @@ fn exported_symbols_provider_local(
|
||||||
|
|
||||||
// FIXME: Sorting this is unnecessary since we are sorting later anyway.
|
// FIXME: Sorting this is unnecessary since we are sorting later anyway.
|
||||||
// Can we skip the later sorting?
|
// Can we skip the later sorting?
|
||||||
let mut symbols: Vec<_> = tcx.with_stable_hashing_context(|hcx| {
|
let sorted = tcx.with_stable_hashing_context(|hcx| {
|
||||||
tcx.reachable_non_generics(LOCAL_CRATE)
|
tcx.reachable_non_generics(LOCAL_CRATE).to_sorted(&hcx, true)
|
||||||
.to_sorted(&hcx, true)
|
|
||||||
.into_iter()
|
|
||||||
.map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
|
|
||||||
.collect()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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() {
|
if tcx.entry_fn(()).is_some() {
|
||||||
let exported_symbol =
|
let exported_symbol =
|
||||||
ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
|
ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
|
||||||
|
@ -380,7 +395,9 @@ fn upstream_monomorphizations_provider(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExportedSymbol::NonGeneric(..) | ExportedSymbol::NoDefId(..) => {
|
ExportedSymbol::NonGeneric(..)
|
||||||
|
| ExportedSymbol::ThreadLocalShim(..)
|
||||||
|
| ExportedSymbol::NoDefId(..) => {
|
||||||
// These are no monomorphizations
|
// These are no monomorphizations
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -500,6 +517,16 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
|
||||||
instantiating_crate,
|
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(
|
ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate(
|
||||||
tcx,
|
tcx,
|
||||||
Instance::resolve_drop_in_place(tcx, ty),
|
Instance::resolve_drop_in_place(tcx, ty),
|
||||||
|
@ -548,6 +575,8 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
|
||||||
ExportedSymbol::DropGlue(..) => None,
|
ExportedSymbol::DropGlue(..) => None,
|
||||||
// NoDefId always follow the target's default symbol decoration scheme.
|
// NoDefId always follow the target's default symbol decoration scheme.
|
||||||
ExportedSymbol::NoDefId(..) => None,
|
ExportedSymbol::NoDefId(..) => None,
|
||||||
|
// ThreadLocalShim always follow the target's default symbol decoration scheme.
|
||||||
|
ExportedSymbol::ThreadLocalShim(..) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (conv, args) = instance
|
let (conv, args) = instance
|
||||||
|
|
|
@ -516,8 +516,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
|
|
||||||
mir::Rvalue::ThreadLocalRef(def_id) => {
|
mir::Rvalue::ThreadLocalRef(def_id) => {
|
||||||
assert!(bx.cx().tcx().is_static(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 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 }
|
OperandRef { val: OperandValue::Immediate(static_), layout }
|
||||||
}
|
}
|
||||||
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
|
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
|
||||||
|
|
|
@ -383,6 +383,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::CloneShim(..)
|
| ty::InstanceDef::CloneShim(..)
|
||||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||||
|
| ty::InstanceDef::ThreadLocalShim(..)
|
||||||
| ty::InstanceDef::Item(_) => {
|
| ty::InstanceDef::Item(_) => {
|
||||||
// We need MIR for this fn
|
// We need MIR for this fn
|
||||||
let Some((body, instance)) =
|
let Some((body, instance)) =
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> {
|
||||||
NonGeneric(DefId),
|
NonGeneric(DefId),
|
||||||
Generic(DefId, SubstsRef<'tcx>),
|
Generic(DefId, SubstsRef<'tcx>),
|
||||||
DropGlue(Ty<'tcx>),
|
DropGlue(Ty<'tcx>),
|
||||||
|
ThreadLocalShim(DefId),
|
||||||
NoDefId(ty::SymbolName<'tcx>),
|
NoDefId(ty::SymbolName<'tcx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +59,10 @@ impl<'tcx> ExportedSymbol<'tcx> {
|
||||||
ExportedSymbol::DropGlue(ty) => {
|
ExportedSymbol::DropGlue(ty) => {
|
||||||
tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, 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,
|
ExportedSymbol::NoDefId(symbol_name) => symbol_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,6 +382,7 @@ impl<'tcx> CodegenUnit<'tcx> {
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| InstanceDef::DropGlue(..)
|
||||||
| InstanceDef::CloneShim(..)
|
| InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::ThreadLocalShim(..)
|
||||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,17 +164,7 @@ impl<'tcx> Rvalue<'tcx> {
|
||||||
Rvalue::Repeat(ref operand, count) => {
|
Rvalue::Repeat(ref operand, count) => {
|
||||||
tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count)
|
tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count)
|
||||||
}
|
}
|
||||||
Rvalue::ThreadLocalRef(did) => {
|
Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(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::Ref(reg, bk, ref place) => {
|
Rvalue::Ref(reg, bk, ref place) => {
|
||||||
let place_ty = place.ty(local_decls, tcx).ty;
|
let place_ty = place.ty(local_decls, tcx).ty;
|
||||||
tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
|
tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
|
||||||
|
|
|
@ -335,6 +335,7 @@ macro_rules! make_mir_visitor {
|
||||||
ty::InstanceDef::VTableShim(_def_id) |
|
ty::InstanceDef::VTableShim(_def_id) |
|
||||||
ty::InstanceDef::ReifyShim(_def_id) |
|
ty::InstanceDef::ReifyShim(_def_id) |
|
||||||
ty::InstanceDef::Virtual(_def_id, _) |
|
ty::InstanceDef::Virtual(_def_id, _) |
|
||||||
|
ty::InstanceDef::ThreadLocalShim(_def_id) |
|
||||||
ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |
|
ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |
|
||||||
ty::InstanceDef::DropGlue(_def_id, None) => {}
|
ty::InstanceDef::DropGlue(_def_id, None) => {}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,11 @@ pub enum InstanceDef<'tcx> {
|
||||||
/// The `DefId` is the ID of the `call_once` method in `FnOnce`.
|
/// The `DefId` is the ID of the `call_once` method in `FnOnce`.
|
||||||
ClosureOnceShim { call_once: DefId, track_caller: bool },
|
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>`.
|
/// `core::ptr::drop_in_place::<T>`.
|
||||||
///
|
///
|
||||||
/// The `DefId` is for `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::FnPtrShim(def_id, _)
|
||||||
| InstanceDef::Virtual(def_id, _)
|
| InstanceDef::Virtual(def_id, _)
|
||||||
| InstanceDef::Intrinsic(def_id)
|
| InstanceDef::Intrinsic(def_id)
|
||||||
|
| InstanceDef::ThreadLocalShim(def_id)
|
||||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||||
| InstanceDef::DropGlue(def_id, _)
|
| InstanceDef::DropGlue(def_id, _)
|
||||||
| InstanceDef::CloneShim(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<DefId> {
|
pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
|
||||||
match self {
|
match self {
|
||||||
ty::InstanceDef::Item(def) => Some(def.did),
|
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::VTableShim(..)
|
||||||
| InstanceDef::ReifyShim(..)
|
| InstanceDef::ReifyShim(..)
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
|
@ -192,6 +200,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||||
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
|
||||||
| InstanceDef::DropGlue(def_id, _)
|
| InstanceDef::DropGlue(def_id, _)
|
||||||
| InstanceDef::CloneShim(def_id, _)
|
| InstanceDef::CloneShim(def_id, _)
|
||||||
|
| InstanceDef::ThreadLocalShim(def_id)
|
||||||
| InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
|
| InstanceDef::FnPtrAddrShim(def_id, _) => ty::WithOptConstParam::unknown(def_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,6 +224,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||||
let def_id = match *self {
|
let def_id = match *self {
|
||||||
ty::InstanceDef::Item(def) => def.did,
|
ty::InstanceDef::Item(def) => def.did,
|
||||||
ty::InstanceDef::DropGlue(_, Some(_)) => return false,
|
ty::InstanceDef::DropGlue(_, Some(_)) => return false,
|
||||||
|
ty::InstanceDef::ThreadLocalShim(_) => return false,
|
||||||
_ => return true,
|
_ => return true,
|
||||||
};
|
};
|
||||||
matches!(
|
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()
|
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 {
|
pub fn has_polymorphic_mir_body(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
InstanceDef::CloneShim(..)
|
InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::ThreadLocalShim(..)
|
||||||
| InstanceDef::FnPtrAddrShim(..)
|
| InstanceDef::FnPtrAddrShim(..)
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
| InstanceDef::DropGlue(_, Some(_)) => false,
|
| InstanceDef::DropGlue(_, Some(_)) => false,
|
||||||
|
@ -310,6 +324,7 @@ fn fmt_instance(
|
||||||
InstanceDef::Item(_) => Ok(()),
|
InstanceDef::Item(_) => Ok(()),
|
||||||
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
|
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
|
||||||
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
|
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
|
||||||
|
InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"),
|
||||||
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
|
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
|
||||||
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
|
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num),
|
||||||
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
|
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty),
|
||||||
|
|
|
@ -2386,6 +2386,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::CloneShim(..)
|
| ty::InstanceDef::CloneShim(..)
|
||||||
|
| ty::InstanceDef::ThreadLocalShim(..)
|
||||||
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -597,6 +597,28 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
self.static_mutability(def_id) == Some(hir::Mutability::Mut)
|
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.
|
/// 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> {
|
pub fn static_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
|
||||||
// Make sure that any constants in the static's type are evaluated.
|
// Make sure that any constants in the static's type are evaluated.
|
||||||
|
|
|
@ -271,6 +271,7 @@ impl<'tcx> Inliner<'tcx> {
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
| InstanceDef::DropGlue(..)
|
| InstanceDef::DropGlue(..)
|
||||||
| InstanceDef::CloneShim(..)
|
| InstanceDef::CloneShim(..)
|
||||||
|
| InstanceDef::ThreadLocalShim(..)
|
||||||
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||||
| InstanceDef::ReifyShim(_)
|
| InstanceDef::ReifyShim(_)
|
||||||
| InstanceDef::FnPtrShim(..)
|
| InstanceDef::FnPtrShim(..)
|
||||||
| InstanceDef::ClosureOnceShim { .. }
|
| InstanceDef::ClosureOnceShim { .. }
|
||||||
|
| InstanceDef::ThreadLocalShim { .. }
|
||||||
| InstanceDef::CloneShim(..) => {}
|
| InstanceDef::CloneShim(..) => {}
|
||||||
|
|
||||||
// This shim does not call any other functions, thus there can be no recursion.
|
// This shim does not call any other functions, thus there can be no recursion.
|
||||||
|
|
|
@ -76,6 +76,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||||
|
|
||||||
build_drop_shim(tcx, def_id, ty)
|
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::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::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
|
||||||
ty::InstanceDef::Virtual(..) => {
|
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`.
|
/// 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> {
|
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);
|
debug!("build_clone_shim(def_id={:?})", def_id);
|
||||||
|
|
|
@ -190,7 +190,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::query::TyCtxtAt;
|
use rustc_middle::ty::query::TyCtxtAt;
|
||||||
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
|
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
|
||||||
use rustc_middle::ty::{
|
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_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
|
||||||
use rustc_session::config::EntryFnType;
|
use rustc_session::config::EntryFnType;
|
||||||
|
@ -462,6 +463,16 @@ fn collect_items_rec<'tcx>(
|
||||||
collect_miri(tcx, id, &mut neighbors);
|
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) => {
|
MonoItem::Fn(instance) => {
|
||||||
// Sanity check whether this ended up being collected accidentally
|
// Sanity check whether this ended up being collected accidentally
|
||||||
|
@ -962,6 +973,9 @@ fn visit_instance_use<'tcx>(
|
||||||
bug!("{:?} being reified", instance);
|
bug!("{:?} being reified", instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ty::InstanceDef::ThreadLocalShim(..) => {
|
||||||
|
bug!("{:?} being reified", instance);
|
||||||
|
}
|
||||||
ty::InstanceDef::DropGlue(_, None) => {
|
ty::InstanceDef::DropGlue(_, None) => {
|
||||||
// Don't need to emit noop drop glue if we are calling directly.
|
// Don't need to emit noop drop glue if we are calling directly.
|
||||||
if !is_direct_call {
|
if !is_direct_call {
|
||||||
|
@ -1210,11 +1224,9 @@ impl<'v> RootCollector<'_, 'v> {
|
||||||
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
|
self.output.push(dummy_spanned(MonoItem::GlobalAsm(id)));
|
||||||
}
|
}
|
||||||
DefKind::Static(..) => {
|
DefKind::Static(..) => {
|
||||||
debug!(
|
let def_id = id.owner_id.to_def_id();
|
||||||
"RootCollector: ItemKind::Static({})",
|
debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id));
|
||||||
self.tcx.def_path_str(id.owner_id.to_def_id())
|
self.output.push(dummy_spanned(MonoItem::Static(def_id)));
|
||||||
);
|
|
||||||
self.output.push(dummy_spanned(MonoItem::Static(id.owner_id.to_def_id())));
|
|
||||||
}
|
}
|
||||||
DefKind::Const => {
|
DefKind::Const => {
|
||||||
// const items only generate mono items if they are
|
// const items only generate mono items if they are
|
||||||
|
|
|
@ -279,6 +279,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
|
||||||
| ty::InstanceDef::DropGlue(..)
|
| ty::InstanceDef::DropGlue(..)
|
||||||
| ty::InstanceDef::Virtual(..)
|
| ty::InstanceDef::Virtual(..)
|
||||||
| ty::InstanceDef::CloneShim(..)
|
| ty::InstanceDef::CloneShim(..)
|
||||||
|
| ty::InstanceDef::ThreadLocalShim(..)
|
||||||
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
|
| ty::InstanceDef::FnPtrAddrShim(..) => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -392,6 +393,19 @@ fn mono_item_linkage_and_visibility<'tcx>(
|
||||||
|
|
||||||
type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
|
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>(
|
fn mono_item_visibility<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mono_item: &MonoItem<'tcx>,
|
mono_item: &MonoItem<'tcx>,
|
||||||
|
@ -403,21 +417,9 @@ fn mono_item_visibility<'tcx>(
|
||||||
MonoItem::Fn(instance) => instance,
|
MonoItem::Fn(instance) => instance,
|
||||||
|
|
||||||
// Misc handling for generics and such, but otherwise:
|
// Misc handling for generics and such, but otherwise:
|
||||||
MonoItem::Static(def_id) => {
|
MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *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::GlobalAsm(item_id) => {
|
MonoItem::GlobalAsm(item_id) => {
|
||||||
return if tcx.is_reachable_non_generic(item_id.owner_id) {
|
return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id());
|
||||||
*can_be_internalized = false;
|
|
||||||
default_visibility(tcx, item_id.owner_id.to_def_id(), false)
|
|
||||||
} else {
|
|
||||||
Visibility::Hidden
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -425,6 +427,11 @@ fn mono_item_visibility<'tcx>(
|
||||||
InstanceDef::Item(def) => def.did,
|
InstanceDef::Item(def) => def.did,
|
||||||
InstanceDef::DropGlue(def_id, Some(_)) => def_id,
|
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.
|
// These are all compiler glue and such, never exported, always hidden.
|
||||||
InstanceDef::VTableShim(..)
|
InstanceDef::VTableShim(..)
|
||||||
| InstanceDef::ReifyShim(..)
|
| InstanceDef::ReifyShim(..)
|
||||||
|
|
|
@ -65,6 +65,10 @@ pub(super) fn mangle<'tcx>(
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
if let ty::InstanceDef::ThreadLocalShim(..) = instance.def {
|
||||||
|
let _ = printer.write_str("{{tls-shim}}");
|
||||||
|
}
|
||||||
|
|
||||||
if let ty::InstanceDef::VTableShim(..) = instance.def {
|
if let ty::InstanceDef::VTableShim(..) = instance.def {
|
||||||
let _ = printer.write_str("{{vtable-shim}}");
|
let _ = printer.write_str("{{vtable-shim}}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ pub(super) fn mangle<'tcx>(
|
||||||
|
|
||||||
// Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
|
// Append `::{shim:...#0}` to shims that can coexist with a non-shim instance.
|
||||||
let shim_kind = match instance.def {
|
let shim_kind = match instance.def {
|
||||||
|
ty::InstanceDef::ThreadLocalShim(_) => Some("tls"),
|
||||||
ty::InstanceDef::VTableShim(_) => Some("vtable"),
|
ty::InstanceDef::VTableShim(_) => Some("vtable"),
|
||||||
ty::InstanceDef::ReifyShim(_) => Some("reify"),
|
ty::InstanceDef::ReifyShim(_) => Some("reify"),
|
||||||
|
|
||||||
|
|
|
@ -1471,6 +1471,8 @@ pub struct TargetOptions {
|
||||||
pub features: StaticCow<str>,
|
pub features: StaticCow<str>,
|
||||||
/// Whether dynamic linking is available on this target. Defaults to false.
|
/// Whether dynamic linking is available on this target. Defaults to false.
|
||||||
pub dynamic_linking: bool,
|
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.
|
/// If dynamic linking is available, whether only cdylibs are supported.
|
||||||
pub only_cdylib: bool,
|
pub only_cdylib: bool,
|
||||||
/// Whether executables are available on this target. Defaults to true.
|
/// Whether executables are available on this target. Defaults to true.
|
||||||
|
@ -1865,6 +1867,7 @@ impl Default for TargetOptions {
|
||||||
cpu: "generic".into(),
|
cpu: "generic".into(),
|
||||||
features: "".into(),
|
features: "".into(),
|
||||||
dynamic_linking: false,
|
dynamic_linking: false,
|
||||||
|
dll_tls_export: true,
|
||||||
only_cdylib: false,
|
only_cdylib: false,
|
||||||
executables: true,
|
executables: true,
|
||||||
relocation_model: RelocModel::Pic,
|
relocation_model: RelocModel::Pic,
|
||||||
|
@ -2537,6 +2540,7 @@ impl Target {
|
||||||
key!(cpu);
|
key!(cpu);
|
||||||
key!(features);
|
key!(features);
|
||||||
key!(dynamic_linking, bool);
|
key!(dynamic_linking, bool);
|
||||||
|
key!(dll_tls_export, bool);
|
||||||
key!(only_cdylib, bool);
|
key!(only_cdylib, bool);
|
||||||
key!(executables, bool);
|
key!(executables, bool);
|
||||||
key!(relocation_model, RelocModel)?;
|
key!(relocation_model, RelocModel)?;
|
||||||
|
@ -2791,6 +2795,7 @@ impl ToJson for Target {
|
||||||
target_option_val!(cpu);
|
target_option_val!(cpu);
|
||||||
target_option_val!(features);
|
target_option_val!(features);
|
||||||
target_option_val!(dynamic_linking);
|
target_option_val!(dynamic_linking);
|
||||||
|
target_option_val!(dll_tls_export);
|
||||||
target_option_val!(only_cdylib);
|
target_option_val!(only_cdylib);
|
||||||
target_option_val!(executables);
|
target_option_val!(executables);
|
||||||
target_option_val!(relocation_model);
|
target_option_val!(relocation_model);
|
||||||
|
|
|
@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions {
|
||||||
|
|
||||||
TargetOptions {
|
TargetOptions {
|
||||||
linker_flavor: LinkerFlavor::Msvc(Lld::No),
|
linker_flavor: LinkerFlavor::Msvc(Lld::No),
|
||||||
|
dll_tls_export: false,
|
||||||
is_like_windows: true,
|
is_like_windows: true,
|
||||||
is_like_msvc: true,
|
is_like_msvc: true,
|
||||||
pre_link_args,
|
pre_link_args,
|
||||||
|
|
|
@ -78,6 +78,7 @@ pub fn opts() -> TargetOptions {
|
||||||
function_sections: false,
|
function_sections: false,
|
||||||
linker: Some("gcc".into()),
|
linker: Some("gcc".into()),
|
||||||
dynamic_linking: true,
|
dynamic_linking: true,
|
||||||
|
dll_tls_export: false,
|
||||||
dll_prefix: "".into(),
|
dll_prefix: "".into(),
|
||||||
dll_suffix: ".dll".into(),
|
dll_suffix: ".dll".into(),
|
||||||
exe_suffix: ".exe".into(),
|
exe_suffix: ".exe".into(),
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub fn opts() -> TargetOptions {
|
||||||
abi: "llvm".into(),
|
abi: "llvm".into(),
|
||||||
linker: Some("clang".into()),
|
linker: Some("clang".into()),
|
||||||
dynamic_linking: true,
|
dynamic_linking: true,
|
||||||
|
dll_tls_export: false,
|
||||||
dll_prefix: "".into(),
|
dll_prefix: "".into(),
|
||||||
dll_suffix: ".dll".into(),
|
dll_suffix: ".dll".into(),
|
||||||
exe_suffix: ".exe".into(),
|
exe_suffix: ".exe".into(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_middle::ty::layout::{
|
use rustc_middle::ty::layout::{
|
||||||
fn_can_unwind, FnAbiError, HasParamEnv, HasTyCtxt, LayoutCx, LayoutOf, TyAndLayout,
|
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_session::config::OptLevel;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_target::abi::call::{
|
use rustc_target::abi::call::{
|
||||||
|
@ -29,6 +29,16 @@ fn fn_sig_for_fn_abi<'tcx>(
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
) -> ty::PolyFnSig<'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);
|
let ty = instance.ty(tcx, param_env);
|
||||||
match *ty.kind() {
|
match *ty.kind() {
|
||||||
ty::FnDef(..) => {
|
ty::FnDef(..) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
macro_rules! __thread_local_inner {
|
macro_rules! __thread_local_inner {
|
||||||
// used to generate the `LocalKey` value for const-initialized thread locals
|
// used to generate the `LocalKey` value for const-initialized thread locals
|
||||||
(@key $t:ty, const $init:expr) => {{
|
(@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)]
|
#[deny(unsafe_op_in_unsafe_fn)]
|
||||||
unsafe fn __getit(
|
unsafe fn __getit(
|
||||||
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||||
|
@ -77,29 +77,7 @@ macro_rules! __thread_local_inner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn __init() -> $t { $init }
|
fn __init() -> $t { $init }
|
||||||
|
|
||||||
// When reading this function you might ask "why is this inlined
|
#[cfg_attr(not(bootstrap), inline)]
|
||||||
// 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)]
|
|
||||||
unsafe fn __getit(
|
unsafe fn __getit(
|
||||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||||
) -> $crate::option::Option<&'static $t> {
|
) -> $crate::option::Option<&'static $t> {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
macro_rules! __thread_local_inner {
|
macro_rules! __thread_local_inner {
|
||||||
// used to generate the `LocalKey` value for const-initialized thread locals
|
// used to generate the `LocalKey` value for const-initialized thread locals
|
||||||
(@key $t:ty, const $init:expr) => {{
|
(@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)]
|
#[deny(unsafe_op_in_unsafe_fn)]
|
||||||
unsafe fn __getit(
|
unsafe fn __getit(
|
||||||
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
_init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||||
|
@ -49,29 +49,7 @@ macro_rules! __thread_local_inner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn __init() -> $t { $init }
|
fn __init() -> $t { $init }
|
||||||
|
|
||||||
// When reading this function you might ask "why is this inlined
|
#[cfg_attr(not(bootstrap), inline)]
|
||||||
// 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)]
|
|
||||||
unsafe fn __getit(
|
unsafe fn __getit(
|
||||||
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
init: $crate::option::Option<&mut $crate::option::Option<$t>>,
|
||||||
) -> $crate::option::Option<&'static $t> {
|
) -> $crate::option::Option<&'static $t> {
|
||||||
|
|
17
tests/ui/thread-local/auxiliary/tls-export.rs
Normal file
17
tests/ui/thread-local/auxiliary/tls-export.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#![crate_type = "dylib"]
|
||||||
|
#![feature(thread_local)]
|
||||||
|
#![feature(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
|
||||||
|
}
|
15
tests/ui/thread-local/auxiliary/tls-rlib.rs
Normal file
15
tests/ui/thread-local/auxiliary/tls-rlib.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
#[cfg(target_thread_local)]
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn bar_addr() -> usize {
|
||||||
|
&BAR as *const bool as usize
|
||||||
|
}
|
19
tests/ui/thread-local/tls-dylib-access.rs
Normal file
19
tests/ui/thread-local/tls-dylib-access.rs
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue