2016-11-15 10:48:07 -06:00
|
|
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2018-09-08 14:14:55 -05:00
|
|
|
use rustc_target::abi::{AbiAndPrefAlign, Size};
|
2016-11-15 10:48:07 -06:00
|
|
|
use rustc_data_structures::fx::{FxHashSet};
|
|
|
|
use std::cmp::{self, Ordering};
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub struct VariantInfo {
|
|
|
|
pub name: Option<String>,
|
|
|
|
pub kind: SizeKind,
|
|
|
|
pub size: u64,
|
|
|
|
pub align: u64,
|
|
|
|
pub fields: Vec<FieldInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
2017-03-12 19:12:13 -05:00
|
|
|
pub enum SizeKind {
|
|
|
|
Exact,
|
|
|
|
Min,
|
|
|
|
}
|
2016-11-15 10:48:07 -06:00
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub struct FieldInfo {
|
|
|
|
pub name: String,
|
|
|
|
pub offset: u64,
|
|
|
|
pub size: u64,
|
|
|
|
pub align: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub enum DataTypeKind {
|
|
|
|
Struct,
|
|
|
|
Union,
|
|
|
|
Enum,
|
|
|
|
Closure,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
|
|
|
pub struct TypeSizeInfo {
|
|
|
|
pub kind: DataTypeKind,
|
|
|
|
pub type_description: String,
|
|
|
|
pub align: u64,
|
|
|
|
pub overall_size: u64,
|
2018-02-04 05:10:28 -06:00
|
|
|
pub packed: bool,
|
2016-11-15 10:48:07 -06:00
|
|
|
pub opt_discr_size: Option<u64>,
|
|
|
|
pub variants: Vec<VariantInfo>,
|
|
|
|
}
|
|
|
|
|
2018-10-16 09:57:53 -05:00
|
|
|
#[derive(PartialEq, Eq, Debug, Default)]
|
2016-11-15 10:48:07 -06:00
|
|
|
pub struct CodeStats {
|
|
|
|
type_sizes: FxHashSet<TypeSizeInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CodeStats {
|
|
|
|
pub fn record_type_size<S: ToString>(&mut self,
|
|
|
|
kind: DataTypeKind,
|
|
|
|
type_desc: S,
|
2018-09-08 14:14:55 -05:00
|
|
|
align: AbiAndPrefAlign,
|
2016-11-15 10:48:07 -06:00
|
|
|
overall_size: Size,
|
2018-02-04 05:10:28 -06:00
|
|
|
packed: bool,
|
2016-11-15 10:48:07 -06:00
|
|
|
opt_discr_size: Option<Size>,
|
|
|
|
variants: Vec<VariantInfo>) {
|
|
|
|
let info = TypeSizeInfo {
|
2017-07-03 13:19:51 -05:00
|
|
|
kind,
|
2016-11-15 10:48:07 -06:00
|
|
|
type_description: type_desc.to_string(),
|
2018-09-08 16:22:22 -05:00
|
|
|
align: align.abi.bytes(),
|
2016-11-15 10:48:07 -06:00
|
|
|
overall_size: overall_size.bytes(),
|
2018-02-04 05:10:28 -06:00
|
|
|
packed: packed,
|
2016-11-15 10:48:07 -06:00
|
|
|
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
|
2017-07-03 13:19:51 -05:00
|
|
|
variants,
|
2016-11-15 10:48:07 -06:00
|
|
|
};
|
|
|
|
self.type_sizes.insert(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn print_type_sizes(&self) {
|
|
|
|
let mut sorted: Vec<_> = self.type_sizes.iter().collect();
|
|
|
|
|
|
|
|
// Primary sort: large-to-small.
|
|
|
|
// Secondary sort: description (dictionary order)
|
|
|
|
sorted.sort_by(|info1, info2| {
|
|
|
|
// (reversing cmp order to get large-to-small ordering)
|
|
|
|
match info2.overall_size.cmp(&info1.overall_size) {
|
|
|
|
Ordering::Equal => info1.type_description.cmp(&info2.type_description),
|
|
|
|
other => other,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
for info in &sorted {
|
|
|
|
println!("print-type-size type: `{}`: {} bytes, alignment: {} bytes",
|
|
|
|
info.type_description, info.overall_size, info.align);
|
|
|
|
let indent = " ";
|
|
|
|
|
|
|
|
let discr_size = if let Some(discr_size) = info.opt_discr_size {
|
|
|
|
println!("print-type-size {}discriminant: {} bytes",
|
|
|
|
indent, discr_size);
|
|
|
|
discr_size
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
// We start this at discr_size (rather than 0) because
|
|
|
|
// things like C-enums do not have variants but we still
|
|
|
|
// want the max_variant_size at the end of the loop below
|
|
|
|
// to reflect the presence of the discriminant.
|
|
|
|
let mut max_variant_size = discr_size;
|
|
|
|
|
|
|
|
let struct_like = match info.kind {
|
|
|
|
DataTypeKind::Struct | DataTypeKind::Closure => true,
|
|
|
|
DataTypeKind::Enum | DataTypeKind::Union => false,
|
|
|
|
};
|
|
|
|
for (i, variant_info) in info.variants.iter().enumerate() {
|
|
|
|
let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
|
|
|
|
let indent = if !struct_like {
|
|
|
|
let name = match name.as_ref() {
|
2018-07-27 04:11:18 -05:00
|
|
|
Some(name) => name.to_owned(),
|
|
|
|
None => i.to_string(),
|
2016-11-15 10:48:07 -06:00
|
|
|
};
|
|
|
|
println!("print-type-size {}variant `{}`: {} bytes",
|
|
|
|
indent, name, size - discr_size);
|
|
|
|
" "
|
|
|
|
} else {
|
|
|
|
assert!(i < 1);
|
|
|
|
" "
|
|
|
|
};
|
|
|
|
max_variant_size = cmp::max(max_variant_size, size);
|
|
|
|
|
|
|
|
let mut min_offset = discr_size;
|
2016-11-26 13:37:15 -06:00
|
|
|
|
|
|
|
// We want to print fields by increasing offset.
|
|
|
|
let mut fields = fields.clone();
|
|
|
|
fields.sort_by_key(|f| f.offset);
|
|
|
|
|
|
|
|
for field in fields.iter() {
|
2016-11-15 10:48:07 -06:00
|
|
|
let FieldInfo { ref name, offset, size, align } = *field;
|
|
|
|
|
2018-02-04 05:10:28 -06:00
|
|
|
if offset > min_offset {
|
|
|
|
let pad = offset - min_offset;
|
|
|
|
println!("print-type-size {}padding: {} bytes",
|
|
|
|
indent, pad);
|
|
|
|
}
|
|
|
|
|
|
|
|
if offset < min_offset {
|
|
|
|
// if this happens something is very wrong
|
|
|
|
println!("print-type-size {}field `.{}`: {} bytes, \
|
|
|
|
offset: {} bytes, \
|
|
|
|
alignment: {} bytes",
|
|
|
|
indent, name, size, offset, align);
|
|
|
|
} else if info.packed || offset == min_offset {
|
2016-11-15 10:48:07 -06:00
|
|
|
println!("print-type-size {}field `.{}`: {} bytes",
|
|
|
|
indent, name, size);
|
2018-02-04 05:10:28 -06:00
|
|
|
} else {
|
|
|
|
// Include field alignment in output only if it caused padding injection
|
|
|
|
println!("print-type-size {}field `.{}`: {} bytes, \
|
|
|
|
alignment: {} bytes",
|
|
|
|
indent, name, size, align);
|
2016-11-15 10:48:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
min_offset = offset + size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(max_variant_size <= info.overall_size,
|
|
|
|
"max_variant_size {} !<= {} overall_size",
|
|
|
|
max_variant_size, info.overall_size);
|
|
|
|
if max_variant_size < info.overall_size {
|
|
|
|
println!("print-type-size {}end padding: {} bytes",
|
|
|
|
indent, info.overall_size - max_variant_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|