mod default;
mod merging;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync;
use rustc_hir::def_id::{CrateNum, DefIdSet, LOCAL_CRATE};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::mir::mono::{CodegenUnit, Linkage};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
use crate::monomorphize::collector::InliningMap;
use crate::monomorphize::collector::{self, MonoItemCollectionMode};
pub struct PartitioningCx<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
target_cgu_count: usize,
inlining_map: &'a InliningMap<'tcx>,
}
trait Partitioner<'tcx> {
fn place_root_mono_items(
&mut self,
cx: &PartitioningCx<'_, 'tcx>,
mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
) -> PreInliningPartitioning<'tcx>;
fn merge_codegen_units(
&mut self,
cx: &PartitioningCx<'_, 'tcx>,
initial_partitioning: &mut PreInliningPartitioning<'tcx>,
);
fn place_inlined_mono_items(
&mut self,
cx: &PartitioningCx<'_, 'tcx>,
initial_partitioning: PreInliningPartitioning<'tcx>,
) -> PostInliningPartitioning<'tcx>;
fn internalize_symbols(
&mut self,
cx: &PartitioningCx<'_, 'tcx>,
partitioning: &mut PostInliningPartitioning<'tcx>,
);
}
fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
let strategy = match &tcx.sess.opts.debugging_opts.cgu_partitioning_strategy {
None => "default",
Some(s) => &s[..],
};
match strategy {
"default" => Box::new(default::DefaultPartitioning),
_ => tcx.sess.fatal("unknown partitioning strategy"),
}
}
pub fn partition<'tcx>(
tcx: TyCtxt<'tcx>,
mono_items: &mut dyn Iterator<Item = MonoItem<'tcx>>,
max_cgu_count: usize,
inlining_map: &InliningMap<'tcx>,
) -> Vec<CodegenUnit<'tcx>> {
let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
let mut partitioner = get_partitioner(tcx);
let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map };
let mut initial_partitioning = {
let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
partitioner.place_root_mono_items(cx, mono_items)
};
initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());
{
let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
partitioner.merge_codegen_units(cx, &mut initial_partitioning);
debug_dump(tcx, "POST MERGING:", initial_partitioning.codegen_units.iter());
}
let mut post_inlining = {
let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
partitioner.place_inlined_mono_items(cx, initial_partitioning)
};
post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());
if !tcx.sess.link_dead_code() {
let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
partitioner.internalize_symbols(cx, &mut post_inlining);
}
let PostInliningPartitioning {
codegen_units: mut result,
mono_item_placements: _,
internalization_candidates: _,
} = post_inlining;
result.sort_by_cached_key(|cgu| cgu.name().as_str());
result
}
pub struct PreInliningPartitioning<'tcx> {
codegen_units: Vec<CodegenUnit<'tcx>>,
roots: FxHashSet<MonoItem<'tcx>>,
internalization_candidates: FxHashSet<MonoItem<'tcx>>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum MonoItemPlacement {
SingleCgu { cgu_name: Symbol },
MultipleCgus,
}
struct PostInliningPartitioning<'tcx> {
codegen_units: Vec<CodegenUnit<'tcx>>,
mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
internalization_candidates: FxHashSet<MonoItem<'tcx>>,
}
fn debug_dump<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, label: &str, cgus: I)
where
I: Iterator<Item = &'a CodegenUnit<'tcx>>,
'tcx: 'a,
{
if cfg!(debug_assertions) {
debug!("{}", label);
for cgu in cgus {
debug!("CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate());
for (mono_item, linkage) in cgu.items() {
let symbol_name = mono_item.symbol_name(tcx).name;
let symbol_hash_start = symbol_name.rfind('h');
let symbol_hash =
symbol_hash_start.map(|i| &symbol_name[i..]).unwrap_or("<no hash>");
debug!(
" - {} [{:?}] [{}] estimated size {}",
mono_item,
linkage,
symbol_hash,
mono_item.size_estimate(tcx)
);
}
debug!("");
}
}
}
#[inline(never)]
fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
where
I: Iterator<Item = &'a MonoItem<'tcx>>,
'tcx: 'a,
{
let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
let mut symbols: Vec<_> =
mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
symbols.sort_by_key(|sym| sym.1);
for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
if sym1 == sym2 {
let span1 = mono_item1.local_span(tcx);
let span2 = mono_item2.local_span(tcx);
let span = match (span1, span2) {
(Some(span1), Some(span2)) => {
Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
}
(span1, span2) => span1.or(span2),
};
let error_message = format!("symbol `{}` is already defined", sym1);
if let Some(span) = span {
tcx.sess.span_fatal(span, &error_message)
} else {
tcx.sess.fatal(&error_message)
}
}
}
}
fn collect_and_partition_mono_items<'tcx>(
tcx: TyCtxt<'tcx>,
cnum: CrateNum,
) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
assert_eq!(cnum, LOCAL_CRATE);
let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items {
Some(ref s) => {
let mode_string = s.to_lowercase();
let mode_string = mode_string.trim();
if mode_string == "eager" {
MonoItemCollectionMode::Eager
} else {
if mode_string != "lazy" {
let message = format!(
"Unknown codegen-item collection mode '{}'. \
Falling back to 'lazy' mode.",
mode_string
);
tcx.sess.warn(&message);
}
MonoItemCollectionMode::Lazy
}
}
None => {
if tcx.sess.link_dead_code() {
MonoItemCollectionMode::Eager
} else {
MonoItemCollectionMode::Lazy
}
}
};
let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
tcx.sess.abort_if_errors();
let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
sync::join(
|| {
&*tcx.arena.alloc_from_iter(partition(
tcx,
&mut items.iter().cloned(),
tcx.sess.codegen_units(),
&inlining_map,
))
},
|| assert_symbols_are_distinct(tcx, items.iter()),
)
});
let mono_items: DefIdSet = items
.iter()
.filter_map(|mono_item| match *mono_item {
MonoItem::Fn(ref instance) => Some(instance.def_id()),
MonoItem::Static(def_id) => Some(def_id),
_ => None,
})
.collect();
if tcx.sess.opts.debugging_opts.print_mono_items.is_some() {
let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
for cgu in codegen_units {
for (&mono_item, &linkage) in cgu.items() {
item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
}
}
let mut item_keys: Vec<_> = items
.iter()
.map(|i| {
let mut output = with_no_trimmed_paths(|| i.to_string());
output.push_str(" @@");
let mut empty = Vec::new();
let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
cgus.sort_by_key(|(name, _)| *name);
cgus.dedup();
for &(ref cgu_name, (linkage, _)) in cgus.iter() {
output.push(' ');
output.push_str(&cgu_name.as_str());
let linkage_abbrev = match linkage {
Linkage::External => "External",
Linkage::AvailableExternally => "Available",
Linkage::LinkOnceAny => "OnceAny",
Linkage::LinkOnceODR => "OnceODR",
Linkage::WeakAny => "WeakAny",
Linkage::WeakODR => "WeakODR",
Linkage::Appending => "Appending",
Linkage::Internal => "Internal",
Linkage::Private => "Private",
Linkage::ExternalWeak => "ExternalWeak",
Linkage::Common => "Common",
};
output.push('[');
output.push_str(linkage_abbrev);
output.push(']');
}
output
})
.collect();
item_keys.sort();
for item in item_keys {
println!("MONO_ITEM {}", item);
}
}
(tcx.arena.alloc(mono_items), codegen_units)
}
pub fn provide(providers: &mut Providers) {
providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
providers.is_codegened_item = |tcx, def_id| {
let (all_mono_items, _) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
all_mono_items.contains(&def_id)
};
providers.codegen_unit = |tcx, name| {
let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
all.iter()
.find(|cgu| cgu.name() == name)
.unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name))
};
}