@@ -92,51 +92,82 @@ impl<'tcx> MonoItem<'tcx> {
92
92
}
93
93
94
94
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.
97
98
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,
137
105
MonoItem :: Static ( ..) | MonoItem :: GlobalAsm ( ..) => {
138
- InstantiationMode :: GloballyShared { may_conflict : false }
106
+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
139
107
}
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
+ // We need to ensure that we do not decide the InstantiationMode of an exported symbol is
127
+ // LocalCopy. Since exported symbols are computed based on the output of
128
+ // cross_crate_inlinable, we are beholden to our previous decisions.
129
+ if !tcx. cross_crate_inlinable ( instance. def_id ( ) ) {
130
+ return InstantiationMode :: GloballyShared { may_conflict : false } ;
131
+ }
132
+
133
+ // Beginning of heuristics. The handling of link-dead-code and inline(always) are QoL only,
134
+ // the compiler should not crash and linkage should work, but codegen may be undesirable.
135
+
136
+ // -Clink-dead-code was given an unfortunate name; the point of the flag is to assist
137
+ // coverage tools which rely on having every function in the program appear in the
138
+ // generated code. If we select LocalCopy, functions which are not used because they are
139
+ // missing test coverage will disappear from such coverage reports, defeating the point.
140
+ // Note that -Cinstrument-coverage does not require such assistance from us, only coverage
141
+ // tools implemented without compiler support ironically require a special compiler flag.
142
+ if tcx. sess . link_dead_code ( ) {
143
+ return InstantiationMode :: GloballyShared { may_conflict : true } ;
144
+ }
145
+
146
+ // To ensure that #[inline(always)] can be inlined as much as possible, especially in unoptimized
147
+ // builds, we always select LocalCopy.
148
+ if codegen_fn_attrs. inline . always ( ) {
149
+ return InstantiationMode :: LocalCopy ;
150
+ }
151
+
152
+ // #[inline(never)] functions in general are poor candidates for inlining and thus since
153
+ // LocalCopy generally increases code size for the benefit of optimizations from inlining,
154
+ // we want to give them GloballyShared codegen.
155
+ // The slight problem is that generic functions need to always support cross-crate
156
+ // compilation, so all previous stages of the compiler are obligated to treat generic
157
+ // functions the same as those that unconditionally get LocalCopy codegen. It's only when
158
+ // we get here that we can at least not codegen a #[inline(never)] generic function in all
159
+ // of our CGUs.
160
+ if let InlineAttr :: Never = tcx. codegen_fn_attrs ( instance. def_id ( ) ) . inline
161
+ && self . is_generic_fn ( )
162
+ {
163
+ return InstantiationMode :: GloballyShared { may_conflict : true } ;
164
+ }
165
+
166
+ // The fallthrough case is to generate LocalCopy for all optimized builds, and
167
+ // GloballyShared with conflict prevention when optimizations are disabled.
168
+ match tcx. sess . opts . optimize {
169
+ OptLevel :: No => InstantiationMode :: GloballyShared { may_conflict : true } ,
170
+ _ => InstantiationMode :: LocalCopy ,
140
171
}
141
172
}
142
173
0 commit comments