@@ -20,7 +20,7 @@ use tracing::debug;
20
20
21
21
use crate :: dep_graph:: { DepNode , WorkProduct , WorkProductId } ;
22
22
use crate :: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
23
- use crate :: ty:: { GenericArgs , Instance , InstanceKind , SymbolName , TyCtxt } ;
23
+ use crate :: ty:: { GenericArgs , Instance , InstanceKind , SymbolName , Ty , TyCtxt } ;
24
24
25
25
/// Describes how a monomorphization will be instantiated in object files.
26
26
#[ derive( PartialEq ) ]
@@ -54,6 +54,33 @@ pub enum MonoItem<'tcx> {
54
54
GlobalAsm ( ItemId ) ,
55
55
}
56
56
57
+ fn opt_incr_drop_glue_mode < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> InstantiationMode {
58
+ // Non-ADTs can't have a Drop impl. This case is mostly hit by closures whose captures require
59
+ // dropping.
60
+ let Some ( adt_def) = ty. ty_adt_def ( ) else {
61
+ return InstantiationMode :: LocalCopy ;
62
+ } ;
63
+
64
+ // Types that don't have a direct Drop impl, but have fields that require dropping.
65
+ let Some ( dtor) = adt_def. destructor ( tcx) else {
66
+ if adt_def. is_enum ( ) {
67
+ return InstantiationMode :: LocalCopy ;
68
+ } else {
69
+ return InstantiationMode :: GloballyShared { may_conflict : true } ;
70
+ }
71
+ } ;
72
+
73
+ // We've gotten to a drop_in_place for a type that directly implements Drop.
74
+ // The drop glue is a wrapper for the Drop::drop impl, and we are an optimized build, so in an
75
+ // effort to coordinate with the mode that the actual impl will get, we make the glue also
76
+ // LocalCopy.
77
+ if tcx. cross_crate_inlinable ( dtor. did ) {
78
+ InstantiationMode :: LocalCopy
79
+ } else {
80
+ InstantiationMode :: GloballyShared { may_conflict : true }
81
+ }
82
+ }
83
+
57
84
impl < ' tcx > MonoItem < ' tcx > {
58
85
/// Returns `true` if the mono item is user-defined (i.e. not compiler-generated, like shims).
59
86
pub fn is_user_defined ( & self ) -> bool {
@@ -123,16 +150,10 @@ impl<'tcx> MonoItem<'tcx> {
123
150
return InstantiationMode :: GloballyShared { may_conflict : false } ;
124
151
}
125
152
126
- // FIXME: The logic for which functions are permitted to get LocalCopy is actually spread
127
- // across 4 functions:
128
- // * cross_crate_inlinable(def_id)
129
- // * InstanceKind::requires_inline
130
- // * InstanceKind::generate_cgu_internal_copy
131
- // * MonoItem::instantiation_mode
132
- // Since reachable_non_generics calls InstanceKind::generates_cgu_internal_copy to decide
133
- // which symbols this crate exports, we are obligated to only generate LocalCopy when
134
- // generates_cgu_internal_copy returns true.
135
- if !instance. def . generates_cgu_internal_copy ( tcx) {
153
+ // We need to ensure that we do not decide the InstantiationMode of an exported symbol is
154
+ // LocalCopy. Since exported symbols are computed based on the output of
155
+ // cross_crate_inlinable, we are beholden to our previous decisions.
156
+ if !tcx. cross_crate_inlinable ( instance. def_id ( ) ) {
136
157
return InstantiationMode :: GloballyShared { may_conflict : false } ;
137
158
}
138
159
@@ -169,6 +190,28 @@ impl<'tcx> MonoItem<'tcx> {
169
190
return InstantiationMode :: GloballyShared { may_conflict : true } ;
170
191
}
171
192
193
+ // The fallback case is to give everything else GloballyShared at OptLevel::No and
194
+ // LocalCopy at all other opt levels. This is a good default, except for one specific build
195
+ // configuration: Optimized incremental builds.
196
+ // In the current compiler architecture there is a fundamental tension between
197
+ // optimizations (which want big CGUs with as many things LocalCopy as possible) and
198
+ // incrementality (which wants small CGUs with as many things GloballyShared as possible).
199
+ // The heuristics implemented here do better than a completely naive approach in the
200
+ // compiler benchmark suite, but there is no reason to believe they are optimal.
201
+ if tcx. sess . opts . incremental . is_some ( ) && tcx. sess . opts . optimize != OptLevel :: No {
202
+ if let InstanceKind :: DropGlue ( .., Some ( ty) ) = instance. def {
203
+ return opt_incr_drop_glue_mode ( tcx, ty) ;
204
+ }
205
+ }
206
+
207
+ // FIXME: This case was previously implemented in generates_cgu_internal_copy without any
208
+ // explanation. It is not required for correctness, but seems kind of reasonable?
209
+ /*
210
+ if let InstanceKind::ThreadLocalShim(..) = instance.def {
211
+ return InstantiationMode::GloballyShared { may_conflict: true };
212
+ }
213
+ */
214
+
172
215
// The fallthrough case is to generate LocalCopy for all optimized builds, and
173
216
// GloballyShared with conflict prevention when optimizations are disabled.
174
217
match tcx. sess . opts . optimize {
0 commit comments