Skip to content

Commit ca9ac8f

Browse files
committed
Add -Z print-type-sizes, a tool for digging into how variants are laid out.
1 parent b8f6c20 commit ca9ac8f

File tree

5 files changed

+238
-1
lines changed

5 files changed

+238
-1
lines changed

src/librustc/session/config.rs

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

src/librustc/session/mod.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,80 @@ pub struct Session {
112112
/// Some measurements that are being gathered during compilation.
113113
pub perf_stats: PerfStats,
114114

115+
/// Data about code being compiled, gathered during compilation.
116+
pub code_stats: RefCell<CodeStats>,
117+
115118
next_node_id: Cell<ast::NodeId>,
116119
}
117120

121+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
122+
pub enum VariantSize {
123+
Exact(u64),
124+
Min(u64),
125+
}
126+
127+
#[derive(PartialEq, Eq, Debug)]
128+
pub struct TypeSizeInfo {
129+
pub type_description: String,
130+
pub overall_size: u64,
131+
pub variant_sizes: Option<Vec<VariantSize>>,
132+
}
133+
134+
#[derive(PartialEq, Eq, Debug)]
135+
pub struct CodeStats {
136+
pub type_sizes: Vec<TypeSizeInfo>,
137+
}
138+
139+
impl CodeStats {
140+
fn new() -> Self {
141+
CodeStats { type_sizes: Vec::new() }
142+
}
143+
144+
pub fn record_type_size<S: ToString>(&mut self,
145+
type_desc: S,
146+
overall_size: u64,
147+
variant_sizes: Vec<VariantSize>) {
148+
let sizes = if variant_sizes.len() == 0 { None } else { Some(variant_sizes) };
149+
let info = TypeSizeInfo {
150+
type_description: type_desc.to_string(),
151+
overall_size: overall_size,
152+
variant_sizes: sizes,
153+
};
154+
if !self.type_sizes.contains(&info) {
155+
self.type_sizes.push(info);
156+
}
157+
}
158+
159+
pub fn sort_by_type_description(&mut self) {
160+
self.type_sizes.sort_by(|info1, info2| {
161+
info1.type_description.cmp(&info2.type_description)
162+
});
163+
}
164+
165+
pub fn sort_by_overall_size(&mut self) {
166+
self.type_sizes.sort_by(|info1, info2| {
167+
// (reversing cmp order to get large-to-small ordering)
168+
info2.overall_size.cmp(&info1.overall_size)
169+
});
170+
}
171+
172+
pub fn print_type_sizes(&self) {
173+
for info in &self.type_sizes {
174+
println!("print-type-size t: `{}` overall bytes: {}",
175+
info.type_description, info.overall_size);
176+
if let Some(ref variant_sizes) = info.variant_sizes {
177+
for (i, variant_size) in variant_sizes.iter().enumerate() {
178+
let (kind, s) = match *variant_size {
179+
VariantSize::Exact(s) => { ("exact", s) }
180+
VariantSize::Min(s) => { (" min", s) }
181+
};
182+
println!("print-type-size variant[{}] {} bytes: {}", i, kind, s);
183+
}
184+
}
185+
}
186+
}
187+
}
188+
118189
pub struct PerfStats {
119190
// The accumulated time needed for computing the SVH of the crate
120191
pub svh_time: Cell<Duration>,
@@ -624,7 +695,8 @@ pub fn build_session_(sopts: config::Options,
624695
incr_comp_hashes_count: Cell::new(0),
625696
incr_comp_bytes_hashed: Cell::new(0),
626697
symbol_hash_time: Cell::new(Duration::from_secs(0)),
627-
}
698+
},
699+
code_stats: RefCell::new(CodeStats::new()),
628700
};
629701

630702
init_llvm(&sess);

src/librustc_driver/driver.rs

+10
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ pub fn compile_input(sess: &Session,
217217
})??
218218
};
219219

220+
if sess.opts.debugging_opts.print_type_sizes {
221+
// (these are stable sorts)
222+
sess.code_stats.borrow_mut().sort_by_type_description();
223+
sess.code_stats.borrow_mut().sort_by_overall_size();
224+
sess.code_stats.borrow().print_type_sizes();
225+
}
226+
220227
let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);
221228

222229
controller_entry_point!(after_llvm,
@@ -1011,6 +1018,9 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10111018
time(time_passes, "MIR optimisations", || {
10121019
let mut passes = ::rustc::mir::transform::Passes::new();
10131020
passes.push_hook(box mir::transform::dump_mir::DumpMir);
1021+
if tcx.sess.opts.debugging_opts.print_type_sizes {
1022+
passes.push_pass(box mir::transform::print_type_sizes::GatherTypeSizesMir::new());
1023+
}
10141024
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
10151025
passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("no-landing-pads"));
10161026

src/librustc_mir/transform/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod simplify;
1313
pub mod erase_regions;
1414
pub mod no_landing_pads;
1515
pub mod type_check;
16+
pub mod print_type_sizes;
1617
pub mod add_call_guards;
1718
pub mod promote_consts;
1819
pub mod qualify_consts;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! This pass implements instrumentation to gather the layout of every type.
12+
13+
use rustc::session::{VariantSize};
14+
use rustc::traits::{Reveal};
15+
use rustc::ty::{self, Ty, TyCtxt};
16+
use rustc::ty::fold::{TypeFoldable};
17+
use rustc::ty::layout::{Layout};
18+
use rustc::mir::{Mir};
19+
use rustc::mir::transform::{MirPass, MirPassHook, MirSource, Pass};
20+
use rustc::mir::visit::Visitor;
21+
22+
use std::collections::HashSet;
23+
24+
pub struct GatherTypeSizesMir {
25+
_hidden: (),
26+
}
27+
28+
impl GatherTypeSizesMir {
29+
pub fn new() -> Self {
30+
GatherTypeSizesMir { _hidden: () }
31+
}
32+
}
33+
34+
impl Pass for GatherTypeSizesMir {
35+
}
36+
37+
impl<'tcx> MirPassHook<'tcx> for GatherTypeSizesMir {
38+
fn on_mir_pass<'a>(&mut self,
39+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
40+
src: MirSource,
41+
mir: &Mir<'tcx>,
42+
_pass: &Pass,
43+
_is_after: bool) {
44+
debug!("on_mir_pass: {}", tcx.node_path_str(src.item_id()));
45+
self.go(tcx, mir);
46+
}
47+
}
48+
49+
impl<'tcx> MirPass<'tcx> for GatherTypeSizesMir {
50+
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
51+
src: MirSource, mir: &mut Mir<'tcx>) {
52+
debug!("run_pass: {}", tcx.node_path_str(src.item_id()));
53+
self.go(tcx, mir);
54+
}
55+
}
56+
57+
impl GatherTypeSizesMir {
58+
fn go<'a, 'tcx>(&mut self,
59+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
60+
mir: &Mir<'tcx>) {
61+
if tcx.sess.err_count() > 0 {
62+
// compiling a broken program can obviously result in a
63+
// broken MIR, so do not bother trying to process it.
64+
return;
65+
}
66+
67+
let mut visitor = TypeVisitor {
68+
tcx: tcx,
69+
seen: HashSet::new(),
70+
};
71+
visitor.visit_mir(mir);
72+
}
73+
}
74+
75+
struct TypeVisitor<'a, 'tcx: 'a> {
76+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
77+
seen: HashSet<Ty<'tcx>>,
78+
}
79+
80+
impl<'a, 'tcx: 'a> Visitor<'tcx> for TypeVisitor<'a, 'tcx> {
81+
fn visit_ty(&mut self, ty: &Ty<'tcx>) {
82+
debug!("TypeVisitor::visit_ty ty=`{:?}`", ty);
83+
84+
match ty.sty {
85+
ty::TyAdt(..) |
86+
ty::TyClosure(..) => {} // fall through
87+
_ => {
88+
debug!("print-type-size t: `{:?}` skip non-nominal", ty);
89+
return;
90+
}
91+
}
92+
93+
if ty.has_param_types() {
94+
debug!("print-type-size t: `{:?}` skip has param types", ty);
95+
return;
96+
}
97+
if ty.has_projection_types() {
98+
debug!("print-type-size t: `{:?}` skip has projections", ty);
99+
return;
100+
}
101+
102+
if self.seen.contains(ty) {
103+
return;
104+
}
105+
self.seen.insert(ty);
106+
107+
let reveal = Reveal::All;
108+
// let reveal = Reveal::NotSpecializable;
109+
110+
self.tcx.infer_ctxt(None, None, reveal).enter(|infcx| {
111+
match ty.layout(&infcx) {
112+
Ok(layout) => {
113+
let type_desc = format!("{:?}", ty);
114+
let overall_size = layout.size(&Default::default());
115+
116+
let variant_sizes: Vec<_> = match *layout {
117+
Layout::General { ref variants, .. } => {
118+
variants.iter()
119+
.map(|v| if v.sized {
120+
VariantSize::Exact(v.min_size.bytes())
121+
} else {
122+
VariantSize::Min(v.min_size.bytes())
123+
})
124+
.collect()
125+
}
126+
127+
Layout::UntaggedUnion { variants: _ } => {
128+
/* layout does not currently store info about each variant... */
129+
Vec::new()
130+
}
131+
132+
// RawNullablePointer/StructWrappedNullablePointer
133+
// don't provide any interesting size info
134+
// beyond what we already reported for their
135+
// total size.
136+
_ => {
137+
Vec::new()
138+
}
139+
};
140+
141+
self.tcx.sess.code_stats.borrow_mut()
142+
.record_type_size(type_desc,
143+
overall_size.bytes(),
144+
variant_sizes);
145+
}
146+
Err(err) => {
147+
self.tcx.sess.warn(&format!("print-type-size t: `{:?}` err: {:?}", ty, err));
148+
}
149+
}
150+
});
151+
}
152+
}

0 commit comments

Comments
 (0)