1 use rustc_target::abi::{Align, Size};
2 use rustc_data_structures::fx::{FxHashSet};
3 use std::cmp::{self, Ordering};
4 use rustc_data_structures::sync::Lock;
6 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
7 pub struct VariantInfo {
8 pub name: Option<String>,
12 pub fields: Vec<FieldInfo>,
15 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
21 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
22 pub struct FieldInfo {
29 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
30 pub enum DataTypeKind {
37 #[derive(PartialEq, Eq, Hash, Debug)]
38 pub struct TypeSizeInfo {
39 pub kind: DataTypeKind,
40 pub type_description: String,
42 pub overall_size: u64,
44 pub opt_discr_size: Option<u64>,
45 pub variants: Vec<VariantInfo>,
49 pub struct CodeStats {
50 type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
54 pub fn record_type_size<S: ToString>(&self,
60 opt_discr_size: Option<Size>,
61 mut variants: Vec<VariantInfo>) {
62 // Sort variants so the largest ones are shown first. A stable sort is
63 // used here so that source code order is preserved for all variants
64 // that have the same size.
65 variants.sort_by(|info1, info2| {
66 info2.size.cmp(&info1.size)
68 let info = TypeSizeInfo {
70 type_description: type_desc.to_string(),
72 overall_size: overall_size.bytes(),
74 opt_discr_size: opt_discr_size.map(|s| s.bytes()),
77 self.type_sizes.borrow_mut().insert(info);
80 pub fn print_type_sizes(&self) {
81 let type_sizes = self.type_sizes.borrow();
82 let mut sorted: Vec<_> = type_sizes.iter().collect();
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),
95 println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
96 info.type_description, info.overall_size, info.align);
99 let discr_size = if let Some(discr_size) = info.opt_discr_size {
100 println!("print-type-size {}discriminant: {} bytes",
107 // We start this at discr_size (rather than 0) because
108 // things like C-enums do not have variants but we still
109 // want the max_variant_size at the end of the loop below
110 // to reflect the presence of the discriminant.
111 let mut max_variant_size = discr_size;
113 let struct_like = match info.kind {
114 DataTypeKind::Struct | DataTypeKind::Closure => true,
115 DataTypeKind::Enum | DataTypeKind::Union => false,
117 for (i, variant_info) in info.variants.iter().enumerate() {
118 let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
119 let indent = if !struct_like {
120 let name = match name.as_ref() {
121 Some(name) => name.to_owned(),
122 None => i.to_string(),
124 println!("print-type-size {}variant `{}`: {} bytes",
125 indent, name, size - discr_size);
131 max_variant_size = cmp::max(max_variant_size, size);
133 let mut min_offset = discr_size;
135 // We want to print fields by increasing offset.
136 let mut fields = fields.clone();
137 fields.sort_by_key(|f| f.offset);
139 for field in fields.iter() {
140 let FieldInfo { ref name, offset, size, align } = *field;
142 if offset > min_offset {
143 let pad = offset - min_offset;
144 println!("print-type-size {}padding: {} bytes",
148 if offset < min_offset {
149 // if this happens something is very wrong
150 println!("print-type-size {}field `.{}`: {} bytes, \
152 alignment: {} bytes",
153 indent, name, size, offset, align);
154 } else if info.packed || offset == min_offset {
155 println!("print-type-size {}field `.{}`: {} bytes",
158 // Include field alignment in output only if it caused padding injection
159 println!("print-type-size {}field `.{}`: {} bytes, \
160 alignment: {} bytes",
161 indent, name, size, align);
164 min_offset = offset + size;
168 assert!(max_variant_size <= info.overall_size,
169 "max_variant_size {} !<= {} overall_size",
170 max_variant_size, info.overall_size);
171 if max_variant_size < info.overall_size {
172 println!("print-type-size {}end padding: {} bytes",
173 indent, info.overall_size - max_variant_size);