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