@@ -92,51 +92,88 @@ impl<'tcx> MonoItem<'tcx> {
9292 }
9393
9494 pub fn instantiation_mode ( & self , tcx : TyCtxt < ' tcx > ) -> InstantiationMode {
95- let generate_cgu_internal_copies =
96- ( tcx. sess . opts . optimize != OptLevel :: No ) && !tcx. sess . link_dead_code ( ) ;
95+ // The case handling here is written in the same style as cross_crate_inlinable, we first
96+ // handle the cases where we must use a particular instantiation mode, then cascade down
97+ // through a sequence of heuristics.
9798
98- match * self {
99- MonoItem :: Fn ( ref instance) => {
100- let entry_def_id = tcx. entry_fn ( ( ) ) . map ( |( id, _) | id) ;
101- // If this function isn't inlined or otherwise has an extern
102- // indicator, then we'll be creating a globally shared version.
103- let codegen_fn_attrs = tcx. codegen_fn_attrs ( instance. def_id ( ) ) ;
104- if codegen_fn_attrs. contains_extern_indicator ( )
105- || codegen_fn_attrs. flags . contains ( CodegenFnAttrFlags :: NAKED )
106- || !instance. def . generates_cgu_internal_copy ( tcx)
107- || Some ( instance. def_id ( ) ) == entry_def_id
108- {
109- return InstantiationMode :: GloballyShared { may_conflict : false } ;
110- }
111-
112- if let InlineAttr :: Never = tcx. codegen_fn_attrs ( instance. def_id ( ) ) . inline
113- && self . is_generic_fn ( )
114- {
115- // Upgrade inline(never) to a globally shared instance.
116- return InstantiationMode :: GloballyShared { may_conflict : true } ;
117- }
118-
119- // At this point we don't have explicit linkage and we're an
120- // inlined function. If this crate's build settings permit,
121- // we'll be creating a local copy per CGU.
122- if generate_cgu_internal_copies {
123- return InstantiationMode :: LocalCopy ;
124- }
125-
126- // Finally, if this is `#[inline(always)]` we're sure to respect
127- // that with an inline copy per CGU, but otherwise we'll be
128- // creating one copy of this `#[inline]` function which may
129- // conflict with upstream crates as it could be an exported
130- // symbol.
131- if tcx. codegen_fn_attrs ( instance. def_id ( ) ) . inline . always ( ) {
132- InstantiationMode :: LocalCopy
133- } else {
134- InstantiationMode :: GloballyShared { may_conflict : true }
135- }
136- }
99+ // The first thing we do is detect MonoItems which we must instantiate exactly once in the
100+ // whole program.
101+
102+ // Statics and global_asm! must be instantiated exactly once.
103+ let instance = match * self {
104+ MonoItem :: Fn ( instance) => instance,
137105 MonoItem :: Static ( ..) | MonoItem :: GlobalAsm ( ..) => {
138- InstantiationMode :: GloballyShared { may_conflict : false }
106+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
139107 }
108+ } ;
109+
110+ // Similarly, the executable entrypoint must be instantiated exactly once.
111+ if let Some ( ( entry_def_id, _) ) = tcx. entry_fn ( ( ) ) {
112+ if instance. def_id ( ) == entry_def_id {
113+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
114+ }
115+ }
116+
117+ // If the function is #[naked] or contains any other attribute that requires exactly-once
118+ // instantiation:
119+ let codegen_fn_attrs = tcx. codegen_fn_attrs ( instance. def_id ( ) ) ;
120+ if codegen_fn_attrs. contains_extern_indicator ( )
121+ || codegen_fn_attrs. flags . contains ( CodegenFnAttrFlags :: NAKED )
122+ {
123+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
124+ }
125+
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) {
136+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
137+ }
138+
139+ // Beginning of heuristics. The handling of link-dead-code and inline(always) are QoL only,
140+ // the compiler should not crash and linkage should work, but codegen may be undesirable.
141+
142+ // -Clink-dead-code was given an unfortunate name; the point of the flag is to assist
143+ // coverage tools which rely on having every function in the program appear in the
144+ // generated code. If we select LocalCopy, functions which are not used because they are
145+ // missing test coverage will disappear from such coverage reports, defeating the point.
146+ // Note that -Cinstrument-coverage does not require such assistance from us, only coverage
147+ // tools implemented without compiler support ironically require a special compiler flag.
148+ if tcx. sess . link_dead_code ( ) {
149+ return InstantiationMode :: GloballyShared { may_conflict : true } ;
150+ }
151+
152+ // To ensure that #[inline(always)] can be inlined as much as possible, especially in unoptimized
153+ // builds, we always select LocalCopy.
154+ if codegen_fn_attrs. inline . always ( ) {
155+ return InstantiationMode :: LocalCopy ;
156+ }
157+
158+ // #[inline(never)] functions in general are poor candidates for inlining and thus since
159+ // LocalCopy generally increases code size for the benefit of optimizations from inlining,
160+ // we want to give them GloballyShared codegen.
161+ // The slight problem is that generic functions need to always support cross-crate
162+ // compilation, so all previous stages of the compiler are obligated to treat generic
163+ // functions the same as those that unconditionally get LocalCopy codegen. It's only when
164+ // we get here that we can at least not codegen a #[inline(never)] generic function in all
165+ // of our CGUs.
166+ if let InlineAttr :: Never = tcx. codegen_fn_attrs ( instance. def_id ( ) ) . inline
167+ && self . is_generic_fn ( )
168+ {
169+ return InstantiationMode :: GloballyShared { may_conflict : true } ;
170+ }
171+
172+ // The fallthrough case is to generate LocalCopy for all optimized builds, and
173+ // GloballyShared with conflict prevention when optimizations are disabled.
174+ match tcx. sess . opts . optimize {
175+ OptLevel :: No => InstantiationMode :: GloballyShared { may_conflict : true } ,
176+ _ => InstantiationMode :: LocalCopy ,
140177 }
141178 }
142179
0 commit comments