Skip to content

Commit 73f76b7

Browse files
committed
Auto merge of rust-lang#68414 - michaelwoerister:share-drop-glue, r=alexcrichton
Also share drop-glue when compiling with -Zshare-generics (i.e. at opt-level=0) This PR adds drop-glue to the set of monomorphizations that can be shared across crates via `-Zshare-generics`. This version of the PR might have detrimental effects on performance as it makes lots of stuff dependent on a single query results (`upstream_monomorphizations_for(def_id_of_drop_in_place)`). That should be fixable but let's do a perf run first. Potentially fixes issue rust-lang#64140. (cc @alexcrichton) The changes here are related to @matthewjasper's rust-lang#67332 but should be mostly orthogonal. r? @ghost
2 parents dee12bb + 197cc1e commit 73f76b7

File tree

14 files changed

+212
-160
lines changed

14 files changed

+212
-160
lines changed
+7-61
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use crate::ich::StableHashingContext;
21
use crate::ty::subst::SubstsRef;
3-
use crate::ty::{self, TyCtxt};
4-
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
2+
use crate::ty::{self, Ty, TyCtxt};
53
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6-
use std::cmp;
7-
use std::mem;
4+
use rustc_macros::HashStable;
85

96
/// The SymbolExportLevel of a symbols specifies from which kinds of crates
107
/// the symbol will be exported. `C` symbols will be exported from any
@@ -24,10 +21,11 @@ impl SymbolExportLevel {
2421
}
2522
}
2623

27-
#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
24+
#[derive(Eq, PartialEq, Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
2825
pub enum ExportedSymbol<'tcx> {
2926
NonGeneric(DefId),
3027
Generic(DefId, SubstsRef<'tcx>),
28+
DropGlue(Ty<'tcx>),
3129
NoDefId(ty::SymbolName),
3230
}
3331

@@ -40,46 +38,12 @@ impl<'tcx> ExportedSymbol<'tcx> {
4038
ExportedSymbol::Generic(def_id, substs) => {
4139
tcx.symbol_name(ty::Instance::new(def_id, substs))
4240
}
41+
ExportedSymbol::DropGlue(ty) => {
42+
tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty))
43+
}
4344
ExportedSymbol::NoDefId(symbol_name) => symbol_name,
4445
}
4546
}
46-
47-
pub fn compare_stable(&self, tcx: TyCtxt<'tcx>, other: &ExportedSymbol<'tcx>) -> cmp::Ordering {
48-
match *self {
49-
ExportedSymbol::NonGeneric(self_def_id) => match *other {
50-
ExportedSymbol::NonGeneric(other_def_id) => {
51-
tcx.def_path_hash(self_def_id).cmp(&tcx.def_path_hash(other_def_id))
52-
}
53-
ExportedSymbol::Generic(..) | ExportedSymbol::NoDefId(_) => cmp::Ordering::Less,
54-
},
55-
ExportedSymbol::Generic(self_def_id, self_substs) => match *other {
56-
ExportedSymbol::NonGeneric(_) => cmp::Ordering::Greater,
57-
ExportedSymbol::Generic(other_def_id, other_substs) => {
58-
// We compare the symbol names because they are cached as query
59-
// results which makes them relatively cheap to access repeatedly.
60-
//
61-
// It might be even faster to build a local cache of stable IDs
62-
// for sorting. Exported symbols are really only sorted once
63-
// in order to make the `exported_symbols` query result stable.
64-
let self_symbol_name =
65-
tcx.symbol_name(ty::Instance::new(self_def_id, self_substs));
66-
let other_symbol_name =
67-
tcx.symbol_name(ty::Instance::new(other_def_id, other_substs));
68-
69-
self_symbol_name.cmp(&other_symbol_name)
70-
}
71-
ExportedSymbol::NoDefId(_) => cmp::Ordering::Less,
72-
},
73-
ExportedSymbol::NoDefId(self_symbol_name) => match *other {
74-
ExportedSymbol::NonGeneric(_) | ExportedSymbol::Generic(..) => {
75-
cmp::Ordering::Greater
76-
}
77-
ExportedSymbol::NoDefId(ref other_symbol_name) => {
78-
self_symbol_name.cmp(other_symbol_name)
79-
}
80-
},
81-
}
82-
}
8347
}
8448

8549
pub fn metadata_symbol_name(tcx: TyCtxt<'_>) -> String {
@@ -89,21 +53,3 @@ pub fn metadata_symbol_name(tcx: TyCtxt<'_>) -> String {
8953
tcx.crate_disambiguator(LOCAL_CRATE).to_fingerprint().to_hex()
9054
)
9155
}
92-
93-
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ExportedSymbol<'tcx> {
94-
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
95-
mem::discriminant(self).hash_stable(hcx, hasher);
96-
match *self {
97-
ExportedSymbol::NonGeneric(def_id) => {
98-
def_id.hash_stable(hcx, hasher);
99-
}
100-
ExportedSymbol::Generic(def_id, substs) => {
101-
def_id.hash_stable(hcx, hasher);
102-
substs.hash_stable(hcx, hasher);
103-
}
104-
ExportedSymbol::NoDefId(symbol_name) => {
105-
symbol_name.hash_stable(hcx, hasher);
106-
}
107-
}
108-
}
109-
}

src/librustc/mir/mono.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl<'tcx> MonoItem<'tcx> {
7979
}
8080

8181
pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode {
82-
let inline_in_all_cgus = tcx
82+
let generate_cgu_internal_copies = tcx
8383
.sess
8484
.opts
8585
.debugging_opts
@@ -93,7 +93,7 @@ impl<'tcx> MonoItem<'tcx> {
9393
// If this function isn't inlined or otherwise has explicit
9494
// linkage, then we'll be creating a globally shared version.
9595
if self.explicit_linkage(tcx).is_some()
96-
|| !instance.def.requires_local(tcx)
96+
|| !instance.def.generates_cgu_internal_copy(tcx)
9797
|| Some(instance.def_id()) == entry_def_id
9898
{
9999
return InstantiationMode::GloballyShared { may_conflict: false };
@@ -102,7 +102,7 @@ impl<'tcx> MonoItem<'tcx> {
102102
// At this point we don't have explicit linkage and we're an
103103
// inlined function. If we're inlining into all CGUs then we'll
104104
// be creating a local copy per CGU
105-
if inline_in_all_cgus {
105+
if generate_cgu_internal_copies {
106106
return InstantiationMode::LocalCopy;
107107
}
108108

src/librustc/query/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -776,13 +776,47 @@ rustc_queries! {
776776
}
777777

778778
Codegen {
779+
/// The entire set of monomorphizations the local crate can safely link
780+
/// to because they are exported from upstream crates. Do not depend on
781+
/// this directly, as its value changes anytime a monomorphization gets
782+
/// added or removed in any upstream crate. Instead use the narrower
783+
/// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even
784+
/// better, `Instance::upstream_monomorphization()`.
779785
query upstream_monomorphizations(
780786
k: CrateNum
781787
) -> &'tcx DefIdMap<FxHashMap<SubstsRef<'tcx>, CrateNum>> {
782788
desc { "collecting available upstream monomorphizations `{:?}`", k }
783789
}
790+
791+
/// Returns the set of upstream monomorphizations available for the
792+
/// generic function identified by the given `def_id`. The query makes
793+
/// sure to make a stable selection if the same monomorphization is
794+
/// available in multiple upstream crates.
795+
///
796+
/// You likely want to call `Instance::upstream_monomorphization()`
797+
/// instead of invoking this query directly.
784798
query upstream_monomorphizations_for(_: DefId)
785799
-> Option<&'tcx FxHashMap<SubstsRef<'tcx>, CrateNum>> {}
800+
801+
/// Returns the upstream crate that exports drop-glue for the given
802+
/// type (`substs` is expected to be a single-item list containing the
803+
/// type one wants drop-glue for).
804+
///
805+
/// This is a subset of `upstream_monomorphizations_for` in order to
806+
/// increase dep-tracking granularity. Otherwise adding or removing any
807+
/// type with drop-glue in any upstream crate would invalidate all
808+
/// functions calling drop-glue of an upstream type.
809+
///
810+
/// You likely want to call `Instance::upstream_monomorphization()`
811+
/// instead of invoking this query directly.
812+
///
813+
/// NOTE: This query could easily be extended to also support other
814+
/// common functions that have are large set of monomorphizations
815+
/// (like `Clone::clone` for example).
816+
query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option<CrateNum> {
817+
desc { "available upstream drop-glue for `{:?}`", substs }
818+
no_force
819+
}
786820
}
787821

788822
Other {

src/librustc/ty/instance.rs

+50-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::traits;
44
use crate::ty::print::{FmtPrinter, Printer};
55
use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
66
use rustc_hir::def::Namespace;
7-
use rustc_hir::def_id::DefId;
7+
use rustc_hir::def_id::{CrateNum, DefId};
88
use rustc_macros::HashStable;
99
use rustc_target::spec::abi::Abi;
1010

@@ -91,6 +91,40 @@ impl<'tcx> Instance<'tcx> {
9191
let ty = tcx.type_of(self.def.def_id());
9292
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
9393
}
94+
95+
/// Finds a crate that contains a monomorphization of this instance that
96+
/// can be linked to from the local crate. A return value of `None` means
97+
/// no upstream crate provides such an exported monomorphization.
98+
///
99+
/// This method already takes into account the global `-Zshare-generics`
100+
/// setting, always returning `None` if `share-generics` is off.
101+
pub fn upstream_monomorphization(&self, tcx: TyCtxt<'tcx>) -> Option<CrateNum> {
102+
// If we are not in share generics mode, we don't link to upstream
103+
// monomorphizations but always instantiate our own internal versions
104+
// instead.
105+
if !tcx.sess.opts.share_generics() {
106+
return None;
107+
}
108+
109+
// If this is an item that is defined in the local crate, no upstream
110+
// crate can know about it/provide a monomorphization.
111+
if self.def_id().is_local() {
112+
return None;
113+
}
114+
115+
// If this a non-generic instance, it cannot be a shared monomorphization.
116+
if self.substs.non_erasable_generics().next().is_none() {
117+
return None;
118+
}
119+
120+
match self.def {
121+
InstanceDef::Item(def_id) => tcx
122+
.upstream_monomorphizations_for(def_id)
123+
.and_then(|monos| monos.get(&self.substs).cloned()),
124+
InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.substs),
125+
_ => None,
126+
}
127+
}
94128
}
95129

96130
impl<'tcx> InstanceDef<'tcx> {
@@ -114,7 +148,12 @@ impl<'tcx> InstanceDef<'tcx> {
114148
tcx.get_attrs(self.def_id())
115149
}
116150

117-
pub fn is_inline(&self, tcx: TyCtxt<'tcx>) -> bool {
151+
/// Returns `true` if the LLVM version of this instance is unconditionally
152+
/// marked with `inline`. This implies that a copy of this instance is
153+
/// generated in every codegen unit.
154+
/// Note that this is only a hint. See the documentation for
155+
/// `generates_cgu_internal_copy` for more information.
156+
pub fn requires_inline(&self, tcx: TyCtxt<'tcx>) -> bool {
118157
use crate::hir::map::DefPathData;
119158
let def_id = match *self {
120159
ty::InstanceDef::Item(def_id) => def_id,
@@ -127,8 +166,15 @@ impl<'tcx> InstanceDef<'tcx> {
127166
}
128167
}
129168

130-
pub fn requires_local(&self, tcx: TyCtxt<'tcx>) -> bool {
131-
if self.is_inline(tcx) {
169+
/// Returns `true` if the machine code for this instance is instantiated in
170+
/// each codegen unit that references it.
171+
/// Note that this is only a hint! The compiler can globally decide to *not*
172+
/// do this in order to speed up compilation. CGU-internal copies are
173+
/// only exist to enable inlining. If inlining is not performed (e.g. at
174+
/// `-Copt-level=0`) then the time for generating them is wasted and it's
175+
/// better to create a single copy with external linkage.
176+
pub fn generates_cgu_internal_copy(&self, tcx: TyCtxt<'tcx>) -> bool {
177+
if self.requires_inline(tcx) {
132178
return true;
133179
}
134180
if let ty::InstanceDef::DropGlue(..) = *self {

src/librustc/ty/query/keys.rs

+9
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ impl Key for (DefId, SimplifiedType) {
116116
}
117117
}
118118

119+
impl<'tcx> Key for SubstsRef<'tcx> {
120+
fn query_crate(&self) -> CrateNum {
121+
LOCAL_CRATE
122+
}
123+
fn default_span(&self, _: TyCtxt<'_>) -> Span {
124+
DUMMY_SP
125+
}
126+
}
127+
119128
impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
120129
fn query_crate(&self) -> CrateNum {
121130
self.0.krate

src/librustc_codegen_llvm/attributes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ pub fn from_fn_attrs(
246246
}
247247

248248
// FIXME(eddyb) consolidate these two `inline` calls (and avoid overwrites).
249-
if instance.def.is_inline(cx.tcx) {
249+
if instance.def.requires_inline(cx.tcx) {
250250
inline(cx, llfn, attributes::InlineAttr::Hint);
251251
}
252252

src/librustc_codegen_llvm/callee.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,7 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value
130130
} else {
131131
// This is a monomorphization of a generic function
132132
// defined in an upstream crate.
133-
if cx
134-
.tcx
135-
.upstream_monomorphizations_for(instance_def_id)
136-
.map(|set| set.contains_key(instance.substs))
137-
.unwrap_or(false)
138-
{
133+
if instance.upstream_monomorphization(tcx).is_some() {
139134
// This is instantiated in another crate. It cannot
140135
// be `hidden`.
141136
} else {

0 commit comments

Comments
 (0)