1mod autodiff;
96
97use std::cmp;
98use std::collections::hash_map::Entry;
99use std::fs::{self, File};
100use std::io::Write;
101use std::path::{Path, PathBuf};
102
103use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
104use rustc_data_structures::sync;
105use rustc_data_structures::unord::{UnordMap, UnordSet};
106use rustc_hir::LangItem;
107use rustc_hir::def::DefKind;
108use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
109use rustc_hir::definitions::DefPathDataName;
110use rustc_middle::bug;
111use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
112use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
113use rustc_middle::mir::mono::{
114 CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData,
115 MonoItemPartitions, Visibility,
116};
117use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths};
118use rustc_middle::ty::{self, InstanceKind, TyCtxt};
119use rustc_middle::util::Providers;
120use rustc_session::CodegenUnits;
121use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
122use rustc_span::Symbol;
123use rustc_target::spec::SymbolVisibility;
124use tracing::debug;
125
126use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
127use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode};
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 => {
226 if !cx.tcx.is_lang_item(mono_item.def_id(), LangItem::Start) {
227 continue;
228 }
229 }
230 }
231
232 let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
233 let is_volatile = is_incremental_build && mono_item.is_generic_fn();
234
235 let cgu_name = match characteristic_def_id {
236 Some(def_id) => compute_codegen_unit_name(
237 cx.tcx,
238 cgu_name_builder,
239 def_id,
240 is_volatile,
241 cgu_name_cache,
242 ),
243 None => fallback_cgu_name(cgu_name_builder),
244 };
245
246 let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name));
247
248 let mut can_be_internalized = true;
249 let (linkage, visibility) = mono_item_linkage_and_visibility(
250 cx.tcx,
251 &mono_item,
252 &mut can_be_internalized,
253 can_export_generics,
254 always_export_generics,
255 );
256
257 let autodiff_active = cfg!(llvm_enzyme)
259 && matches!(mono_item, MonoItem::Fn(_))
260 && cx
261 .tcx
262 .codegen_fn_attrs(mono_item.def_id())
263 .autodiff_item
264 .as_ref()
265 .is_some_and(|ad| ad.is_active());
266
267 if !autodiff_active && visibility == Visibility::Hidden && can_be_internalized {
268 internalization_candidates.insert(mono_item);
269 }
270 let size_estimate = mono_item.size_estimate(cx.tcx);
271
272 cgu.items_mut()
273 .insert(mono_item, MonoItemData { inlined: false, linkage, visibility, size_estimate });
274
275 let mut reachable_inlined_items = FxIndexSet::default();
280 get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items);
281
282 for inlined_item in reachable_inlined_items {
286 cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData {
288 inlined: true,
289 linkage: Linkage::Internal,
290 visibility: Visibility::Default,
291 size_estimate: inlined_item.size_estimate(cx.tcx),
292 });
293 }
294 }
295
296 if codegen_units.is_empty() {
299 let cgu_name = fallback_cgu_name(cgu_name_builder);
300 codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name));
301 }
302
303 let mut codegen_units: Vec<_> = cx.tcx.with_stable_hashing_context(|ref hcx| {
304 codegen_units.into_items().map(|(_, cgu)| cgu).collect_sorted(hcx, true)
305 });
306
307 for cgu in codegen_units.iter_mut() {
308 cgu.compute_size_estimate();
309 }
310
311 return PlacedMonoItems { codegen_units, internalization_candidates };
312
313 fn get_reachable_inlined_items<'tcx>(
314 tcx: TyCtxt<'tcx>,
315 item: MonoItem<'tcx>,
316 usage_map: &UsageMap<'tcx>,
317 visited: &mut FxIndexSet<MonoItem<'tcx>>,
318 ) {
319 usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| {
320 let is_new = visited.insert(inlined_item);
321 if is_new {
322 get_reachable_inlined_items(tcx, inlined_item, usage_map, visited);
323 }
324 });
325 }
326}
327
328fn merge_codegen_units<'tcx>(
331 cx: &PartitioningCx<'_, 'tcx>,
332 codegen_units: &mut Vec<CodegenUnit<'tcx>>,
333) {
334 assert!(cx.tcx.sess.codegen_units().as_usize() >= 1);
335
336 assert!(codegen_units.is_sorted_by(|a, b| a.name().as_str() <= b.name().as_str()));
338
339 let mut cgu_contents: UnordMap<Symbol, Vec<Symbol>> =
341 codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect();
342
343 let max_codegen_units = cx.tcx.sess.codegen_units().as_usize();
358 while codegen_units.len() > max_codegen_units {
359 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
361
362 let cgu_dst = &codegen_units[max_codegen_units - 1];
363
364 let mut max_overlap = 0;
367 let mut max_overlap_i = max_codegen_units;
368 for (i, cgu_src) in codegen_units.iter().enumerate().skip(max_codegen_units) {
369 if cgu_src.size_estimate() <= max_overlap {
370 break;
373 }
374
375 let overlap = compute_inlined_overlap(cgu_dst, cgu_src);
376 if overlap > max_overlap {
377 max_overlap = overlap;
378 max_overlap_i = i;
379 }
380 }
381
382 let mut cgu_src = codegen_units.swap_remove(max_overlap_i);
383 let cgu_dst = &mut codegen_units[max_codegen_units - 1];
384
385 cgu_dst.items_mut().append(cgu_src.items_mut());
389 cgu_dst.compute_size_estimate();
390
391 let mut consumed_cgu_names = cgu_contents.remove(&cgu_src.name()).unwrap();
394 cgu_contents.get_mut(&cgu_dst.name()).unwrap().append(&mut consumed_cgu_names);
395 }
396
397 const NON_INCR_MIN_CGU_SIZE: usize = 1800;
403
404 while cx.tcx.sess.opts.incremental.is_none()
414 && matches!(cx.tcx.sess.codegen_units(), CodegenUnits::Default(_))
415 && codegen_units.len() > 1
416 && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE)
417 {
418 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
420
421 let mut smallest = codegen_units.pop().unwrap();
422 let second_smallest = codegen_units.last_mut().unwrap();
423
424 second_smallest.items_mut().append(smallest.items_mut());
428 second_smallest.compute_size_estimate();
429
430 }
432
433 let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
434
435 if cx.tcx.sess.opts.incremental.is_some() {
437 let new_cgu_names = UnordMap::from(
443 cgu_contents
444 .items()
445 .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
448 .map(|(current_cgu_name, cgu_contents)| {
449 let mut cgu_contents: Vec<&str> =
450 cgu_contents.iter().map(|s| s.as_str()).collect();
451
452 cgu_contents.sort_unstable();
456
457 (*current_cgu_name, cgu_contents.join("--"))
458 }),
459 );
460
461 for cgu in codegen_units.iter_mut() {
462 if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
463 if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
464 cgu.set_name(Symbol::intern(new_cgu_name));
465 } else {
466 let new_cgu_name = CodegenUnit::mangle_name(new_cgu_name);
470 cgu.set_name(Symbol::intern(&new_cgu_name));
471 }
472 }
473 }
474
475 codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
477 } else {
478 codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
498 let num_digits = codegen_units.len().ilog10() as usize + 1;
499 for (index, cgu) in codegen_units.iter_mut().enumerate() {
500 let suffix = format!("{index:0num_digits$}");
504 let numbered_codegen_unit_name =
505 cgu_name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(suffix));
506 cgu.set_name(numbered_codegen_unit_name);
507 }
508 }
509}
510
511fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'tcx>) -> usize {
514 let (src_cgu, dst_cgu) =
517 if cgu1.items().len() <= cgu2.items().len() { (cgu1, cgu2) } else { (cgu2, cgu1) };
518
519 let mut overlap = 0;
520 for (item, data) in src_cgu.items().iter() {
521 if data.inlined && dst_cgu.items().contains_key(item) {
522 overlap += data.size_estimate;
523 }
524 }
525 overlap
526}
527
528fn internalize_symbols<'tcx>(
529 cx: &PartitioningCx<'_, 'tcx>,
530 codegen_units: &mut [CodegenUnit<'tcx>],
531 internalization_candidates: UnordSet<MonoItem<'tcx>>,
532) {
533 #[derive(Clone, PartialEq, Eq, Debug)]
537 enum MonoItemPlacement {
538 SingleCgu(Symbol),
539 MultipleCgus,
540 }
541
542 let mut mono_item_placements = UnordMap::default();
543 let single_codegen_unit = codegen_units.len() == 1;
544
545 if !single_codegen_unit {
546 for cgu in codegen_units.iter() {
547 for item in cgu.items().keys() {
548 match mono_item_placements.entry(*item) {
551 Entry::Occupied(e) => {
552 let placement = e.into_mut();
553 debug_assert!(match *placement {
554 MonoItemPlacement::SingleCgu(cgu_name) => cgu_name != cgu.name(),
555 MonoItemPlacement::MultipleCgus => true,
556 });
557 *placement = MonoItemPlacement::MultipleCgus;
558 }
559 Entry::Vacant(e) => {
560 e.insert(MonoItemPlacement::SingleCgu(cgu.name()));
561 }
562 }
563 }
564 }
565 }
566
567 for cgu in codegen_units {
570 let home_cgu = MonoItemPlacement::SingleCgu(cgu.name());
571
572 for (item, data) in cgu.items_mut() {
573 if !internalization_candidates.contains(item) {
574 continue;
576 }
577
578 if !single_codegen_unit {
579 debug_assert_eq!(mono_item_placements[item], home_cgu);
580
581 if cx
582 .usage_map
583 .get_user_items(*item)
584 .iter()
585 .filter_map(|user_item| {
586 mono_item_placements.get(user_item)
589 })
590 .any(|placement| *placement != home_cgu)
591 {
592 continue;
595 }
596 }
597
598 data.linkage = Linkage::Internal;
601 data.visibility = Visibility::Default;
602 }
603 }
604}
605
606fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx>]) {
607 assert!(!codegen_units.is_empty());
608
609 let dead_code_cgu = codegen_units
617 .iter_mut()
618 .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage == Linkage::External))
619 .min_by_key(|cgu| cgu.size_estimate());
620
621 let dead_code_cgu = if let Some(cgu) = dead_code_cgu { cgu } else { &mut codegen_units[0] };
624
625 dead_code_cgu.make_code_coverage_dead_code_cgu();
626}
627
628fn characteristic_def_id_of_mono_item<'tcx>(
629 tcx: TyCtxt<'tcx>,
630 mono_item: MonoItem<'tcx>,
631) -> Option<DefId> {
632 match mono_item {
633 MonoItem::Fn(instance) => {
634 let def_id = match instance.def {
635 ty::InstanceKind::Item(def) => def,
636 ty::InstanceKind::VTableShim(..)
637 | ty::InstanceKind::ReifyShim(..)
638 | ty::InstanceKind::FnPtrShim(..)
639 | ty::InstanceKind::ClosureOnceShim { .. }
640 | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
641 | ty::InstanceKind::Intrinsic(..)
642 | ty::InstanceKind::DropGlue(..)
643 | ty::InstanceKind::Virtual(..)
644 | ty::InstanceKind::CloneShim(..)
645 | ty::InstanceKind::ThreadLocalShim(..)
646 | ty::InstanceKind::FnPtrAddrShim(..)
647 | ty::InstanceKind::FutureDropPollShim(..)
648 | ty::InstanceKind::AsyncDropGlue(..)
649 | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None,
650 };
651
652 if tcx.trait_of_item(def_id).is_some() {
657 let self_ty = instance.args.type_at(0);
658 return characteristic_def_id_of_type(self_ty).or(Some(def_id));
660 }
661
662 if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
663 if tcx.sess.opts.incremental.is_some()
664 && tcx
665 .trait_id_of_impl(impl_def_id)
666 .is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Drop))
667 {
668 return None;
672 }
673
674 let impl_self_ty = tcx.instantiate_and_normalize_erasing_regions(
676 instance.args,
677 ty::TypingEnv::fully_monomorphized(),
678 tcx.type_of(impl_def_id),
679 );
680 if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
681 return Some(def_id);
682 }
683 }
684
685 Some(def_id)
686 }
687 MonoItem::Static(def_id) => Some(def_id),
688 MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.to_def_id()),
689 }
690}
691
692fn compute_codegen_unit_name(
693 tcx: TyCtxt<'_>,
694 name_builder: &mut CodegenUnitNameBuilder<'_>,
695 def_id: DefId,
696 volatile: bool,
697 cache: &mut CguNameCache,
698) -> Symbol {
699 let mut current_def_id = def_id;
701 let mut cgu_def_id = None;
702 loop {
704 if current_def_id.is_crate_root() {
705 if cgu_def_id.is_none() {
706 cgu_def_id = Some(def_id.krate.as_def_id());
708 }
709 break;
710 } else if tcx.def_kind(current_def_id) == DefKind::Mod {
711 if cgu_def_id.is_none() {
712 cgu_def_id = Some(current_def_id);
713 }
714 } else {
715 cgu_def_id = None;
719 }
720
721 current_def_id = tcx.parent(current_def_id);
722 }
723
724 let cgu_def_id = cgu_def_id.unwrap();
725
726 *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
727 let def_path = tcx.def_path(cgu_def_id);
728
729 let components = def_path.data.iter().map(|part| match part.data.name() {
730 DefPathDataName::Named(name) => name,
731 DefPathDataName::Anon { .. } => unreachable!(),
732 });
733
734 let volatile_suffix = volatile.then_some("volatile");
735
736 name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
737 })
738}
739
740fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
742 name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
743}
744
745fn mono_item_linkage_and_visibility<'tcx>(
746 tcx: TyCtxt<'tcx>,
747 mono_item: &MonoItem<'tcx>,
748 can_be_internalized: &mut bool,
749 can_export_generics: bool,
750 always_export_generics: bool,
751) -> (Linkage, Visibility) {
752 if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
753 return (explicit_linkage, Visibility::Default);
754 }
755 let vis = mono_item_visibility(
756 tcx,
757 mono_item,
758 can_be_internalized,
759 can_export_generics,
760 always_export_generics,
761 );
762 (Linkage::External, vis)
763}
764
765type CguNameCache = UnordMap<(DefId, bool), Symbol>;
766
767fn static_visibility<'tcx>(
768 tcx: TyCtxt<'tcx>,
769 can_be_internalized: &mut bool,
770 def_id: DefId,
771) -> Visibility {
772 if tcx.is_reachable_non_generic(def_id) {
773 *can_be_internalized = false;
774 default_visibility(tcx, def_id, false)
775 } else {
776 Visibility::Hidden
777 }
778}
779
780fn mono_item_visibility<'tcx>(
781 tcx: TyCtxt<'tcx>,
782 mono_item: &MonoItem<'tcx>,
783 can_be_internalized: &mut bool,
784 can_export_generics: bool,
785 always_export_generics: bool,
786) -> Visibility {
787 let instance = match mono_item {
788 MonoItem::Fn(instance) => instance,
790
791 MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id),
793 MonoItem::GlobalAsm(item_id) => {
794 return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id());
795 }
796 };
797
798 let def_id = match instance.def {
799 InstanceKind::Item(def_id)
800 | InstanceKind::DropGlue(def_id, Some(_))
801 | InstanceKind::FutureDropPollShim(def_id, _, _)
802 | InstanceKind::AsyncDropGlue(def_id, _)
803 | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id,
804
805 InstanceKind::ThreadLocalShim(def_id) => {
807 return static_visibility(tcx, can_be_internalized, def_id);
808 }
809
810 InstanceKind::VTableShim(..)
812 | InstanceKind::ReifyShim(..)
813 | InstanceKind::FnPtrShim(..)
814 | InstanceKind::Virtual(..)
815 | InstanceKind::Intrinsic(..)
816 | InstanceKind::ClosureOnceShim { .. }
817 | InstanceKind::ConstructCoroutineInClosureShim { .. }
818 | InstanceKind::DropGlue(..)
819 | InstanceKind::CloneShim(..)
820 | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden,
821 };
822
823 if tcx.is_lang_item(def_id, LangItem::Start) {
837 *can_be_internalized = false;
838 return Visibility::Hidden;
839 }
840
841 let is_generic = instance.args.non_erasable_generics().next().is_some();
842
843 let Some(def_id) = def_id.as_local() else {
845 return if is_generic
846 && (always_export_generics
847 || (can_export_generics
848 && tcx.codegen_fn_attrs(def_id).inline
849 == rustc_attr_parsing::InlineAttr::Never))
850 {
851 *can_be_internalized = false;
854 default_visibility(tcx, def_id, true)
855 } else {
856 Visibility::Hidden
857 };
858 };
859
860 if is_generic {
861 if always_export_generics
862 || (can_export_generics
863 && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never)
864 {
865 if tcx.is_unreachable_local_definition(def_id) {
866 Visibility::Hidden
868 } else {
869 *can_be_internalized = false;
871 default_visibility(tcx, def_id.to_def_id(), true)
872 }
873 } else {
874 Visibility::Hidden
877 }
878 } else {
879 if tcx.is_reachable_non_generic(def_id.to_def_id()) {
883 *can_be_internalized = false;
884 debug_assert!(!is_generic);
885 return default_visibility(tcx, def_id.to_def_id(), false);
886 }
887
888 let attrs = tcx.codegen_fn_attrs(def_id);
923 if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
924 *can_be_internalized = false;
925 }
926
927 Visibility::Hidden
928 }
929}
930
931fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
932 if tcx.sess.default_visibility() == SymbolVisibility::Interposable {
934 return Visibility::Default;
935 }
936
937 let export_level = if is_generic {
938 SymbolExportLevel::Rust
940 } else {
941 match tcx.reachable_non_generics(id.krate).get(&id) {
942 Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C,
943 _ => SymbolExportLevel::Rust,
944 }
945 };
946
947 match export_level {
948 SymbolExportLevel::C => Visibility::Default,
951
952 SymbolExportLevel::Rust => tcx.sess.default_visibility().into(),
954 }
955}
956
957fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) {
958 let dump = move || {
959 use std::fmt::Write;
960
961 let mut num_cgus = 0;
962 let mut all_cgu_sizes = Vec::new();
963
964 let mut inlined_items = UnordSet::default();
970
971 let mut root_items = 0;
972 let mut unique_inlined_items = 0;
973 let mut placed_inlined_items = 0;
974
975 let mut root_size = 0;
976 let mut unique_inlined_size = 0;
977 let mut placed_inlined_size = 0;
978
979 for cgu in cgus.iter() {
980 num_cgus += 1;
981 all_cgu_sizes.push(cgu.size_estimate());
982
983 for (item, data) in cgu.items() {
984 if !data.inlined {
985 root_items += 1;
986 root_size += data.size_estimate;
987 } else {
988 if inlined_items.insert(item) {
989 unique_inlined_items += 1;
990 unique_inlined_size += data.size_estimate;
991 }
992 placed_inlined_items += 1;
993 placed_inlined_size += data.size_estimate;
994 }
995 }
996 }
997
998 all_cgu_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
999
1000 let unique_items = root_items + unique_inlined_items;
1001 let placed_items = root_items + placed_inlined_items;
1002 let items_ratio = placed_items as f64 / unique_items as f64;
1003
1004 let unique_size = root_size + unique_inlined_size;
1005 let placed_size = root_size + placed_inlined_size;
1006 let size_ratio = placed_size as f64 / unique_size as f64;
1007
1008 let mean_cgu_size = placed_size as f64 / num_cgus as f64;
1009
1010 assert_eq!(placed_size, all_cgu_sizes.iter().sum::<usize>());
1011
1012 let s = &mut String::new();
1013 let _ = writeln!(s, "{label}");
1014 let _ = writeln!(
1015 s,
1016 "- unique items: {unique_items} ({root_items} root + {unique_inlined_items} inlined), \
1017 unique size: {unique_size} ({root_size} root + {unique_inlined_size} inlined)\n\
1018 - placed items: {placed_items} ({root_items} root + {placed_inlined_items} inlined), \
1019 placed size: {placed_size} ({root_size} root + {placed_inlined_size} inlined)\n\
1020 - placed/unique items ratio: {items_ratio:.2}, \
1021 placed/unique size ratio: {size_ratio:.2}\n\
1022 - CGUs: {num_cgus}, mean size: {mean_cgu_size:.1}, sizes: {}",
1023 list(&all_cgu_sizes),
1024 );
1025 let _ = writeln!(s);
1026
1027 for (i, cgu) in cgus.iter().enumerate() {
1028 let name = cgu.name();
1029 let size = cgu.size_estimate();
1030 let num_items = cgu.items().len();
1031 let mean_size = size as f64 / num_items as f64;
1032
1033 let mut placed_item_sizes: Vec<_> =
1034 cgu.items().values().map(|data| data.size_estimate).collect();
1035 placed_item_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
1036 let sizes = list(&placed_item_sizes);
1037
1038 let _ = writeln!(s, "- CGU[{i}]");
1039 let _ = writeln!(s, " - {name}, size: {size}");
1040 let _ =
1041 writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",);
1042
1043 for (item, data) in cgu.items_in_deterministic_order(tcx) {
1044 let linkage = data.linkage;
1045 let symbol_name = item.symbol_name(tcx).name;
1046 let symbol_hash_start = symbol_name.rfind('h');
1047 let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]);
1048 let kind = if !data.inlined { "root" } else { "inlined" };
1049 let size = data.size_estimate;
1050 let _ = with_no_trimmed_paths!(writeln!(
1051 s,
1052 " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})"
1053 ));
1054 }
1055
1056 let _ = writeln!(s);
1057 }
1058
1059 return std::mem::take(s);
1060
1061 fn list(ns: &[usize]) -> String {
1064 let mut v = Vec::new();
1065 if ns.is_empty() {
1066 return "[]".to_string();
1067 }
1068
1069 let mut elem = |curr, curr_count| {
1070 if curr_count == 1 {
1071 v.push(format!("{curr}"));
1072 } else {
1073 v.push(format!("{curr} (x{curr_count})"));
1074 }
1075 };
1076
1077 let mut curr = ns[0];
1078 let mut curr_count = 1;
1079
1080 for &n in &ns[1..] {
1081 if n != curr {
1082 elem(curr, curr_count);
1083 curr = n;
1084 curr_count = 1;
1085 } else {
1086 curr_count += 1;
1087 }
1088 }
1089 elem(curr, curr_count);
1090
1091 format!("[{}]", v.join(", "))
1092 }
1093 };
1094
1095 debug!("{}", dump());
1096}
1097
1098#[inline(never)] fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
1100where
1101 I: Iterator<Item = &'a MonoItem<'tcx>>,
1102 'tcx: 'a,
1103{
1104 let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
1105
1106 let mut symbols: Vec<_> =
1107 mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
1108
1109 symbols.sort_by_key(|sym| sym.1);
1110
1111 for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
1112 if sym1 == sym2 {
1113 let span1 = mono_item1.local_span(tcx);
1114 let span2 = mono_item2.local_span(tcx);
1115
1116 let span = match (span1, span2) {
1118 (Some(span1), Some(span2)) => {
1119 Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
1120 }
1121 (span1, span2) => span1.or(span2),
1122 };
1123
1124 tcx.dcx().emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
1125 }
1126 }
1127}
1128
1129fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> {
1130 let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items {
1131 Some(ref s) => {
1132 let mode = s.to_lowercase();
1133 let mode = mode.trim();
1134 if mode == "eager" {
1135 MonoItemCollectionStrategy::Eager
1136 } else {
1137 if mode != "lazy" {
1138 tcx.dcx().emit_warn(UnknownCguCollectionMode { mode });
1139 }
1140
1141 MonoItemCollectionStrategy::Lazy
1142 }
1143 }
1144 None => {
1145 if tcx.sess.link_dead_code() {
1146 MonoItemCollectionStrategy::Eager
1147 } else {
1148 MonoItemCollectionStrategy::Lazy
1149 }
1150 }
1151 };
1152
1153 let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy);
1154
1155 tcx.dcx().abort_if_errors();
1159
1160 let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
1161 sync::join(
1162 || {
1163 let mut codegen_units = partition(tcx, items.iter().copied(), &usage_map);
1164 codegen_units[0].make_primary();
1165 &*tcx.arena.alloc_from_iter(codegen_units)
1166 },
1167 || assert_symbols_are_distinct(tcx, items.iter()),
1168 )
1169 });
1170
1171 if tcx.prof.enabled() {
1172 for cgu in codegen_units {
1174 tcx.prof.artifact_size(
1175 "codegen_unit_size_estimate",
1176 cgu.name().as_str(),
1177 cgu.size_estimate() as u64,
1178 );
1179 }
1180 }
1181
1182 #[cfg(not(llvm_enzyme))]
1183 let autodiff_mono_items: Vec<_> = vec![];
1184 #[cfg(llvm_enzyme)]
1185 let mut autodiff_mono_items: Vec<_> = vec![];
1186 let mono_items: DefIdSet = items
1187 .iter()
1188 .filter_map(|mono_item| match *mono_item {
1189 MonoItem::Fn(ref instance) => {
1190 #[cfg(llvm_enzyme)]
1191 autodiff_mono_items.push((mono_item, instance));
1192 Some(instance.def_id())
1193 }
1194 MonoItem::Static(def_id) => Some(def_id),
1195 _ => None,
1196 })
1197 .collect();
1198
1199 let autodiff_items =
1200 autodiff::find_autodiff_source_functions(tcx, &usage_map, autodiff_mono_items);
1201 let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items);
1202
1203 if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats {
1205 if let Err(err) =
1206 dump_mono_items_stats(tcx, codegen_units, path, tcx.crate_name(LOCAL_CRATE))
1207 {
1208 tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() });
1209 }
1210 }
1211
1212 if tcx.sess.opts.unstable_opts.print_mono_items.is_some() {
1213 let mut item_to_cgus: UnordMap<_, Vec<_>> = Default::default();
1214
1215 for cgu in codegen_units {
1216 for (&mono_item, &data) in cgu.items() {
1217 item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage));
1218 }
1219 }
1220
1221 let mut item_keys: Vec<_> = items
1222 .iter()
1223 .map(|i| {
1224 let mut output = with_no_trimmed_paths!(i.to_string());
1225 output.push_str(" @@");
1226 let mut empty = Vec::new();
1227 let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
1228 cgus.sort_by_key(|(name, _)| *name);
1229 cgus.dedup();
1230 for &(ref cgu_name, linkage) in cgus.iter() {
1231 output.push(' ');
1232 output.push_str(cgu_name.as_str());
1233
1234 let linkage_abbrev = match linkage {
1235 Linkage::External => "External",
1236 Linkage::AvailableExternally => "Available",
1237 Linkage::LinkOnceAny => "OnceAny",
1238 Linkage::LinkOnceODR => "OnceODR",
1239 Linkage::WeakAny => "WeakAny",
1240 Linkage::WeakODR => "WeakODR",
1241 Linkage::Internal => "Internal",
1242 Linkage::ExternalWeak => "ExternalWeak",
1243 Linkage::Common => "Common",
1244 };
1245
1246 output.push('[');
1247 output.push_str(linkage_abbrev);
1248 output.push(']');
1249 }
1250 output
1251 })
1252 .collect();
1253
1254 item_keys.sort();
1255
1256 for item in item_keys {
1257 println!("MONO_ITEM {item}");
1258 }
1259 }
1260
1261 MonoItemPartitions {
1262 all_mono_items: tcx.arena.alloc(mono_items),
1263 codegen_units,
1264 autodiff_items,
1265 }
1266}
1267
1268fn dump_mono_items_stats<'tcx>(
1271 tcx: TyCtxt<'tcx>,
1272 codegen_units: &[CodegenUnit<'tcx>],
1273 output_directory: &Option<PathBuf>,
1274 crate_name: Symbol,
1275) -> Result<(), Box<dyn std::error::Error>> {
1276 let output_directory = if let Some(directory) = output_directory {
1277 fs::create_dir_all(directory)?;
1278 directory
1279 } else {
1280 Path::new(".")
1281 };
1282
1283 let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
1284 let ext = format.extension();
1285 let filename = format!("{crate_name}.mono_items.{ext}");
1286 let output_path = output_directory.join(&filename);
1287 let mut file = File::create_buffered(&output_path)?;
1288
1289 let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default();
1291 for cgu in codegen_units {
1292 cgu.items()
1293 .keys()
1294 .filter(|mono_item| mono_item.is_user_defined())
1296 .for_each(|mono_item| {
1297 items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
1298 });
1299 }
1300
1301 #[derive(serde::Serialize)]
1302 struct MonoItem {
1303 name: String,
1304 instantiation_count: usize,
1305 size_estimate: usize,
1306 total_estimate: usize,
1307 }
1308
1309 let mut stats: Vec<_> = items_per_def_id
1311 .into_iter()
1312 .map(|(def_id, items)| {
1313 let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
1314 let instantiation_count = items.len();
1315 let size_estimate = items[0].size_estimate(tcx);
1316 let total_estimate = instantiation_count * size_estimate;
1317 MonoItem { name, instantiation_count, size_estimate, total_estimate }
1318 })
1319 .collect();
1320 stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
1321
1322 if !stats.is_empty() {
1323 match format {
1324 DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
1325 DumpMonoStatsFormat::Markdown => {
1326 writeln!(
1327 file,
1328 "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
1329 )?;
1330 writeln!(file, "| --- | ---: | ---: | ---: |")?;
1331
1332 for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
1333 writeln!(
1334 file,
1335 "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
1336 )?;
1337 }
1338 }
1339 }
1340 }
1341
1342 Ok(())
1343}
1344
1345pub(crate) fn provide(providers: &mut Providers) {
1346 providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
1347
1348 providers.is_codegened_item =
1349 |tcx, def_id| tcx.collect_and_partition_mono_items(()).all_mono_items.contains(&def_id);
1350
1351 providers.codegen_unit = |tcx, name| {
1352 tcx.collect_and_partition_mono_items(())
1353 .codegen_units
1354 .iter()
1355 .find(|cgu| cgu.name() == name)
1356 .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}"))
1357 };
1358
1359 providers.size_estimate = |tcx, instance| {
1360 match instance.def {
1361 InstanceKind::Item(..)
1364 | InstanceKind::DropGlue(..)
1365 | InstanceKind::AsyncDropGlueCtorShim(..) => {
1366 let mir = tcx.instance_mir(instance.def);
1367 mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum()
1368 }
1369 _ => 1,
1371 }
1372 };
1373
1374 collector::provide(providers);
1375}