Skip to content

Commit

Permalink
Auto merge of #37770 - pnkfelix:print-type-sizes, r=arielb1
Browse files Browse the repository at this point in the history
Add debug flag `-Z print-type-sizes` for instrumention type/variant sizes

Add debug flag `-Z print-type-sizes` for instrumention type/variant sizes

This is meant to help with things like #36799 in a very local way; namely, once you have a hypothesis as to which types have a large population or are "too large", you can use `-Z print-type-sizes` to learn how large each type is, and how much each variant in an enum contributes to the size of that overall enum.
  • Loading branch information
bors authored Nov 22, 2016
2 parents 1c11ea3 + 559c135 commit 7189dbb
Show file tree
Hide file tree
Showing 22 changed files with 813 additions and 2 deletions.
173 changes: 173 additions & 0 deletions src/librustc/session/code_stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// 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.

use ty::AdtKind;
use ty::layout::{Align, Size};

use rustc_data_structures::fx::{FxHashSet};

use std::cmp::{self, Ordering};

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantInfo {
pub name: Option<String>,
pub kind: SizeKind,
pub size: u64,
pub align: u64,
pub fields: Vec<FieldInfo>,
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum SizeKind { Exact, Min }

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct FieldInfo {
pub name: String,
pub offset: u64,
pub size: u64,
pub align: u64,
}

impl From<AdtKind> for DataTypeKind {
fn from(kind: AdtKind) -> Self {
match kind {
AdtKind::Struct => DataTypeKind::Struct,
AdtKind::Enum => DataTypeKind::Enum,
AdtKind::Union => DataTypeKind::Union,
}
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum DataTypeKind {
Struct,
Union,
Enum,
Closure,
}

#[derive(PartialEq, Eq, Hash, Debug)]
pub struct TypeSizeInfo {
pub kind: DataTypeKind,
pub type_description: String,
pub align: u64,
pub overall_size: u64,
pub opt_discr_size: Option<u64>,
pub variants: Vec<VariantInfo>,
}

#[derive(PartialEq, Eq, Debug)]
pub struct CodeStats {
type_sizes: FxHashSet<TypeSizeInfo>,
}

impl CodeStats {
pub fn new() -> Self { CodeStats { type_sizes: FxHashSet() } }

pub fn record_type_size<S: ToString>(&mut self,
kind: DataTypeKind,
type_desc: S,
align: Align,
overall_size: Size,
opt_discr_size: Option<Size>,
variants: Vec<VariantInfo>) {
let info = TypeSizeInfo {
kind: kind,
type_description: type_desc.to_string(),
align: align.abi(),
overall_size: overall_size.bytes(),
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
variants: variants,
};
self.type_sizes.insert(info);
}

pub fn print_type_sizes(&self) {
let mut sorted: Vec<_> = self.type_sizes.iter().collect();

// Primary sort: large-to-small.
// Secondary sort: description (dictionary order)
sorted.sort_by(|info1, info2| {
// (reversing cmp order to get large-to-small ordering)
match info2.overall_size.cmp(&info1.overall_size) {
Ordering::Equal => info1.type_description.cmp(&info2.type_description),
other => other,
}
});

for info in &sorted {
println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
info.type_description, info.overall_size, info.align);
let indent = " ";

let discr_size = if let Some(discr_size) = info.opt_discr_size {
println!("print-type-size {}discriminant: {} bytes",
indent, discr_size);
discr_size
} else {
0
};

// We start this at discr_size (rather than 0) because
// things like C-enums do not have variants but we still
// want the max_variant_size at the end of the loop below
// to reflect the presence of the discriminant.
let mut max_variant_size = discr_size;

let struct_like = match info.kind {
DataTypeKind::Struct | DataTypeKind::Closure => true,
DataTypeKind::Enum | DataTypeKind::Union => false,
};
for (i, variant_info) in info.variants.iter().enumerate() {
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
let indent = if !struct_like {
let name = match name.as_ref() {
Some(name) => format!("{}", name),
None => format!("{}", i),
};
println!("print-type-size {}variant `{}`: {} bytes",
indent, name, size - discr_size);
" "
} else {
assert!(i < 1);
" "
};
max_variant_size = cmp::max(max_variant_size, size);

let mut min_offset = discr_size;
for field in fields {
let FieldInfo { ref name, offset, size, align } = *field;

// Include field alignment in output only if it caused padding injection
if min_offset != offset {
let pad = offset - min_offset;
println!("print-type-size {}padding bytes: {}",
indent, pad);
println!("print-type-size {}field `.{}`: {} bytes, alignment: {} bytes",
indent, name, size, align);
} else {
println!("print-type-size {}field `.{}`: {} bytes",
indent, name, size);
}

min_offset = offset + size;
}
}

assert!(max_variant_size <= info.overall_size,
"max_variant_size {} !<= {} overall_size",
max_variant_size, info.overall_size);
if max_variant_size < info.overall_size {
println!("print-type-size {}end padding bytes: {}",
indent, info.overall_size - max_variant_size);
}
}
}
}
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,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],
Expand Down
10 changes: 9 additions & 1 deletion src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo};
pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo};

use dep_graph::DepGraph;
use hir::def_id::{CrateNum, DefIndex};
use hir::svh::Svh;
Expand Down Expand Up @@ -49,6 +52,7 @@ use std::fmt;
use std::time::Duration;
use libc::c_int;

mod code_stats;
pub mod config;
pub mod filesearch;
pub mod search_paths;
Expand Down Expand Up @@ -112,6 +116,9 @@ 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>,
}

Expand Down Expand Up @@ -624,7 +631,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);
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,11 +559,14 @@ impl<'a, 'gcx, 'tcx> Struct {

self.offsets.push(offset);

debug!("Struct::extend offset: {:?} field: {:?} {:?}", offset, field, field.size(dl));

offset = offset.checked_add(field.size(dl), dl)
.map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?;
}

debug!("Struct::extend min_size: {:?}", offset);

self.min_size = offset;

Ok(())
Expand Down Expand Up @@ -707,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Union {
index, scapegoat);
}

debug!("Union::extend field: {:?} {:?}", field, field.size(dl));

if !self.packed {
self.align = self.align.max(field.align(dl));
}
self.min_size = cmp::max(self.min_size, field.size(dl));
}

debug!("Union::extend min-size: {:?}", self.min_size);

Ok(())
}

Expand Down
4 changes: 4 additions & 0 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ pub fn compile_input(sess: &Session,
})??
};

if sess.opts.debugging_opts.print_type_sizes {
sess.code_stats.borrow().print_type_sizes();
}

let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);

controller_entry_point!(after_llvm,
Expand Down
1 change: 1 addition & 0 deletions src/librustc_trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
// of the size.
let size = size.bytes();
let align = align.abi();
assert!(align <= std::u32::MAX as u64);
let discr_ty = Type::from_integer(cx, discr);
let discr_size = discr.size().bytes();
let padded_discr_size = roundup(discr_size, align as u32);
Expand Down
Loading

0 comments on commit 7189dbb

Please sign in to comment.