Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Nov 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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],
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we do this in the "post-trans" closure a few lines above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The closure we pass into phase_3_run_analysis_passes?

What is the benefit you see of putting it in there? Is there some internal instrumentation that needs to be aware of this print pass taking place?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just looks better there. Also, then you could have the type size list on the global tcx.

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