Skip to content

Commit ddf011d

Browse files
authored
Auto merge of rust-lang#37770 - pnkfelix:print-type-sizes, r=arielb1
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 rust-lang#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.
2 parents 217f57c + 75825fe commit ddf011d

22 files changed

+830
-2
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/config.rs

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

src/librustc/session/mod.rs

+9-1
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;
@@ -112,6 +116,9 @@ pub struct Session {
112116
/// Some measurements that are being gathered during compilation.
113117
pub perf_stats: PerfStats,
114118

119+
/// Data about code being compiled, gathered during compilation.
120+
pub code_stats: RefCell<CodeStats>,
121+
115122
next_node_id: Cell<ast::NodeId>,
116123
}
117124

@@ -624,7 +631,8 @@ pub fn build_session_(sopts: config::Options,
624631
incr_comp_hashes_count: Cell::new(0),
625632
incr_comp_bytes_hashed: Cell::new(0),
626633
symbol_hash_time: Cell::new(Duration::from_secs(0)),
627-
}
634+
},
635+
code_stats: RefCell::new(CodeStats::new()),
628636
};
629637

630638
init_llvm(&sess);

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

+4
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ pub fn compile_input(sess: &Session,
215215
})??
216216
};
217217

218+
if sess.opts.debugging_opts.print_type_sizes {
219+
sess.code_stats.borrow().print_type_sizes();
220+
}
221+
218222
let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);
219223

220224
controller_entry_point!(after_llvm,

src/librustc_trans/adt.rs

+1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
247247
// of the size.
248248
let size = size.bytes();
249249
let align = align.abi();
250+
assert!(align <= std::u32::MAX as u64);
250251
let discr_ty = Type::from_integer(cx, discr);
251252
let discr_size = discr.size().bytes();
252253
let padded_discr_size = roundup(discr_size, align as u32);

0 commit comments

Comments
 (0)