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