diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index f3677b80819..26dafed7019 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -909,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "keep the AST after lowering it to HIR"), show_span: Option = (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 = (None, parse_opt_string, [UNTRACKED], "print the result of the translation item collection pass"), mir_opt_level: Option = (None, parse_opt_uint, [TRACKED], diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9577a25b3f8..128e4d878a8 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -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, + next_node_id: Cell, } +#[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>, +} + +#[derive(PartialEq, Eq, Debug)] +pub struct CodeStats { + pub type_sizes: Vec, +} + +impl CodeStats { + fn new() -> Self { + CodeStats { type_sizes: Vec::new() } + } + + pub fn record_type_size(&mut self, + type_desc: S, + overall_size: u64, + variant_sizes: Vec) { + 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, @@ -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); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 5cbb8f93fc9..a91525c6b2d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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")); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index ae255f70fb7..eed4763c17d 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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; diff --git a/src/librustc_mir/transform/print_type_sizes.rs b/src/librustc_mir/transform/print_type_sizes.rs new file mode 100644 index 00000000000..617a5ac78df --- /dev/null +++ b/src/librustc_mir/transform/print_type_sizes.rs @@ -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 or the MIT license +// , 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>, +} + +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)); + } + } + }); + } +}