]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/monomorphize/partitioning/mod.rs
Auto merge of #75912 - scottmcm:manuallydrop-vs-forget, r=Mark-Simulacrum
[rust.git] / src / librustc_mir / monomorphize / partitioning / mod.rs
1 //! Partitioning Codegen Units for Incremental Compilation
2 //! ======================================================
3 //!
4 //! The task of this module is to take the complete set of monomorphizations of
5 //! a crate and produce a set of codegen units from it, where a codegen unit
6 //! is a named set of (mono-item, linkage) pairs. That is, this module
7 //! decides which monomorphization appears in which codegen units with which
8 //! linkage. The following paragraphs describe some of the background on the
9 //! partitioning scheme.
10 //!
11 //! The most important opportunity for saving on compilation time with
12 //! incremental compilation is to avoid re-codegenning and re-optimizing code.
13 //! Since the unit of codegen and optimization for LLVM is "modules" or, how
14 //! we call them "codegen units", the particulars of how much time can be saved
15 //! by incremental compilation are tightly linked to how the output program is
16 //! partitioned into these codegen units prior to passing it to LLVM --
17 //! especially because we have to treat codegen units as opaque entities once
18 //! they are created: There is no way for us to incrementally update an existing
19 //! LLVM module and so we have to build any such module from scratch if it was
20 //! affected by some change in the source code.
21 //!
22 //! From that point of view it would make sense to maximize the number of
23 //! codegen units by, for example, putting each function into its own module.
24 //! That way only those modules would have to be re-compiled that were actually
25 //! affected by some change, minimizing the number of functions that could have
26 //! been re-used but just happened to be located in a module that is
27 //! re-compiled.
28 //!
29 //! However, since LLVM optimization does not work across module boundaries,
30 //! using such a highly granular partitioning would lead to very slow runtime
31 //! code since it would effectively prohibit inlining and other inter-procedure
32 //! optimizations. We want to avoid that as much as possible.
33 //!
34 //! Thus we end up with a trade-off: The bigger the codegen units, the better
35 //! LLVM's optimizer can do its work, but also the smaller the compilation time
36 //! reduction we get from incremental compilation.
37 //!
38 //! Ideally, we would create a partitioning such that there are few big codegen
39 //! units with few interdependencies between them. For now though, we use the
40 //! following heuristic to determine the partitioning:
41 //!
42 //! - There are two codegen units for every source-level module:
43 //! - One for "stable", that is non-generic, code
44 //! - One for more "volatile" code, i.e., monomorphized instances of functions
45 //!   defined in that module
46 //!
47 //! In order to see why this heuristic makes sense, let's take a look at when a
48 //! codegen unit can get invalidated:
49 //!
50 //! 1. The most straightforward case is when the BODY of a function or global
51 //! changes. Then any codegen unit containing the code for that item has to be
52 //! re-compiled. Note that this includes all codegen units where the function
53 //! has been inlined.
54 //!
55 //! 2. The next case is when the SIGNATURE of a function or global changes. In
56 //! this case, all codegen units containing a REFERENCE to that item have to be
57 //! re-compiled. This is a superset of case 1.
58 //!
59 //! 3. The final and most subtle case is when a REFERENCE to a generic function
60 //! is added or removed somewhere. Even though the definition of the function
61 //! might be unchanged, a new REFERENCE might introduce a new monomorphized
62 //! instance of this function which has to be placed and compiled somewhere.
63 //! Conversely, when removing a REFERENCE, it might have been the last one with
64 //! that particular set of generic arguments and thus we have to remove it.
65 //!
66 //! From the above we see that just using one codegen unit per source-level
67 //! module is not such a good idea, since just adding a REFERENCE to some
68 //! generic item somewhere else would invalidate everything within the module
69 //! containing the generic item. The heuristic above reduces this detrimental
70 //! side-effect of references a little by at least not touching the non-generic
71 //! code of the module.
72 //!
73 //! A Note on Inlining
74 //! ------------------
75 //! As briefly mentioned above, in order for LLVM to be able to inline a
76 //! function call, the body of the function has to be available in the LLVM
77 //! module where the call is made. This has a few consequences for partitioning:
78 //!
79 //! - The partitioning algorithm has to take care of placing functions into all
80 //!   codegen units where they should be available for inlining. It also has to
81 //!   decide on the correct linkage for these functions.
82 //!
83 //! - The partitioning algorithm has to know which functions are likely to get
84 //!   inlined, so it can distribute function instantiations accordingly. Since
85 //!   there is no way of knowing for sure which functions LLVM will decide to
86 //!   inline in the end, we apply a heuristic here: Only functions marked with
87 //!   `#[inline]` are considered for inlining by the partitioner. The current
88 //!   implementation will not try to determine if a function is likely to be
89 //!   inlined by looking at the functions definition.
90 //!
91 //! Note though that as a side-effect of creating a codegen units per
92 //! source-level module, functions from the same module will be available for
93 //! inlining, even when they are not marked `#[inline]`.
94
95 mod default;
96 mod merging;
97
98 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
99 use rustc_data_structures::sync;
100 use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE};
101 use rustc_middle::mir::mono::MonoItem;
102 use rustc_middle::mir::mono::{CodegenUnit, Linkage};
103 use rustc_middle::ty::query::Providers;
104 use rustc_middle::ty::TyCtxt;
105 use rustc_span::symbol::Symbol;
106
107 use crate::monomorphize::collector::InliningMap;
108 use crate::monomorphize::collector::{self, MonoItemCollectionMode};
109
110 trait Partitioner<'tcx> {
111     fn place_root_mono_items(
112         &mut self,
113         tcx: TyCtxt<'tcx>,
114         mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
115     ) -> PreInliningPartitioning<'tcx>;
116
117     fn merge_codegen_units(
118         &mut self,
119         tcx: TyCtxt<'tcx>,
120         initial_partitioning: &mut PreInliningPartitioning<'tcx>,
121         target_cgu_count: usize,
122     );
123
124     fn place_inlined_mono_items(
125         &mut self,
126         initial_partitioning: PreInliningPartitioning<'tcx>,
127         inlining_map: &InliningMap<'tcx>,
128     ) -> PostInliningPartitioning<'tcx>;
129
130     fn internalize_symbols(
131         &mut self,
132         tcx: TyCtxt<'tcx>,
133         partitioning: &mut PostInliningPartitioning<'tcx>,
134         inlining_map: &InliningMap<'tcx>,
135     );
136 }
137
138 fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
139     let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy {
140         None => "default",
141         Some(s) => &s[..],
142     };
143
144     match strategy {
145         "default" => Box::new(default::DefaultPartitioning),
146         _ => tcx.sess.fatal("unknown partitioning strategy"),
147     }
148 }
149
150 pub fn partition<'tcx>(
151     tcx: TyCtxt<'tcx>,
152     mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
153     max_cgu_count: usize,
154     inlining_map: &InliningMap<'tcx>,
155 ) -> Vec<CodegenUnit<'tcx>> {
156     let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
157
158     let mut partitioner = get_partitioner(tcx);
159     // In the first step, we place all regular monomorphizations into their
160     // respective 'home' codegen unit. Regular monomorphizations are all
161     // functions and statics defined in the local crate.
162     let mut initial_partitioning = {
163         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
164         partitioner.place_root_mono_items(tcx, mono_items)
165     };
166
167     initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
168
169     debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());
170
171     // Merge until we have at most `max_cgu_count` codegen units.
172     {
173         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
174         partitioner.merge_codegen_units(tcx, &mut initial_partitioning, max_cgu_count);
175         debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
176     }
177
178     // In the next step, we use the inlining map to determine which additional
179     // monomorphizations have to go into each codegen unit. These additional
180     // monomorphizations can be drop-glue, functions from external crates, and
181     // local functions the definition of which is marked with `#[inline]`.
182     let mut post_inlining = {
183         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
184         partitioner.place_inlined_mono_items(initial_partitioning, inlining_map)
185     };
186
187     post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
188
189     debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());
190
191     // Next we try to make as many symbols "internal" as possible, so LLVM has
192     // more freedom to optimize.
193     if tcx.sess.opts.cg.link_dead_code != Some(true) {
194         let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
195         partitioner.internalize_symbols(tcx, &mut post_inlining, inlining_map);
196     }
197
198     // Finally, sort by codegen unit name, so that we get deterministic results.
199     let PostInliningPartitioning {
200         codegen_units: mut result,
201         mono_item_placements: _,
202         internalization_candidates: _,
203     } = post_inlining;
204
205     result.sort_by_cached_key(|cgu| cgu.name().as_str());
206
207     result
208 }
209
210 pub struct PreInliningPartitioning<'tcx> {
211     codegen_units: Vec<CodegenUnit<'tcx>>,
212     roots: FxHashSet<MonoItem<'tcx>>,
213     internalization_candidates: FxHashSet<MonoItem<'tcx>>,
214 }
215
216 /// For symbol internalization, we need to know whether a symbol/mono-item is
217 /// accessed from outside the codegen unit it is defined in. This type is used
218 /// to keep track of that.
219 #[derive(Clone, PartialEq, Eq, Debug)]
220 enum MonoItemPlacement {
221     SingleCgu { cgu_name: Symbol },
222     MultipleCgus,
223 }
224
225 struct PostInliningPartitioning<'tcx> {
226     codegen_units: Vec<CodegenUnit<'tcx>>,
227     mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
228     internalization_candidates: FxHashSet<MonoItem<'tcx>>,
229 }
230
231 fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I)
232 where
233     I: Iterator<Item = &'a CodegenUnit<'tcx>>,
234     'tcx: 'a,
235 {
236     if cfg!(debug_assertions) {
237         debug!("{}", label);
238         for cgu in cgus {
239             debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
240
241             for (mono_item, linkage) in cgu.items() {
242                 let symbol_name = mono_item.symbol_name(tcx).name;
243                 let symbol_hash_start = symbol_name.rfind('h');
244                 let symbol_hash =
245                     symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or("<no hash>");
246
247                 debug!(
248                     " - {} [{:?}] [{}] estimated size {}",
249                     mono_item.to_string(tcx, true),
250                     linkage,
251                     symbol_hash,
252                     mono_item.size_estimate(tcx)
253                 );
254             }
255
256             debug!("");
257         }
258     }
259 }
260
261 #[inline(never)] // give this a place in the profiler
262 fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
263 where
264     I: Iterator<Item = &'a MonoItem<'tcx>>,
265     'tcx: 'a,
266 {
267     let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
268
269     let mut symbols: Vec<_> =
270         mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
271
272     symbols.sort_by_key(|sym| sym.1);
273
274     for pair in symbols.windows(2) {
275         let sym1 = &pair[0].1;
276         let sym2 = &pair[1].1;
277
278         if sym1 == sym2 {
279             let mono_item1 = pair[0].0;
280             let mono_item2 = pair[1].0;
281
282             let span1 = mono_item1.local_span(tcx);
283             let span2 = mono_item2.local_span(tcx);
284
285             // Deterministically select one of the spans for error reporting
286             let span = match (span1, span2) {
287                 (Some(span1), Some(span2)) => {
288                     Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
289                 }
290                 (span1, span2) => span1.or(span2),
291             };
292
293             let error_message = format!("symbol `{}` is already defined", sym1);
294
295             if let Some(span) = span {
296                 tcx.sess.span_fatal(span, &error_message)
297             } else {
298                 tcx.sess.fatal(&error_message)
299             }
300         }
301     }
302 }
303
304 fn collect_and_partition_mono_items<'tcx>(
305     tcx: TyCtxt<'tcx>,
306     cnum: CrateNum,
307 ) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
308     assert_eq!(cnum, LOCAL_CRATE);
309
310     let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
311         Some(ref s) => {
312             let mode_string = s.to_lowercase();
313             let mode_string = mode_string.trim();
314             if mode_string == "eager" {
315                 MonoItemCollectionMode::Eager
316             } else {
317                 if mode_string != "lazy" {
318                     let message = format!(
319                         "Unknown codegen-item collection mode '{}'. \
320                                            Falling back to 'lazy' mode.",
321                         mode_string
322                     );
323                     tcx.sess.warn(&message);
324                 }
325
326                 MonoItemCollectionMode::Lazy
327             }
328         }
329         None => {
330             if tcx.sess.opts.cg.link_dead_code == Some(true) {
331                 MonoItemCollectionMode::Eager
332             } else {
333                 MonoItemCollectionMode::Lazy
334             }
335         }
336     };
337
338     let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
339
340     tcx.sess.abort_if_errors();
341
342     let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
343         sync::join(
344             || {
345                 &*tcx.arena.alloc_from_iter(partition(
346                     tcx,
347                     &mut items.iter().cloned(),
348                     tcx.sess.codegen_units(),
349                     &inlining_map,
350                 ))
351             },
352             || assert_symbols_are_distinct(tcx, items.iter()),
353         )
354     });
355
356     let mono_items: DefIdSet = items
357         .iter()
358         .filter_map(|mono_item| match *mono_item {
359             MonoItem::Fn(ref instance) => Some(instance.def_id()),
360             MonoItem::Static(def_id) => Some(def_id),
361             _ => None,
362         })
363         .collect();
364
365     if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
366         let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
367
368         for cgu in codegen_units {
369             for (&mono_item, &linkage) in cgu.items() {
370                 item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
371             }
372         }
373
374         let mut item_keys: Vec<_> = items
375             .iter()
376             .map(|i| {
377                 let mut output = i.to_string(tcx, false);
378                 output.push_str(" @@");
379                 let mut empty = Vec::new();
380                 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
381                 cgus.sort_by_key(|(name, _)| *name);
382                 cgus.dedup();
383                 for &(ref cgu_name, (linkage, _)) in cgus.iter() {
384                     output.push_str(" ");
385                     output.push_str(&cgu_name.as_str());
386
387                     let linkage_abbrev = match linkage {
388                         Linkage::External => "External",
389                         Linkage::AvailableExternally => "Available",
390                         Linkage::LinkOnceAny => "OnceAny",
391                         Linkage::LinkOnceODR => "OnceODR",
392                         Linkage::WeakAny => "WeakAny",
393                         Linkage::WeakODR => "WeakODR",
394                         Linkage::Appending => "Appending",
395                         Linkage::Internal => "Internal",
396                         Linkage::Private => "Private",
397                         Linkage::ExternalWeak => "ExternalWeak",
398                         Linkage::Common => "Common",
399                     };
400
401                     output.push_str("[");
402                     output.push_str(linkage_abbrev);
403                     output.push_str("]");
404                 }
405                 output
406             })
407             .collect();
408
409         item_keys.sort();
410
411         for item in item_keys {
412             println!("MONO_ITEM {}", item);
413         }
414     }
415
416     (tcx.arena.alloc(mono_items), codegen_units)
417 }
418
419 pub fn provide(providers: &mut Providers) {
420     providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
421
422     providers.is_codegened_item = |tcx, def_id| {
423         let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
424         all_mono_items.contains(&def_id)
425     };
426
427     providers.codegen_unit = |tcx, name| {
428         let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
429         all.iter()
430             .find(|cgu| cgu.name() == name)
431             .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
432     };
433 }