Skip to content

Commit 34c8708

Browse files
committed
Support TLS access into dylibs on Windows
1 parent be3452a commit 34c8708

File tree

23 files changed

+333
-35
lines changed

23 files changed

+333
-35
lines changed

Diff for: compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+33-7
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,29 @@ fn exported_symbols_provider_local(
178178

179179
// FIXME: Sorting this is unnecessary since we are sorting later anyway.
180180
// Can we skip the later sorting?
181-
let mut symbols: Vec<_> = tcx.with_stable_hashing_context(|hcx| {
182-
tcx.reachable_non_generics(LOCAL_CRATE)
183-
.to_sorted(&hcx, true)
184-
.into_iter()
185-
.map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
186-
.collect()
181+
let sorted = tcx.with_stable_hashing_context(|hcx| {
182+
tcx.reachable_non_generics(LOCAL_CRATE).to_sorted(&hcx, true)
187183
});
188184

185+
let mut symbols: Vec<_> =
186+
sorted.iter().map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info)).collect();
187+
188+
// Export TLS shims
189+
if !tcx.sess.target.dll_tls_export {
190+
symbols.extend(sorted.iter().filter_map(|(&def_id, &info)| {
191+
tcx.is_thread_local_static(def_id).then(|| {
192+
(
193+
ExportedSymbol::ThreadLocalShim(def_id),
194+
SymbolExportInfo {
195+
level: info.level,
196+
kind: SymbolExportKind::Text,
197+
used: info.used,
198+
},
199+
)
200+
})
201+
}))
202+
}
203+
189204
if tcx.entry_fn(()).is_some() {
190205
let exported_symbol =
191206
ExportedSymbol::NoDefId(SymbolName::new(tcx, tcx.sess.target.entry_name.as_ref()));
@@ -380,7 +395,9 @@ fn upstream_monomorphizations_provider(
380395
continue;
381396
}
382397
}
383-
ExportedSymbol::NonGeneric(..) | ExportedSymbol::NoDefId(..) => {
398+
ExportedSymbol::NonGeneric(..)
399+
| ExportedSymbol::ThreadLocalShim(..)
400+
| ExportedSymbol::NoDefId(..) => {
384401
// These are no monomorphizations
385402
continue;
386403
}
@@ -500,6 +517,13 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
500517
instantiating_crate,
501518
)
502519
}
520+
ExportedSymbol::ThreadLocalShim(def_id) => {
521+
rustc_symbol_mangling::symbol_name_for_instance_in_crate(
522+
tcx,
523+
Instance::new(def_id, ty::InternalSubsts::empty()),
524+
instantiating_crate,
525+
)
526+
}
503527
ExportedSymbol::DropGlue(ty) => rustc_symbol_mangling::symbol_name_for_instance_in_crate(
504528
tcx,
505529
Instance::resolve_drop_in_place(tcx, ty),
@@ -548,6 +572,8 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
548572
ExportedSymbol::DropGlue(..) => None,
549573
// NoDefId always follow the target's default symbol decoration scheme.
550574
ExportedSymbol::NoDefId(..) => None,
575+
// ThreadLocalShim always follow the target's default symbol decoration scheme.
576+
ExportedSymbol::ThreadLocalShim(..) => None,
551577
};
552578

553579
let (conv, args) = instance

Diff for: compiler/rustc_codegen_ssa/src/mir/block.rs

+52-19
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use crate::MemFlags;
1212
use rustc_ast as ast;
1313
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
1414
use rustc_hir::lang_items::LangItem;
15-
use rustc_index::vec::Idx;
15+
use rustc_hir::Unsafety;
16+
use rustc_index::vec::{Idx, IndexVec};
1617
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
1718
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
1819
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
@@ -743,25 +744,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
743744
let source_info = terminator.source_info;
744745
let span = source_info.span;
745746

746-
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
747-
let callee = self.codegen_operand(bx, func);
747+
let thread_local_shim_call = if !self.call_thread_local_shims.is_empty() {
748+
self.call_thread_local_shims.iter().find(|e| &e.0 == func).map(|e| e.1)
749+
} else {
750+
None
751+
};
748752

749-
let (instance, mut llfn) = match *callee.layout.ty.kind() {
750-
ty::FnDef(def_id, substs) => (
751-
Some(
752-
ty::Instance::expect_resolve(
753-
bx.tcx(),
754-
ty::ParamEnv::reveal_all(),
755-
def_id,
756-
substs,
757-
)
758-
.polymorphize(bx.tcx()),
759-
),
760-
None,
761-
),
762-
ty::FnPtr(_) => (None, Some(callee.immediate())),
763-
_ => bug!("{} is not callable", callee.layout.ty),
753+
let (sig, instance, mut llfn) = match thread_local_shim_call {
754+
Some(thread_local) => {
755+
// Replace thread local dummy calls with calls to the real shim
756+
let instance = ty::Instance {
757+
def: ty::InstanceDef::ThreadLocalShim(thread_local),
758+
substs: ty::InternalSubsts::empty(),
759+
};
760+
let ty = mir::Rvalue::ThreadLocalRef(thread_local).ty(&IndexVec::new(), bx.tcx());
761+
(
762+
ty::Binder::dummy(bx.tcx().mk_fn_sig(
763+
[].iter(),
764+
&ty,
765+
false,
766+
Unsafety::Normal,
767+
Abi::Unadjusted,
768+
)),
769+
Some(instance),
770+
None,
771+
)
772+
}
773+
None => {
774+
// Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar.
775+
let callee = self.codegen_operand(bx, func);
776+
777+
let sig = callee.layout.ty.fn_sig(bx.tcx());
778+
779+
match *callee.layout.ty.kind() {
780+
ty::FnDef(def_id, substs) => (
781+
sig,
782+
Some(
783+
ty::Instance::expect_resolve(
784+
bx.tcx(),
785+
ty::ParamEnv::reveal_all(),
786+
def_id,
787+
substs,
788+
)
789+
.polymorphize(bx.tcx()),
790+
),
791+
None,
792+
),
793+
ty::FnPtr(_) => (sig, None, Some(callee.immediate())),
794+
_ => bug!("{} is not callable", callee.layout.ty),
795+
}
796+
}
764797
};
798+
765799
let def = instance.map(|i| i.def);
766800

767801
if let Some(ty::InstanceDef::DropGlue(_, None)) = def {
@@ -773,7 +807,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
773807
// FIXME(eddyb) avoid computing this if possible, when `instance` is
774808
// available - right now `sig` is only needed for getting the `abi`
775809
// and figuring out how many extra args were passed to a C-variadic `fn`.
776-
let sig = callee.layout.ty.fn_sig(bx.tcx());
777810
let abi = sig.abi();
778811

779812
// Handle intrinsics old codegen wants Expr's for, ourselves.

Diff for: compiler/rustc_codegen_ssa/src/mir/mod.rs

+119-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
use crate::base;
22
use crate::traits::*;
3+
use rustc_hir::def_id::DefId;
4+
use rustc_index::vec::Idx;
35
use rustc_middle::mir;
46
use rustc_middle::mir::interpret::ErrorHandled;
7+
use rustc_middle::mir::visit::MutVisitor;
8+
use rustc_middle::mir::visit::Visitor;
59
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
10+
use rustc_middle::ty::TyCtxt;
611
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
12+
use rustc_span::DUMMY_SP;
713
use rustc_target::abi::call::{FnAbi, PassMode};
814

915
use std::iter;
@@ -43,6 +49,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4349

4450
fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
4551

52+
// Used to replace call terminators with a call to a thread local shim.
53+
call_thread_local_shims: Vec<(mir::Operand<'tcx>, DefId)>,
54+
4655
/// When unwinding is initiated, we have to store this personality
4756
/// value somewhere so that we can load it and re-use it in the
4857
/// resume instruction. The personality is (afaik) some kind of
@@ -142,6 +151,112 @@ impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
142151
}
143152
}
144153

154+
struct FindThreadLocal(bool);
155+
156+
impl<'tcx> Visitor<'tcx> for FindThreadLocal {
157+
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
158+
if let mir::Rvalue::ThreadLocalRef(..) = rvalue {
159+
self.0 = true;
160+
}
161+
self.super_rvalue(rvalue, location);
162+
}
163+
}
164+
165+
struct ReplaceThreadLocal<'tcx> {
166+
tcx: TyCtxt<'tcx>,
167+
local_start: usize,
168+
list: Vec<DefId>,
169+
}
170+
171+
impl<'tcx> MutVisitor<'tcx> for ReplaceThreadLocal<'tcx> {
172+
fn tcx(&self) -> TyCtxt<'tcx> {
173+
self.tcx
174+
}
175+
176+
fn visit_rvalue(&mut self, rvalue: &mut mir::Rvalue<'tcx>, location: mir::Location) {
177+
if let mir::Rvalue::ThreadLocalRef(def_id) = *rvalue {
178+
if self.tcx.is_in_upstream_dylib(def_id.krate) {
179+
*rvalue = mir::Rvalue::Use(mir::Operand::Copy(mir::Place {
180+
local: mir::Local::new(self.local_start + self.list.len()),
181+
projection: self.tcx.intern_place_elems(&[]),
182+
}));
183+
self.list.push(def_id);
184+
}
185+
}
186+
self.super_rvalue(rvalue, location);
187+
}
188+
}
189+
190+
// Convert thread local references to thread local function shims if necessary
191+
fn convert_tls_rvalues<'tcx>(
192+
tcx: TyCtxt<'tcx>,
193+
mir: &mut &'tcx mir::Body<'tcx>,
194+
) -> Vec<(mir::Operand<'tcx>, DefId)> {
195+
if tcx.sess.target.dll_tls_export {
196+
// The target supports DLL TLS exports. We don't need to do anything
197+
return Vec::new();
198+
}
199+
200+
// Fast path to look for any thread locals
201+
let mut visitor = FindThreadLocal(false);
202+
visitor.visit_body(&mir);
203+
if !visitor.0 {
204+
return Vec::new();
205+
}
206+
207+
// Don't modify shims
208+
if let ty::InstanceDef::ThreadLocalShim(..) = mir.source.instance {
209+
return Vec::new();
210+
}
211+
212+
let mut result = Vec::new();
213+
let mut body = mir.clone();
214+
215+
let mut visitor =
216+
ReplaceThreadLocal { tcx, local_start: mir.local_decls.len(), list: Vec::new() };
217+
visitor.visit_body(&mut body);
218+
219+
for (i, &def_id) in visitor.list.iter().enumerate() {
220+
let ty = mir::Rvalue::ThreadLocalRef(def_id).ty(&IndexVec::new(), tcx);
221+
body.local_decls.push(mir::LocalDecl::new(ty, DUMMY_SP));
222+
let local = mir::Local::new(visitor.local_start + i);
223+
let place = mir::Place { local, projection: tcx.intern_place_elems(&[]) };
224+
let func = mir::Operand::Copy(place);
225+
226+
result.push((func.clone(), def_id));
227+
228+
let blocks = body.basic_blocks.as_mut();
229+
230+
let new_entry = mir::BasicBlock::new(blocks.len());
231+
232+
let entry = std::mem::replace(
233+
&mut blocks[mir::BasicBlock::new(0)],
234+
mir::BasicBlockData {
235+
statements: Vec::new(),
236+
terminator: Some(mir::Terminator {
237+
source_info: mir::SourceInfo::outermost(DUMMY_SP),
238+
kind: mir::TerminatorKind::Call {
239+
func,
240+
args: Vec::new(),
241+
destination: place,
242+
target: Some(new_entry),
243+
cleanup: None,
244+
from_hir_call: false,
245+
fn_span: DUMMY_SP,
246+
},
247+
}),
248+
is_cleanup: false,
249+
},
250+
);
251+
252+
blocks.push(entry);
253+
}
254+
255+
*mir = tcx.arena.alloc(body);
256+
257+
result
258+
}
259+
145260
///////////////////////////////////////////////////////////////////////////
146261

147262
#[instrument(level = "debug", skip(cx))]
@@ -153,7 +268,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
153268

154269
let llfn = cx.get_fn(instance);
155270

156-
let mir = cx.tcx().instance_mir(instance.def);
271+
let mut mir = cx.tcx().instance_mir(instance.def);
272+
273+
let call_thread_local_shims = convert_tls_rvalues(cx.tcx(), &mut mir);
157274

158275
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
159276
debug!("fn_abi: {:?}", fn_abi);
@@ -183,6 +300,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
183300
llfn,
184301
fn_abi,
185302
cx,
303+
call_thread_local_shims,
186304
personality_slot: None,
187305
cached_llbbs,
188306
unreachable_block: None,

Diff for: compiler/rustc_const_eval/src/interpret/terminator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
389389
| ty::InstanceDef::FnPtrShim(..)
390390
| ty::InstanceDef::DropGlue(..)
391391
| ty::InstanceDef::CloneShim(..)
392+
| ty::InstanceDef::ThreadLocalShim(..)
392393
| ty::InstanceDef::Item(_) => {
393394
// We need MIR for this fn
394395
let Some((body, instance)) =

Diff for: compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE};
1111
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
1212
use rustc_middle::arena::ArenaAllocatable;
1313
use rustc_middle::metadata::ModChild;
14+
use rustc_middle::middle::dependency_format::Linkage;
1415
use rustc_middle::middle::exported_symbols::ExportedSymbol;
1516
use rustc_middle::middle::stability::DeprecationEntry;
1617
use rustc_middle::ty::fast_reject::SimplifiedType;
@@ -501,6 +502,17 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
501502
tcx.arena
502503
.alloc_slice(&CStore::from_tcx(tcx).crate_dependencies_in_postorder(LOCAL_CRATE))
503504
},
505+
is_in_upstream_dylib: |tcx, cnum| {
506+
if cnum == LOCAL_CRATE {
507+
return false;
508+
}
509+
tcx.dependency_formats(()).iter().any(|(_, linkage)| {
510+
match linkage[cnum.as_usize() - 1] {
511+
Linkage::NotLinked | Linkage::Static => false,
512+
Linkage::IncludedFromDylib | Linkage::Dynamic => true,
513+
}
514+
})
515+
},
504516
crates: |tcx, ()| tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).crates_untracked()),
505517
..*providers
506518
};

Diff for: compiler/rustc_middle/src/middle/exported_symbols.rs

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> {
4343
NonGeneric(DefId),
4444
Generic(DefId, SubstsRef<'tcx>),
4545
DropGlue(Ty<'tcx>),
46+
ThreadLocalShim(DefId),
4647
NoDefId(ty::SymbolName<'tcx>),
4748
}
4849

@@ -58,6 +59,10 @@ impl<'tcx> ExportedSymbol<'tcx> {
5859
ExportedSymbol::DropGlue(ty) => {
5960
tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty))
6061
}
62+
ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance {
63+
def: ty::InstanceDef::ThreadLocalShim(def_id),
64+
substs: ty::InternalSubsts::empty(),
65+
}),
6166
ExportedSymbol::NoDefId(symbol_name) => symbol_name,
6267
}
6368
}

Diff for: compiler/rustc_middle/src/mir/mono.rs

+1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ impl<'tcx> CodegenUnit<'tcx> {
381381
| InstanceDef::Virtual(..)
382382
| InstanceDef::ClosureOnceShim { .. }
383383
| InstanceDef::DropGlue(..)
384+
| InstanceDef::ThreadLocalShim(..)
384385
| InstanceDef::CloneShim(..) => None,
385386
}
386387
}

Diff for: compiler/rustc_middle/src/mir/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ macro_rules! make_mir_visitor {
335335
ty::InstanceDef::VTableShim(_def_id) |
336336
ty::InstanceDef::ReifyShim(_def_id) |
337337
ty::InstanceDef::Virtual(_def_id, _) |
338+
ty::InstanceDef::ThreadLocalShim(_def_id) |
338339
ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |
339340
ty::InstanceDef::DropGlue(_def_id, None) => {}
340341

0 commit comments

Comments
 (0)