Auto merge of #32779 - michaelwoerister:partitioning, r=nikomatsakis
Add initial version of codegen unit partitioning for incremental compilation. The task of the partitioning 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. This version only handles the case of partitioning for incremental compilation, not the regular N-codegen units case. In the future the regular case should be handled too, maybe even doing a bit more analysis to intelligently figure out a good partitioning. One thing that could be improved is the syntax of the codegen unit tests. Right now they still use the compile-fail error specification infrastructure, so everything has to be on one line. Would be nice to be able to format things in a more readable way.
This commit is contained in:
commit
fef6c64a80
@ -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");
|
||||
|
||||
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));
|
||||
for item in &missing {
|
||||
println!("{}", item);
|
||||
}
|
||||
|
||||
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 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
|
||||
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 @@ impl DefPathData {
|
||||
match *self {
|
||||
TypeNs(name) |
|
||||
ValueNs(name) |
|
||||
Module(name) |
|
||||
MacroDef(name) |
|
||||
TypeParam(name) |
|
||||
LifetimeDef(name) |
|
||||
|
@ -147,6 +147,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
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 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
// 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 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
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 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 attributes;
|
||||
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::hir::def_id::DefId;
|
||||
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 rustc::mir::visit::Visitor as MirVisitor;
|
||||
|
||||
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 @@ impl<'tcx> Hash for TransItem<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ mod cabi_x86_win64;
|
||||
mod callee;
|
||||
mod cleanup;
|
||||
mod closure;
|
||||
mod collector;
|
||||
mod common;
|
||||
mod consts;
|
||||
mod context;
|
||||
@ -120,7 +121,7 @@ mod _match;
|
||||
mod meth;
|
||||
mod mir;
|
||||
mod monomorphize;
|
||||
mod collector;
|
||||
mod partitioning;
|
||||
mod symbol_names_test;
|
||||
mod tvec;
|
||||
mod type_;
|
||||
|
363
src/librustc_trans/partitioning.rs
Normal file
363
src/librustc_trans/partitioning.rs
Normal file
@ -0,0 +1,363 @@
|
||||
// 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!(")");
|
||||
}
|
||||
}
|
20
src/test/auxiliary/cgu_explicit_inlining.rs
Normal file
20
src/test/auxiliary/cgu_explicit_inlining.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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() {}
|
17
src/test/auxiliary/cgu_extern_drop_glue.rs
Normal file
17
src/test/auxiliary/cgu_extern_drop_glue.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// 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))
|
||||
|
43
src/test/codegen-units/partitioning/extern-drop-glue.rs
Normal file
43
src/test/codegen-units/partitioning/extern-drop-glue.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
62
src/test/codegen-units/partitioning/extern-generic.rs
Normal file
62
src/test/codegen-units/partitioning/extern-generic.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// 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
|
@ -0,0 +1,61 @@
|
||||
// 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
|
61
src/test/codegen-units/partitioning/local-drop-glue.rs
Normal file
61
src/test/codegen-units/partitioning/local-drop-glue.rs
Normal file
@ -0,0 +1,61 @@
|
||||
// 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 }),
|
||||
};
|
||||
}
|
||||
}
|
58
src/test/codegen-units/partitioning/local-generic.rs
Normal file
58
src/test/codegen-units/partitioning/local-generic.rs
Normal file
@ -0,0 +1,58 @@
|
||||
// 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
|
54
src/test/codegen-units/partitioning/local-inlining.rs
Normal file
54
src/test/codegen-units/partitioning/local-inlining.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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
|
@ -0,0 +1,54 @@
|
||||
// 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
|
@ -0,0 +1,78 @@
|
||||
// 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
|
82
src/test/codegen-units/partitioning/regular-modules.rs
Normal file
82
src/test/codegen-units/partitioning/regular-modules.rs
Normal file
@ -0,0 +1,82 @@
|
||||
// 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
|
48
src/test/codegen-units/partitioning/statics.rs
Normal file
48
src/test/codegen-units/partitioning/statics.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// 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
|
Loading…
x
Reference in New Issue
Block a user