]> git.proxmox.com Git - rustc.git/blame - src/librustc/session/code_stats.rs
New upstream version 1.40.0+dfsg1
[rustc.git] / src / librustc / session / code_stats.rs
CommitLineData
b7449926 1use rustc_target::abi::{Align, Size};
476ff2be 2use rustc_data_structures::fx::{FxHashSet};
476ff2be
SL
3use std::cmp::{self, Ordering};
4
5#[derive(Clone, PartialEq, Eq, Hash, Debug)]
6pub struct VariantInfo {
7 pub name: Option<String>,
8 pub kind: SizeKind,
9 pub size: u64,
10 pub align: u64,
11 pub fields: Vec<FieldInfo>,
12}
13
14#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
cc61c64b
XL
15pub enum SizeKind {
16 Exact,
17 Min,
18}
476ff2be
SL
19
20#[derive(Clone, PartialEq, Eq, Hash, Debug)]
21pub struct FieldInfo {
22 pub name: String,
23 pub offset: u64,
24 pub size: u64,
25 pub align: u64,
26}
27
476ff2be
SL
28#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
29pub enum DataTypeKind {
30 Struct,
31 Union,
32 Enum,
33 Closure,
34}
35
36#[derive(PartialEq, Eq, Hash, Debug)]
37pub struct TypeSizeInfo {
38 pub kind: DataTypeKind,
39 pub type_description: String,
40 pub align: u64,
41 pub overall_size: u64,
83c7162d 42 pub packed: bool,
476ff2be
SL
43 pub opt_discr_size: Option<u64>,
44 pub variants: Vec<VariantInfo>,
45}
46
0bf4aa26 47#[derive(PartialEq, Eq, Debug, Default)]
476ff2be
SL
48pub struct CodeStats {
49 type_sizes: FxHashSet<TypeSizeInfo>,
50}
51
52impl CodeStats {
476ff2be
SL
53 pub fn record_type_size<S: ToString>(&mut self,
54 kind: DataTypeKind,
55 type_desc: S,
56 align: Align,
57 overall_size: Size,
83c7162d 58 packed: bool,
476ff2be 59 opt_discr_size: Option<Size>,
48663c56
XL
60 mut variants: Vec<VariantInfo>) {
61 // Sort variants so the largest ones are shown first. A stable sort is
62 // used here so that source code order is preserved for all variants
63 // that have the same size.
64 variants.sort_by(|info1, info2| {
65 info2.size.cmp(&info1.size)
66 });
476ff2be 67 let info = TypeSizeInfo {
041b39d2 68 kind,
476ff2be 69 type_description: type_desc.to_string(),
a1dfa0c6 70 align: align.bytes(),
476ff2be 71 overall_size: overall_size.bytes(),
83c7162d 72 packed: packed,
476ff2be 73 opt_discr_size: opt_discr_size.map(|s| s.bytes()),
041b39d2 74 variants,
476ff2be
SL
75 };
76 self.type_sizes.insert(info);
77 }
78
79 pub fn print_type_sizes(&self) {
80 let mut sorted: Vec<_> = self.type_sizes.iter().collect();
81
82 // Primary sort: large-to-small.
83 // Secondary sort: description (dictionary order)
84 sorted.sort_by(|info1, info2| {
85 // (reversing cmp order to get large-to-small ordering)
86 match info2.overall_size.cmp(&info1.overall_size) {
87 Ordering::Equal => info1.type_description.cmp(&info2.type_description),
88 other => other,
89 }
90 });
91
92 for info in &sorted {
93 println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
94 info.type_description, info.overall_size, info.align);
95 let indent = " ";
96
97 let discr_size = if let Some(discr_size) = info.opt_discr_size {
98 println!("print-type-size {}discriminant: {} bytes",
99 indent, discr_size);
100 discr_size
101 } else {
102 0
103 };
104
105 // We start this at discr_size (rather than 0) because
106 // things like C-enums do not have variants but we still
107 // want the max_variant_size at the end of the loop below
108 // to reflect the presence of the discriminant.
109 let mut max_variant_size = discr_size;
110
111 let struct_like = match info.kind {
112 DataTypeKind::Struct | DataTypeKind::Closure => true,
113 DataTypeKind::Enum | DataTypeKind::Union => false,
114 };
115 for (i, variant_info) in info.variants.iter().enumerate() {
116 let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
117 let indent = if !struct_like {
118 let name = match name.as_ref() {
8faf50e0
XL
119 Some(name) => name.to_owned(),
120 None => i.to_string(),
476ff2be
SL
121 };
122 println!("print-type-size {}variant `{}`: {} bytes",
123 indent, name, size - discr_size);
124 " "
125 } else {
126 assert!(i < 1);
127 " "
128 };
129 max_variant_size = cmp::max(max_variant_size, size);
130
131 let mut min_offset = discr_size;
132
133 // We want to print fields by increasing offset.
134 let mut fields = fields.clone();
135 fields.sort_by_key(|f| f.offset);
136
137 for field in fields.iter() {
138 let FieldInfo { ref name, offset, size, align } = *field;
139
83c7162d
XL
140 if offset > min_offset {
141 let pad = offset - min_offset;
142 println!("print-type-size {}padding: {} bytes",
143 indent, pad);
144 }
145
146 if offset < min_offset {
147 // if this happens something is very wrong
148 println!("print-type-size {}field `.{}`: {} bytes, \
149 offset: {} bytes, \
150 alignment: {} bytes",
151 indent, name, size, offset, align);
152 } else if info.packed || offset == min_offset {
476ff2be
SL
153 println!("print-type-size {}field `.{}`: {} bytes",
154 indent, name, size);
83c7162d
XL
155 } else {
156 // Include field alignment in output only if it caused padding injection
157 println!("print-type-size {}field `.{}`: {} bytes, \
158 alignment: {} bytes",
159 indent, name, size, align);
476ff2be
SL
160 }
161
162 min_offset = offset + size;
163 }
164 }
165
166 assert!(max_variant_size <= info.overall_size,
167 "max_variant_size {} !<= {} overall_size",
168 max_variant_size, info.overall_size);
169 if max_variant_size < info.overall_size {
170 println!("print-type-size {}end padding: {} bytes",
171 indent, info.overall_size - max_variant_size);
172 }
173 }
174 }
175}