From 9383fcf07f85d8e8b91a6150cf4ca252b37a5383 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 14 Nov 2016 17:46:20 +0100 Subject: [PATCH 1/3] Add `-Z print-type-sizes`, a tool for digging into how variants are laid out. --- src/librustc/session/config.rs | 2 + src/librustc/session/mod.rs | 74 ++++++++- src/librustc_driver/driver.rs | 10 ++ src/librustc_mir/transform/mod.rs | 1 + .../transform/print_type_sizes.rs | 152 ++++++++++++++++++ 5 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/librustc_mir/transform/print_type_sizes.rs diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index f3677b8081953..26dafed7019ed 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -909,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "keep the AST after lowering it to HIR"), show_span: Option = (None, parse_opt_string, [TRACKED], "show spans for compiler debugging (expr|pat|ty)"), + print_type_sizes: bool = (false, parse_bool, [UNTRACKED], + "print layout information for each type encountered"), print_trans_items: Option = (None, parse_opt_string, [UNTRACKED], "print the result of the translation item collection pass"), mir_opt_level: Option = (None, parse_opt_uint, [TRACKED], diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9577a25b3f830..128e4d878a8dc 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -112,9 +112,80 @@ pub struct Session { /// Some measurements that are being gathered during compilation. pub perf_stats: PerfStats, + /// Data about code being compiled, gathered during compilation. + pub code_stats: RefCell, + next_node_id: Cell, } +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum VariantSize { + Exact(u64), + Min(u64), +} + +#[derive(PartialEq, Eq, Debug)] +pub struct TypeSizeInfo { + pub type_description: String, + pub overall_size: u64, + pub variant_sizes: Option>, +} + +#[derive(PartialEq, Eq, Debug)] +pub struct CodeStats { + pub type_sizes: Vec, +} + +impl CodeStats { + fn new() -> Self { + CodeStats { type_sizes: Vec::new() } + } + + pub fn record_type_size(&mut self, + type_desc: S, + overall_size: u64, + variant_sizes: Vec) { + let sizes = if variant_sizes.len() == 0 { None } else { Some(variant_sizes) }; + let info = TypeSizeInfo { + type_description: type_desc.to_string(), + overall_size: overall_size, + variant_sizes: sizes, + }; + if !self.type_sizes.contains(&info) { + self.type_sizes.push(info); + } + } + + pub fn sort_by_type_description(&mut self) { + self.type_sizes.sort_by(|info1, info2| { + info1.type_description.cmp(&info2.type_description) + }); + } + + pub fn sort_by_overall_size(&mut self) { + self.type_sizes.sort_by(|info1, info2| { + // (reversing cmp order to get large-to-small ordering) + info2.overall_size.cmp(&info1.overall_size) + }); + } + + pub fn print_type_sizes(&self) { + for info in &self.type_sizes { + println!("print-type-size t: `{}` overall bytes: {}", + info.type_description, info.overall_size); + if let Some(ref variant_sizes) = info.variant_sizes { + for (i, variant_size) in variant_sizes.iter().enumerate() { + let (kind, s) = match *variant_size { + VariantSize::Exact(s) => { ("exact", s) } + VariantSize::Min(s) => { (" min", s) } + }; + println!("print-type-size variant[{}] {} bytes: {}", i, kind, s); + } + } + } + } +} + pub struct PerfStats { // The accumulated time needed for computing the SVH of the crate pub svh_time: Cell, @@ -624,7 +695,8 @@ pub fn build_session_(sopts: config::Options, incr_comp_hashes_count: Cell::new(0), incr_comp_bytes_hashed: Cell::new(0), symbol_hash_time: Cell::new(Duration::from_secs(0)), - } + }, + code_stats: RefCell::new(CodeStats::new()), }; init_llvm(&sess); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 5cbb8f93fc9d4..a91525c6b2d0a 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -215,6 +215,13 @@ pub fn compile_input(sess: &Session, })?? }; + if sess.opts.debugging_opts.print_type_sizes { + // (these are stable sorts) + sess.code_stats.borrow_mut().sort_by_type_description(); + sess.code_stats.borrow_mut().sort_by_overall_size(); + sess.code_stats.borrow().print_type_sizes(); + } + let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs); controller_entry_point!(after_llvm, @@ -1008,6 +1015,9 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time(time_passes, "MIR optimisations", || { let mut passes = ::rustc::mir::transform::Passes::new(); passes.push_hook(box mir::transform::dump_mir::DumpMir); + if tcx.sess.opts.debugging_opts.print_type_sizes { + passes.push_pass(box mir::transform::print_type_sizes::GatherTypeSizesMir::new()); + } passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("no-landing-pads")); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index ae255f70fb788..eed4763c17dd2 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -13,6 +13,7 @@ pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; pub mod type_check; +pub mod print_type_sizes; pub mod add_call_guards; pub mod promote_consts; pub mod qualify_consts; diff --git a/src/librustc_mir/transform/print_type_sizes.rs b/src/librustc_mir/transform/print_type_sizes.rs new file mode 100644 index 0000000000000..617a5ac78df35 --- /dev/null +++ b/src/librustc_mir/transform/print_type_sizes.rs @@ -0,0 +1,152 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass implements instrumentation to gather the layout of every type. + +use rustc::session::{VariantSize}; +use rustc::traits::{Reveal}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::fold::{TypeFoldable}; +use rustc::ty::layout::{Layout}; +use rustc::mir::{Mir}; +use rustc::mir::transform::{MirPass, MirPassHook, MirSource, Pass}; +use rustc::mir::visit::Visitor; + +use std::collections::HashSet; + +pub struct GatherTypeSizesMir { + _hidden: (), +} + +impl GatherTypeSizesMir { + pub fn new() -> Self { + GatherTypeSizesMir { _hidden: () } + } +} + +impl Pass for GatherTypeSizesMir { +} + +impl<'tcx> MirPassHook<'tcx> for GatherTypeSizesMir { + fn on_mir_pass<'a>(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + _pass: &Pass, + _is_after: bool) { + debug!("on_mir_pass: {}", tcx.node_path_str(src.item_id())); + self.go(tcx, mir); + } +} + +impl<'tcx> MirPass<'tcx> for GatherTypeSizesMir { + fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, mir: &mut Mir<'tcx>) { + debug!("run_pass: {}", tcx.node_path_str(src.item_id())); + self.go(tcx, mir); + } +} + +impl GatherTypeSizesMir { + fn go<'a, 'tcx>(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>) { + if tcx.sess.err_count() > 0 { + // compiling a broken program can obviously result in a + // broken MIR, so do not bother trying to process it. + return; + } + + let mut visitor = TypeVisitor { + tcx: tcx, + seen: HashSet::new(), + }; + visitor.visit_mir(mir); + } +} + +struct TypeVisitor<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + seen: HashSet>, +} + +impl<'a, 'tcx: 'a> Visitor<'tcx> for TypeVisitor<'a, 'tcx> { + fn visit_ty(&mut self, ty: &Ty<'tcx>) { + debug!("TypeVisitor::visit_ty ty=`{:?}`", ty); + + match ty.sty { + ty::TyAdt(..) | + ty::TyClosure(..) => {} // fall through + _ => { + debug!("print-type-size t: `{:?}` skip non-nominal", ty); + return; + } + } + + if ty.has_param_types() { + debug!("print-type-size t: `{:?}` skip has param types", ty); + return; + } + if ty.has_projection_types() { + debug!("print-type-size t: `{:?}` skip has projections", ty); + return; + } + + if self.seen.contains(ty) { + return; + } + self.seen.insert(ty); + + let reveal = Reveal::All; + // let reveal = Reveal::NotSpecializable; + + self.tcx.infer_ctxt(None, None, reveal).enter(|infcx| { + match ty.layout(&infcx) { + Ok(layout) => { + let type_desc = format!("{:?}", ty); + let overall_size = layout.size(&Default::default()); + + let variant_sizes: Vec<_> = match *layout { + Layout::General { ref variants, .. } => { + variants.iter() + .map(|v| if v.sized { + VariantSize::Exact(v.min_size.bytes()) + } else { + VariantSize::Min(v.min_size.bytes()) + }) + .collect() + } + + Layout::UntaggedUnion { variants: _ } => { + /* layout does not currently store info about each variant... */ + Vec::new() + } + + // RawNullablePointer/StructWrappedNullablePointer + // don't provide any interesting size info + // beyond what we already reported for their + // total size. + _ => { + Vec::new() + } + }; + + self.tcx.sess.code_stats.borrow_mut() + .record_type_size(type_desc, + overall_size.bytes(), + variant_sizes); + } + Err(err) => { + self.tcx.sess.warn(&format!("print-type-size t: `{:?}` err: {:?}", ty, err)); + } + } + }); + } +} From 70e5ca2ab49ca31485780f6fa981c5164b2cc848 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 15 Nov 2016 17:48:07 +0100 Subject: [PATCH 2/3] Revisions from review comments, squashed. Biggest change: Revised print-type-sizes output to include breakdown of layout. Includes info about field sizes (and alignment + padding when padding is injected; the injected padding is derived from the offsets computed by layout module). Output format is illustrated in commit that has the ui tests. Note: there exists (at least) one case of variant w/o name: empty enums. Namely, empty enums use anonymous univariant repr. So for such cases, print the number of the variant instead of the name. ---- Also, eddyb suggested of reading from `layout_cache` post-trans. (For casual readers: the compiler source often uses the word "cache" for tables that are in fact not periodically purged, and thus are useful as the basis for data like this.) Some types that were previously not printed are now included in the output. (See e.g. the tests `print_type_sizes/generics.rs` and `print_type_sizes/variants.rs`) ---- Other review feedback: switch to an exhaustive match when filtering in just structural types. switch to hashset for layout info and move sort into print method. ---- Driveby change: Factored session::code_stats into its own module ---- incorporate njn feedback re output formatting. --- src/librustc/session/code_stats.rs | 173 ++++++++++++++++ src/librustc/session/mod.rs | 72 +------ src/librustc/ty/layout.rs | 7 + src/librustc_driver/driver.rs | 6 - src/librustc_mir/transform/mod.rs | 1 - .../transform/print_type_sizes.rs | 152 -------------- src/librustc_trans/adt.rs | 1 + src/librustc_trans/base.rs | 193 +++++++++++++++++- 8 files changed, 377 insertions(+), 228 deletions(-) create mode 100644 src/librustc/session/code_stats.rs delete mode 100644 src/librustc_mir/transform/print_type_sizes.rs diff --git a/src/librustc/session/code_stats.rs b/src/librustc/session/code_stats.rs new file mode 100644 index 0000000000000..8308c54d70bf4 --- /dev/null +++ b/src/librustc/session/code_stats.rs @@ -0,0 +1,173 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ty::AdtKind; +use ty::layout::{Align, Size}; + +use rustc_data_structures::fx::{FxHashSet}; + +use std::cmp::{self, Ordering}; + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct VariantInfo { + pub name: Option, + pub kind: SizeKind, + pub size: u64, + pub align: u64, + pub fields: Vec, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum SizeKind { Exact, Min } + +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct FieldInfo { + pub name: String, + pub offset: u64, + pub size: u64, + pub align: u64, +} + +impl From for DataTypeKind { + fn from(kind: AdtKind) -> Self { + match kind { + AdtKind::Struct => DataTypeKind::Struct, + AdtKind::Enum => DataTypeKind::Enum, + AdtKind::Union => DataTypeKind::Union, + } + } +} + +#[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, + pub opt_discr_size: Option, + pub variants: Vec, +} + +#[derive(PartialEq, Eq, Debug)] +pub struct CodeStats { + type_sizes: FxHashSet, +} + +impl CodeStats { + pub fn new() -> Self { CodeStats { type_sizes: FxHashSet() } } + + pub fn record_type_size(&mut self, + kind: DataTypeKind, + type_desc: S, + align: Align, + overall_size: Size, + opt_discr_size: Option, + variants: Vec) { + let info = TypeSizeInfo { + kind: kind, + type_description: type_desc.to_string(), + align: align.abi(), + overall_size: overall_size.bytes(), + opt_discr_size: opt_discr_size.map(|s| s.bytes()), + variants: variants, + }; + 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() { + Some(name) => format!("{}", name), + None => format!("{}", i), + }; + 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; + for field in fields { + let FieldInfo { ref name, offset, size, align } = *field; + + // Include field alignment in output only if it caused padding injection + if min_offset != offset { + let pad = offset - min_offset; + println!("print-type-size {}padding: {} bytes", + indent, pad); + println!("print-type-size {}field `.{}`: {} bytes, alignment: {} bytes", + indent, name, size, align); + } else { + println!("print-type-size {}field `.{}`: {} bytes", + indent, name, size); + } + + 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); + } + } + } +} diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 128e4d878a8dc..3d8cfd199615e 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo}; +pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo}; + use dep_graph::DepGraph; use hir::def_id::{CrateNum, DefIndex}; use hir::svh::Svh; @@ -49,6 +52,7 @@ use std::fmt; use std::time::Duration; use libc::c_int; +mod code_stats; pub mod config; pub mod filesearch; pub mod search_paths; @@ -118,74 +122,6 @@ pub struct Session { next_node_id: Cell, } -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum VariantSize { - Exact(u64), - Min(u64), -} - -#[derive(PartialEq, Eq, Debug)] -pub struct TypeSizeInfo { - pub type_description: String, - pub overall_size: u64, - pub variant_sizes: Option>, -} - -#[derive(PartialEq, Eq, Debug)] -pub struct CodeStats { - pub type_sizes: Vec, -} - -impl CodeStats { - fn new() -> Self { - CodeStats { type_sizes: Vec::new() } - } - - pub fn record_type_size(&mut self, - type_desc: S, - overall_size: u64, - variant_sizes: Vec) { - let sizes = if variant_sizes.len() == 0 { None } else { Some(variant_sizes) }; - let info = TypeSizeInfo { - type_description: type_desc.to_string(), - overall_size: overall_size, - variant_sizes: sizes, - }; - if !self.type_sizes.contains(&info) { - self.type_sizes.push(info); - } - } - - pub fn sort_by_type_description(&mut self) { - self.type_sizes.sort_by(|info1, info2| { - info1.type_description.cmp(&info2.type_description) - }); - } - - pub fn sort_by_overall_size(&mut self) { - self.type_sizes.sort_by(|info1, info2| { - // (reversing cmp order to get large-to-small ordering) - info2.overall_size.cmp(&info1.overall_size) - }); - } - - pub fn print_type_sizes(&self) { - for info in &self.type_sizes { - println!("print-type-size t: `{}` overall bytes: {}", - info.type_description, info.overall_size); - if let Some(ref variant_sizes) = info.variant_sizes { - for (i, variant_size) in variant_sizes.iter().enumerate() { - let (kind, s) = match *variant_size { - VariantSize::Exact(s) => { ("exact", s) } - VariantSize::Min(s) => { (" min", s) } - }; - println!("print-type-size variant[{}] {} bytes: {}", i, kind, s); - } - } - } - } -} - pub struct PerfStats { // The accumulated time needed for computing the SVH of the crate pub svh_time: Cell, diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5ee1c3678d675..bc3c5d6ed4e20 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -559,11 +559,14 @@ impl<'a, 'gcx, 'tcx> Struct { self.offsets.push(offset); + debug!("Struct::extend offset: {:?} field: {:?} {:?}", offset, field, field.size(dl)); offset = offset.checked_add(field.size(dl), dl) .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; } + debug!("Struct::extend min_size: {:?}", offset); + self.min_size = offset; Ok(()) @@ -707,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Union { index, scapegoat); } + debug!("Union::extend field: {:?} {:?}", field, field.size(dl)); + if !self.packed { self.align = self.align.max(field.align(dl)); } self.min_size = cmp::max(self.min_size, field.size(dl)); } + debug!("Union::extend min-size: {:?}", self.min_size); + Ok(()) } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index a91525c6b2d0a..9a4ecef0c0ebd 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -216,9 +216,6 @@ pub fn compile_input(sess: &Session, }; if sess.opts.debugging_opts.print_type_sizes { - // (these are stable sorts) - sess.code_stats.borrow_mut().sort_by_type_description(); - sess.code_stats.borrow_mut().sort_by_overall_size(); sess.code_stats.borrow().print_type_sizes(); } @@ -1015,9 +1012,6 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, time(time_passes, "MIR optimisations", || { let mut passes = ::rustc::mir::transform::Passes::new(); passes.push_hook(box mir::transform::dump_mir::DumpMir); - if tcx.sess.opts.debugging_opts.print_type_sizes { - passes.push_pass(box mir::transform::print_type_sizes::GatherTypeSizesMir::new()); - } passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("no-landing-pads")); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index eed4763c17dd2..ae255f70fb788 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -13,7 +13,6 @@ pub mod simplify; pub mod erase_regions; pub mod no_landing_pads; pub mod type_check; -pub mod print_type_sizes; pub mod add_call_guards; pub mod promote_consts; pub mod qualify_consts; diff --git a/src/librustc_mir/transform/print_type_sizes.rs b/src/librustc_mir/transform/print_type_sizes.rs deleted file mode 100644 index 617a5ac78df35..0000000000000 --- a/src/librustc_mir/transform/print_type_sizes.rs +++ /dev/null @@ -1,152 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! This pass implements instrumentation to gather the layout of every type. - -use rustc::session::{VariantSize}; -use rustc::traits::{Reveal}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::fold::{TypeFoldable}; -use rustc::ty::layout::{Layout}; -use rustc::mir::{Mir}; -use rustc::mir::transform::{MirPass, MirPassHook, MirSource, Pass}; -use rustc::mir::visit::Visitor; - -use std::collections::HashSet; - -pub struct GatherTypeSizesMir { - _hidden: (), -} - -impl GatherTypeSizesMir { - pub fn new() -> Self { - GatherTypeSizesMir { _hidden: () } - } -} - -impl Pass for GatherTypeSizesMir { -} - -impl<'tcx> MirPassHook<'tcx> for GatherTypeSizesMir { - fn on_mir_pass<'a>(&mut self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - _pass: &Pass, - _is_after: bool) { - debug!("on_mir_pass: {}", tcx.node_path_str(src.item_id())); - self.go(tcx, mir); - } -} - -impl<'tcx> MirPass<'tcx> for GatherTypeSizesMir { - fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, mir: &mut Mir<'tcx>) { - debug!("run_pass: {}", tcx.node_path_str(src.item_id())); - self.go(tcx, mir); - } -} - -impl GatherTypeSizesMir { - fn go<'a, 'tcx>(&mut self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - mir: &Mir<'tcx>) { - if tcx.sess.err_count() > 0 { - // compiling a broken program can obviously result in a - // broken MIR, so do not bother trying to process it. - return; - } - - let mut visitor = TypeVisitor { - tcx: tcx, - seen: HashSet::new(), - }; - visitor.visit_mir(mir); - } -} - -struct TypeVisitor<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - seen: HashSet>, -} - -impl<'a, 'tcx: 'a> Visitor<'tcx> for TypeVisitor<'a, 'tcx> { - fn visit_ty(&mut self, ty: &Ty<'tcx>) { - debug!("TypeVisitor::visit_ty ty=`{:?}`", ty); - - match ty.sty { - ty::TyAdt(..) | - ty::TyClosure(..) => {} // fall through - _ => { - debug!("print-type-size t: `{:?}` skip non-nominal", ty); - return; - } - } - - if ty.has_param_types() { - debug!("print-type-size t: `{:?}` skip has param types", ty); - return; - } - if ty.has_projection_types() { - debug!("print-type-size t: `{:?}` skip has projections", ty); - return; - } - - if self.seen.contains(ty) { - return; - } - self.seen.insert(ty); - - let reveal = Reveal::All; - // let reveal = Reveal::NotSpecializable; - - self.tcx.infer_ctxt(None, None, reveal).enter(|infcx| { - match ty.layout(&infcx) { - Ok(layout) => { - let type_desc = format!("{:?}", ty); - let overall_size = layout.size(&Default::default()); - - let variant_sizes: Vec<_> = match *layout { - Layout::General { ref variants, .. } => { - variants.iter() - .map(|v| if v.sized { - VariantSize::Exact(v.min_size.bytes()) - } else { - VariantSize::Min(v.min_size.bytes()) - }) - .collect() - } - - Layout::UntaggedUnion { variants: _ } => { - /* layout does not currently store info about each variant... */ - Vec::new() - } - - // RawNullablePointer/StructWrappedNullablePointer - // don't provide any interesting size info - // beyond what we already reported for their - // total size. - _ => { - Vec::new() - } - }; - - self.tcx.sess.code_stats.borrow_mut() - .record_type_size(type_desc, - overall_size.bytes(), - variant_sizes); - } - Err(err) => { - self.tcx.sess.warn(&format!("print-type-size t: `{:?}` err: {:?}", ty, err)); - } - } - }); - } -} diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index c3340281d0732..e091ba07d4fea 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -247,6 +247,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // of the size. let size = size.bytes(); let align = align.abi(); + assert!(align <= std::u32::MAX as u64); let discr_ty = Type::from_integer(cx, discr); let discr_size = discr.size().bytes(); let padded_discr_size = roundup(discr_size, align as u32); diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 4353c7bd58645..d697a5bafb79e 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -47,7 +47,7 @@ use rustc::hir::map as hir_map; use rustc::util::common::time; use session::config::{self, NoDebugInfo}; use rustc_incremental::IncrementalHashesMap; -use session::Session; +use session::{self, DataTypeKind, Session}; use abi::{self, Abi, FnType}; use adt; use attributes; @@ -93,6 +93,7 @@ use std::i32; use syntax_pos::{Span, DUMMY_SP}; use syntax::attr; use rustc::hir; +use rustc::ty::layout::{self, Layout}; use syntax::ast; thread_local! { @@ -1741,6 +1742,10 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, .collect()) }); + if tcx.sess.opts.debugging_opts.print_type_sizes { + gather_type_sizes(tcx); + } + if sess.target.target.options.is_like_msvc && sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) { create_imps(&crate_context_list); @@ -1771,6 +1776,192 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +fn gather_type_sizes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + let layout_cache = tcx.layout_cache.borrow(); + for (ty, layout) in layout_cache.iter() { + + // (delay format until we actually need it) + let record = |kind, opt_discr_size, variants| { + let type_desc = format!("{:?}", ty); + let overall_size = layout.size(&tcx.data_layout); + let align = layout.align(&tcx.data_layout); + tcx.sess.code_stats.borrow_mut().record_type_size(kind, + type_desc, + align, + overall_size, + opt_discr_size, + variants); + }; + + let (adt_def, substs) = match ty.sty { + ty::TyAdt(ref adt_def, substs) => { + debug!("print-type-size t: `{:?}` process adt", ty); + (adt_def, substs) + } + + ty::TyClosure(..) => { + debug!("print-type-size t: `{:?}` record closure", ty); + record(DataTypeKind::Closure, None, vec![]); + continue; + } + + _ => { + debug!("print-type-size t: `{:?}` skip non-nominal", ty); + continue; + } + }; + + let adt_kind = adt_def.adt_kind(); + + let build_field_info = |(field_name, field_ty): (ast::Name, Ty), offset: &layout::Size| { + match layout_cache.get(&field_ty) { + None => bug!("no layout found for field {} type: `{:?}`", field_name, field_ty), + Some(field_layout) => { + session::FieldInfo { + name: field_name.to_string(), + offset: offset.bytes(), + size: field_layout.size(&tcx.data_layout).bytes(), + align: field_layout.align(&tcx.data_layout).abi(), + } + } + } + }; + + let build_primitive_info = |name: ast::Name, value: &layout::Primitive| { + session::VariantInfo { + name: Some(name.to_string()), + kind: session::SizeKind::Exact, + align: value.align(&tcx.data_layout).abi(), + size: value.size(&tcx.data_layout).bytes(), + fields: vec![], + } + }; + + enum Fields<'a> { + WithDiscrim(&'a layout::Struct), + NoDiscrim(&'a layout::Struct), + } + + let build_variant_info = |n: Option, flds: &[(ast::Name, Ty)], layout: Fields| { + let (s, field_offsets) = match layout { + Fields::WithDiscrim(s) => (s, &s.offsets[1..]), + Fields::NoDiscrim(s) => (s, &s.offsets[0..]), + }; + let field_info: Vec<_> = flds.iter() + .zip(field_offsets.iter()) + .map(|(&field_name_ty, offset)| build_field_info(field_name_ty, offset)) + .collect(); + + session::VariantInfo { + name: n.map(|n|n.to_string()), + kind: if s.sized { + session::SizeKind::Exact + } else { + session::SizeKind::Min + }, + align: s.align.abi(), + size: s.min_size.bytes(), + fields: field_info, + } + }; + + match **layout { + Layout::StructWrappedNullablePointer { nonnull: ref variant_layout, + nndiscr, + discrfield: _ } => { + debug!("print-type-size t: `{:?}` adt struct-wrapped nullable nndiscr {} is {:?}", + ty, nndiscr, variant_layout); + let variant_def = &adt_def.variants[nndiscr as usize]; + let fields: Vec<_> = variant_def.fields.iter() + .map(|field_def| (field_def.name, field_def.ty(tcx, substs))) + .collect(); + record(adt_kind.into(), + None, + vec![build_variant_info(Some(variant_def.name), + &fields, + Fields::NoDiscrim(variant_layout))]); + } + Layout::RawNullablePointer { nndiscr, value } => { + debug!("print-type-size t: `{:?}` adt raw nullable nndiscr {} is {:?}", + ty, nndiscr, value); + let variant_def = &adt_def.variants[nndiscr as usize]; + record(adt_kind.into(), None, + vec![build_primitive_info(variant_def.name, &value)]); + } + Layout::Univariant { variant: ref variant_layout, non_zero: _ } => { + let variant_names = || { + adt_def.variants.iter().map(|v|format!("{}", v.name)).collect::>() + }; + debug!("print-type-size t: `{:?}` adt univariant {:?} variants: {:?}", + ty, variant_layout, variant_names()); + assert!(adt_def.variants.len() <= 1, + "univariant with variants {:?}", variant_names()); + if adt_def.variants.len() == 1 { + let variant_def = &adt_def.variants[0]; + let fields: Vec<_> = variant_def.fields.iter() + .map(|field_def| (field_def.name, field_def.ty(tcx, substs))) + .collect(); + record(adt_kind.into(), + None, + vec![build_variant_info(Some(variant_def.name), + &fields, + Fields::NoDiscrim(variant_layout))]); + } else { + // (This case arises for *empty* enums; so give it + // zero variants.) + record(adt_kind.into(), None, vec![]); + } + } + + Layout::General { ref variants, discr, .. } => { + debug!("print-type-size t: `{:?}` adt general variants def {} layouts {} {:?}", + ty, adt_def.variants.len(), variants.len(), variants); + let variant_infos: Vec<_> = adt_def.variants.iter() + .zip(variants.iter()) + .map(|(variant_def, variant_layout)| { + let fields: Vec<_> = variant_def.fields.iter() + .map(|field_def| (field_def.name, field_def.ty(tcx, substs))) + .collect(); + build_variant_info(Some(variant_def.name), + &fields, + Fields::WithDiscrim(variant_layout)) + }) + .collect(); + record(adt_kind.into(), Some(discr.size()), variant_infos); + } + + Layout::UntaggedUnion { ref variants } => { + debug!("print-type-size t: `{:?}` adt union variants {:?}", + ty, variants); + // layout does not currently store info about each + // variant... + record(adt_kind.into(), None, Vec::new()); + } + + Layout::CEnum { discr, .. } => { + debug!("print-type-size t: `{:?}` adt c-like enum", ty); + let variant_infos: Vec<_> = adt_def.variants.iter() + .map(|variant_def| { + build_primitive_info(variant_def.name, + &layout::Primitive::Int(discr)) + }) + .collect(); + record(adt_kind.into(), Some(discr.size()), variant_infos); + } + + // other cases provide little interesting (i.e. adjustable + // via representation tweaks) size info beyond total size. + Layout::Scalar { .. } | + Layout::Vector { .. } | + Layout::Array { .. } | + Layout::FatPointer { .. } => { + debug!("print-type-size t: `{:?}` adt other", ty); + record(adt_kind.into(), None, Vec::new()) + } + } + } +} + /// For each CGU, identify if we can reuse an existing object file (or /// maybe other context). fn trans_reuse_previous_work_products(tcx: TyCtxt, From 75825fe1df47866e1821d8b09f4c75930b6e57c1 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 24 Nov 2016 10:28:29 +0100 Subject: [PATCH 3/3] Tests of `-Z print-type-sizes` functionality. Note that the tests have been updated to initialize the local variables; originally it was enough just to declare them. Back when I started this, the `layout_cache` contained entries even just for types that had been declared but not initialized. Apparently things have changed in the interim so that if I want one of those layouts to be computed, I need to actually initialize the value. (Incidentally, this shows a weakness in the strategy of just walking the `layout_cache`; the original strategy of using a MIR visitor would probably have exhibited more robustness in terms of consistent output, but it had other weaknesses so I chose not to reimplement it. At least, not yet.) ---- Also, I have updated tests to avoid target-specific alignments. --- src/test/ui/print_type_sizes/anonymous.rs | 27 +++++++ src/test/ui/print_type_sizes/generics.rs | 73 +++++++++++++++++++ src/test/ui/print_type_sizes/generics.stdout | 14 ++++ .../ui/print_type_sizes/multiple_types.rs | 28 +++++++ .../ui/print_type_sizes/multiple_types.stdout | 10 +++ src/test/ui/print_type_sizes/no_duplicates.rs | 25 +++++++ .../ui/print_type_sizes/no_duplicates.stdout | 2 + src/test/ui/print_type_sizes/nullable.rs | 69 ++++++++++++++++++ src/test/ui/print_type_sizes/nullable.stdout | 27 +++++++ src/test/ui/print_type_sizes/packed.rs | 49 +++++++++++++ src/test/ui/print_type_sizes/packed.stdout | 17 +++++ src/test/ui/print_type_sizes/padding.rs | 39 ++++++++++ src/test/ui/print_type_sizes/padding.stdout | 21 ++++++ src/test/ui/print_type_sizes/variants.rs | 31 ++++++++ src/test/ui/print_type_sizes/variants.stdout | 10 +++ 15 files changed, 442 insertions(+) create mode 100644 src/test/ui/print_type_sizes/anonymous.rs create mode 100644 src/test/ui/print_type_sizes/generics.rs create mode 100644 src/test/ui/print_type_sizes/generics.stdout create mode 100644 src/test/ui/print_type_sizes/multiple_types.rs create mode 100644 src/test/ui/print_type_sizes/multiple_types.stdout create mode 100644 src/test/ui/print_type_sizes/no_duplicates.rs create mode 100644 src/test/ui/print_type_sizes/no_duplicates.stdout create mode 100644 src/test/ui/print_type_sizes/nullable.rs create mode 100644 src/test/ui/print_type_sizes/nullable.stdout create mode 100644 src/test/ui/print_type_sizes/packed.rs create mode 100644 src/test/ui/print_type_sizes/packed.stdout create mode 100644 src/test/ui/print_type_sizes/padding.rs create mode 100644 src/test/ui/print_type_sizes/padding.stdout create mode 100644 src/test/ui/print_type_sizes/variants.rs create mode 100644 src/test/ui/print_type_sizes/variants.stdout diff --git a/src/test/ui/print_type_sizes/anonymous.rs b/src/test/ui/print_type_sizes/anonymous.rs new file mode 100644 index 0000000000000..dc93bddbad8e2 --- /dev/null +++ b/src/test/ui/print_type_sizes/anonymous.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// All of the types that occur in this function are uninteresting, in +// that one cannot control the sizes of these types with the same sort +// of enum-variant manipulation tricks. + +pub fn main() { + let _byte: u8 = 0; + let _word: usize = 0; + let _tuple: (u8, usize)= (0, 0); + let _array: [u8; 128] = [0; 128]; + let _fn: fn (u8) -> u8 = id; + let _diverging: fn (u8) -> ! = bye; + + fn id(x: u8) -> u8 { x }; + fn bye(_: u8) -> ! { loop { } } +} diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs new file mode 100644 index 0000000000000..93bcd1c36e592 --- /dev/null +++ b/src/test/ui/print_type_sizes/generics.rs @@ -0,0 +1,73 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how generics are handled: types have to be +// monomorphized, in the MIR of the original function in which they +// occur, to have their size reported. + +// In an ad-hoc attempt to avoid the injection of unwinding code +// (which clutters the output of `-Z print-type-sizes` with types from +// `unwind::libunwind`): +// +// * I am not using Default to build values because that seems to +// cause the injection of unwinding code. (Instead I just make `fn new` +// methods.) +// +// * Pair derive Copy to ensure that we don't inject +// unwinding code into generic uses of Pair when T itself is also +// Copy. +// +// (I suspect this reflect some naivety within the rust compiler +// itself; it should be checking for drop glue, i.e. a destructor +// somewhere in the monomorphized types. It should not matter whether +// the type is Copy.) +#[derive(Copy, Clone)] +pub struct Pair { + _car: T, + _cdr: T, +} + +impl Pair { + fn new(a: T, d: T) -> Self { + Pair { + _car: a, + _cdr: d, + } + } +} + +#[derive(Copy, Clone)] +pub struct SevenBytes([u8; 7]); +pub struct FiftyBytes([u8; 50]); + +pub struct ZeroSized; + +impl SevenBytes { + fn new() -> Self { SevenBytes([0; 7]) } +} + +impl FiftyBytes { + fn new() -> Self { FiftyBytes([0; 50]) } +} + +pub fn f1(x: T) { + let _v: Pair = Pair::new(x, x); + let _v2: Pair = + Pair::new(FiftyBytes::new(), FiftyBytes::new()); +} + +pub fn main() { + let _b: Pair = Pair::new(0, 0); + let _s: Pair = Pair::new(SevenBytes::new(), SevenBytes::new()); + let _z: ZeroSized = ZeroSized; + f1::(SevenBytes::new()); +} diff --git a/src/test/ui/print_type_sizes/generics.stdout b/src/test/ui/print_type_sizes/generics.stdout new file mode 100644 index 0000000000000..0f02f39795365 --- /dev/null +++ b/src/test/ui/print_type_sizes/generics.stdout @@ -0,0 +1,14 @@ +print-type-size type: `Pair`: 100 bytes, alignment: 1 bytes +print-type-size field `._car`: 50 bytes +print-type-size field `._cdr`: 50 bytes +print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes +print-type-size field `.0`: 50 bytes +print-type-size type: `Pair`: 14 bytes, alignment: 1 bytes +print-type-size field `._car`: 7 bytes +print-type-size field `._cdr`: 7 bytes +print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes +print-type-size field `.0`: 7 bytes +print-type-size type: `Pair`: 2 bytes, alignment: 1 bytes +print-type-size field `._car`: 1 bytes +print-type-size field `._cdr`: 1 bytes +print-type-size type: `ZeroSized`: 0 bytes, alignment: 1 bytes diff --git a/src/test/ui/print_type_sizes/multiple_types.rs b/src/test/ui/print_type_sizes/multiple_types.rs new file mode 100644 index 0000000000000..2b5010767fdef --- /dev/null +++ b/src/test/ui/print_type_sizes/multiple_types.rs @@ -0,0 +1,28 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates that when multiple structural types occur in +// a function, every one of them is included in the output. + +pub struct SevenBytes([u8; 7]); +pub struct FiftyBytes([u8; 50]); + +pub enum Enum { + Small(SevenBytes), + Large(FiftyBytes), +} + +pub fn main() { + let _e: Enum; + let _f: FiftyBytes; + let _s: SevenBytes; +} diff --git a/src/test/ui/print_type_sizes/multiple_types.stdout b/src/test/ui/print_type_sizes/multiple_types.stdout new file mode 100644 index 0000000000000..eed9af26987b4 --- /dev/null +++ b/src/test/ui/print_type_sizes/multiple_types.stdout @@ -0,0 +1,10 @@ +print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Small`: 7 bytes +print-type-size field `.0`: 7 bytes +print-type-size variant `Large`: 50 bytes +print-type-size field `.0`: 50 bytes +print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes +print-type-size field `.0`: 50 bytes +print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes +print-type-size field `.0`: 7 bytes diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs new file mode 100644 index 0000000000000..6008a346c059f --- /dev/null +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -0,0 +1,25 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates that when the same type occurs repeatedly +// (even if multiple functions), it is only printed once in the +// print-type-sizes output. + +pub struct SevenBytes([u8; 7]); + +pub fn f1() { + let _s: SevenBytes = SevenBytes([0; 7]); +} + +pub fn main() { + let _s: SevenBytes = SevenBytes([0; 7]); +} diff --git a/src/test/ui/print_type_sizes/no_duplicates.stdout b/src/test/ui/print_type_sizes/no_duplicates.stdout new file mode 100644 index 0000000000000..50180f356ea36 --- /dev/null +++ b/src/test/ui/print_type_sizes/no_duplicates.stdout @@ -0,0 +1,2 @@ +print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes +print-type-size field `.0`: 7 bytes diff --git a/src/test/ui/print_type_sizes/nullable.rs b/src/test/ui/print_type_sizes/nullable.rs new file mode 100644 index 0000000000000..f7fdcac81daad --- /dev/null +++ b/src/test/ui/print_type_sizes/nullable.rs @@ -0,0 +1,69 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how enums with a non-null field are handled, +// modelled after cases like `Option<&u32>` and such. +// +// It uses NonZero directly, rather than `&_` or `Unique<_>`, because +// the test is not set up to deal with target-dependent pointer width. +// +// It avoids using u64/i64 because on some targets that is only 4-byte +// aligned (while on most it is 8-byte aligned) and so the resulting +// padding and overall computed sizes can be quite different. + +#![feature(nonzero)] +#![allow(dead_code)] + +extern crate core; +use core::nonzero::{NonZero, Zeroable}; + +pub enum MyOption { None, Some(T) } + +impl Default for MyOption { + fn default() -> Self { MyOption::None } +} + +pub enum EmbeddedDiscr { + None, + Record { pre: u8, val: NonZero, post: u16 }, +} + +impl Default for EmbeddedDiscr { + fn default() -> Self { EmbeddedDiscr::None } +} + +#[derive(Default)] +pub struct IndirectNonZero { + pre: u8, + nested: NestedNonZero, + post: u16, +} + +pub struct NestedNonZero { + pre: u8, + val: NonZero, + post: u16, +} + +impl Default for NestedNonZero { + fn default() -> Self { + unsafe { + NestedNonZero { pre: 0, val: NonZero::new(Default::default()), post: 0 } + } + } +} + +pub fn main() { + let _x: MyOption> = Default::default(); + let _y: EmbeddedDiscr = Default::default(); + let _z: MyOption> = Default::default(); +} diff --git a/src/test/ui/print_type_sizes/nullable.stdout b/src/test/ui/print_type_sizes/nullable.stdout new file mode 100644 index 0000000000000..dd999c4a5e4c7 --- /dev/null +++ b/src/test/ui/print_type_sizes/nullable.stdout @@ -0,0 +1,27 @@ +print-type-size type: `IndirectNonZero`: 20 bytes, alignment: 4 bytes +print-type-size field `.pre`: 1 bytes +print-type-size padding: 3 bytes +print-type-size field `.nested`: 12 bytes, alignment: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size end padding: 2 bytes +print-type-size type: `MyOption>`: 20 bytes, alignment: 4 bytes +print-type-size variant `Some`: 20 bytes +print-type-size field `.0`: 20 bytes +print-type-size type: `EmbeddedDiscr`: 12 bytes, alignment: 4 bytes +print-type-size variant `Record`: 10 bytes +print-type-size field `.pre`: 1 bytes +print-type-size padding: 3 bytes +print-type-size field `.val`: 4 bytes, alignment: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size end padding: 2 bytes +print-type-size type: `NestedNonZero`: 12 bytes, alignment: 4 bytes +print-type-size field `.pre`: 1 bytes +print-type-size padding: 3 bytes +print-type-size field `.val`: 4 bytes, alignment: 4 bytes +print-type-size field `.post`: 2 bytes +print-type-size end padding: 2 bytes +print-type-size type: `MyOption>`: 4 bytes, alignment: 4 bytes +print-type-size variant `Some`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size type: `core::nonzero::NonZero`: 4 bytes, alignment: 4 bytes +print-type-size field `.0`: 4 bytes diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs new file mode 100644 index 0000000000000..cd7ef86d70ee3 --- /dev/null +++ b/src/test/ui/print_type_sizes/packed.rs @@ -0,0 +1,49 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how packing is handled; it should cause +// the elimination of padding that would normally be introduced +// to satisfy alignment desirata. +// +// It avoids using u64/i64 because on some targets that is only 4-byte +// aligned (while on most it is 8-byte aligned) and so the resulting +// padding and overall computed sizes can be quite different. + +#![feature(untagged_unions)] + +#![allow(dead_code)] + +#[derive(Default)] +#[repr(packed)] +struct Packed { + a: u8, + b: u8, + g: i32, + c: u8, + h: i16, + d: u8, +} + +#[derive(Default)] +struct Padded { + a: u8, + b: u8, + g: i32, + c: u8, + h: i16, + d: u8, +} + +pub fn main() { + let _c: Packed = Default::default(); + let _d: Padded = Default::default(); +} diff --git a/src/test/ui/print_type_sizes/packed.stdout b/src/test/ui/print_type_sizes/packed.stdout new file mode 100644 index 0000000000000..1278a7d7c92c6 --- /dev/null +++ b/src/test/ui/print_type_sizes/packed.stdout @@ -0,0 +1,17 @@ +print-type-size type: `Padded`: 16 bytes, alignment: 4 bytes +print-type-size field `.a`: 1 bytes +print-type-size field `.b`: 1 bytes +print-type-size padding: 2 bytes +print-type-size field `.g`: 4 bytes, alignment: 4 bytes +print-type-size field `.c`: 1 bytes +print-type-size padding: 1 bytes +print-type-size field `.h`: 2 bytes, alignment: 2 bytes +print-type-size field `.d`: 1 bytes +print-type-size end padding: 3 bytes +print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes +print-type-size field `.a`: 1 bytes +print-type-size field `.b`: 1 bytes +print-type-size field `.g`: 4 bytes +print-type-size field `.c`: 1 bytes +print-type-size field `.h`: 2 bytes +print-type-size field `.d`: 1 bytes diff --git a/src/test/ui/print_type_sizes/padding.rs b/src/test/ui/print_type_sizes/padding.rs new file mode 100644 index 0000000000000..af34a908ce883 --- /dev/null +++ b/src/test/ui/print_type_sizes/padding.rs @@ -0,0 +1,39 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how padding is handled: alignment +// requirements can lead to the introduction of padding, either before +// fields or at the end of the structure as a whole. +// +// It avoids using u64/i64 because on some targets that is only 4-byte +// aligned (while on most it is 8-byte aligned) and so the resulting +// padding and overall computed sizes can be quite different. + +#![allow(dead_code)] + +struct S { + a: bool, + b: bool, + g: i32, +} + +enum E1 { + A(i32, i8), + B(S), +} + +enum E2 { + A(i8, i32), + B(S), +} + +fn main() { } diff --git a/src/test/ui/print_type_sizes/padding.stdout b/src/test/ui/print_type_sizes/padding.stdout new file mode 100644 index 0000000000000..bb95f790bd9e4 --- /dev/null +++ b/src/test/ui/print_type_sizes/padding.stdout @@ -0,0 +1,21 @@ +print-type-size type: `E1`: 12 bytes, alignment: 4 bytes +print-type-size discriminant: 4 bytes +print-type-size variant `A`: 5 bytes +print-type-size field `.0`: 4 bytes +print-type-size field `.1`: 1 bytes +print-type-size variant `B`: 8 bytes +print-type-size field `.0`: 8 bytes +print-type-size type: `E2`: 12 bytes, alignment: 4 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `A`: 7 bytes +print-type-size field `.0`: 1 bytes +print-type-size padding: 2 bytes +print-type-size field `.1`: 4 bytes, alignment: 4 bytes +print-type-size variant `B`: 11 bytes +print-type-size padding: 3 bytes +print-type-size field `.0`: 8 bytes, alignment: 4 bytes +print-type-size type: `S`: 8 bytes, alignment: 4 bytes +print-type-size field `.a`: 1 bytes +print-type-size field `.b`: 1 bytes +print-type-size padding: 2 bytes +print-type-size field `.g`: 4 bytes, alignment: 4 bytes diff --git a/src/test/ui/print_type_sizes/variants.rs b/src/test/ui/print_type_sizes/variants.rs new file mode 100644 index 0000000000000..875edb4515a17 --- /dev/null +++ b/src/test/ui/print_type_sizes/variants.rs @@ -0,0 +1,31 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z print-type-sizes + +// This file illustrates two things: +// +// 1. Only types that appear in a monomorphized function appear in the +// print-type-sizes output, and +// +// 2. For an enum, the print-type-sizes output will also include the +// size of each variant. + +pub struct SevenBytes([u8; 7]); +pub struct FiftyBytes([u8; 50]); + +pub enum Enum { + Small(SevenBytes), + Large(FiftyBytes), +} + +pub fn main() { + let _e: Enum; +} diff --git a/src/test/ui/print_type_sizes/variants.stdout b/src/test/ui/print_type_sizes/variants.stdout new file mode 100644 index 0000000000000..eed9af26987b4 --- /dev/null +++ b/src/test/ui/print_type_sizes/variants.stdout @@ -0,0 +1,10 @@ +print-type-size type: `Enum`: 51 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Small`: 7 bytes +print-type-size field `.0`: 7 bytes +print-type-size variant `Large`: 50 bytes +print-type-size field `.0`: 50 bytes +print-type-size type: `FiftyBytes`: 50 bytes, alignment: 1 bytes +print-type-size field `.0`: 50 bytes +print-type-size type: `SevenBytes`: 7 bytes, alignment: 1 bytes +print-type-size field `.0`: 7 bytes