Module rustc_mir::monomorphize::collector [−][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?
Mono Item Collection
This module is responsible for discovering all items that will contribute to to code generation of the crate. The important part here is that it not only needs to find syntax-level items (functions, structs, etc) but also all their monomorphized instantiations. Every non-generic, non-const function maps to one LLVM artifact. Every generic function can produce from zero to N artifacts, depending on the sets of type arguments it is instantiated with. This also applies to generic items from other crates: A generic definition in crate X might produce monomorphizations that are compiled into crate Y. We also have to collect these here.
The following kinds of "mono items" are handled here:
- Functions
- Methods
- Closures
- Statics
- Drop glue
The following things also result in LLVM artifacts, but are not collected here, since we instantiate them locally on demand when needed in a given codegen unit:
- Constants
- Vtables
- Object Shims
General Algorithm
Let's define some terms first:
-
A "mono item" is something that results in a function or global in the LLVM IR of a codegen unit. Mono items do not stand on their own, they can reference other mono items. For example, if function
foo()
calls functionbar()
then the mono item forfoo()
references the mono item for functionbar()
. In general, the definition for mono item A referencing a mono item B is that the LLVM artifact produced for A references the LLVM artifact produced for B. -
Mono items and the references between them form a directed graph, where the mono items are the nodes and references form the edges. Let's call this graph the "mono item graph".
-
The mono item graph for a program contains all mono items that are needed in order to produce the complete LLVM IR of the program.
The purpose of the algorithm implemented in this module is to build the mono item graph for the current crate. It runs in two phases:
- Discover the roots of the graph by traversing the HIR of the crate.
- Starting from the roots, find neighboring nodes by inspecting the MIR representation of the item corresponding to a given node, until no more new nodes are found.
Discovering roots
The roots of the mono item graph correspond to the non-generic syntactic items in the source code. We find them by walking the HIR of the crate, and whenever we hit upon a function, method, or static item, we create a mono item consisting of the items DefId and, since we only consider non-generic items, an empty type-substitution set.
Finding neighbor nodes
Given a mono item node, we can discover neighbors by inspecting its MIR. We walk the MIR and any time we hit upon something that signifies a reference to another mono item, we have found a neighbor. Since the mono item we are currently at is always monomorphic, we also know the concrete type arguments of its neighbors, and so all neighbors again will be monomorphic. The specific forms a reference to a neighboring node can take in MIR are quite diverse. Here is an overview:
Calling Functions/Methods
The most obvious form of one mono item referencing another is a function or method call (represented by a CALL terminator in MIR). But calls are not the only thing that might introduce a reference between two function mono items, and as we will see below, they are just a specialized of the form described next, and consequently will don't get any special treatment in the algorithm.
Taking a reference to a function or method
A function does not need to actually be called in order to be a neighbor of another function. It suffices to just take a reference in order to introduce an edge. Consider the following example:
fn print_val<T: Display>(x: T) { println!("{}", x); } fn call_fn(f: &Fn(i32), x: i32) { f(x); } fn main() { let print_i32 = print_val::<i32>; call_fn(&print_i32, 0); }
The MIR of none of these functions will contain an explicit call to
print_val::<i32>
. Nonetheless, in order to mono this program, we need
an instance of this function. Thus, whenever we encounter a function or
method in operand position, we treat it as a neighbor of the current
mono item. Calls are just a special case of that.
Closures
In a way, closures are a simple case. Since every closure object needs to be
constructed somewhere, we can reliably discover them by observing
RValue::Aggregate
expressions with AggregateKind::Closure
. This is also
true for closures inlined from other crates.
Drop glue
Drop glue mono items are introduced by MIR drop-statements. The
generated mono item will again have drop-glue item neighbors if the
type to be dropped contains nested values that also need to be dropped. It
might also have a function item neighbor for the explicit Drop::drop
implementation of its type.
Unsizing Casts
A subtle way of introducing neighbor edges is by casting to a trait object. Since the resulting fat-pointer contains a reference to a vtable, we need to instantiate all object-save methods of the trait, as we need to store pointers to these functions even if they never get called anywhere. This can be seen as a special case of taking a function reference.
Boxes
Since Box
expression have special compiler support, no explicit calls to
exchange_malloc()
and exchange_free()
may show up in MIR, even if the
compiler will generate them. We have to observe Rvalue::Box
expressions
and Box-typed drop-statements for that purpose.
Interaction with Cross-Crate Inlining
The binary of a crate will not only contain machine code for the items
defined in the source code of that crate. It will also contain monomorphic
instantiations of any extern generic functions and of functions marked with
#[inline]
.
The collection algorithm handles this more or less mono. If it is
about to create a mono item for something with an external DefId
,
it will take a look if the MIR for that item is available, and if so just
proceed normally. If the MIR is not available, it assumes that the item is
just linked to and no node is created; which is exactly what we want, since
no machine code should be generated in the current crate for such an item.
Eager and Lazy Collection Mode
Mono item collection can be performed in one of two modes:
-
Lazy mode means that items will only be instantiated when actually referenced. The goal is to produce the least amount of machine code possible.
-
Eager mode is meant to be used in conjunction with incremental compilation where a stable set of mono items is more important than a minimal one. Thus, eager mode will instantiate drop-glue for every drop-able type in the crate, even of no drop call for that type exists (yet). It will also instantiate default implementations of trait methods, something that otherwise is only done on demand.
Open Issues
Some things are not yet fully implemented in the current version of this module.
Initializers of Constants and Statics
Since no MIR is constructed yet for initializer expressions of constants and statics we cannot inspect these properly.
Const Fns
Ideally, no mono item should be generated for const fns unless there is a call to them that cannot be evaluated at compile time. At the moment this is not implemented however: a mono item will be produced regardless of whether it is actually needed or not.
Re-exports
use rustc::hir; |
use rustc::hir::TransFnAttrFlags; |
use rustc::hir::itemlikevisit::ItemLikeVisitor; |
use rustc::hir::map as hir_map; |
use rustc::hir::def_id::DefId; |
use rustc::middle::const_val::ConstVal; |
use rustc::mir::interpret::Value; |
use rustc::mir::interpret::PrimVal; |
use rustc::mir::interpret::AllocId; |
use rustc::mir::interpret::Pointer; |
use rustc::middle::lang_items::ExchangeMallocFnLangItem; |
use rustc::middle::lang_items::StartFnLangItem; |
use rustc::ty::subst::Substs; |
use rustc::ty::subst::Kind; |
use rustc::ty; |
use rustc::ty::TypeFoldable; |
use rustc::ty::Ty; |
use rustc::ty::TyCtxt; |
use rustc::ty::adjustment::CustomCoerceUnsized; |
use rustc::session::config; |
use rustc::mir; |
use rustc::mir::Location; |
use rustc::mir::Promoted; |
use rustc::mir::visit::Visitor as MirVisitor; |
use rustc::mir::mono::MonoItem; |
use rustc::mir::interpret::GlobalId; |
use monomorphize; |
use monomorphize::Instance; |
use rustc::util::nodemap::FxHashSet; |
use rustc::util::nodemap::FxHashMap; |
use rustc::util::nodemap::DefIdMap; |
use monomorphize::item::MonoItemExt; |
use monomorphize::item::DefPathBasedNames; |
use monomorphize::item::InstantiationMode; |
use rustc_data_structures::bitvec::BitVector; |
Structs
InliningMap |
[ Experimental ] Maps every mono item to all mono items it references in its body. |
MirNeighborCollector |
[ Experimental ]
|
RootCollector |
[ Experimental ]
|
Enums
MonoItemCollectionMode |
[ Experimental ]
|
Functions
check_recursion_limit |
[ Experimental ]
|
check_type_length_limit |
[ Experimental ]
|
collect_const |
[ Experimental ]
|
collect_crate_mono_items |
[ Experimental ]
|
collect_items_rec |
[ Experimental ]
|
collect_miri |
[ Experimental ] Scan the miri alloc in order to find function calls, closures, and drop-glue |
collect_neighbours |
[ Experimental ] Scan the MIR in order to find function calls, closures, and drop-glue |
collect_roots |
[ Experimental ]
|
create_fn_mono_item |
[ Experimental ]
|
create_mono_items_for_default_impls |
[ Experimental ]
|
create_mono_items_for_vtable_methods |
[ Experimental ] Creates a |
def_id_to_string |
[ Experimental ]
|
find_vtable_types_for_unsizing |
[ Experimental ] For given pair of source and target type that occur in an unsizing coercion, this function finds the pair of types that determines the vtable linking them. |
item_has_type_parameters |
[ Experimental ]
|
record_accesses |
[ Experimental ]
|
should_monomorphize_locally |
[ Experimental ]
|
visit_drop_use |
[ Experimental ]
|
visit_fn_use |
[ Experimental ]
|
visit_instance_use |
[ Experimental ]
|