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