about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2016-11-14 17:46:20 +0100
committerFelix S. Klock II <pnkfelix@pnkfx.org>2016-11-24 10:30:18 +0100
commit9383fcf07f85d8e8b91a6150cf4ca252b37a5383 (patch)
tree6bfa0dc5689d5fcdd2cea55d1267957121f7e008
parent696fab844aef55eb4bcbeb470c01ce7d301c51ed (diff)
downloadrust-9383fcf07f85d8e8b91a6150cf4ca252b37a5383.tar.gz
rust-9383fcf07f85d8e8b91a6150cf4ca252b37a5383.zip
Add `-Z print-type-sizes`, a tool for digging into how variants are laid out.
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc/session/mod.rs74
-rw-r--r--src/librustc_driver/driver.rs10
-rw-r--r--src/librustc_mir/transform/mod.rs1
-rw-r--r--src/librustc_mir/transform/print_type_sizes.rs152
5 files changed, 238 insertions, 1 deletions
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<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],
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<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);
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 <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));
+                }
+            }
+        });
+    }
+}