1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use ty::layout::{Align, Size};
14 use rustc_data_structures::fx::{FxHashSet};
16 use std::cmp::{self, Ordering};
18 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
19 pub struct VariantInfo {
20 pub name: Option<String>,
24 pub fields: Vec<FieldInfo>,
27 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
33 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
34 pub struct FieldInfo {
41 impl From<AdtKind> for DataTypeKind {
42 fn from(kind: AdtKind) -> Self {
44 AdtKind::Struct => DataTypeKind::Struct,
45 AdtKind::Enum => DataTypeKind::Enum,
46 AdtKind::Union => DataTypeKind::Union,
51 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
52 pub enum DataTypeKind {
59 #[derive(PartialEq, Eq, Hash, Debug)]
60 pub struct TypeSizeInfo {
61 pub kind: DataTypeKind,
62 pub type_description: String,
64 pub overall_size: u64,
66 pub opt_discr_size: Option<u64>,
67 pub variants: Vec<VariantInfo>,
70 #[derive(PartialEq, Eq, Debug)]
71 pub struct CodeStats {
72 type_sizes: FxHashSet<TypeSizeInfo>,
76 pub fn new() -> Self { CodeStats { type_sizes: FxHashSet() } }
78 pub fn record_type_size<S: ToString>(&mut self,
84 opt_discr_size: Option<Size>,
85 variants: Vec<VariantInfo>) {
86 let info = TypeSizeInfo {
88 type_description: type_desc.to_string(),
90 overall_size: overall_size.bytes(),
92 opt_discr_size: opt_discr_size.map(|s| s.bytes()),
95 self.type_sizes.insert(info);
98 pub fn print_type_sizes(&self) {
99 let mut sorted: Vec<_> = self.type_sizes.iter().collect();
101 // Primary sort: large-to-small.
102 // Secondary sort: description (dictionary order)
103 sorted.sort_by(|info1, info2| {
104 // (reversing cmp order to get large-to-small ordering)
105 match info2.overall_size.cmp(&info1.overall_size) {
106 Ordering::Equal => info1.type_description.cmp(&info2.type_description),
111 for info in &sorted {
112 println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
113 info.type_description, info.overall_size, info.align);
116 let discr_size = if let Some(discr_size) = info.opt_discr_size {
117 println!("print-type-size {}discriminant: {} bytes",
124 // We start this at discr_size (rather than 0) because
125 // things like C-enums do not have variants but we still
126 // want the max_variant_size at the end of the loop below
127 // to reflect the presence of the discriminant.
128 let mut max_variant_size = discr_size;
130 let struct_like = match info.kind {
131 DataTypeKind::Struct | DataTypeKind::Closure => true,
132 DataTypeKind::Enum | DataTypeKind::Union => false,
134 for (i, variant_info) in info.variants.iter().enumerate() {
135 let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
136 let indent = if !struct_like {
137 let name = match name.as_ref() {
138 Some(name) => name.to_owned(),
139 None => i.to_string(),
141 println!("print-type-size {}variant `{}`: {} bytes",
142 indent, name, size - discr_size);
148 max_variant_size = cmp::max(max_variant_size, size);
150 let mut min_offset = discr_size;
152 // We want to print fields by increasing offset.
153 let mut fields = fields.clone();
154 fields.sort_by_key(|f| f.offset);
156 for field in fields.iter() {
157 let FieldInfo { ref name, offset, size, align } = *field;
159 if offset > min_offset {
160 let pad = offset - min_offset;
161 println!("print-type-size {}padding: {} bytes",
165 if offset < min_offset {
166 // if this happens something is very wrong
167 println!("print-type-size {}field `.{}`: {} bytes, \
169 alignment: {} bytes",
170 indent, name, size, offset, align);
171 } else if info.packed || offset == min_offset {
172 println!("print-type-size {}field `.{}`: {} bytes",
175 // Include field alignment in output only if it caused padding injection
176 println!("print-type-size {}field `.{}`: {} bytes, \
177 alignment: {} bytes",
178 indent, name, size, align);
181 min_offset = offset + size;
185 assert!(max_variant_size <= info.overall_size,
186 "max_variant_size {} !<= {} overall_size",
187 max_variant_size, info.overall_size);
188 if max_variant_size < info.overall_size {
189 println!("print-type-size {}end padding: {} bytes",
190 indent, info.overall_size - max_variant_size);