]> git.lizzy.rs Git - rust.git/blob - src/librustc/session/code_stats.rs
1eee6508c59cc6f59a4fa6519bf86a3cb2b63dc6
[rust.git] / src / librustc / session / code_stats.rs
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.
4 //
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.
10
11 use ty::AdtKind;
12 use ty::layout::{Align, Size};
13
14 use rustc_data_structures::fx::{FxHashSet};
15
16 use std::cmp::{self, Ordering};
17
18 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
19 pub struct VariantInfo {
20     pub name: Option<String>,
21     pub kind: SizeKind,
22     pub size: u64,
23     pub align: u64,
24     pub fields: Vec<FieldInfo>,
25 }
26
27 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
28 pub enum SizeKind {
29     Exact,
30     Min,
31 }
32
33 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
34 pub struct FieldInfo {
35     pub name: String,
36     pub offset: u64,
37     pub size: u64,
38     pub align: u64,
39 }
40
41 impl From<AdtKind> for DataTypeKind {
42     fn from(kind: AdtKind) -> Self {
43         match kind {
44             AdtKind::Struct => DataTypeKind::Struct,
45             AdtKind::Enum => DataTypeKind::Enum,
46             AdtKind::Union => DataTypeKind::Union,
47         }
48     }
49 }
50
51 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
52 pub enum DataTypeKind {
53     Struct,
54     Union,
55     Enum,
56     Closure,
57 }
58
59 #[derive(PartialEq, Eq, Hash, Debug)]
60 pub struct TypeSizeInfo {
61     pub kind: DataTypeKind,
62     pub type_description: String,
63     pub align: u64,
64     pub overall_size: u64,
65     pub packed: bool,
66     pub opt_discr_size: Option<u64>,
67     pub variants: Vec<VariantInfo>,
68 }
69
70 #[derive(PartialEq, Eq, Debug)]
71 pub struct CodeStats {
72     type_sizes: FxHashSet<TypeSizeInfo>,
73 }
74
75 impl CodeStats {
76     pub fn new() -> Self { CodeStats { type_sizes: FxHashSet() } }
77
78     pub fn record_type_size<S: ToString>(&mut self,
79                                          kind: DataTypeKind,
80                                          type_desc: S,
81                                          align: Align,
82                                          overall_size: Size,
83                                          packed: bool,
84                                          opt_discr_size: Option<Size>,
85                                          variants: Vec<VariantInfo>) {
86         let info = TypeSizeInfo {
87             kind,
88             type_description: type_desc.to_string(),
89             align: align.abi(),
90             overall_size: overall_size.bytes(),
91             packed: packed,
92             opt_discr_size: opt_discr_size.map(|s| s.bytes()),
93             variants,
94         };
95         self.type_sizes.insert(info);
96     }
97
98     pub fn print_type_sizes(&self) {
99         let mut sorted: Vec<_> = self.type_sizes.iter().collect();
100
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),
107                 other => other,
108             }
109         });
110
111         for info in &sorted {
112             println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
113                      info.type_description, info.overall_size, info.align);
114             let indent = "    ";
115
116             let discr_size = if let Some(discr_size) = info.opt_discr_size {
117                 println!("print-type-size {}discriminant: {} bytes",
118                          indent, discr_size);
119                 discr_size
120             } else {
121                 0
122             };
123
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;
129
130             let struct_like = match info.kind {
131                 DataTypeKind::Struct | DataTypeKind::Closure => true,
132                 DataTypeKind::Enum | DataTypeKind::Union => false,
133             };
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(),
140                     };
141                     println!("print-type-size {}variant `{}`: {} bytes",
142                              indent, name, size - discr_size);
143                     "        "
144                 } else {
145                     assert!(i < 1);
146                     "    "
147                 };
148                 max_variant_size = cmp::max(max_variant_size, size);
149
150                 let mut min_offset = discr_size;
151
152                 // We want to print fields by increasing offset.
153                 let mut fields = fields.clone();
154                 fields.sort_by_key(|f| f.offset);
155
156                 for field in fields.iter() {
157                     let FieldInfo { ref name, offset, size, align } = *field;
158
159                     if offset > min_offset {
160                         let pad = offset - min_offset;
161                         println!("print-type-size {}padding: {} bytes",
162                                  indent, pad);
163                     }
164
165                     if offset < min_offset {
166                         // if this happens something is very wrong
167                         println!("print-type-size {}field `.{}`: {} bytes, \
168                                   offset: {} 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",
173                                  indent, name, size);
174                     } else {
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);
179                     }
180
181                     min_offset = offset + size;
182                 }
183             }
184
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);
191             }
192         }
193     }
194 }