1use std::cmp;
96use std::collections::hash_map::Entry;
97use std::fs::{self, File};
98use std::io::Write;
99use std::path::{Path, PathBuf};
100
101use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
102use rustc_data_structures::sync;
103use rustc_data_structures::unord::{UnordMap, UnordSet};
104use rustc_hir::LangItem;
105use rustc_hir::attrs::{InlineAttr, Linkage};
106use rustc_hir::def::DefKind;
107use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
108use rustc_hir::definitions::DefPathDataName;
109use rustc_middle::bug;
110use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
111use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
112use rustc_middle::mir::mono::{
113 CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, MonoItem, MonoItemData,
114 MonoItemPartitions, Visibility,
115};
116use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths};
117use rustc_middle::ty::{self, InstanceKind, TyCtxt};
118use rustc_middle::util::Providers;
119use rustc_session::CodegenUnits;
120use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
121use rustc_span::Symbol;
122use rustc_target::spec::SymbolVisibility;
123use tracing::debug;
124
125use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
126use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined};
127
128struct PartitioningCx<'a, 'tcx> {
129 tcx: TyCtxt<'tcx>,
130 usage_map: &'a UsageMap<'tcx>,
131}
132
133struct PlacedMonoItems<'tcx> {
134 codegen_units: Vec<CodegenUnit<'tcx>>,
136
137 internalization_candidates: UnordSet<MonoItem<'tcx>>,
138}
139
140fn partition<'tcx, I>(
142 tcx: TyCtxt<'tcx>,
143 mono_items: I,
144 usage_map: &UsageMap<'tcx>,
145) -> Vec<CodegenUnit<'tcx>>
146where
147 I: Iterator<Item = MonoItem<'tcx>>,
148{
149 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
150
151 let cx = &PartitioningCx { tcx, usage_map };
152
153 let PlacedMonoItems { mut codegen_units, internalization_candidates } = {
156 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_items");
157 let placed = place_mono_items(cx, mono_items);
158
159 debug_dump(tcx, "PLACE", &placed.codegen_units);
160
161 placed
162 };
163
164 {
168 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
169 merge_codegen_units(cx, &mut codegen_units);
170 debug_dump(tcx, "MERGE", &codegen_units);
171 }
172
173 if !tcx.sess.link_dead_code() {
176 let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
177 internalize_symbols(cx, &mut codegen_units, internalization_candidates);
178
179 debug_dump(tcx, "INTERNALIZE", &codegen_units);
180 }
181
182 if tcx.sess.instrument_coverage() {
184 mark_code_coverage_dead_code_cgu(&mut codegen_units);
185 }
186
187 if !codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str()) {
189 let mut names = String::new();
190 for cgu in codegen_units.iter() {
191 names += &format!("- {}\n", cgu.name());
192 }
193 bug!("unsorted CGUs:\n{names}");
194 }
195
196 codegen_units
197}
198
199fn place_mono_items<'tcx, I>(cx: &PartitioningCx<'_, 'tcx>, mono_items: I) -> PlacedMonoItems<'tcx>
200where
201 I: Iterator<Item = MonoItem<'tcx>>,
202{
203 let mut codegen_units = UnordMap::default();
204 let is_incremental_build = cx.tcx.sess.opts.incremental.is_some();
205 let mut internalization_candidates = UnordSet::default();
206
207 let can_export_generics = cx.tcx.local_crate_exports_generics();
212 let always_export_generics = can_export_generics && cx.tcx.sess.opts.share_generics();
213
214 let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
215 let cgu_name_cache = &mut UnordMap::default();
216
217 for mono_item in mono_items {
218 match mono_item.instantiation_mode(cx.tcx) {
223 InstantiationMode::GloballyShared { .. } => {}
224 InstantiationMode::LocalCopy => continue,
225 }
226
227 let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
228 let is_volatile = is_incremental_build && mono_item.is_generic_fn();
229
230 let cgu_name = match characteristic_def_id {
231 Some(def_id) => compute_codegen_unit_name(
232 cx.tcx,
233 cgu_name_builder,
234 def_id,
235 is_volatile,
236 cgu_name_cache,
237 ),
238 None => fallback_cgu_name(cgu_name_builder),
239 };
240
241 let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name));
242
243 let mut can_be_internalized = true;
244 let (linkage, visibility) = mono_item_linkage_and_visibility(
245 cx.tcx,
246 &mono_item,
247 &mut can_be_internalized,
248 can_export_generics,
249 always_export_generics,
250 );
251
252 if visibility == Visibility::Hidden && can_be_internalized {
253 internalization_candidates.insert(mono_item);
254 }
255 let size_estimate = mono_item.size_estimate(cx.tcx);
256
257 cgu.items_mut()
258 .insert(mono_item, MonoItemData { inlined: false, linkage, visibility, size_estimate });
259
260 let mut reachable_inlined_items = FxIndexSet::default();
265 get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items);
266
267 for inlined_item in reachable_inlined_items {
271 cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData {
273 inlined: true,
274 linkage: Linkage::Internal,
275 visibility: Visibility::Default,
276 size_estimate: inlined_item.size_estimate(cx.tcx),
277 });
278 }
279 }
280
281 if codegen_units.is_empty() {
284 let cgu_name = fallback_cgu_name(cgu_name_builder);
285 codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name));
286 }
287
288 let mut codegen_units: Vec<_> = cx.tcx.with_stable_hashing_context(|ref hcx| {
289 codegen_units.into_items().map(|(_, cgu)| cgu).collect_sorted(hcx, true)
290 });
291
292 for cgu in codegen_units.iter_mut() {
293 cgu.compute_size_estimate();
294 }
295
296 return PlacedMonoItems { codegen_units, internalization_candidates };
297
298 fn get_reachable_inlined_items<'tcx>(
299 tcx: TyCtxt<'tcx>,
300 item: MonoItem<'tcx>,
301 usage_map: &UsageMap<'tcx>,
302 visited: &mut FxIndexSet<MonoItem<'tcx>>,
303 ) {
304 usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| {
305 let is_new = visited.insert(inlined_item);
306 if is_new {
307 get_reachable_inlined_items(tcx, inlined_item, usage_map, visited);
308 }
309 });
310 }
311}
312
313fn merge_codegen_units<'tcx>(
316 cx: &PartitioningCx<'_, 'tcx>,
317 codegen_units: &mut Vec<CodegenUnit<'tcx>>,
318) {
319 assert!(cx.tcx.sess.codegen_units().as_usize() >= 1);
320
321 assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str()));
323
324 let mut cgu_contents: UnordMap<Symbol, Vec<Symbol>> =
326 codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect();
327
328 let max_codegen_units = cx.tcx.sess.codegen_units().as_usize();
343 while codegen_units.len() > max_codegen_units {
344 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
346
347 let cgu_dst = &codegen_units[max_codegen_units - 1];
348
349 let mut max_overlap = 0;
352 let mut max_overlap_i = max_codegen_units;
353 for (i, cgu_src) in codegen_units.iter().enumerate().skip(max_codegen_units) {
354 if cgu_src.size_estimate() <= max_overlap {
355 break;
358 }
359
360 let overlap = compute_inlined_overlap(cgu_dst, cgu_src);
361 if overlap > max_overlap {
362 max_overlap = overlap;
363 max_overlap_i = i;
364 }
365 }
366
367 let mut cgu_src = codegen_units.swap_remove(max_overlap_i);
368 let cgu_dst = &mut codegen_units[max_codegen_units - 1];
369
370 cgu_dst.items_mut().append(cgu_src.items_mut());
374 cgu_dst.compute_size_estimate();
375
376 let mut consumed_cgu_names = cgu_contents.remove(&cgu_src.name()).unwrap();
379 cgu_contents.get_mut(&cgu_dst.name()).unwrap().append(&mut consumed_cgu_names);
380 }
381
382 const NON_INCR_MIN_CGU_SIZE: usize = 1800;
388
389 while cx.tcx.sess.opts.incremental.is_none()
399 && matches!(cx.tcx.sess.codegen_units(), CodegenUnits::Default(_))
400 && codegen_units.len() > 1
401 && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE)
402 {
403 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
405
406 let mut smallest = codegen_units.pop().unwrap();
407 let second_smallest = codegen_units.last_mut().unwrap();
408
409 second_smallest.items_mut().append(smallest.items_mut());
413 second_smallest.compute_size_estimate();
414
415 }
417
418 let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
419
420 if cx.tcx.sess.opts.incremental.is_some() {
422 let new_cgu_names = UnordMap::from(
428 cgu_contents
429 .items()
430 .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
433 .map(|(current_cgu_name, cgu_contents)| {
434 let mut cgu_contents: Vec<&str> =
435 cgu_contents.iter().map(|s| s.as_str()).collect();
436
437 cgu_contents.sort_unstable();
441
442 (*current_cgu_name, cgu_contents.join("--"))
443 }),
444 );
445
446 for cgu in codegen_units.iter_mut() {
447 if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
448 let new_cgu_name = if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
449 Symbol::intern(&CodegenUnit::shorten_name(new_cgu_name))
450 } else {
451 Symbol::intern(&CodegenUnit::mangle_name(new_cgu_name))
455 };
456 cgu.set_name(new_cgu_name);
457 }
458 }
459
460 codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
462 } else {
463 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
483 let num_digits = codegen_units.len().ilog10() as usize + 1;
484 for (index, cgu) in codegen_units.iter_mut().enumerate() {
485 let suffix = format!("{index:0num_digits$}");
489 let numbered_codegen_unit_name =
490 cgu_name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(suffix));
491 cgu.set_name(numbered_codegen_unit_name);
492 }
493 }
494}
495
496fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'tcx>) -> usize {
499 let (src_cgu, dst_cgu) =
502 if cgu1.items().len() <= cgu2.items().len() { (cgu1, cgu2) } else { (cgu2, cgu1) };
503
504 let mut overlap = 0;
505 for (item, data) in src_cgu.items().iter() {
506 if data.inlined && dst_cgu.items().contains_key(item) {
507 overlap += data.size_estimate;
508 }
509 }
510 overlap
511}
512
513fn internalize_symbols<'tcx>(
514 cx: &PartitioningCx<'_, 'tcx>,
515 codegen_units: &mut [CodegenUnit<'tcx>],
516 internalization_candidates: UnordSet<MonoItem<'tcx>>,
517) {
518 #[derive(Clone, PartialEq, Eq, Debug)]
522 enum MonoItemPlacement {
523 SingleCgu(Symbol),
524 MultipleCgus,
525 }
526
527 let mut mono_item_placements = UnordMap::default();
528 let single_codegen_unit = codegen_units.len() == 1;
529
530 if !single_codegen_unit {
531 for cgu in codegen_units.iter() {
532 for item in cgu.items().keys() {
533 match mono_item_placements.entry(*item) {
536 Entry::Occupied(e) => {
537 let placement = e.into_mut();
538 debug_assert!(match *placement {
539 MonoItemPlacement::SingleCgu(cgu_name) => cgu_name != cgu.name(),
540 MonoItemPlacement::MultipleCgus => true,
541 });
542 *placement = MonoItemPlacement::MultipleCgus;
543 }
544 Entry::Vacant(e) => {
545 e.insert(MonoItemPlacement::SingleCgu(cgu.name()));
546 }
547 }
548 }
549 }
550 }
551
552 for cgu in codegen_units {
555 let home_cgu = MonoItemPlacement::SingleCgu(cgu.name());
556
557 for (item, data) in cgu.items_mut() {
558 if !internalization_candidates.contains(item) {
559 continue;
561 }
562
563 if !single_codegen_unit {
564 debug_assert_eq!(mono_item_placements[item], home_cgu);
565
566 if cx
567 .usage_map
568 .get_user_items(*item)
569 .iter()
570 .filter_map(|user_item| {
571 mono_item_placements.get(user_item)
574 })
575 .any(|placement| *placement != home_cgu)
576 {
577 continue;
580 }
581 }
582
583 data.linkage = Linkage::Internal;
586 data.visibility = Visibility::Default;
587 }
588 }
589}
590
591fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx>]) {
592 assert!(!codegen_units.is_empty());
593
594 let dead_code_cgu = codegen_units
602 .iter_mut()
603 .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage == Linkage::External))
604 .min_by_key(|cgu| cgu.size_estimate());
605
606 let dead_code_cgu = if let Some(cgu) = dead_code_cgu { cgu } else { &mut codegen_units[0] };
609
610 dead_code_cgu.make_code_coverage_dead_code_cgu();
611}
612
613fn characteristic_def_id_of_mono_item<'tcx>(
614 tcx: TyCtxt<'tcx>,
615 mono_item: MonoItem<'tcx>,
616) -> Option<DefId> {
617 match mono_item {
618 MonoItem::Fn(instance) => {
619 let def_id = match instance.def {
620 ty::InstanceKind::Item(def) => def,
621 ty::InstanceKind::VTableShim(..)
622 | ty::InstanceKind::ReifyShim(..)
623 | ty::InstanceKind::FnPtrShim(..)
624 | ty::InstanceKind::ClosureOnceShim { .. }
625 | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
626 | ty::InstanceKind::Intrinsic(..)
627 | ty::InstanceKind::DropGlue(..)
628 | ty::InstanceKind::Virtual(..)
629 | ty::InstanceKind::CloneShim(..)
630 | ty::InstanceKind::ThreadLocalShim(..)
631 | ty::InstanceKind::FnPtrAddrShim(..)
632 | ty::InstanceKind::FutureDropPollShim(..)
633 | ty::InstanceKind::AsyncDropGlue(..)
634 | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None,
635 };
636
637 let assoc_parent = tcx.assoc_parent(def_id);
642
643 if let Some((_, DefKind::Trait)) = assoc_parent {
644 let self_ty = instance.args.type_at(0);
645 return characteristic_def_id_of_type(self_ty).or(Some(def_id));
647 }
648
649 if let Some((impl_def_id, DefKind::Impl { of_trait })) = assoc_parent {
650 if of_trait
651 && tcx.sess.opts.incremental.is_some()
652 && tcx.is_lang_item(tcx.impl_trait_id(impl_def_id), LangItem::Drop)
653 {
654 return None;
658 }
659
660 let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions(
662 instance.args,
663 ty::TypingEnv::fully_monomorphized(),
664 tcx.type_of(impl_def_id),
665 );
666 if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
667 return Some(def_id);
668 }
669 }
670
671 Some(def_id)
672 }
673 MonoItem::Static(def_id) => Some(def_id),
674 MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.to_def_id()),
675 }
676}
677
678fn compute_codegen_unit_name(
679 tcx: TyCtxt<'_>,
680 name_builder: &mut CodegenUnitNameBuilder<'_>,
681 def_id: DefId,
682 volatile: bool,
683 cache: &mut CguNameCache,
684) -> Symbol {
685 let mut current_def_id = def_id;
687 let mut cgu_def_id = None;
688 loop {
690 if current_def_id.is_crate_root() {
691 if cgu_def_id.is_none() {
692 cgu_def_id = Some(def_id.krate.as_def_id());
694 }
695 break;
696 } else if tcx.def_kind(current_def_id) == DefKind::Mod {
697 if cgu_def_id.is_none() {
698 cgu_def_id = Some(current_def_id);
699 }
700 } else {
701 cgu_def_id = None;
705 }
706
707 current_def_id = tcx.parent(current_def_id);
708 }
709
710 let cgu_def_id = cgu_def_id.unwrap();
711
712 *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
713 let def_path = tcx.def_path(cgu_def_id);
714
715 let components = def_path.data.iter().map(|part| match part.data.name() {
716 DefPathDataName::Named(name) => name,
717 DefPathDataName::Anon { .. } => unreachable!(),
718 });
719
720 let volatile_suffix = volatile.then_some("volatile");
721
722 name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
723 })
724}
725
726fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
728 name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
729}
730
731fn mono_item_linkage_and_visibility<'tcx>(
732 tcx: TyCtxt<'tcx>,
733 mono_item: &MonoItem<'tcx>,
734 can_be_internalized: &mut bool,
735 can_export_generics: bool,
736 always_export_generics: bool,
737) -> (Linkage, Visibility) {
738 if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
739 return (explicit_linkage, Visibility::Default);
740 }
741 let vis = mono_item_visibility(
742 tcx,
743 mono_item,
744 can_be_internalized,
745 can_export_generics,
746 always_export_generics,
747 );
748 (Linkage::External, vis)
749}
750
751type CguNameCache = UnordMap<(DefId, bool), Symbol>;
752
753fn static_visibility<'tcx>(
754 tcx: TyCtxt<'tcx>,
755 can_be_internalized: &mut bool,
756 def_id: DefId,
757) -> Visibility {
758 if tcx.is_reachable_non_generic(def_id) {
759 *can_be_internalized = false;
760 default_visibility(tcx, def_id, false)
761 } else {
762 Visibility::Hidden
763 }
764}
765
766fn mono_item_visibility<'tcx>(
767 tcx: TyCtxt<'tcx>,
768 mono_item: &MonoItem<'tcx>,
769 can_be_internalized: &mut bool,
770 can_export_generics: bool,
771 always_export_generics: bool,
772) -> Visibility {
773 let instance = match mono_item {
774 MonoItem::Fn(instance) => instance,
776
777 MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id),
779 MonoItem::GlobalAsm(item_id) => {
780 return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id());
781 }
782 };
783
784 let def_id = match instance.def {
785 InstanceKind::Item(def_id)
786 | InstanceKind::DropGlue(def_id, Some(_))
787 | InstanceKind::FutureDropPollShim(def_id, _, _)
788 | InstanceKind::AsyncDropGlue(def_id, _)
789 | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id,
790
791 InstanceKind::ThreadLocalShim(def_id) => {
793 return static_visibility(tcx, can_be_internalized, def_id);
794 }
795
796 InstanceKind::VTableShim(..)
798 | InstanceKind::ReifyShim(..)
799 | InstanceKind::FnPtrShim(..)
800 | InstanceKind::Virtual(..)
801 | InstanceKind::Intrinsic(..)
802 | InstanceKind::ClosureOnceShim { .. }
803 | InstanceKind::ConstructCoroutineInClosureShim { .. }
804 | InstanceKind::DropGlue(..)
805 | InstanceKind::CloneShim(..)
806 | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden,
807 };
808
809 if tcx.is_entrypoint(def_id) {
822 *can_be_internalized = false;
823 return Visibility::Hidden;
824 }
825
826 let is_generic = instance.args.non_erasable_generics().next().is_some();
827
828 let Some(def_id) = def_id.as_local() else {
830 return if is_generic
831 && (always_export_generics
832 || (can_export_generics
833 && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never))
834 {
835 *can_be_internalized = false;
838 default_visibility(tcx, def_id, true)
839 } else {
840 Visibility::Hidden
841 };
842 };
843
844 if is_generic {
845 if always_export_generics
846 || (can_export_generics && tcx.codegen_fn_attrs(def_id).inline == InlineAttr::Never)
847 {
848 if tcx.is_unreachable_local_definition(def_id) {
849 Visibility::Hidden
851 } else {
852 *can_be_internalized = false;
854 default_visibility(tcx, def_id.to_def_id(), true)
855 }
856 } else {
857 Visibility::Hidden
860 }
861 } else {
862 if tcx.is_reachable_non_generic(def_id.to_def_id()) {
866 *can_be_internalized = false;
867 debug_assert!(!is_generic);
868 return default_visibility(tcx, def_id.to_def_id(), false);
869 }
870
871 let attrs = tcx.codegen_fn_attrs(def_id);
911 if attrs.flags.intersects(
912 CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
913 | CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
914 ) {
915 *can_be_internalized = false;
916 }
917
918 Visibility::Hidden
919 }
920}
921
922fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
923 if tcx.sess.default_visibility() == SymbolVisibility::Interposable {
925 return Visibility::Default;
926 }
927
928 let export_level = if is_generic {
929 SymbolExportLevel::Rust
931 } else {
932 match tcx.reachable_non_generics(id.krate).get(&id) {
933 Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C,
934 _ => SymbolExportLevel::Rust,
935 }
936 };
937
938 match export_level {
939 SymbolExportLevel::C => Visibility::Default,
942
943 SymbolExportLevel::Rust => tcx.sess.default_visibility().into(),
945 }
946}
947
948fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) {
949 let dump = move || {
950 use std::fmt::Write;
951
952 let mut num_cgus = 0;
953 let mut all_cgu_sizes = Vec::new();
954
955 let mut inlined_items = UnordSet::default();
961
962 let mut root_items = 0;
963 let mut unique_inlined_items = 0;
964 let mut placed_inlined_items = 0;
965
966 let mut root_size = 0;
967 let mut unique_inlined_size = 0;
968 let mut placed_inlined_size = 0;
969
970 for cgu in cgus.iter() {
971 num_cgus += 1;
972 all_cgu_sizes.push(cgu.size_estimate());
973
974 for (item, data) in cgu.items() {
975 if !data.inlined {
976 root_items += 1;
977 root_size += data.size_estimate;
978 } else {
979 if inlined_items.insert(item) {
980 unique_inlined_items += 1;
981 unique_inlined_size += data.size_estimate;
982 }
983 placed_inlined_items += 1;
984 placed_inlined_size += data.size_estimate;
985 }
986 }
987 }
988
989 all_cgu_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
990
991 let unique_items = root_items + unique_inlined_items;
992 let placed_items = root_items + placed_inlined_items;
993 let items_ratio = placed_items as f64 / unique_items as f64;
994
995 let unique_size = root_size + unique_inlined_size;
996 let placed_size = root_size + placed_inlined_size;
997 let size_ratio = placed_size as f64 / unique_size as f64;
998
999 let mean_cgu_size = placed_size as f64 / num_cgus as f64;
1000
1001 assert_eq!(placed_size, all_cgu_sizes.iter().sum::<usize>());
1002
1003 let s = &mut String::new();
1004 let _ = writeln!(s, "{label}");
1005 let _ = writeln!(
1006 s,
1007 "- unique items: {unique_items} ({root_items} root + {unique_inlined_items} inlined), \
1008 unique size: {unique_size} ({root_size} root + {unique_inlined_size} inlined)\n\
1009 - placed items: {placed_items} ({root_items} root + {placed_inlined_items} inlined), \
1010 placed size: {placed_size} ({root_size} root + {placed_inlined_size} inlined)\n\
1011 - placed/unique items ratio: {items_ratio:.2}, \
1012 placed/unique size ratio: {size_ratio:.2}\n\
1013 - CGUs: {num_cgus}, mean size: {mean_cgu_size:.1}, sizes: {}",
1014 list(&all_cgu_sizes),
1015 );
1016 let _ = writeln!(s);
1017
1018 for (i, cgu) in cgus.iter().enumerate() {
1019 let name = cgu.name();
1020 let size = cgu.size_estimate();
1021 let num_items = cgu.items().len();
1022 let mean_size = size as f64 / num_items as f64;
1023
1024 let mut placed_item_sizes: Vec<_> =
1025 cgu.items().values().map(|data| data.size_estimate).collect();
1026 placed_item_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
1027 let sizes = list(&placed_item_sizes);
1028
1029 let _ = writeln!(s, "- CGU[{i}]");
1030 let _ = writeln!(s, " - {name}, size: {size}");
1031 let _ =
1032 writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",);
1033
1034 for (item, data) in cgu.items_in_deterministic_order(tcx) {
1035 let linkage = data.linkage;
1036 let symbol_name = item.symbol_name(tcx).name;
1037 let symbol_hash_start = symbol_name.rfind('h');
1038 let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]);
1039 let kind = if !data.inlined { "root" } else { "inlined" };
1040 let size = data.size_estimate;
1041 let _ = with_no_trimmed_paths!(writeln!(
1042 s,
1043 " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})"
1044 ));
1045 }
1046
1047 let _ = writeln!(s);
1048 }
1049
1050 return std::mem::take(s);
1051
1052 fn list(ns: &[usize]) -> String {
1055 let mut v = Vec::new();
1056 if ns.is_empty() {
1057 return "[]".to_string();
1058 }
1059
1060 let mut elem = |curr, curr_count| {
1061 if curr_count == 1 {
1062 v.push(format!("{curr}"));
1063 } else {
1064 v.push(format!("{curr} (x{curr_count})"));
1065 }
1066 };
1067
1068 let mut curr = ns[0];
1069 let mut curr_count = 1;
1070
1071 for &n in &ns[1..] {
1072 if n != curr {
1073 elem(curr, curr_count);
1074 curr = n;
1075 curr_count = 1;
1076 } else {
1077 curr_count += 1;
1078 }
1079 }
1080 elem(curr, curr_count);
1081
1082 format!("[{}]", v.join(", "))
1083 }
1084 };
1085
1086 debug!("{}", dump());
1087}
1088
1089#[inline(never)] fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
1091where
1092 I: Iterator<Item = &'a MonoItem<'tcx>>,
1093 'tcx: 'a,
1094{
1095 let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
1096
1097 let mut symbols: Vec<_> =
1098 mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
1099
1100 symbols.sort_by_key(|sym| sym.1);
1101
1102 for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
1103 if sym1 == sym2 {
1104 let span1 = mono_item1.local_span(tcx);
1105 let span2 = mono_item2.local_span(tcx);
1106
1107 let span = match (span1, span2) {
1109 (Some(span1), Some(span2)) => {
1110 Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
1111 }
1112 (span1, span2) => span1.or(span2),
1113 };
1114
1115 tcx.dcx().emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
1116 }
1117 }
1118}
1119
1120fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> {
1121 let collection_strategy = if tcx.sess.link_dead_code() {
1122 MonoItemCollectionStrategy::Eager
1123 } else {
1124 MonoItemCollectionStrategy::Lazy
1125 };
1126
1127 let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy);
1128
1129 tcx.dcx().abort_if_errors();
1133
1134 let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
1135 sync::join(
1136 || {
1137 let mut codegen_units = partition(tcx, items.iter().copied(), &usage_map);
1138 codegen_units[0].make_primary();
1139 &*tcx.arena.alloc_from_iter(codegen_units)
1140 },
1141 || assert_symbols_are_distinct(tcx, items.iter()),
1142 )
1143 });
1144
1145 if tcx.prof.enabled() {
1146 for cgu in codegen_units {
1148 tcx.prof.artifact_size(
1149 "codegen_unit_size_estimate",
1150 cgu.name().as_str(),
1151 cgu.size_estimate() as u64,
1152 );
1153 }
1154 }
1155
1156 let mono_items: DefIdSet = items
1157 .iter()
1158 .filter_map(|mono_item| match *mono_item {
1159 MonoItem::Fn(ref instance) => Some(instance.def_id()),
1160 MonoItem::Static(def_id) => Some(def_id),
1161 _ => None,
1162 })
1163 .collect();
1164
1165 if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats
1167 && let Err(err) =
1168 dump_mono_items_stats(tcx, codegen_units, path, tcx.crate_name(LOCAL_CRATE))
1169 {
1170 tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() });
1171 }
1172
1173 if tcx.sess.opts.unstable_opts.print_mono_items {
1174 let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default();
1175
1176 for cgu in codegen_units {
1177 for (&mono_item, &data) in cgu.items() {
1178 item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage));
1179 }
1180 }
1181
1182 let mut item_keys: Vec<_> = items
1183 .iter()
1184 .map(|i| {
1185 let mut output = with_no_trimmed_paths!(i.to_string());
1186 output.push_str(" @@");
1187 let mut empty = Vec::new();
1188 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
1189 cgus.sort_by_key(|(name, _)| *name);
1190 cgus.dedup();
1191 for &(ref cgu_name, linkage) in cgus.iter() {
1192 output.push(' ');
1193 output.push_str(cgu_name.as_str());
1194
1195 let linkage_abbrev = match linkage {
1196 Linkage::External => "External",
1197 Linkage::AvailableExternally => "Available",
1198 Linkage::LinkOnceAny => "OnceAny",
1199 Linkage::LinkOnceODR => "OnceODR",
1200 Linkage::WeakAny => "WeakAny",
1201 Linkage::WeakODR => "WeakODR",
1202 Linkage::Internal => "Internal",
1203 Linkage::ExternalWeak => "ExternalWeak",
1204 Linkage::Common => "Common",
1205 };
1206
1207 output.push('[');
1208 output.push_str(linkage_abbrev);
1209 output.push(']');
1210 }
1211 output
1212 })
1213 .collect();
1214
1215 item_keys.sort();
1216
1217 for item in item_keys {
1218 println!("MONO_ITEM {item}");
1219 }
1220 }
1221
1222 MonoItemPartitions { all_mono_items: tcx.arena.alloc(mono_items), codegen_units }
1223}
1224
1225fn dump_mono_items_stats<'tcx>(
1228 tcx: TyCtxt<'tcx>,
1229 codegen_units: &[CodegenUnit<'tcx>],
1230 output_directory: &Option<PathBuf>,
1231 crate_name: Symbol,
1232) -> Result<(), Box<dyn std::error::Error>> {
1233 let output_directory = if let Some(directory) = output_directory {
1234 fs::create_dir_all(directory)?;
1235 directory
1236 } else {
1237 Path::new(".")
1238 };
1239
1240 let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
1241 let ext = format.extension();
1242 let filename = format!("{crate_name}.mono_items.{ext}");
1243 let output_path = output_directory.join(&filename);
1244 let mut file = File::create_buffered(&output_path)?;
1245
1246 let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default();
1248 for cgu in codegen_units {
1249 cgu.items()
1250 .keys()
1251 .filter(|mono_item| mono_item.is_user_defined())
1253 .for_each(|mono_item| {
1254 items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
1255 });
1256 }
1257
1258 #[derive(serde::Serialize)]
1259 struct MonoItem {
1260 name: String,
1261 instantiation_count: usize,
1262 size_estimate: usize,
1263 total_estimate: usize,
1264 }
1265
1266 let mut stats: Vec<_> = items_per_def_id
1268 .into_iter()
1269 .map(|(def_id, items)| {
1270 let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
1271 let instantiation_count = items.len();
1272 let size_estimate = items[0].size_estimate(tcx);
1273 let total_estimate = instantiation_count * size_estimate;
1274 MonoItem { name, instantiation_count, size_estimate, total_estimate }
1275 })
1276 .collect();
1277 stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
1278
1279 if !stats.is_empty() {
1280 match format {
1281 DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
1282 DumpMonoStatsFormat::Markdown => {
1283 writeln!(
1284 file,
1285 "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
1286 )?;
1287 writeln!(file, "| --- | ---: | ---: | ---: |")?;
1288
1289 for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
1290 writeln!(
1291 file,
1292 "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
1293 )?;
1294 }
1295 }
1296 }
1297 }
1298
1299 Ok(())
1300}
1301
1302pub(crate) fn provide(providers: &mut Providers) {
1303 providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
1304
1305 providers.is_codegened_item =
1306 |tcx, def_id| tcx.collect_and_partition_mono_items(()).all_mono_items.contains(&def_id);
1307
1308 providers.codegen_unit = |tcx, name| {
1309 tcx.collect_and_partition_mono_items(())
1310 .codegen_units
1311 .iter()
1312 .find(|cgu| cgu.name() == name)
1313 .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}"))
1314 };
1315
1316 providers.size_estimate = |tcx, instance| {
1317 match instance.def {
1318 InstanceKind::Item(..)
1321 | InstanceKind::DropGlue(..)
1322 | InstanceKind::AsyncDropGlueCtorShim(..) => {
1323 let mir = tcx.instance_mir(instance.def);
1324 mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
1325 }
1326 _ => 1,
1328 }
1329 };
1330
1331 collector::provide(providers);
1332}