Module rustc_mir::monomorphize::partitioning [] [src]

🔬 This is a nightly-only experimental API. (rustc_private)

this crate is being loaded from the sysroot, an unstable location; did you mean to load this crate from crates.io via Cargo.toml instead?

Partitioning Codegen Units for Incremental Compilation

The task of this module is to take the complete set of translation items of a crate and produce a set of codegen units from it, where a codegen unit is a named set of (translation-item, linkage) pairs. That is, this module decides which translation item appears in which codegen units with which linkage. The following paragraphs describe some of the background on the partitioning scheme.

The most important opportunity for saving on compilation time with incremental compilation is to avoid re-translating and re-optimizing code. Since the unit of translation and optimization for LLVM is "modules" or, how we call them "codegen units", the particulars of how much time can be saved by incremental compilation are tightly linked to how the output program is partitioned into these codegen units prior to passing it to LLVM -- especially because we have to treat codegen units as opaque entities once they are created: There is no way for us to incrementally update an existing LLVM module and so we have to build any such module from scratch if it was affected by some change in the source code.

From that point of view it would make sense to maximize the number of codegen units by, for example, putting each function into its own module. That way only those modules would have to be re-compiled that were actually affected by some change, minimizing the number of functions that could have been re-used but just happened to be located in a module that is re-compiled.

However, since LLVM optimization does not work across module boundaries, using such a highly granular partitioning would lead to very slow runtime code since it would effectively prohibit inlining and other inter-procedure optimizations. We want to avoid that as much as possible.

Thus we end up with a trade-off: The bigger the codegen units, the better LLVM's optimizer can do its work, but also the smaller the compilation time reduction we get from incremental compilation.

Ideally, we would create a partitioning such that there are few big codegen units with few interdependencies between them. For now though, we use the following heuristic to determine the partitioning:

In order to see why this heuristic makes sense, let's take a look at when a codegen unit can get invalidated:

  1. The most straightforward case is when the BODY of a function or global changes. Then any codegen unit containing the code for that item has to be re-compiled. Note that this includes all codegen units where the function has been inlined.

  2. The next case is when the SIGNATURE of a function or global changes. In this case, all codegen units containing a REFERENCE to that item have to be re-compiled. This is a superset of case 1.

  3. The final and most subtle case is when a REFERENCE to a generic function is added or removed somewhere. Even though the definition of the function might be unchanged, a new REFERENCE might introduce a new monomorphized instance of this function which has to be placed and compiled somewhere. Conversely, when removing a REFERENCE, it might have been the last one with that particular set of generic arguments and thus we have to remove it.

From the above we see that just using one codegen unit per source-level module is not such a good idea, since just adding a REFERENCE to some generic item somewhere else would invalidate everything within the module containing the generic item. The heuristic above reduces this detrimental side-effect of references a little by at least not touching the non-generic code of the module.

A Note on Inlining

As briefly mentioned above, in order for LLVM to be able to inline a function call, the body of the function has to be available in the LLVM module where the call is made. This has a few consequences for partitioning:

Note though that as a side-effect of creating a codegen units per source-level module, functions from the same module will be available for inlining, even when they are not marked #[inline].

Structs

CodegenUnit [
Experimental
]

Enums

PartitioningStrategy [
Experimental
]

Traits

CodegenUnitExt [
Experimental
]

Functions

partition [
Experimental
]