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