Skip to content

Commit cf2d375

Browse files
committed
Revisions from review comments, squashed.
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.
1 parent ca9ac8f commit cf2d375

File tree

8 files changed

+377
-228
lines changed

8 files changed

+377
-228
lines changed

src/librustc/session/code_stats.rs

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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+
use ty::AdtKind;
12+
use ty::layout::{Align, Size};
13+
14+
use rustc_data_structures::fx::{FxHashSet};
15+
16+
use std::cmp::{self, Ordering};
17+
18+
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
19+
pub struct VariantInfo {
20+
pub name: Option<String>,
21+
pub kind: SizeKind,
22+
pub size: u64,
23+
pub align: u64,
24+
pub fields: Vec<FieldInfo>,
25+
}
26+
27+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
28+
pub enum SizeKind { Exact, Min }
29+
30+
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
31+
pub struct FieldInfo {
32+
pub name: String,
33+
pub offset: u64,
34+
pub size: u64,
35+
pub align: u64,
36+
}
37+
38+
impl From<AdtKind> for DataTypeKind {
39+
fn from(kind: AdtKind) -> Self {
40+
match kind {
41+
AdtKind::Struct => DataTypeKind::Struct,
42+
AdtKind::Enum => DataTypeKind::Enum,
43+
AdtKind::Union => DataTypeKind::Union,
44+
}
45+
}
46+
}
47+
48+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
49+
pub enum DataTypeKind {
50+
Struct,
51+
Union,
52+
Enum,
53+
Closure,
54+
}
55+
56+
#[derive(PartialEq, Eq, Hash, Debug)]
57+
pub struct TypeSizeInfo {
58+
pub kind: DataTypeKind,
59+
pub type_description: String,
60+
pub align: u64,
61+
pub overall_size: u64,
62+
pub opt_discr_size: Option<u64>,
63+
pub variants: Vec<VariantInfo>,
64+
}
65+
66+
#[derive(PartialEq, Eq, Debug)]
67+
pub struct CodeStats {
68+
type_sizes: FxHashSet<TypeSizeInfo>,
69+
}
70+
71+
impl CodeStats {
72+
pub fn new() -> Self { CodeStats { type_sizes: FxHashSet() } }
73+
74+
pub fn record_type_size<S: ToString>(&mut self,
75+
kind: DataTypeKind,
76+
type_desc: S,
77+
align: Align,
78+
overall_size: Size,
79+
opt_discr_size: Option<Size>,
80+
variants: Vec<VariantInfo>) {
81+
let info = TypeSizeInfo {
82+
kind: kind,
83+
type_description: type_desc.to_string(),
84+
align: align.abi(),
85+
overall_size: overall_size.bytes(),
86+
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
87+
variants: variants,
88+
};
89+
self.type_sizes.insert(info);
90+
}
91+
92+
pub fn print_type_sizes(&self) {
93+
let mut sorted: Vec<_> = self.type_sizes.iter().collect();
94+
95+
// Primary sort: large-to-small.
96+
// Secondary sort: description (dictionary order)
97+
sorted.sort_by(|info1, info2| {
98+
// (reversing cmp order to get large-to-small ordering)
99+
match info2.overall_size.cmp(&info1.overall_size) {
100+
Ordering::Equal => info1.type_description.cmp(&info2.type_description),
101+
other => other,
102+
}
103+
});
104+
105+
for info in &sorted {
106+
println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
107+
info.type_description, info.overall_size, info.align);
108+
let indent = " ";
109+
110+
let discr_size = if let Some(discr_size) = info.opt_discr_size {
111+
println!("print-type-size {}discriminant: {} bytes",
112+
indent, discr_size);
113+
discr_size
114+
} else {
115+
0
116+
};
117+
118+
// We start this at discr_size (rather than 0) because
119+
// things like C-enums do not have variants but we still
120+
// want the max_variant_size at the end of the loop below
121+
// to reflect the presence of the discriminant.
122+
let mut max_variant_size = discr_size;
123+
124+
let struct_like = match info.kind {
125+
DataTypeKind::Struct | DataTypeKind::Closure => true,
126+
DataTypeKind::Enum | DataTypeKind::Union => false,
127+
};
128+
for (i, variant_info) in info.variants.iter().enumerate() {
129+
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
130+
let indent = if !struct_like {
131+
let name = match name.as_ref() {
132+
Some(name) => format!("{}", name),
133+
None => format!("{}", i),
134+
};
135+
println!("print-type-size {}variant `{}`: {} bytes",
136+
indent, name, size - discr_size);
137+
" "
138+
} else {
139+
assert!(i < 1);
140+
" "
141+
};
142+
max_variant_size = cmp::max(max_variant_size, size);
143+
144+
let mut min_offset = discr_size;
145+
for field in fields {
146+
let FieldInfo { ref name, offset, size, align } = *field;
147+
148+
// Include field alignment in output only if it caused padding injection
149+
if min_offset != offset {
150+
let pad = offset - min_offset;
151+
println!("print-type-size {}padding bytes: {}",
152+
indent, pad);
153+
println!("print-type-size {}field `.{}`: {} bytes, alignment: {} bytes",
154+
indent, name, size, align);
155+
} else {
156+
println!("print-type-size {}field `.{}`: {} bytes",
157+
indent, name, size);
158+
}
159+
160+
min_offset = offset + size;
161+
}
162+
}
163+
164+
assert!(max_variant_size <= info.overall_size,
165+
"max_variant_size {} !<= {} overall_size",
166+
max_variant_size, info.overall_size);
167+
if max_variant_size < info.overall_size {
168+
println!("print-type-size {}end padding bytes: {}",
169+
indent, info.overall_size - max_variant_size);
170+
}
171+
}
172+
}
173+
}

src/librustc/session/mod.rs

+4-68
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo};
12+
pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo};
13+
1114
use dep_graph::DepGraph;
1215
use hir::def_id::{CrateNum, DefIndex};
1316
use hir::svh::Svh;
@@ -49,6 +52,7 @@ use std::fmt;
4952
use std::time::Duration;
5053
use libc::c_int;
5154

55+
mod code_stats;
5256
pub mod config;
5357
pub mod filesearch;
5458
pub mod search_paths;
@@ -118,74 +122,6 @@ pub struct Session {
118122
next_node_id: Cell<ast::NodeId>,
119123
}
120124

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-
189125
pub struct PerfStats {
190126
// The accumulated time needed for computing the SVH of the crate
191127
pub svh_time: Cell<Duration>,

src/librustc/ty/layout.rs

+7
Original file line numberDiff line numberDiff line change
@@ -559,11 +559,14 @@ impl<'a, 'gcx, 'tcx> Struct {
559559

560560
self.offsets.push(offset);
561561

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

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

568+
debug!("Struct::extend min_size: {:?}", offset);
569+
567570
self.min_size = offset;
568571

569572
Ok(())
@@ -707,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Union {
707710
index, scapegoat);
708711
}
709712

713+
debug!("Union::extend field: {:?} {:?}", field, field.size(dl));
714+
710715
if !self.packed {
711716
self.align = self.align.max(field.align(dl));
712717
}
713718
self.min_size = cmp::max(self.min_size, field.size(dl));
714719
}
715720

721+
debug!("Union::extend min-size: {:?}", self.min_size);
722+
716723
Ok(())
717724
}
718725

src/librustc_driver/driver.rs

-6
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,6 @@ pub fn compile_input(sess: &Session,
218218
};
219219

220220
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();
224221
sess.code_stats.borrow().print_type_sizes();
225222
}
226223

@@ -1018,9 +1015,6 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10181015
time(time_passes, "MIR optimisations", || {
10191016
let mut passes = ::rustc::mir::transform::Passes::new();
10201017
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-
}
10241018
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
10251019
passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("no-landing-pads"));
10261020

src/librustc_mir/transform/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ 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;
1716
pub mod add_call_guards;
1817
pub mod promote_consts;
1918
pub mod qualify_consts;

0 commit comments

Comments
 (0)