Add `-Z print-type-sizes`, a tool for digging into how variants are laid out.
This commit is contained in:
parent
696fab844a
commit
9383fcf07f
|
@ -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],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue