Skip to content

Commit

Permalink
Revisions from review comments, squashed.
Browse files Browse the repository at this point in the history
Biggest change: Revised print-type-sizes output to include breakdown
of layout.

Includes info about field sizes (and alignment + padding when padding
is injected; the injected padding is derived from the offsets computed
by layout module).

Output format is illustrated in commit that has the ui tests.

Note: there exists (at least) one case of variant w/o name: empty
enums.  Namely, empty enums use anonymous univariant repr. So for such
cases, print the number of the variant instead of the name.

----

Also, eddyb suggested of reading from `layout_cache` post-trans.

(For casual readers: the compiler source often uses the word "cache"
for tables that are in fact not periodically purged, and thus are
useful as the basis for data like this.)

Some types that were previously not printed are now included in the
output. (See e.g. the tests `print_type_sizes/generics.rs` and
`print_type_sizes/variants.rs`)

----

Other review feedback:

switch to an exhaustive match when filtering in just structural types.
switch to hashset for layout info and move sort into print method.

----

Driveby change: Factored session::code_stats into its own module

----

incorporate njn feedback re output formatting.
  • Loading branch information
pnkfelix committed Nov 21, 2016
1 parent ca9ac8f commit cf2d375
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 228 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);
}
}
}
}
72 changes: 4 additions & 68 deletions 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 @@ -118,74 +122,6 @@ pub struct Session {
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>,
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
6 changes: 0 additions & 6 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,6 @@ 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();
}

Expand Down Expand Up @@ -1018,9 +1015,6 @@ 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"));

Expand Down
1 change: 0 additions & 1 deletion src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ 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;
Expand Down
Loading

0 comments on commit cf2d375

Please sign in to comment.