Add `-Z print-type-sizes`, a tool for digging into how variants are laid out.

This commit is contained in:
Felix S. Klock II 2016-11-14 17:46:20 +01:00
parent 696fab844a
commit 9383fcf07f
5 changed files with 238 additions and 1 deletions

View File

@ -909,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"keep the AST after lowering it to HIR"),
show_span: Option<String> = (None, parse_opt_string, [TRACKED],
"show spans for compiler debugging (expr|pat|ty)"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered"),
print_trans_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
"print the result of the translation item collection pass"),
mir_opt_level: Option<usize> = (None, parse_opt_uint, [TRACKED],

View File

@ -112,9 +112,80 @@ pub struct Session {
/// Some measurements that are being gathered during compilation.
pub perf_stats: PerfStats,
/// Data about code being compiled, gathered during compilation.
pub code_stats: RefCell<CodeStats>,
next_node_id: Cell<ast::NodeId>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum VariantSize {
Exact(u64),
Min(u64),
}
#[derive(PartialEq, Eq, Debug)]
pub struct TypeSizeInfo {
pub type_description: String,
pub overall_size: u64,
pub variant_sizes: Option<Vec<VariantSize>>,
}
#[derive(PartialEq, Eq, Debug)]
pub struct CodeStats {
pub type_sizes: Vec<TypeSizeInfo>,
}
impl CodeStats {
fn new() -> Self {
CodeStats { type_sizes: Vec::new() }
}
pub fn record_type_size<S: ToString>(&mut self,
type_desc: S,
overall_size: u64,
variant_sizes: Vec<VariantSize>) {
let sizes = if variant_sizes.len() == 0 { None } else { Some(variant_sizes) };
let info = TypeSizeInfo {
type_description: type_desc.to_string(),
overall_size: overall_size,
variant_sizes: sizes,
};
if !self.type_sizes.contains(&info) {
self.type_sizes.push(info);
}
}
pub fn sort_by_type_description(&mut self) {
self.type_sizes.sort_by(|info1, info2| {
info1.type_description.cmp(&info2.type_description)
});
}
pub fn sort_by_overall_size(&mut self) {
self.type_sizes.sort_by(|info1, info2| {
// (reversing cmp order to get large-to-small ordering)
info2.overall_size.cmp(&info1.overall_size)
});
}
pub fn print_type_sizes(&self) {
for info in &self.type_sizes {
println!("print-type-size t: `{}` overall bytes: {}",
info.type_description, info.overall_size);
if let Some(ref variant_sizes) = info.variant_sizes {
for (i, variant_size) in variant_sizes.iter().enumerate() {
let (kind, s) = match *variant_size {
VariantSize::Exact(s) => { ("exact", s) }
VariantSize::Min(s) => { (" min", s) }
};
println!("print-type-size variant[{}] {} bytes: {}", i, kind, s);
}
}
}
}
}
pub struct PerfStats {
// The accumulated time needed for computing the SVH of the crate
pub svh_time: Cell<Duration>,
@ -624,7 +695,8 @@ pub fn build_session_(sopts: config::Options,
incr_comp_hashes_count: Cell::new(0),
incr_comp_bytes_hashed: Cell::new(0),
symbol_hash_time: Cell::new(Duration::from_secs(0)),
}
},
code_stats: RefCell::new(CodeStats::new()),
};
init_llvm(&sess);

View File

@ -215,6 +215,13 @@ pub fn compile_input(sess: &Session,
})??
};
if sess.opts.debugging_opts.print_type_sizes {
// (these are stable sorts)
sess.code_stats.borrow_mut().sort_by_type_description();
sess.code_stats.borrow_mut().sort_by_overall_size();
sess.code_stats.borrow().print_type_sizes();
}
let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);
controller_entry_point!(after_llvm,
@ -1008,6 +1015,9 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
time(time_passes, "MIR optimisations", || {
let mut passes = ::rustc::mir::transform::Passes::new();
passes.push_hook(box mir::transform::dump_mir::DumpMir);
if tcx.sess.opts.debugging_opts.print_type_sizes {
passes.push_pass(box mir::transform::print_type_sizes::GatherTypeSizesMir::new());
}
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("no-landing-pads"));

View File

@ -13,6 +13,7 @@ pub mod simplify;
pub mod erase_regions;
pub mod no_landing_pads;
pub mod type_check;
pub mod print_type_sizes;
pub mod add_call_guards;
pub mod promote_consts;
pub mod qualify_consts;

View File

@ -0,0 +1,152 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! This pass implements instrumentation to gather the layout of every type.
use rustc::session::{VariantSize};
use rustc::traits::{Reveal};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::fold::{TypeFoldable};
use rustc::ty::layout::{Layout};
use rustc::mir::{Mir};
use rustc::mir::transform::{MirPass, MirPassHook, MirSource, Pass};
use rustc::mir::visit::Visitor;
use std::collections::HashSet;
pub struct GatherTypeSizesMir {
_hidden: (),
}
impl GatherTypeSizesMir {
pub fn new() -> Self {
GatherTypeSizesMir { _hidden: () }
}
}
impl Pass for GatherTypeSizesMir {
}
impl<'tcx> MirPassHook<'tcx> for GatherTypeSizesMir {
fn on_mir_pass<'a>(&mut self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource,
mir: &Mir<'tcx>,
_pass: &Pass,
_is_after: bool) {
debug!("on_mir_pass: {}", tcx.node_path_str(src.item_id()));
self.go(tcx, mir);
}
}
impl<'tcx> MirPass<'tcx> for GatherTypeSizesMir {
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource, mir: &mut Mir<'tcx>) {
debug!("run_pass: {}", tcx.node_path_str(src.item_id()));
self.go(tcx, mir);
}
}
impl GatherTypeSizesMir {
fn go<'a, 'tcx>(&mut self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>) {
if tcx.sess.err_count() > 0 {
// compiling a broken program can obviously result in a
// broken MIR, so do not bother trying to process it.
return;
}
let mut visitor = TypeVisitor {
tcx: tcx,
seen: HashSet::new(),
};
visitor.visit_mir(mir);
}
}
struct TypeVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
seen: HashSet<Ty<'tcx>>,
}
impl<'a, 'tcx: 'a> Visitor<'tcx> for TypeVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: &Ty<'tcx>) {
debug!("TypeVisitor::visit_ty ty=`{:?}`", ty);
match ty.sty {
ty::TyAdt(..) |
ty::TyClosure(..) => {} // fall through
_ => {
debug!("print-type-size t: `{:?}` skip non-nominal", ty);
return;
}
}
if ty.has_param_types() {
debug!("print-type-size t: `{:?}` skip has param types", ty);
return;
}
if ty.has_projection_types() {
debug!("print-type-size t: `{:?}` skip has projections", ty);
return;
}
if self.seen.contains(ty) {
return;
}
self.seen.insert(ty);
let reveal = Reveal::All;
// let reveal = Reveal::NotSpecializable;
self.tcx.infer_ctxt(None, None, reveal).enter(|infcx| {
match ty.layout(&infcx) {
Ok(layout) => {
let type_desc = format!("{:?}", ty);
let overall_size = layout.size(&Default::default());
let variant_sizes: Vec<_> = match *layout {
Layout::General { ref variants, .. } => {
variants.iter()
.map(|v| if v.sized {
VariantSize::Exact(v.min_size.bytes())
} else {
VariantSize::Min(v.min_size.bytes())
})
.collect()
}
Layout::UntaggedUnion { variants: _ } => {
/* layout does not currently store info about each variant... */
Vec::new()
}
// RawNullablePointer/StructWrappedNullablePointer
// don't provide any interesting size info
// beyond what we already reported for their
// total size.
_ => {
Vec::new()
}
};
self.tcx.sess.code_stats.borrow_mut()
.record_type_size(type_desc,
overall_size.bytes(),
variant_sizes);
}
Err(err) => {
self.tcx.sess.warn(&format!("print-type-size t: `{:?}` err: {:?}", ty, err));
}
}
});
}
}