Skip to content

Commit

Permalink
Auto merge of rust-lang#112400 - WaffleLapkin:vtable_stats, r=compile…
Browse files Browse the repository at this point in the history
…r-errors

Collect VTable stats & add `-Zprint-vtable-sizes`

This is a bit hacky/buggy, but I'm not entirely sure how to fix it, so I want to ask reviewers for help...

To try this, use either of those:
- `cargo clean && RUSTFLAGS="-Zprint-vtable-sizes" cargo +toolchain b`
- `cargo clean && cargo rustc +toolchain -Zprint-vtable-sizes`
- `rustc +toolchain -Zprint-vtable-sizes ./file.rs`
  • Loading branch information
bors committed Jun 14, 2023
2 parents 3ed2a10 + af4631a commit 7b0eac4
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 5 deletions.
7 changes: 7 additions & 0 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,13 @@ fn run_compiler(
sess.code_stats.print_type_sizes();
}

if sess.opts.unstable_opts.print_vtable_sizes {
let crate_name =
compiler.session().opts.crate_name.as_deref().unwrap_or("<UNKNOWN_CRATE>");

sess.code_stats.print_vtable_sizes(crate_name);
}

let linker = queries.linker()?;
Ok(Some(linker))
})?;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down
87 changes: 87 additions & 0 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -866,6 +867,92 @@ 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 entries_ignoring_upcasting = 0;
// Number of vtable entries needed solely for upcasting
let mut entries_for_upcasting = 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) {
entries_ignoring_upcasting += 3;
} else {
entries_for_upcasting += 3;
}
}

traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
// Lookup the shape of vtable for the trait.
let own_existential_entries =
tcx.own_existential_vtable_entries(trait_ref.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.
entries_ignoring_upcasting += own_existential_entries.len();

if emit_vptr {
entries_for_upcasting += 1;
}
}
}

std::ops::ControlFlow::Continue::<std::convert::Infallible>(())
});

sess.code_stats.record_vtable_size(
tr,
&name,
VTableSizeInfo {
trait_name: name.clone(),
entries: entries_ignoring_upcasting + entries_for_upcasting,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent: entries_for_upcasting as f64
/ entries_ignoring_upcasting as f64
* 100.,
},
)
}
}

Ok(())
}

Expand Down
60 changes: 59 additions & 1 deletion compiler/rustc_session/src/code_stats.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lock;
use rustc_span::def_id::DefId;
use rustc_span::Symbol;
use rustc_target::abi::{Align, Size};
use std::cmp;
Expand Down Expand Up @@ -65,9 +66,29 @@ pub struct TypeSizeInfo {
pub variants: Vec<VariantInfo>,
}

pub struct VTableSizeInfo {
pub trait_name: String,

/// Number of entries in a vtable with the current algorithm
/// (i.e. with upcasting).
pub entries: usize,

/// Number of entries in a vtable, as-if we did not have trait upcasting.
pub entries_ignoring_upcasting: usize,

/// Number of entries in a vtable needed solely for upcasting
/// (i.e. `entries - entries_ignoring_upcasting`).
pub entries_for_upcasting: usize,

/// Cost of having upcasting in % relative to the number of entries without
/// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`).
pub upcasting_cost_percent: f64,
}

#[derive(Default)]
pub struct CodeStats {
type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
vtable_sizes: Lock<FxHashMap<DefId, VTableSizeInfo>>,
}

impl CodeStats {
Expand Down Expand Up @@ -101,6 +122,14 @@ impl CodeStats {
self.type_sizes.borrow_mut().insert(info);
}

pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) {
let prev = self.vtable_sizes.lock().insert(trait_did, info);
assert!(
prev.is_none(),
"size of vtable for `{trait_name}` ({trait_did:?}) is already recorded"
);
}

pub fn print_type_sizes(&self) {
let type_sizes = self.type_sizes.borrow();
let mut sorted: Vec<_> = type_sizes.iter().collect();
Expand Down Expand Up @@ -196,4 +225,33 @@ impl CodeStats {
}
}
}

pub fn print_vtable_sizes(&self, crate_name: &str) {
let mut infos = std::mem::take(&mut *self.vtable_sizes.lock())
.into_iter()
.map(|(_did, stats)| stats)
.collect::<Vec<_>>();

// Primary sort: cost % in reverse order (from largest to smallest)
// Secondary sort: trait_name
infos.sort_by(|a, b| {
a.upcasting_cost_percent
.total_cmp(&b.upcasting_cost_percent)
.reverse()
.then_with(|| a.trait_name.cmp(&b.trait_name))
});

for VTableSizeInfo {
trait_name,
entries,
entries_ignoring_upcasting,
entries_for_upcasting,
upcasting_cost_percent,
} in infos
{
println!(
r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"#
);
}
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pas
pub use rustc_lint_defs as lint;
pub mod parse;

mod code_stats;
pub mod code_stats;
#[macro_use]
pub mod config;
pub mod cstore;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,8 @@ options! {
"print the result of the monomorphization collection pass"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered (default: no)"),
print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED],
"print size comparison between old and new vtable layouts (default: no)"),
proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show backtraces for panics during proc-macro execution (default: no)"),
proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod structural_match;
mod structural_normalize;
#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod util;
mod vtable;
pub mod vtable;
pub mod wf;

use crate::infer::outlives::env::OutlivesEnvironment;
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ use std::fmt::Debug;
use std::ops::ControlFlow;

#[derive(Clone, Debug)]
pub(super) enum VtblSegment<'tcx> {
pub enum VtblSegment<'tcx> {
MetadataDSA,
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
}

/// Prepare the segments for a vtable
pub(super) fn prepare_vtable_segments<'tcx, T>(
pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
Expand Down
61 changes: 61 additions & 0 deletions tests/ui/traits/object/print_vtable_sizes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// check-pass
// compile-flags: -Z print-vtable-sizes
#![crate_type = "lib"]

trait A<T: help::V>: AsRef<[T::V]> + AsMut<[T::V]> {}

trait B<T>: AsRef<T> + AsRef<T> + AsRef<T> + AsRef<T> {}

trait C {
fn x() {} // not object safe, shouldn't be reported
}

// This ideally should not have any upcasting cost,
// but currently does due to a bug
trait D: Send + Sync + help::MarkerWithSuper {}

// This can't have no cost without reordering,
// because `Super::f`.
trait E: help::MarkerWithSuper + Send + Sync {}

trait F {
fn a(&self);
fn b(&self);
fn c(&self);

fn d() -> Self
where
Self: Sized;
}

trait G: AsRef<u8> + AsRef<u16> + help::MarkerWithSuper {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
fn e(&self);

fn f() -> Self
where
Self: Sized;
}

// Traits with the same name
const _: () = {
trait S {}
};
const _: () = {
trait S {}
};

mod help {
pub trait V {
type V;
}

pub trait MarkerWithSuper: Super {}

pub trait Super {
fn f(&self);
}
}
11 changes: 11 additions & 0 deletions tests/ui/traits/object/print_vtable_sizes.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "D", "entries": "7", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "3", "upcasting_cost_percent": "75" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "E", "entries": "6", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "2", "upcasting_cost_percent": "50" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "G", "entries": "14", "entries_ignoring_upcasting": "11", "entries_for_upcasting": "3", "upcasting_cost_percent": "27.27272727272727" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "A", "entries": "6", "entries_ignoring_upcasting": "5", "entries_for_upcasting": "1", "upcasting_cost_percent": "20" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "B", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "F", "entries": "6", "entries_ignoring_upcasting": "6", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "_::S", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "help::MarkerWithSuper", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "help::Super", "entries": "4", "entries_ignoring_upcasting": "4", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }
print-vtable-sizes { "crate_name": "<UNKNOWN_CRATE>", "trait_name": "help::V", "entries": "3", "entries_ignoring_upcasting": "3", "entries_for_upcasting": "0", "upcasting_cost_percent": "0" }

0 comments on commit 7b0eac4

Please sign in to comment.