]>
Commit | Line | Data |
---|---|---|
b7449926 | 1 | use rustc_target::abi::{Align, Size}; |
476ff2be | 2 | use rustc_data_structures::fx::{FxHashSet}; |
476ff2be SL |
3 | use std::cmp::{self, Ordering}; |
4 | ||
5 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | |
6 | pub 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 |
15 | pub enum SizeKind { |
16 | Exact, | |
17 | Min, | |
18 | } | |
476ff2be SL |
19 | |
20 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | |
21 | pub 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)] |
29 | pub enum DataTypeKind { | |
30 | Struct, | |
31 | Union, | |
32 | Enum, | |
33 | Closure, | |
34 | } | |
35 | ||
36 | #[derive(PartialEq, Eq, Hash, Debug)] | |
37 | pub 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 |
48 | pub struct CodeStats { |
49 | type_sizes: FxHashSet<TypeSizeInfo>, | |
50 | } | |
51 | ||
52 | impl 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 | } |