@@ -20,7 +20,7 @@ use tracing::debug;
2020
2121use crate :: dep_graph:: { DepNode , WorkProduct , WorkProductId } ;
2222use 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 } ;
2424
2525/// Describes how a monomorphization will be instantiated in object files.
2626#[ derive( PartialEq ) ]
@@ -54,6 +54,33 @@ pub enum MonoItem<'tcx> {
5454 GlobalAsm ( ItemId ) ,
5555}
5656
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+
5784impl < ' tcx > MonoItem < ' tcx > {
5885 /// Returns `true` if the mono item is user-defined (i.e. not compiler-generated, like shims).
5986 pub fn is_user_defined ( & self ) -> bool {
@@ -123,16 +150,10 @@ impl<'tcx> MonoItem<'tcx> {
123150 return InstantiationMode :: GloballyShared { may_conflict : false } ;
124151 }
125152
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 ( ) ) {
136157 return InstantiationMode :: GloballyShared { may_conflict : false } ;
137158 }
138159
@@ -169,6 +190,20 @@ impl<'tcx> MonoItem<'tcx> {
169190 return InstantiationMode :: GloballyShared { may_conflict : true } ;
170191 }
171192
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+
172207 // The fallthrough case is to generate LocalCopy for all optimized builds, and
173208 // GloballyShared with conflict prevention when optimizations are disabled.
174209 match tcx. sess . opts . optimize {
0 commit comments