1 //! Partitioning Codegen Units for Incremental Compilation
2 //! ======================================================
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.
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.
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
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.
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.
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:
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
47 //! In order to see why this heuristic makes sense, let's take a look at when a
48 //! codegen unit can get invalidated:
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
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.
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.
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.
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:
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.
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.
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]`.
98 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
99 use rustc_data_structures::sync;
100 use rustc_hir::def_id::DefIdSet;
101 use rustc_middle::mir::mono::MonoItem;
102 use rustc_middle::mir::mono::{CodegenUnit, Linkage};
103 use rustc_middle::ty::print::with_no_trimmed_paths;
104 use rustc_middle::ty::query::Providers;
105 use rustc_middle::ty::TyCtxt;
106 use rustc_span::symbol::Symbol;
108 use crate::collector::InliningMap;
109 use crate::collector::{self, MonoItemCollectionMode};
111 pub struct PartitioningCx<'a, 'tcx> {
113 target_cgu_count: usize,
114 inlining_map: &'a InliningMap<'tcx>,
117 trait Partitioner<'tcx> {
118 fn place_root_mono_items(
120 cx: &PartitioningCx<'_, 'tcx>,
121 mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
122 ) -> PreInliningPartitioning<'tcx>;
124 fn merge_codegen_units(
126 cx: &PartitioningCx<'_, 'tcx>,
127 initial_partitioning: &mut PreInliningPartitioning<'tcx>,
130 fn place_inlined_mono_items(
132 cx: &PartitioningCx<'_, 'tcx>,
133 initial_partitioning: PreInliningPartitioning<'tcx>,
134 ) -> PostInliningPartitioning<'tcx>;
136 fn internalize_symbols(
138 cx: &PartitioningCx<'_, 'tcx>,
139 partitioning: &mut PostInliningPartitioning<'tcx>,
143 fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
144 let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy {
150 "default" => Box::new(default::DefaultPartitioning),
151 _ => tcx.sess.fatal("unknown partitioning strategy"),
155 pub fn partition<'tcx>(
157 mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
158 max_cgu_count: usize,
159 inlining_map: &InliningMap<'tcx>,
160 ) -> Vec<CodegenUnit<'tcx>> {
161 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
163 let mut partitioner = get_partitioner(tcx);
164 let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map };
165 // In the first step, we place all regular monomorphizations into their
166 // respective 'home' codegen unit. Regular monomorphizations are all
167 // functions and statics defined in the local crate.
168 let mut initial_partitioning = {
169 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
170 partitioner.place_root_mono_items(cx, mono_items)
173 initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
175 debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());
177 // Merge until we have at most `max_cgu_count` codegen units.
179 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
180 partitioner.merge_codegen_units(cx, &mut initial_partitioning);
181 debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
184 // In the next step, we use the inlining map to determine which additional
185 // monomorphizations have to go into each codegen unit. These additional
186 // monomorphizations can be drop-glue, functions from external crates, and
187 // local functions the definition of which is marked with `#[inline]`.
188 let mut post_inlining = {
189 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
190 partitioner.place_inlined_mono_items(cx, initial_partitioning)
193 post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
195 debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());
197 // Next we try to make as many symbols "internal" as possible, so LLVM has
198 // more freedom to optimize.
199 if !tcx.sess.link_dead_code() {
200 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
201 partitioner.internalize_symbols(cx, &mut post_inlining);
204 let instrument_dead_code =
205 tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions();
207 if instrument_dead_code {
209 post_inlining.codegen_units.len() > 0,
210 "There must be at least one CGU that code coverage data can be generated in."
213 // Find the smallest CGU that has exported symbols and put the dead
214 // function stubs in that CGU. We look for exported symbols to increase
215 // the likelihood the linker won't throw away the dead functions.
216 // FIXME(#92165): In order to truly resolve this, we need to make sure
217 // the object file (CGU) containing the dead function stubs is included
218 // in the final binary. This will probably require forcing these
219 // function symbols to be included via `-u` or `/include` linker args.
220 let mut cgus: Vec<_> = post_inlining.codegen_units.iter_mut().collect();
221 cgus.sort_by_key(|cgu| cgu.size_estimate());
224 if let Some(cgu) = cgus.into_iter().rev().find(|cgu| {
225 cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External)
229 // If there are no CGUs that have externally linked items,
230 // then we just pick the first CGU as a fallback.
231 &mut post_inlining.codegen_units[0]
233 dead_code_cgu.make_code_coverage_dead_code_cgu();
236 // Finally, sort by codegen unit name, so that we get deterministic results.
237 let PostInliningPartitioning {
238 codegen_units: mut result,
239 mono_item_placements: _,
240 internalization_candidates: _,
243 result.sort_by(|a, b| a.name().as_str().partial_cmp(b.name().as_str()).unwrap());
248 pub struct PreInliningPartitioning<'tcx> {
249 codegen_units: Vec<CodegenUnit<'tcx>>,
250 roots: FxHashSet<MonoItem<'tcx>>,
251 internalization_candidates: FxHashSet<MonoItem<'tcx>>,
254 /// For symbol internalization, we need to know whether a symbol/mono-item is
255 /// accessed from outside the codegen unit it is defined in. This type is used
256 /// to keep track of that.
257 #[derive(Clone, PartialEq, Eq, Debug)]
258 enum MonoItemPlacement {
259 SingleCgu { cgu_name: Symbol },
263 struct PostInliningPartitioning<'tcx> {
264 codegen_units: Vec<CodegenUnit<'tcx>>,
265 mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
266 internalization_candidates: FxHashSet<MonoItem<'tcx>>,
269 fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I)
271 I: Iterator<Item = &'a CodegenUnit<'tcx>>,
277 let s = &mut String::new();
278 let _ = writeln!(s, "{}", label);
281 writeln!(s, "CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
283 for (mono_item, linkage) in cgu.items() {
284 let symbol_name = mono_item.symbol_name(tcx).name;
285 let symbol_hash_start = symbol_name.rfind('h');
286 let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]);
290 " - {} [{:?}] [{}] estimated size {}",
294 mono_item.size_estimate(tcx)
298 let _ = writeln!(s, "");
304 debug!("{}", dump());
307 #[inline(never)] // give this a place in the profiler
308 fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
310 I: Iterator<Item = &'a MonoItem<'tcx>>,
313 let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
315 let mut symbols: Vec<_> =
316 mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
318 symbols.sort_by_key(|sym| sym.1);
320 for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
322 let span1 = mono_item1.local_span(tcx);
323 let span2 = mono_item2.local_span(tcx);
325 // Deterministically select one of the spans for error reporting
326 let span = match (span1, span2) {
327 (Some(span1), Some(span2)) => {
328 Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
330 (span1, span2) => span1.or(span2),
333 let error_message = format!("symbol `{}` is already defined", sym1);
335 if let Some(span) = span {
336 tcx.sess.span_fatal(span, &error_message)
338 tcx.sess.fatal(&error_message)
344 fn collect_and_partition_mono_items<'tcx>(
347 ) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
348 let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
350 let mode_string = s.to_lowercase();
351 let mode_string = mode_string.trim();
352 if mode_string == "eager" {
353 MonoItemCollectionMode::Eager
355 if mode_string != "lazy" {
356 let message = format!(
357 "Unknown codegen-item collection mode '{}'. \
358 Falling back to 'lazy' mode.",
361 tcx.sess.warn(&message);
364 MonoItemCollectionMode::Lazy
368 if tcx.sess.link_dead_code() {
369 MonoItemCollectionMode::Eager
371 MonoItemCollectionMode::Lazy
376 let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
378 tcx.sess.abort_if_errors();
380 let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
383 let mut codegen_units = partition(
385 &mut items.iter().cloned(),
386 tcx.sess.codegen_units(),
389 codegen_units[0].make_primary();
390 &*tcx.arena.alloc_from_iter(codegen_units)
392 || assert_symbols_are_distinct(tcx, items.iter()),
396 if tcx.prof.enabled() {
397 // Record CGU size estimates for self-profiling.
398 for cgu in codegen_units {
399 tcx.prof.artifact_size(
400 "codegen_unit_size_estimate",
402 cgu.size_estimate() as u64,
407 let mono_items: DefIdSet = items
409 .filter_map(|mono_item| match *mono_item {
410 MonoItem::Fn(ref instance) => Some(instance.def_id()),
411 MonoItem::Static(def_id) => Some(def_id),
416 if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
417 let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
419 for cgu in codegen_units {
420 for (&mono_item, &linkage) in cgu.items() {
421 item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
425 let mut item_keys: Vec<_> = items
428 let mut output = with_no_trimmed_paths!(i.to_string());
429 output.push_str(" @@");
430 let mut empty = Vec::new();
431 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
432 cgus.sort_by_key(|(name, _)| *name);
434 for &(ref cgu_name, (linkage, _)) in cgus.iter() {
436 output.push_str(cgu_name.as_str());
438 let linkage_abbrev = match linkage {
439 Linkage::External => "External",
440 Linkage::AvailableExternally => "Available",
441 Linkage::LinkOnceAny => "OnceAny",
442 Linkage::LinkOnceODR => "OnceODR",
443 Linkage::WeakAny => "WeakAny",
444 Linkage::WeakODR => "WeakODR",
445 Linkage::Appending => "Appending",
446 Linkage::Internal => "Internal",
447 Linkage::Private => "Private",
448 Linkage::ExternalWeak => "ExternalWeak",
449 Linkage::Common => "Common",
453 output.push_str(linkage_abbrev);
462 for item in item_keys {
463 println!("MONO_ITEM {}", item);
467 (tcx.arena.alloc(mono_items), codegen_units)
470 fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSet {
471 let (items, cgus) = tcx.collect_and_partition_mono_items(());
472 let mut visited = DefIdSet::default();
473 let mut result = items.clone();
476 for (item, _) in cgu.items() {
477 if let MonoItem::Fn(ref instance) = item {
478 let did = instance.def_id();
479 if !visited.insert(did) {
482 for scope in &tcx.instance_mir(instance.def).source_scopes {
483 if let Some((ref inlined, _)) = scope.inlined {
484 result.insert(inlined.def_id());
491 tcx.arena.alloc(result)
494 pub fn provide(providers: &mut Providers) {
495 providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
496 providers.codegened_and_inlined_items = codegened_and_inlined_items;
498 providers.is_codegened_item = |tcx, def_id| {
499 let (all_mono_items, _) = tcx.collect_and_partition_mono_items(());
500 all_mono_items.contains(&def_id)
503 providers.codegen_unit = |tcx, name| {
504 let (_, all) = tcx.collect_and_partition_mono_items(());
506 .find(|cgu| cgu.name() == name)
507 .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))