提交 e8441b67 编写于 作者: M Michael Woerister

Add initial version of codegen unit partitioning for incremental compilation.

上级 a2217ddb
......@@ -1911,6 +1911,7 @@ fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
}
fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
assert!(props.revisions.is_empty(), "revisions not relevant here");
let proc_res = compile_test(config, props, testpaths);
......@@ -1921,36 +1922,148 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPa
check_no_compiler_crash(None, &proc_res);
let prefix = "TRANS_ITEM ";
const PREFIX: &'static str = "TRANS_ITEM ";
const CGU_MARKER: &'static str = "@@";
let actual: HashSet<String> = proc_res
let actual: Vec<TransItem> = proc_res
.stdout
.lines()
.filter(|line| line.starts_with(prefix))
.map(|s| (&s[prefix.len()..]).to_string())
.filter(|line| line.starts_with(PREFIX))
.map(str_to_trans_item)
.collect();
let expected: HashSet<String> = errors::load_errors(&testpaths.file, None)
let expected: Vec<TransItem> = errors::load_errors(&testpaths.file, None)
.iter()
.map(|e| e.msg.trim().to_string())
.map(|e| str_to_trans_item(&e.msg[..]))
.collect();
if actual != expected {
let mut missing: Vec<_> = expected.difference(&actual).collect();
let mut missing = Vec::new();
let mut wrong_cgus = Vec::new();
for expected_item in &expected {
let actual_item_with_same_name = actual.iter()
.find(|ti| ti.name == expected_item.name);
if let Some(actual_item) = actual_item_with_same_name {
if !expected_item.codegen_units.is_empty() {
// Also check for codegen units
if expected_item.codegen_units != actual_item.codegen_units {
wrong_cgus.push((expected_item.clone(), actual_item.clone()));
}
}
} else {
missing.push(expected_item.string.clone());
}
}
let unexpected: Vec<_> =
actual.iter()
.filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
.map(|acgu| acgu.string.clone())
.collect();
if !missing.is_empty() {
missing.sort();
let mut too_much: Vec<_> = actual.difference(&expected).collect();
too_much.sort();
println!("\nThese items should have been contained but were not:\n");
for item in &missing {
println!("{}", item);
}
println!("Expected and actual sets of codegen-items differ.\n\
These items should have been contained but were not:\n\n\
{}\n\n\
These items were contained but should not have been:\n\n\
{}\n\n",
missing.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2),
too_much.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2));
println!("\n");
}
if !unexpected.is_empty() {
let sorted = {
let mut sorted = unexpected.clone();
sorted.sort();
sorted
};
println!("\nThese items were contained but should not have been:\n");
for item in sorted {
println!("{}", item);
}
println!("\n");
}
if !wrong_cgus.is_empty() {
wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
println!("\nThe following items were assigned to wrong codegen units:\n");
for &(ref expected_item, ref actual_item) in &wrong_cgus {
println!("{}", expected_item.name);
println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
println!("");
}
}
if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty())
{
panic!();
}
#[derive(Clone, Eq, PartialEq)]
struct TransItem {
name: String,
codegen_units: HashSet<String>,
string: String,
}
// [TRANS_ITEM] name [@@ (cgu)+]
fn str_to_trans_item(s: &str) -> TransItem {
let s = if s.starts_with(PREFIX) {
(&s[PREFIX.len()..]).trim()
} else {
s.trim()
};
let full_string = format!("{}{}", PREFIX, s.trim().to_owned());
let parts: Vec<&str> = s.split(CGU_MARKER)
.map(str::trim)
.filter(|s| !s.is_empty())
.collect();
let name = parts[0].trim();
let cgus = if parts.len() > 1 {
let cgus_str = parts[1];
cgus_str.split(" ")
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_owned)
.collect()
}
else {
HashSet::new()
};
TransItem {
name: name.to_owned(),
codegen_units: cgus,
string: full_string,
}
}
fn codegen_units_to_str(cgus: &HashSet<String>) -> String
{
let mut cgus: Vec<_> = cgus.iter().collect();
cgus.sort();
let mut string = String::new();
for cgu in cgus {
string.push_str(&cgu[..]);
string.push_str(" ");
}
string
}
}
fn run_incremental_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
......
......@@ -136,9 +136,10 @@ fn visit_item(&mut self, i: &'ast Item) {
ItemDefaultImpl(..) | ItemImpl(..) =>
DefPathData::Impl,
ItemEnum(..) | ItemStruct(..) | ItemTrait(..) |
ItemExternCrate(..) | ItemMod(..) | ItemForeignMod(..) |
ItemTy(..) =>
ItemExternCrate(..) | ItemForeignMod(..) | ItemTy(..) =>
DefPathData::TypeNs(i.name),
ItemMod(..) =>
DefPathData::Module(i.name),
ItemStatic(..) | ItemConst(..) | ItemFn(..) =>
DefPathData::ValueNs(i.name),
ItemUse(..) =>
......
......@@ -147,6 +147,7 @@ pub enum DefPathData {
Impl,
TypeNs(ast::Name), // something in the type NS
ValueNs(ast::Name), // something in the value NS
Module(ast::Name),
MacroDef(ast::Name),
ClosureExpr,
......@@ -288,6 +289,7 @@ pub fn as_interned_str(&self) -> InternedString {
match *self {
TypeNs(name) |
ValueNs(name) |
Module(name) |
MacroDef(name) |
TypeParam(name) |
LifetimeDef(name) |
......
......@@ -147,6 +147,7 @@ pub fn push_item_path<T>(&self, buffer: &mut T, def_id: DefId)
data @ DefPathData::Misc |
data @ DefPathData::TypeNs(..) |
data @ DefPathData::ValueNs(..) |
data @ DefPathData::Module(..) |
data @ DefPathData::TypeParam(..) |
data @ DefPathData::LifetimeDef(..) |
data @ DefPathData::EnumVariant(..) |
......@@ -189,7 +190,7 @@ fn push_impl_path<T>(&self,
// the impl is either in the same module as the self-type or
// as the trait.
let self_ty = self.lookup_item_type(impl_def_id).ty;
let in_self_mod = match self.characteristic_def_id_of_type(self_ty) {
let in_self_mod = match characteristic_def_id_of_type(self_ty) {
None => false,
Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id),
};
......@@ -268,38 +269,6 @@ fn push_impl_path_fallback<T>(&self,
buffer.push(&format!("<impl at {}>", span_str));
}
/// As a heuristic, when we see an impl, if we see that the
/// 'self-type' is a type defined in the same module as the impl,
/// we can omit including the path to the impl itself. This
/// function tries to find a "characteristic def-id" for a
/// type. It's just a heuristic so it makes some questionable
/// decisions and we may want to adjust it later.
fn characteristic_def_id_of_type(&self, ty: Ty<'tcx>) -> Option<DefId> {
match ty.sty {
ty::TyStruct(adt_def, _) |
ty::TyEnum(adt_def, _) =>
Some(adt_def.did),
ty::TyTrait(ref data) =>
Some(data.principal_def_id()),
ty::TyBox(subty) =>
self.characteristic_def_id_of_type(subty),
ty::TyRawPtr(mt) |
ty::TyRef(_, mt) =>
self.characteristic_def_id_of_type(mt.ty),
ty::TyTuple(ref tys) =>
tys.iter()
.filter_map(|ty| self.characteristic_def_id_of_type(ty))
.next(),
_ =>
None
}
}
/// Returns the def-id of `def_id`'s parent in the def tree. If
/// this returns `None`, then `def_id` represents a crate root or
/// inlined root.
......@@ -309,6 +278,47 @@ fn parent_def_id(&self, def_id: DefId) -> Option<DefId> {
}
}
/// As a heuristic, when we see an impl, if we see that the
/// 'self-type' is a type defined in the same module as the impl,
/// we can omit including the path to the impl itself. This
/// function tries to find a "characteristic def-id" for a
/// type. It's just a heuristic so it makes some questionable
/// decisions and we may want to adjust it later.
pub fn characteristic_def_id_of_type<'tcx>(ty: Ty<'tcx>) -> Option<DefId> {
match ty.sty {
ty::TyStruct(adt_def, _) |
ty::TyEnum(adt_def, _) => Some(adt_def.did),
ty::TyTrait(ref data) => Some(data.principal_def_id()),
ty::TyArray(subty, _) |
ty::TySlice(subty) |
ty::TyBox(subty) => characteristic_def_id_of_type(subty),
ty::TyRawPtr(mt) |
ty::TyRef(_, mt) => characteristic_def_id_of_type(mt.ty),
ty::TyTuple(ref tys) => tys.iter()
.filter_map(|ty| characteristic_def_id_of_type(ty))
.next(),
ty::TyFnDef(def_id, _, _) |
ty::TyClosure(def_id, _) => Some(def_id),
ty::TyBool |
ty::TyChar |
ty::TyInt(_) |
ty::TyUint(_) |
ty::TyStr |
ty::TyFnPtr(_) |
ty::TyProjection(_) |
ty::TyParam(_) |
ty::TyInfer(_) |
ty::TyError |
ty::TyFloat(_) => None,
}
}
/// Unifying Trait for different kinds of item paths we might
/// construct. The basic interface is that components get pushed: the
/// instance can also customize how we handle the root of a crate.
......
......@@ -97,7 +97,7 @@ pub enum Visibility {
// DLLExportLinkage, GhostLinkage and LinkOnceODRAutoHideLinkage.
// LinkerPrivateLinkage and LinkerPrivateWeakLinkage are not included either;
// they've been removed in upstream LLVM commit r203866.
#[derive(Copy, Clone)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Linkage {
ExternalLinkage = 0,
AvailableExternallyLinkage = 1,
......
......@@ -31,6 +31,7 @@ pub enum DefPathData {
Impl,
TypeNs,
ValueNs,
Module,
MacroDef,
ClosureExpr,
TypeParam,
......@@ -61,6 +62,7 @@ fn simplify_def_path_data(data: hir_map::DefPathData) -> DefPathData {
hir_map::DefPathData::Impl => DefPathData::Impl,
hir_map::DefPathData::TypeNs(_) => DefPathData::TypeNs,
hir_map::DefPathData::ValueNs(_) => DefPathData::ValueNs,
hir_map::DefPathData::Module(_) => DefPathData::Module,
hir_map::DefPathData::MacroDef(_) => DefPathData::MacroDef,
hir_map::DefPathData::ClosureExpr => DefPathData::ClosureExpr,
hir_map::DefPathData::TypeParam(_) => DefPathData::TypeParam,
......@@ -91,6 +93,7 @@ fn recover_def_path_data(data: DefPathData, name: Option<Name>) -> hir_map::DefP
DefPathData::Impl => hir_map::DefPathData::Impl,
DefPathData::TypeNs => hir_map::DefPathData::TypeNs(name.unwrap()),
DefPathData::ValueNs => hir_map::DefPathData::ValueNs(name.unwrap()),
DefPathData::Module => hir_map::DefPathData::Module(name.unwrap()),
DefPathData::MacroDef => hir_map::DefPathData::MacroDef(name.unwrap()),
DefPathData::ClosureExpr => hir_map::DefPathData::ClosureExpr,
DefPathData::TypeParam => hir_map::DefPathData::TypeParam(name.unwrap()),
......
......@@ -58,6 +58,7 @@
use build::*;
use builder::{Builder, noname};
use callee::{Callee, CallArgs, ArgExprs, ArgVals};
use partitioning;
use cleanup::{self, CleanupMethods, DropHint};
use closure;
use common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_uint, C_integral};
......@@ -2958,14 +2959,60 @@ fn collect_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>) {
None => TransItemCollectionMode::Lazy
};
let items = time(time_passes, "translation item collection", || {
let (items, inlining_map) = time(time_passes, "translation item collection", || {
collector::collect_crate_translation_items(&ccx, collection_mode)
});
let codegen_units = time(time_passes, "codegen unit partitioning", || {
partitioning::partition(ccx.tcx(), items.iter().cloned(), &inlining_map)
});
if ccx.sess().opts.debugging_opts.print_trans_items.is_some() {
let mut item_keys: Vec<_> = items.iter()
.map(|i| i.to_string(ccx))
.collect();
let mut item_to_cgus = HashMap::new();
for cgu in codegen_units {
for (trans_item, linkage) in cgu.items {
item_to_cgus.entry(trans_item)
.or_insert(Vec::new())
.push((cgu.name.clone(), linkage));
}
}
let mut item_keys: Vec<_> = items
.iter()
.map(|i| {
let mut output = i.to_string(ccx);
output.push_str(" @@");
let mut empty = Vec::new();
let mut cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone());
cgus.dedup();
for &(ref cgu_name, linkage) in cgus.iter() {
output.push_str(" ");
output.push_str(&cgu_name[..]);
let linkage_abbrev = match linkage {
llvm::ExternalLinkage => "External",
llvm::AvailableExternallyLinkage => "Available",
llvm::LinkOnceAnyLinkage => "OnceAny",
llvm::LinkOnceODRLinkage => "OnceODR",
llvm::WeakAnyLinkage => "WeakAny",
llvm::WeakODRLinkage => "WeakODR",
llvm::AppendingLinkage => "Appending",
llvm::InternalLinkage => "Internal",
llvm::PrivateLinkage => "Private",
llvm::ExternalWeakLinkage => "ExternalWeak",
llvm::CommonLinkage => "Common",
};
output.push_str("[");
output.push_str(linkage_abbrev);
output.push_str("]");
}
output
})
.collect();
item_keys.sort();
for item in item_keys {
......
......@@ -196,7 +196,7 @@
use rustc::middle::lang_items::{ExchangeFreeFnLangItem, ExchangeMallocFnLangItem};
use rustc::traits;
use rustc::ty::subst::{self, Substs, Subst};
use rustc::ty::{self, Ty, TypeFoldable};
use rustc::ty::{self, Ty, TypeFoldable, TyCtxt};
use rustc::ty::adjustment::CustomCoerceUnsized;
use rustc::mir::repr as mir;
use rustc::mir::visit as mir_visit;
......@@ -204,14 +204,15 @@
use syntax::ast::{self, NodeId};
use syntax::codemap::DUMMY_SP;
use syntax::errors;
use syntax::{attr, errors};
use syntax::parse::token;
use base::custom_coerce_unsize_info;
use base::{custom_coerce_unsize_info, llvm_linkage_by_name};
use context::CrateContext;
use common::{fulfill_obligation, normalize_and_test_predicates,
type_is_sized};
use glue;
use llvm;
use meth;
use monomorphize::{self, Instance};
use util::nodemap::{FnvHashSet, FnvHashMap, DefIdMap};
......@@ -251,9 +252,12 @@ fn hash<H: Hasher>(&self, s: &mut H) {
}
}
pub type InliningMap<'tcx> = FnvHashMap<TransItem<'tcx>, FnvHashSet<TransItem<'tcx>>>;
pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
mode: TransItemCollectionMode)
-> FnvHashSet<TransItem<'tcx>> {
-> (FnvHashSet<TransItem<'tcx>>,
InliningMap<'tcx>) {
// We are not tracking dependencies of this pass as it has to be re-executed
// every time no matter what.
ccx.tcx().dep_graph.with_ignore(|| {
......@@ -262,12 +266,17 @@ pub fn collect_crate_translation_items<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
debug!("Building translation item graph, beginning at roots");
let mut visited = FnvHashSet();
let mut recursion_depths = DefIdMap();
let mut inlining_map = FnvHashMap();
for root in roots {
collect_items_rec(ccx, root, &mut visited, &mut recursion_depths);
collect_items_rec(ccx,
root,
&mut visited,
&mut recursion_depths,
&mut inlining_map);
}
visited
(visited, inlining_map)
})
}
......@@ -297,7 +306,8 @@ fn collect_roots<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
starting_point: TransItem<'tcx>,
visited: &mut FnvHashSet<TransItem<'tcx>>,
recursion_depths: &mut DefIdMap<usize>) {
recursion_depths: &mut DefIdMap<usize>,
inlining_map: &mut InliningMap<'tcx>) {
if !visited.insert(starting_point.clone()) {
// We've been here already, no need to search again.
return;
......@@ -312,7 +322,11 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
find_drop_glue_neighbors(ccx, t, &mut neighbors);
recursion_depth_reset = None;
}
TransItem::Static(_) => {
TransItem::Static(node_id) => {
let def_id = ccx.tcx().map.local_def_id(node_id);
let ty = ccx.tcx().lookup_item_type(def_id).ty;
let ty = glue::get_drop_glue_type(ccx, ty);
neighbors.push(TransItem::DropGlue(ty));
recursion_depth_reset = None;
}
TransItem::Fn(instance) => {
......@@ -338,7 +352,8 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
}
for neighbour in neighbors {
collect_items_rec(ccx, neighbour, visited, recursion_depths);
record_inlined_use(ccx, starting_point, neighbour, inlining_map);
collect_items_rec(ccx, neighbour, visited, recursion_depths, inlining_map);
}
if let Some((def_id, depth)) = recursion_depth_reset {
......@@ -348,6 +363,18 @@ fn collect_items_rec<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
debug!("END collect_items_rec({})", starting_point.to_string(ccx));
}
fn record_inlined_use<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
caller: TransItem<'tcx>,
callee: TransItem<'tcx>,
inlining_map: &mut InliningMap<'tcx>) {
if callee.is_from_extern_crate() ||
callee.requests_inline(ccx.tcx()) {
inlining_map.entry(caller)
.or_insert_with(|| FnvHashSet())
.insert(callee);
}
}
fn check_recursion_limit<'a, 'tcx: 'a>(ccx: &CrateContext<'a, 'tcx>,
instance: Instance<'tcx>,
recursion_depths: &mut DefIdMap<usize>)
......@@ -1315,7 +1342,7 @@ fn push_instance_as_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
push_type_params(ccx, &instance.substs.types, &[], output);
}
fn def_id_to_string(ccx: &CrateContext, def_id: DefId) -> String {
pub fn def_id_to_string(ccx: &CrateContext, def_id: DefId) -> String {
let mut output = String::new();
push_item_name(ccx, def_id, &mut output);
output
......@@ -1331,6 +1358,57 @@ fn type_to_string<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
impl<'tcx> TransItem<'tcx> {
pub fn requests_inline(&self, tcx: &TyCtxt<'tcx>) -> bool {
match *self {
TransItem::Fn(ref instance) => {
let attributes = tcx.get_attrs(instance.def);
attr::requests_inline(&attributes[..])
}
TransItem::DropGlue(..) => true,
TransItem::Static(..) => false,
}
}
pub fn is_from_extern_crate(&self) -> bool {
match *self {
TransItem::Fn(ref instance) => !instance.def.is_local(),
TransItem::DropGlue(..) |
TransItem::Static(..) => false,
}
}
pub fn is_lazily_instantiated(&self) -> bool {
match *self {
TransItem::Fn(ref instance) => !instance.substs.types.is_empty(),
TransItem::DropGlue(..) => true,
TransItem::Static(..) => false,
}
}
pub fn explicit_linkage(&self, tcx: &TyCtxt<'tcx>) -> Option<llvm::Linkage> {
let def_id = match *self {
TransItem::Fn(ref instance) => instance.def,
TransItem::Static(node_id) => tcx.map.local_def_id(node_id),
TransItem::DropGlue(..) => return None,
};
let attributes = tcx.get_attrs(def_id);
if let Some(name) = attr::first_attr_value_str_by_name(&attributes, "linkage") {
if let Some(linkage) = llvm_linkage_by_name(&name) {
Some(linkage)
} else {
let span = tcx.map.span_if_local(def_id);
if let Some(span) = span {
tcx.sess.span_fatal(span, "invalid linkage specified")
} else {
tcx.sess.fatal(&format!("invalid linkage specified: {}", name))
}
}
} else {
None
}
}
pub fn to_string<'a>(&self, ccx: &CrateContext<'a, 'tcx>) -> String {
let hir_map = &ccx.tcx().map;
......
......@@ -103,6 +103,7 @@ pub mod back {
mod callee;
mod cleanup;
mod closure;
mod collector;
mod common;
mod consts;
mod context;
......@@ -120,7 +121,7 @@ pub mod back {
mod meth;
mod mir;
mod monomorphize;
mod collector;
mod partitioning;
mod symbol_names_test;
mod tvec;
mod type_;
......
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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:
//!
//! - There are two codegen units for every source-level module:
//! - One for "stable", that is non-generic, code
//! - One for more "volatile" code, i.e. monomorphized instances of functions
//! defined in that module
//! - Code for monomorphized instances of functions from external crates gets
//! placed into every codegen unit that uses that instance.
//!
//! 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.
//!
//! As another optimization, monomorphized functions from external crates get
//! some special handling. Since we assume that the definition of such a
//! function changes rather infrequently compared to local items, we can just
//! instantiate external functions in every codegen unit where it is referenced
//! -- without having to fear that doing this will cause a lot of unnecessary
//! re-compilations. If such a reference is added or removed, the codegen unit
//! has to be re-translated anyway.
//! (Note that this only makes sense if external crates actually don't change
//! frequently. For certain multi-crate projects this might not be a valid
//! assumption).
//!
//! 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:
//!
//! - The partitioning algorithm has to take care of placing functions into all
//! codegen units where they should be available for inlining. It also has to
//! decide on the correct linkage for these functions.
//!
//! - The partitioning algorithm has to know which functions are likely to get
//! inlined, so it can distribute function instantiations accordingly. Since
//! there is no way of knowing for sure which functions LLVM will decide to
//! inline in the end, we apply a heuristic here: Only functions marked with
//! #[inline] and (as stated above) functions from external crates are
//! considered for inlining by the partitioner. The current implementation
//! will not try to determine if a function is likely to be inlined by looking
//! at the functions definition.
//!
//! 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].
use collector::{InliningMap, TransItem};
use context::CrateContext;
use monomorphize;
use rustc::hir::def_id::DefId;
use rustc::hir::map::DefPathData;
use rustc::ty::TyCtxt;
use rustc::ty::item_path::characteristic_def_id_of_type;
use llvm;
use syntax::parse::token::{self, InternedString};
use util::nodemap::{FnvHashMap, FnvHashSet};
pub struct CodegenUnit<'tcx> {
pub name: InternedString,
pub items: FnvHashMap<TransItem<'tcx>, llvm::Linkage>,
}
// Anything we can't find a proper codegen unit for goes into this.
const FALLBACK_CODEGEN_UNIT: &'static str = "__rustc_fallback_codegen_unit";
pub fn partition<'tcx, I>(tcx: &TyCtxt<'tcx>,
trans_items: I,
inlining_map: &InliningMap<'tcx>)
-> Vec<CodegenUnit<'tcx>>
where I: Iterator<Item = TransItem<'tcx>>
{
// In the first step, we place all regular translation items into their
// respective 'home' codegen unit. Regular translation items are all
// functions and statics defined in the local crate.
let initial_partitioning = place_root_translation_items(tcx, trans_items);
// In the next step, we use the inlining map to determine which addtional
// translation items have to go into each codegen unit. These additional
// translation items can be drop-glue, functions from external crates, and
// local functions the definition of which is marked with #[inline].
place_inlined_translation_items(initial_partitioning, inlining_map)
}
struct InitialPartitioning<'tcx> {
codegen_units: Vec<CodegenUnit<'tcx>>,
roots: FnvHashSet<TransItem<'tcx>>,
}
fn place_root_translation_items<'tcx, I>(tcx: &TyCtxt<'tcx>,
trans_items: I)
-> InitialPartitioning<'tcx>
where I: Iterator<Item = TransItem<'tcx>>
{
let mut roots = FnvHashSet();
let mut codegen_units = FnvHashMap();
for trans_item in trans_items {
let is_root = match trans_item {
TransItem::Static(..) => true,
TransItem::DropGlue(..) => false,
TransItem::Fn(_) => !trans_item.is_from_extern_crate(),
};
if is_root {
let characteristic_def_id = characteristic_def_id_of_trans_item(tcx, trans_item);
let is_volatile = trans_item.is_lazily_instantiated();
let codegen_unit_name = match characteristic_def_id {
Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile),
None => InternedString::new(FALLBACK_CODEGEN_UNIT),
};
let make_codegen_unit = || {
CodegenUnit {
name: codegen_unit_name.clone(),
items: FnvHashMap(),
}
};
let mut codegen_unit = codegen_units.entry(codegen_unit_name.clone())
.or_insert_with(make_codegen_unit);
let linkage = match trans_item.explicit_linkage(tcx) {
Some(explicit_linkage) => explicit_linkage,
None => {
match trans_item {
TransItem::Static(..) => llvm::ExternalLinkage,
TransItem::DropGlue(..) => unreachable!(),
// Is there any benefit to using ExternalLinkage?:
TransItem::Fn(..) => llvm::WeakODRLinkage,
}
}
};
codegen_unit.items.insert(trans_item, linkage);
roots.insert(trans_item);
}
}
InitialPartitioning {
codegen_units: codegen_units.into_iter()
.map(|(_, codegen_unit)| codegen_unit)
.collect(),
roots: roots,
}
}
fn place_inlined_translation_items<'tcx>(initial_partitioning: InitialPartitioning<'tcx>,
inlining_map: &InliningMap<'tcx>)
-> Vec<CodegenUnit<'tcx>> {
let mut final_partitioning = Vec::new();
for codegen_unit in &initial_partitioning.codegen_units[..] {
// Collect all items that need to be available in this codegen unit
let mut reachable = FnvHashSet();
for root in codegen_unit.items.keys() {
follow_inlining(*root, inlining_map, &mut reachable);
}
let mut final_codegen_unit = CodegenUnit {
name: codegen_unit.name.clone(),
items: FnvHashMap(),
};
// Add all translation items that are not already there
for trans_item in reachable {
if let Some(linkage) = codegen_unit.items.get(&trans_item) {
// This is a root, just copy it over
final_codegen_unit.items.insert(trans_item, *linkage);
} else {
if initial_partitioning.roots.contains(&trans_item) {
// This item will be instantiated in some other codegen unit,
// so we just add it here with AvailableExternallyLinkage
final_codegen_unit.items.insert(trans_item, llvm::AvailableExternallyLinkage);
} else {
// We can't be sure if this will also be instantiated
// somewhere else, so we add an instance here with
// LinkOnceODRLinkage. That way the item can be discarded if
// it's not needed (inlined) after all.
final_codegen_unit.items.insert(trans_item, llvm::LinkOnceODRLinkage);
}
}
}
final_partitioning.push(final_codegen_unit);
}
return final_partitioning;
fn follow_inlining<'tcx>(trans_item: TransItem<'tcx>,
inlining_map: &InliningMap<'tcx>,
visited: &mut FnvHashSet<TransItem<'tcx>>) {
if !visited.insert(trans_item) {
return;
}
if let Some(inlined_items) = inlining_map.get(&trans_item) {
for &inlined_item in inlined_items {
follow_inlining(inlined_item, inlining_map, visited);
}
}
}
}
fn characteristic_def_id_of_trans_item<'tcx>(tcx: &TyCtxt<'tcx>,
trans_item: TransItem<'tcx>)
-> Option<DefId> {
match trans_item {
TransItem::Fn(instance) => {
// If this is a method, we want to put it into the same module as
// its self-type. If the self-type does not provide a characteristic
// DefId, we use the location of the impl after all.
if let Some(self_ty) = instance.substs.self_ty() {
// This is an implementation of a trait method.
return characteristic_def_id_of_type(self_ty).or(Some(instance.def));
}
if let Some(impl_def_id) = tcx.impl_of_method(instance.def) {
// This is a method within an inherent impl, find out what the
// self-type is:
let impl_self_ty = tcx.lookup_item_type(impl_def_id).ty;
let impl_self_ty = tcx.erase_regions(&impl_self_ty);
let impl_self_ty = monomorphize::apply_param_substs(tcx,
instance.substs,
&impl_self_ty);
if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
return Some(def_id);
}
}
Some(instance.def)
}
TransItem::DropGlue(t) => characteristic_def_id_of_type(t),
TransItem::Static(node_id) => Some(tcx.map.local_def_id(node_id)),
}
}
fn compute_codegen_unit_name<'tcx>(tcx: &TyCtxt<'tcx>,
def_id: DefId,
volatile: bool)
-> InternedString {
// Unfortunately we cannot just use the `ty::item_path` infrastructure here
// because we need paths to modules and the DefIds of those are not
// available anymore for external items.
let mut mod_path = String::with_capacity(64);
let def_path = tcx.def_path(def_id);
mod_path.push_str(&tcx.crate_name(def_path.krate));
for part in tcx.def_path(def_id)
.data
.iter()
.take_while(|part| {
match part.data {
DefPathData::Module(..) => true,
_ => false,
}
}) {
mod_path.push_str("-");
mod_path.push_str(&part.data.as_interned_str());
}
if volatile {
mod_path.push_str(".volatile");
}
return token::intern_and_get_ident(&mod_path[..]);
}
impl<'tcx> CodegenUnit<'tcx> {
pub fn _dump<'a>(&self, ccx: &CrateContext<'a, 'tcx>) {
println!("CodegenUnit {} (", self.name);
let mut items: Vec<_> = self.items
.iter()
.map(|(trans_item, inst)| {
format!("{} -- ({:?})", trans_item.to_string(ccx), inst)
})
.collect();
items.as_mut_slice().sort();
for s in items {
println!(" {}", s);
}
println!(")");
}
}
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "lib"]
#[inline]
pub fn inlined() {}
#[inline(always)]
pub fn always_inlined() {}
#[inline(never)]
pub fn never_inlined() {}
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "lib"]
pub struct Struct(pub u32);
impl Drop for Struct {
fn drop(&mut self) {}
}
......@@ -12,12 +12,13 @@
struct Struct(u32);
#[inline(never)]
pub fn foo<T>(x: T) -> (T, u32, i8) {
let (x, Struct(y)) = bar(x);
(x, y, 2)
}
#[inline(never)]
fn bar<T>(x: T) -> (T, Struct) {
let _ = not_exported_and_not_generic(0);
(x, Struct(1))
......
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![allow(dead_code)]
#![crate_type="lib"]
// aux-build:cgu_extern_drop_glue.rs
extern crate cgu_extern_drop_glue;
//~ TRANS_ITEM drop-glue cgu_extern_drop_glue::Struct[0] @@ extern_drop_glue[OnceODR] extern_drop_glue-mod1[OnceODR]
struct LocalStruct(cgu_extern_drop_glue::Struct);
//~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[WeakODR]
fn user()
{
//~ TRANS_ITEM drop-glue extern_drop_glue::LocalStruct[0] @@ extern_drop_glue[OnceODR]
let _ = LocalStruct(cgu_extern_drop_glue::Struct(0));
}
mod mod1 {
use cgu_extern_drop_glue;
struct LocalStruct(cgu_extern_drop_glue::Struct);
//~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[WeakODR]
fn user()
{
//~ TRANS_ITEM drop-glue extern_drop_glue::mod1[0]::LocalStruct[0] @@ extern_drop_glue-mod1[OnceODR]
let _ = LocalStruct(cgu_extern_drop_glue::Struct(0));
}
}
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=eager
#![allow(dead_code)]
#![crate_type="lib"]
// aux-build:cgu_generic_function.rs
extern crate cgu_generic_function;
//~ TRANS_ITEM fn extern_generic::user[0] @@ extern_generic[WeakODR]
fn user() {
let _ = cgu_generic_function::foo("abc");
}
mod mod1 {
use cgu_generic_function;
//~ TRANS_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[WeakODR]
fn user() {
let _ = cgu_generic_function::foo("abc");
}
mod mod1 {
use cgu_generic_function;
//~ TRANS_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[WeakODR]
fn user() {
let _ = cgu_generic_function::foo("abc");
}
}
}
mod mod2 {
use cgu_generic_function;
//~ TRANS_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[WeakODR]
fn user() {
let _ = cgu_generic_function::foo("abc");
}
}
mod mod3 {
//~ TRANS_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[WeakODR]
fn non_user() {}
}
// Make sure the two generic functions from the extern crate get instantiated
// privately in every module they are use in.
//~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ extern_generic[OnceODR] extern_generic-mod1[OnceODR] extern_generic-mod2[OnceODR] extern_generic-mod1-mod1[OnceODR]
//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ extern_generic[OnceODR] extern_generic-mod1[OnceODR] extern_generic-mod2[OnceODR] extern_generic-mod1-mod1[OnceODR]
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![crate_type="lib"]
// aux-build:cgu_explicit_inlining.rs
extern crate cgu_explicit_inlining;
// This test makes sure that items inlined from external crates are privately
// instantiated in every codegen unit they are used in.
//~ TRANS_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[OnceODR] inlining_from_extern_crate-mod1[OnceODR]
//~ TRANS_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[OnceODR] inlining_from_extern_crate-mod2[OnceODR]
//~ TRANS_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[WeakODR]
pub fn user()
{
cgu_explicit_inlining::inlined();
cgu_explicit_inlining::always_inlined();
// does not generate a translation item in this crate
cgu_explicit_inlining::never_inlined();
}
mod mod1 {
use cgu_explicit_inlining;
//~ TRANS_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[WeakODR]
pub fn user()
{
cgu_explicit_inlining::inlined();
// does not generate a translation item in this crate
cgu_explicit_inlining::never_inlined();
}
}
mod mod2 {
use cgu_explicit_inlining;
//~ TRANS_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[WeakODR]
pub fn user()
{
cgu_explicit_inlining::always_inlined();
// does not generate a translation item in this crate
cgu_explicit_inlining::never_inlined();
}
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![allow(dead_code)]
#![crate_type="lib"]
//~ TRANS_ITEM drop-glue local_drop_glue::Struct[0] @@ local_drop_glue[OnceODR] local_drop_glue-mod1[OnceODR]
struct Struct {
_a: u32
}
impl Drop for Struct {
//~ TRANS_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue[WeakODR]
fn drop(&mut self) {}
}
//~ TRANS_ITEM drop-glue local_drop_glue::Outer[0] @@ local_drop_glue[OnceODR]
struct Outer {
_a: Struct
}
//~ TRANS_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[WeakODR]
fn user()
{
let _ = Outer {
_a: Struct {
_a: 0
}
};
}
mod mod1
{
use super::Struct;
//~ TRANS_ITEM drop-glue local_drop_glue::mod1[0]::Struct2[0] @@ local_drop_glue-mod1[OnceODR]
struct Struct2 {
_a: Struct,
//~ TRANS_ITEM drop-glue (u32, local_drop_glue::Struct[0]) @@ local_drop_glue-mod1[OnceODR]
_b: (u32, Struct),
}
//~ TRANS_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[WeakODR]
fn user()
{
let _ = Struct2 {
_a: Struct { _a: 0 },
_b: (0, Struct { _a: 0 }),
};
}
}
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=eager
#![allow(dead_code)]
#![crate_type="lib"]
// Used in different modules/codegen units but always instantiated in the same
// codegen unit.
//~ TRANS_ITEM fn local_generic::generic[0]<u32> @@ local_generic.volatile[WeakODR]
//~ TRANS_ITEM fn local_generic::generic[0]<u64> @@ local_generic.volatile[WeakODR]
//~ TRANS_ITEM fn local_generic::generic[0]<char> @@ local_generic.volatile[WeakODR]
//~ TRANS_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[WeakODR]
pub fn generic<T>(x: T) -> T { x }
//~ TRANS_ITEM fn local_generic::user[0] @@ local_generic[WeakODR]
fn user() {
let _ = generic(0u32);
}
mod mod1 {
pub use super::generic;
//~ TRANS_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[WeakODR]
fn user() {
let _ = generic(0u64);
}
mod mod1 {
use super::generic;
//~ TRANS_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[WeakODR]
fn user() {
let _ = generic('c');
}
}
}
mod mod2 {
use super::generic;
//~ TRANS_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[WeakODR]
fn user() {
let _ = generic("abc");
}
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![allow(dead_code)]
#![crate_type="lib"]
mod inline {
// Important: This function should show up in all codegen units where it is inlined
//~ TRANS_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-inline[WeakODR] local_inlining-user1[Available] local_inlining-user2[Available]
#[inline(always)]
pub fn inlined_function()
{
}
}
mod user1 {
use super::inline;
//~ TRANS_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[WeakODR]
fn foo() {
inline::inlined_function();
}
}
mod user2 {
use super::inline;
//~ TRANS_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[WeakODR]
fn bar() {
inline::inlined_function();
}
}
mod non_user {
//~ TRANS_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[WeakODR]
fn baz() {
}
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![allow(dead_code)]
#![crate_type="lib"]
mod inline {
//~ TRANS_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-inline[WeakODR] local_transitive_inlining-direct_user[Available] local_transitive_inlining-indirect_user[Available]
#[inline(always)]
pub fn inlined_function()
{
}
}
mod direct_user {
use super::inline;
//~ TRANS_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-direct_user[WeakODR] local_transitive_inlining-indirect_user[Available]
#[inline(always)]
pub fn foo() {
inline::inlined_function();
}
}
mod indirect_user {
use super::direct_user;
//~ TRANS_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[WeakODR]
fn bar() {
direct_user::foo();
}
}
mod non_user {
//~ TRANS_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[WeakODR]
fn baz() {
}
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![allow(dead_code)]
struct SomeType;
struct SomeGenericType<T1, T2>(T1, T2);
mod mod1 {
use super::{SomeType, SomeGenericType};
// Even though the impl is in `mod1`, the methods should end up in the
// parent module, since that is where their self-type is.
impl SomeType {
//~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::method[0] @@ methods_are_with_self_type[WeakODR]
fn method(&self) {}
//~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::associated_fn[0] @@ methods_are_with_self_type[WeakODR]
fn associated_fn() {}
}
impl<T1, T2> SomeGenericType<T1, T2> {
pub fn method(&self) {}
pub fn associated_fn(_: T1, _: T2) {}
}
}
trait Trait {
fn foo(&self);
fn default(&self) {}
}
// We provide an implementation of `Trait` for all types. The corresponding
// monomorphizations should end up in whichever module the concrete `T` is.
impl<T> Trait for T
{
fn foo(&self) {}
}
mod type1 {
pub struct Struct;
}
mod type2 {
pub struct Struct;
}
//~ TRANS_ITEM fn methods_are_with_self_type::main[0]
fn main()
{
//~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::method[0]<u32, u64> @@ methods_are_with_self_type.volatile[WeakODR]
SomeGenericType(0u32, 0u64).method();
//~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::associated_fn[0]<char, &str> @@ methods_are_with_self_type.volatile[WeakODR]
SomeGenericType::associated_fn('c', "&str");
//~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0]<methods_are_with_self_type::type1[0]::Struct[0]> @@ methods_are_with_self_type-type1.volatile[WeakODR]
type1::Struct.foo();
//~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0]<methods_are_with_self_type::type2[0]::Struct[0]> @@ methods_are_with_self_type-type2.volatile[WeakODR]
type2::Struct.foo();
//~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0]<methods_are_with_self_type::type1[0]::Struct[0]> @@ methods_are_with_self_type-type1.volatile[WeakODR]
type1::Struct.default();
//~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0]<methods_are_with_self_type::type2[0]::Struct[0]> @@ methods_are_with_self_type-type2.volatile[WeakODR]
type2::Struct.default();
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=eager
#![allow(dead_code)]
#![crate_type="lib"]
//~ TRANS_ITEM fn regular_modules::foo[0] @@ regular_modules[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::bar[0] @@ regular_modules[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::BAZ[0] @@ regular_modules[External]
static BAZ: u64 = 0;
mod mod1 {
//~ TRANS_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[External]
static BAZ: u64 = 0;
mod mod1 {
//~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[External]
static BAZ: u64 = 0;
}
mod mod2 {
//~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[External]
static BAZ: u64 = 0;
}
}
mod mod2 {
//~ TRANS_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[External]
static BAZ: u64 = 0;
mod mod1 {
//~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[External]
static BAZ: u64 = 0;
}
mod mod2 {
//~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[WeakODR]
fn foo() {}
//~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[WeakODR]
fn bar() {}
//~ TRANS_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[External]
static BAZ: u64 = 0;
}
}
//~ TRANS_ITEM drop-glue i8
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-tidy-linelength
// compile-flags:-Zprint-trans-items=lazy
#![crate_type="lib"]
//~ TRANS_ITEM static statics::FOO[0] @@ statics[External]
static FOO: u32 = 0;
//~ TRANS_ITEM static statics::BAR[0] @@ statics[External]
static BAR: u32 = 0;
//~ TRANS_ITEM fn statics::function[0] @@ statics[WeakODR]
fn function() {
//~ TRANS_ITEM static statics::function[0]::FOO[0] @@ statics[External]
static FOO: u32 = 0;
//~ TRANS_ITEM static statics::function[0]::BAR[0] @@ statics[External]
static BAR: u32 = 0;
}
mod mod1 {
//~ TRANS_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[External]
static FOO: u32 = 0;
//~ TRANS_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[External]
static BAR: u32 = 0;
//~ TRANS_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[WeakODR]
fn function() {
//~ TRANS_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[External]
static FOO: u32 = 0;
//~ TRANS_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[External]
static BAR: u32 = 0;
}
}
//~ TRANS_ITEM drop-glue i8
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册