diff options
| author | Maybe Waffle <waffle.lapkin@gmail.com> | 2023-05-17 12:28:04 +0000 |
|---|---|---|
| committer | Maybe Waffle <waffle.lapkin@gmail.com> | 2023-06-12 15:58:35 +0000 |
| commit | f2545fb22554eb1b04528d490c681e583fdc31d0 (patch) | |
| tree | ea8549a633134a21e24d3d44a881a025d0fa229a /compiler/rustc_interface/src | |
| parent | cb882fa998571e8a7ec1c06bb5d9dd9bc3423629 (diff) | |
| download | rust-f2545fb22554eb1b04528d490c681e583fdc31d0.tar.gz rust-f2545fb22554eb1b04528d490c681e583fdc31d0.zip | |
Collect VTable stats & add `-Zprint-vtable-sizes`
Diffstat (limited to 'compiler/rustc_interface/src')
| -rw-r--r-- | compiler/rustc_interface/src/interface.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/passes.rs | 94 |
2 files changed, 95 insertions, 0 deletions
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2edc72ba72e..5e3ea71f0e7 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -333,6 +333,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se }; let prof = compiler.sess.prof.clone(); + prof.generic_activity("drop_compiler").run(move || drop(compiler)); r }) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 83a74742f5b..be6bd913790 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -24,6 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::Resolver; +use rustc_session::code_stats::VTableSizeInfo; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::{MetadataLoader, Untracked}; use rustc_session::output::filename_for_input; @@ -866,6 +867,99 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { sess.time("check_lint_expectations", || tcx.check_expectations(None)); }); + if sess.opts.unstable_opts.print_vtable_sizes { + let traits = tcx.traits(LOCAL_CRATE); + + for &tr in traits { + if !tcx.check_is_object_safe(tr) { + continue; + } + + let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr)); + + let mut first_dsa = true; + + // Number of vtable entries, if we didn't have upcasting + let mut unupcasted_cost = 0; + // Number of vtable entries needed solely for upcasting + let mut upcast_cost = 0; + + let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr)); + + // A slightly edited version of the code in `rustc_trait_selection::traits::vtable::vtable_entries`, + // that works without self type and just counts number of entries. + // + // Note that this is technically wrong, for traits which have associated types in supertraits: + // + // trait A: AsRef<Self::T> + AsRef<()> { type T; } + // + // Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>` and + // `AsRef<()>` are the same trait, thus we assume that those are different, and potentially + // over-estimate how many vtable entries there are. + // + // Similarly this is wrong for traits that have methods with possibly-impossible bounds. + // For example: + // + // trait B<T> { fn f(&self) where T: Copy; } + // + // Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3. + // However, since we don't know `T`, we can't know if `T: Copy` holds or not, + // thus we lean on the bigger side and say it has 4 entries. + traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| { + match segment { + traits::vtable::VtblSegment::MetadataDSA => { + // If this is the first dsa, it would be included either way, + // otherwise it's needed for upcasting + if std::mem::take(&mut first_dsa) { + unupcasted_cost += 3; + } else { + upcast_cost += 3; + } + } + + traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let existential_trait_ref = trait_ref.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }); + + // Lookup the shape of vtable for the trait. + let own_existential_entries = + tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); + + let own_entries = own_existential_entries.iter().copied().map(|_def_id| { + // The original code here ignores the method if its predicates are impossible. + // We can't really do that as, for example, all not trivial bounds on generic + // parameters are impossible (since we don't know the parameters...), + // see the comment above. + + 1 + }); + + unupcasted_cost += own_entries.sum::<usize>(); + + if emit_vptr { + upcast_cost += 1; + } + } + } + + std::ops::ControlFlow::Continue::<std::convert::Infallible>(()) + }); + + sess.code_stats.record_vtable_size( + tr, + &name, + VTableSizeInfo { + trait_name: name.clone(), + size_words_without_upcasting: unupcasted_cost, + size_words_with_upcasting: unupcasted_cost + upcast_cost, + difference_words: upcast_cost, + difference_percent: upcast_cost as f64 / unupcasted_cost as f64 * 100., + }, + ) + } + } + Ok(()) } |
