From b45b9489bb3fb918fbe267154f8dcf4fee61854d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 5 Nov 2022 18:25:41 +0000 Subject: [PATCH 1/4] Compute generator sizes with -Zprint_type_sizes --- compiler/rustc_session/src/code_stats.rs | 3 ++- compiler/rustc_ty_utils/src/layout.rs | 6 +++++ src/test/ui/print_type_sizes/async.rs | 19 ++++++++++++++ src/test/ui/print_type_sizes/async.stdout | 25 +++++++++++++++++++ src/test/ui/print_type_sizes/generator.rs | 20 +++++++++++++++ src/test/ui/print_type_sizes/generator.stdout | 2 ++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/print_type_sizes/async.rs create mode 100644 src/test/ui/print_type_sizes/async.stdout create mode 100644 src/test/ui/print_type_sizes/generator.rs create mode 100644 src/test/ui/print_type_sizes/generator.stdout diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index eede4d16ea378..7a6da1b7350d2 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -33,6 +33,7 @@ pub enum DataTypeKind { Union, Enum, Closure, + Generator, } #[derive(PartialEq, Eq, Hash, Debug)] @@ -113,7 +114,7 @@ impl CodeStats { let mut max_variant_size = discr_size; let struct_like = match kind { - DataTypeKind::Struct | DataTypeKind::Closure => true, + DataTypeKind::Struct | DataTypeKind::Closure | DataTypeKind::Generator => true, DataTypeKind::Enum | DataTypeKind::Union => false, }; for (i, variant_info) in variants.into_iter().enumerate() { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index fbc055b5d238f..1c65bec6964c9 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -826,6 +826,12 @@ fn record_layout_for_printing_outlined<'tcx>( return; } + ty::Generator(..) => { + debug!("print-type-size t: `{:?}` record generator", layout.ty); + record(DataTypeKind::Generator, false, None, vec![]); + return; + } + _ => { debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); return; diff --git a/src/test/ui/print_type_sizes/async.rs b/src/test/ui/print_type_sizes/async.rs new file mode 100644 index 0000000000000..3491ad5afbc15 --- /dev/null +++ b/src/test/ui/print_type_sizes/async.rs @@ -0,0 +1,19 @@ +// compile-flags: -Z print-type-sizes +// edition:2021 +// build-pass +// ignore-pass + +#![feature(start)] + +async fn wait() {} + +async fn test(arg: [u8; 8192]) { + wait().await; + drop(arg); +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + let _ = test([0; 8192]); + 0 +} diff --git a/src/test/ui/print_type_sizes/async.stdout b/src/test/ui/print_type_sizes/async.stdout new file mode 100644 index 0000000000000..3ea0ff65f610d --- /dev/null +++ b/src/test/ui/print_type_sizes/async.stdout @@ -0,0 +1,25 @@ +print-type-size type: `[static generator@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes +print-type-size end padding: 16386 bytes +print-type-size type: `std::future::from_generator::GenFuture<[static generator@$DIR/async.rs:10:32: 13:2]>`: 16386 bytes, alignment: 1 bytes +print-type-size field `.0`: 16386 bytes +print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size field `.value`: 8192 bytes +print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 8192 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 8192 bytes +print-type-size type: `[static generator@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes +print-type-size end padding: 1 bytes +print-type-size type: `std::future::from_generator::GenFuture<[static generator@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes +print-type-size field `.0`: 1 bytes +print-type-size type: `std::mem::ManuallyDrop>`: 1 bytes, alignment: 1 bytes +print-type-size field `.value`: 1 bytes +print-type-size type: `std::mem::MaybeUninit>`: 1 bytes, alignment: 1 bytes +print-type-size variant `MaybeUninit`: 1 bytes +print-type-size field `.uninit`: 0 bytes +print-type-size field `.value`: 1 bytes +print-type-size type: `std::task::Poll<()>`: 1 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Ready`: 0 bytes +print-type-size field `.0`: 0 bytes +print-type-size variant `Pending`: 0 bytes diff --git a/src/test/ui/print_type_sizes/generator.rs b/src/test/ui/print_type_sizes/generator.rs new file mode 100644 index 0000000000000..a46db6121046b --- /dev/null +++ b/src/test/ui/print_type_sizes/generator.rs @@ -0,0 +1,20 @@ +// compile-flags: -Z print-type-sizes +// build-pass +// ignore-pass + +#![feature(start, generators, generator_trait)] + +use std::ops::Generator; + +fn generator(array: [u8; C]) -> impl Generator { + move |()| { + yield (); + let _ = array; + } +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + let _ = generator([0; 8192]); + 0 +} diff --git a/src/test/ui/print_type_sizes/generator.stdout b/src/test/ui/print_type_sizes/generator.stdout new file mode 100644 index 0000000000000..8270991829767 --- /dev/null +++ b/src/test/ui/print_type_sizes/generator.stdout @@ -0,0 +1,2 @@ +print-type-size type: `[generator@$DIR/generator.rs:10:5: 10:14]`: 8193 bytes, alignment: 1 bytes +print-type-size end padding: 8193 bytes From b0dcadfc45bb04be3ba56d8bd62f1331a98949dc Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Dec 2022 17:00:33 +0000 Subject: [PATCH 2/4] Move closure/generator type info methods to TyCtxt --- .../src/debuginfo/metadata.rs | 68 +---------------- .../src/debuginfo/metadata/enums/cpp_like.rs | 8 +- .../src/debuginfo/metadata/enums/native.rs | 7 +- compiler/rustc_middle/src/ty/util.rs | 76 +++++++++++++++++++ 4 files changed, 84 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index d87117dffdc60..a9e3dcf4cb39a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -27,9 +27,7 @@ use rustc_codegen_ssa::traits::*; use rustc_fs_util::path_to_c_string; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::bug; -use rustc_middle::mir::{self, GeneratorLayout}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{ @@ -1026,33 +1024,6 @@ fn build_struct_type_di_node<'ll, 'tcx>( // Tuples //=----------------------------------------------------------------------------- -/// Returns names of captured upvars for closures and generators. -/// -/// Here are some examples: -/// - `name__field1__field2` when the upvar is captured by value. -/// - `_ref__name__field` when the upvar is captured by reference. -/// -/// For generators this only contains upvars that are shared by all states. -fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec { - let body = tcx.optimized_mir(def_id); - - body.var_debug_info - .iter() - .filter_map(|var| { - let is_ref = match var.value { - mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => { - // The projection is either `[.., Field, Deref]` or `[.., Field]`. It - // implies whether the variable is captured by value or by reference. - matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref) - } - _ => return None, - }; - let prefix = if is_ref { "_ref__" } else { "" }; - Some(prefix.to_owned() + var.name.as_str()) - }) - .collect() -} - /// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator. /// For a generator, this will handle upvars shared by all states. fn build_upvar_field_di_nodes<'ll, 'tcx>( @@ -1083,7 +1054,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>( .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)) ); - let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id); + let capture_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); let layout = cx.layout_of(closure_or_generator_ty); up_var_tys @@ -1229,43 +1200,6 @@ fn build_union_type_di_node<'ll, 'tcx>( ) } -// FIXME(eddyb) maybe precompute this? Right now it's computed once -// per generator monomorphization, but it doesn't depend on substs. -fn generator_layout_and_saved_local_names<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, -) -> (&'tcx GeneratorLayout<'tcx>, IndexVec>) { - let body = tcx.optimized_mir(def_id); - let generator_layout = body.generator_layout().unwrap(); - let mut generator_saved_local_names = IndexVec::from_elem(None, &generator_layout.field_tys); - - let state_arg = mir::Local::new(1); - for var in &body.var_debug_info { - let mir::VarDebugInfoContents::Place(place) = &var.value else { continue }; - if place.local != state_arg { - continue; - } - match place.projection[..] { - [ - // Deref of the `Pin<&mut Self>` state argument. - mir::ProjectionElem::Field(..), - mir::ProjectionElem::Deref, - // Field of a variant of the state. - mir::ProjectionElem::Downcast(_, variant), - mir::ProjectionElem::Field(field, _), - ] => { - let name = &mut generator_saved_local_names - [generator_layout.variant_fields[variant][field]]; - if name.is_none() { - name.replace(var.name); - } - } - _ => {} - } - } - (generator_layout, generator_saved_local_names) -} - /// Computes the type parameters for a type, if any, for the given metadata. fn build_generic_type_param_di_nodes<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 53e8a291d1e8a..69443b9b828e2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -22,9 +22,9 @@ use crate::{ common::CodegenCx, debuginfo::{ metadata::{ - build_field_di_node, closure_saved_names_of_captured_variables, + build_field_di_node, enums::{tag_base_type, DiscrResult}, - file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node, + file_metadata, size_and_align_of, type_di_node, type_map::{self, Stub, UniqueTypeId}, unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA, UNKNOWN_LINE_NUMBER, @@ -677,9 +677,9 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>( }; let (generator_layout, state_specific_upvar_names) = - generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + cx.tcx.generator_layout_and_saved_local_names(generator_def_id); - let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(generator_def_id); let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx); let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len(); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index becbccc434d9a..93419d27a6236 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -4,9 +4,8 @@ use crate::{ common::CodegenCx, debuginfo::{ metadata::{ - closure_saved_names_of_captured_variables, enums::tag_base_type, - file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node, + file_metadata, size_and_align_of, type_di_node, type_map::{self, Stub, StubInfo, UniqueTypeId}, unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, UNKNOWN_LINE_NUMBER, @@ -157,7 +156,7 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>( ), |cx, generator_type_di_node| { let (generator_layout, state_specific_upvar_names) = - generator_layout_and_saved_local_names(cx.tcx, generator_def_id); + cx.tcx.generator_layout_and_saved_local_names(generator_def_id); let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else { bug!( @@ -167,7 +166,7 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>( }; let common_upvar_names = - closure_saved_names_of_captured_variables(cx.tcx, generator_def_id); + cx.tcx.closure_saved_names_of_captured_variables(generator_def_id); // Build variant struct types let variant_struct_type_di_nodes: SmallVec<_> = variants diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 47c1ce8075674..9ea8dc6e69fdd 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1,6 +1,7 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir; use crate::ty::layout::IntegerExt; use crate::ty::{ self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, @@ -15,6 +16,7 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_index::bit_set::GrowableBitSet; +use rustc_index::vec::{Idx, IndexVec}; use rustc_macros::HashStable; use rustc_span::{sym, DUMMY_SP}; use rustc_target::abi::{Integer, IntegerType, Size, TargetDataLayout}; @@ -692,6 +694,80 @@ impl<'tcx> TyCtxt<'tcx> { pub fn bound_impl_subject(self, def_id: DefId) -> ty::EarlyBinder> { ty::EarlyBinder(self.impl_subject(def_id)) } + + /// Returns names of captured upvars for closures and generators. + /// + /// Here are some examples: + /// - `name__field1__field2` when the upvar is captured by value. + /// - `_ref__name__field` when the upvar is captured by reference. + /// + /// For generators this only contains upvars that are shared by all states. + pub fn closure_saved_names_of_captured_variables( + self, + def_id: DefId, + ) -> SmallVec<[String; 16]> { + let body = self.optimized_mir(def_id); + + body.var_debug_info + .iter() + .filter_map(|var| { + let is_ref = match var.value { + mir::VarDebugInfoContents::Place(place) + if place.local == mir::Local::new(1) => + { + // The projection is either `[.., Field, Deref]` or `[.., Field]`. It + // implies whether the variable is captured by value or by reference. + matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref) + } + _ => return None, + }; + let prefix = if is_ref { "_ref__" } else { "" }; + Some(prefix.to_owned() + var.name.as_str()) + }) + .collect() + } + + // FIXME(eddyb) maybe precompute this? Right now it's computed once + // per generator monomorphization, but it doesn't depend on substs. + pub fn generator_layout_and_saved_local_names( + self, + def_id: DefId, + ) -> ( + &'tcx ty::GeneratorLayout<'tcx>, + IndexVec>, + ) { + let tcx = self; + let body = tcx.optimized_mir(def_id); + let generator_layout = body.generator_layout().unwrap(); + let mut generator_saved_local_names = + IndexVec::from_elem(None, &generator_layout.field_tys); + + let state_arg = mir::Local::new(1); + for var in &body.var_debug_info { + let mir::VarDebugInfoContents::Place(place) = &var.value else { continue }; + if place.local != state_arg { + continue; + } + match place.projection[..] { + [ + // Deref of the `Pin<&mut Self>` state argument. + mir::ProjectionElem::Field(..), + mir::ProjectionElem::Deref, + // Field of a variant of the state. + mir::ProjectionElem::Downcast(_, variant), + mir::ProjectionElem::Field(field, _), + ] => { + let name = &mut generator_saved_local_names + [generator_layout.variant_fields[variant][field]]; + if name.is_none() { + name.replace(var.name); + } + } + _ => {} + } + } + (generator_layout, generator_saved_local_names) + } } struct OpaqueTypeExpander<'tcx> { From 57b722688d03fc8115987375eee1d076cc89bc68 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Dec 2022 18:33:26 +0000 Subject: [PATCH 3/4] Properly print generator interior type sizes --- compiler/rustc_session/src/code_stats.rs | 6 +- compiler/rustc_ty_utils/src/layout.rs | 274 ++++++++++++------ src/test/ui/print_type_sizes/async.stdout | 29 +- src/test/ui/print_type_sizes/generator.stdout | 10 +- 4 files changed, 219 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index 7a6da1b7350d2..1085bce44758f 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -19,7 +19,7 @@ pub enum SizeKind { Min, } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FieldInfo { pub name: Symbol, pub offset: u64, @@ -114,8 +114,8 @@ impl CodeStats { let mut max_variant_size = discr_size; let struct_like = match kind { - DataTypeKind::Struct | DataTypeKind::Closure | DataTypeKind::Generator => true, - DataTypeKind::Enum | DataTypeKind::Union => false, + DataTypeKind::Struct | DataTypeKind::Closure => true, + DataTypeKind::Enum | DataTypeKind::Union | DataTypeKind::Generator => false, }; for (i, variant_info) in variants.into_iter().enumerate() { let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 1c65bec6964c9..8bbbf26f47069 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -814,10 +814,196 @@ fn record_layout_for_printing_outlined<'tcx>( ); }; - let adt_def = match *layout.ty.kind() { + match *layout.ty.kind() { ty::Adt(ref adt_def, _) => { debug!("print-type-size t: `{:?}` process adt", layout.ty); - adt_def + let adt_kind = adt_def.adt_kind(); + let adt_packed = adt_def.repr().pack.is_some(); + + let build_variant_info = + |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let mut min_size = Size::ZERO; + let field_info: Vec<_> = flds + .iter() + .enumerate() + .map(|(i, &name)| { + let field_layout = layout.field(cx, i); + let offset = layout.fields.offset(i); + min_size = min_size.max(offset + field_layout.size); + FieldInfo { + name, + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .collect(); + + VariantInfo { + name: n, + kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, + align: layout.align.abi.bytes(), + size: if min_size.bytes() == 0 { + layout.size.bytes() + } else { + min_size.bytes() + }, + fields: field_info, + } + }; + + match layout.variants { + Variants::Single { index } => { + if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive { + debug!( + "print-type-size `{:#?}` variant {}", + layout, + adt_def.variant(index).name + ); + let variant_def = &adt_def.variant(index); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); + record( + adt_kind.into(), + adt_packed, + None, + vec![build_variant_info(Some(variant_def.name), &fields, layout)], + ); + } else { + // (This case arises for *empty* enums; so give it + // zero variants.) + record(adt_kind.into(), adt_packed, None, vec![]); + } + } + + Variants::Multiple { tag, ref tag_encoding, .. } => { + debug!( + "print-type-size `{:#?}` adt general variants def {}", + layout.ty, + adt_def.variants().len() + ); + let variant_infos: Vec<_> = adt_def + .variants() + .iter_enumerated() + .map(|(i, variant_def)| { + let fields: Vec<_> = + variant_def.fields.iter().map(|f| f.name).collect(); + build_variant_info( + Some(variant_def.name), + &fields, + layout.for_variant(cx, i), + ) + }) + .collect(); + record( + adt_kind.into(), + adt_packed, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + variant_infos, + ); + } + } + } + + ty::Generator(def_id, substs, _) => { + debug!("print-type-size t: `{:?}` record generator", layout.ty); + // Generators always have a begin/poisoned/end state with additional suspend points + match layout.variants { + Variants::Multiple { tag, ref tag_encoding, .. } => { + let (generator, state_specific_names) = + cx.tcx.generator_layout_and_saved_local_names(def_id); + let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); + + let mut upvars_size = Size::ZERO; + let upvar_fields: Vec<_> = substs + .as_generator() + .upvar_tys() + .zip(upvar_names) + .enumerate() + .map(|(field_idx, (_, name))| { + let field_layout = layout.field(cx, field_idx); + let offset = layout.fields.offset(field_idx); + upvars_size = upvars_size.max(offset + field_layout.size); + FieldInfo { + name: Symbol::intern(&name), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .collect(); + + let variant_infos: Vec<_> = generator + .variant_fields + .iter_enumerated() + .map(|(variant_idx, variant_def)| { + let variant_layout = layout.for_variant(cx, variant_idx); + let mut variant_size = Size::ZERO; + let fields = variant_def + .iter() + .enumerate() + .map(|(field_idx, local)| { + let field_layout = variant_layout.field(cx, field_idx); + let offset = variant_layout.fields.offset(field_idx); + // The struct is as large as the last field's end + variant_size = variant_size.max(offset + field_layout.size); + FieldInfo { + name: state_specific_names + .get(*local) + .copied() + .flatten() + .unwrap_or(Symbol::intern(&format!( + ".generator_field{}", + local.as_usize() + ))), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .chain(upvar_fields.iter().copied()) + .collect(); + + // If the variant has no state-specific fields, then it's the size of the upvars. + if variant_size == Size::ZERO { + variant_size = upvars_size; + } + // We need to add the discriminant size back into min_size, since it is subtracted + // later during printing. + variant_size += match tag_encoding { + TagEncoding::Direct => tag.size(cx), + _ => Size::ZERO, + }; + + VariantInfo { + name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name( + variant_idx, + ))), + kind: SizeKind::Exact, + size: variant_size.bytes(), + align: variant_layout.align.abi.bytes(), + fields, + } + }) + .collect(); + record( + DataTypeKind::Generator, + false, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + variant_infos, + ); + } + _ => { + // This should never happen, but I would rather not panic. + record(DataTypeKind::Generator, false, None, vec![]); + return; + } + } } ty::Closure(..) => { @@ -826,93 +1012,9 @@ fn record_layout_for_printing_outlined<'tcx>( return; } - ty::Generator(..) => { - debug!("print-type-size t: `{:?}` record generator", layout.ty); - record(DataTypeKind::Generator, false, None, vec![]); - return; - } - _ => { debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); return; } }; - - let adt_kind = adt_def.adt_kind(); - let adt_packed = adt_def.repr().pack.is_some(); - - let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { - let mut min_size = Size::ZERO; - let field_info: Vec<_> = flds - .iter() - .enumerate() - .map(|(i, &name)| { - let field_layout = layout.field(cx, i); - let offset = layout.fields.offset(i); - let field_end = offset + field_layout.size; - if min_size < field_end { - min_size = field_end; - } - FieldInfo { - name, - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - }) - .collect(); - - VariantInfo { - name: n, - kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), - size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, - fields: field_info, - } - }; - - match layout.variants { - Variants::Single { index } => { - if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive { - debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name); - let variant_def = &adt_def.variant(index); - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); - record( - adt_kind.into(), - adt_packed, - None, - vec![build_variant_info(Some(variant_def.name), &fields, layout)], - ); - } else { - // (This case arises for *empty* enums; so give it - // zero variants.) - record(adt_kind.into(), adt_packed, None, vec![]); - } - } - - Variants::Multiple { tag, ref tag_encoding, .. } => { - debug!( - "print-type-size `{:#?}` adt general variants def {}", - layout.ty, - adt_def.variants().len() - ); - let variant_infos: Vec<_> = adt_def - .variants() - .iter_enumerated() - .map(|(i, variant_def)| { - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); - build_variant_info(Some(variant_def.name), &fields, layout.for_variant(cx, i)) - }) - .collect(); - record( - adt_kind.into(), - adt_packed, - match tag_encoding { - TagEncoding::Direct => Some(tag.size(cx)), - _ => None, - }, - variant_infos, - ); - } - } } diff --git a/src/test/ui/print_type_sizes/async.stdout b/src/test/ui/print_type_sizes/async.stdout index 3ea0ff65f610d..94ad09ef296d3 100644 --- a/src/test/ui/print_type_sizes/async.stdout +++ b/src/test/ui/print_type_sizes/async.stdout @@ -1,20 +1,29 @@ -print-type-size type: `[static generator@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes -print-type-size end padding: 16386 bytes -print-type-size type: `std::future::from_generator::GenFuture<[static generator@$DIR/async.rs:10:32: 13:2]>`: 16386 bytes, alignment: 1 bytes -print-type-size field `.0`: 16386 bytes +print-type-size type: `[async fn body@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Suspend0`: 16385 bytes +print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size field `.arg`: 8192 bytes +print-type-size field `.__awaitee`: 1 bytes +print-type-size variant `Unresumed`: 8192 bytes +print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Returned`: 8192 bytes +print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Panicked`: 8192 bytes +print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size type: `std::mem::ManuallyDrop<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes print-type-size field `.value`: 8192 bytes print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment: 1 bytes print-type-size variant `MaybeUninit`: 8192 bytes print-type-size field `.uninit`: 0 bytes print-type-size field `.value`: 8192 bytes -print-type-size type: `[static generator@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes -print-type-size end padding: 1 bytes -print-type-size type: `std::future::from_generator::GenFuture<[static generator@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes -print-type-size field `.0`: 1 bytes -print-type-size type: `std::mem::ManuallyDrop>`: 1 bytes, alignment: 1 bytes +print-type-size type: `[async fn body@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Unresumed`: 0 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes +print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes print-type-size field `.value`: 1 bytes -print-type-size type: `std::mem::MaybeUninit>`: 1 bytes, alignment: 1 bytes +print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes print-type-size variant `MaybeUninit`: 1 bytes print-type-size field `.uninit`: 0 bytes print-type-size field `.value`: 1 bytes diff --git a/src/test/ui/print_type_sizes/generator.stdout b/src/test/ui/print_type_sizes/generator.stdout index 8270991829767..28d4a6e6cff40 100644 --- a/src/test/ui/print_type_sizes/generator.stdout +++ b/src/test/ui/print_type_sizes/generator.stdout @@ -1,2 +1,10 @@ print-type-size type: `[generator@$DIR/generator.rs:10:5: 10:14]`: 8193 bytes, alignment: 1 bytes -print-type-size end padding: 8193 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Unresumed`: 8192 bytes +print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Returned`: 8192 bytes +print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Panicked`: 8192 bytes +print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes +print-type-size variant `Suspend0`: 8192 bytes +print-type-size field `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes From 7d23e29f9fb61985aca0227acbabb62f95208c01 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Dec 2022 20:30:42 +0000 Subject: [PATCH 4/4] Pull out logic into distinct functions --- compiler/rustc_ty_utils/src/layout.rs | 349 ++++++++++++-------------- 1 file changed, 165 insertions(+), 184 deletions(-) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 8bbbf26f47069..f4672a70072b2 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,3 +1,4 @@ +use hir::def_id::DefId; use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; @@ -6,7 +7,7 @@ use rustc_middle::ty::layout::{ IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, MAX_SIMD_LANES, }; use rustc_middle::ty::{ - self, subst::SubstsRef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable, + self, subst::SubstsRef, AdtDef, EarlyBinder, ReprOptions, Ty, TyCtxt, TypeVisitable, }; use rustc_session::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use rustc_span::symbol::Symbol; @@ -815,206 +816,186 @@ fn record_layout_for_printing_outlined<'tcx>( }; match *layout.ty.kind() { - ty::Adt(ref adt_def, _) => { + ty::Adt(adt_def, _) => { debug!("print-type-size t: `{:?}` process adt", layout.ty); let adt_kind = adt_def.adt_kind(); let adt_packed = adt_def.repr().pack.is_some(); - - let build_variant_info = - |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { - let mut min_size = Size::ZERO; - let field_info: Vec<_> = flds - .iter() - .enumerate() - .map(|(i, &name)| { - let field_layout = layout.field(cx, i); - let offset = layout.fields.offset(i); - min_size = min_size.max(offset + field_layout.size); - FieldInfo { - name, - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - }) - .collect(); - - VariantInfo { - name: n, - kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), - size: if min_size.bytes() == 0 { - layout.size.bytes() - } else { - min_size.bytes() - }, - fields: field_info, - } - }; - - match layout.variants { - Variants::Single { index } => { - if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive { - debug!( - "print-type-size `{:#?}` variant {}", - layout, - adt_def.variant(index).name - ); - let variant_def = &adt_def.variant(index); - let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); - record( - adt_kind.into(), - adt_packed, - None, - vec![build_variant_info(Some(variant_def.name), &fields, layout)], - ); - } else { - // (This case arises for *empty* enums; so give it - // zero variants.) - record(adt_kind.into(), adt_packed, None, vec![]); - } - } - - Variants::Multiple { tag, ref tag_encoding, .. } => { - debug!( - "print-type-size `{:#?}` adt general variants def {}", - layout.ty, - adt_def.variants().len() - ); - let variant_infos: Vec<_> = adt_def - .variants() - .iter_enumerated() - .map(|(i, variant_def)| { - let fields: Vec<_> = - variant_def.fields.iter().map(|f| f.name).collect(); - build_variant_info( - Some(variant_def.name), - &fields, - layout.for_variant(cx, i), - ) - }) - .collect(); - record( - adt_kind.into(), - adt_packed, - match tag_encoding { - TagEncoding::Direct => Some(tag.size(cx)), - _ => None, - }, - variant_infos, - ); - } - } + let (variant_infos, opt_discr_size) = variant_info_for_adt(cx, layout, adt_def); + record(adt_kind.into(), adt_packed, opt_discr_size, variant_infos); } ty::Generator(def_id, substs, _) => { debug!("print-type-size t: `{:?}` record generator", layout.ty); // Generators always have a begin/poisoned/end state with additional suspend points - match layout.variants { - Variants::Multiple { tag, ref tag_encoding, .. } => { - let (generator, state_specific_names) = - cx.tcx.generator_layout_and_saved_local_names(def_id); - let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); - - let mut upvars_size = Size::ZERO; - let upvar_fields: Vec<_> = substs - .as_generator() - .upvar_tys() - .zip(upvar_names) - .enumerate() - .map(|(field_idx, (_, name))| { - let field_layout = layout.field(cx, field_idx); - let offset = layout.fields.offset(field_idx); - upvars_size = upvars_size.max(offset + field_layout.size); - FieldInfo { - name: Symbol::intern(&name), - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - }) - .collect(); - - let variant_infos: Vec<_> = generator - .variant_fields - .iter_enumerated() - .map(|(variant_idx, variant_def)| { - let variant_layout = layout.for_variant(cx, variant_idx); - let mut variant_size = Size::ZERO; - let fields = variant_def - .iter() - .enumerate() - .map(|(field_idx, local)| { - let field_layout = variant_layout.field(cx, field_idx); - let offset = variant_layout.fields.offset(field_idx); - // The struct is as large as the last field's end - variant_size = variant_size.max(offset + field_layout.size); - FieldInfo { - name: state_specific_names - .get(*local) - .copied() - .flatten() - .unwrap_or(Symbol::intern(&format!( - ".generator_field{}", - local.as_usize() - ))), - offset: offset.bytes(), - size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), - } - }) - .chain(upvar_fields.iter().copied()) - .collect(); - - // If the variant has no state-specific fields, then it's the size of the upvars. - if variant_size == Size::ZERO { - variant_size = upvars_size; - } - // We need to add the discriminant size back into min_size, since it is subtracted - // later during printing. - variant_size += match tag_encoding { - TagEncoding::Direct => tag.size(cx), - _ => Size::ZERO, - }; - - VariantInfo { - name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name( - variant_idx, - ))), - kind: SizeKind::Exact, - size: variant_size.bytes(), - align: variant_layout.align.abi.bytes(), - fields, - } - }) - .collect(); - record( - DataTypeKind::Generator, - false, - match tag_encoding { - TagEncoding::Direct => Some(tag.size(cx)), - _ => None, - }, - variant_infos, - ); - } - _ => { - // This should never happen, but I would rather not panic. - record(DataTypeKind::Generator, false, None, vec![]); - return; - } - } + let (variant_infos, opt_discr_size) = + variant_info_for_generator(cx, layout, def_id, substs); + record(DataTypeKind::Generator, false, opt_discr_size, variant_infos); } ty::Closure(..) => { debug!("print-type-size t: `{:?}` record closure", layout.ty); record(DataTypeKind::Closure, false, None, vec![]); - return; } _ => { debug!("print-type-size t: `{:?}` skip non-nominal", layout.ty); - return; } }; } + +fn variant_info_for_adt<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + layout: TyAndLayout<'tcx>, + adt_def: AdtDef<'tcx>, +) -> (Vec, Option) { + let build_variant_info = |n: Option, flds: &[Symbol], layout: TyAndLayout<'tcx>| { + let mut min_size = Size::ZERO; + let field_info: Vec<_> = flds + .iter() + .enumerate() + .map(|(i, &name)| { + let field_layout = layout.field(cx, i); + let offset = layout.fields.offset(i); + min_size = min_size.max(offset + field_layout.size); + FieldInfo { + name, + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .collect(); + + VariantInfo { + name: n, + kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, + align: layout.align.abi.bytes(), + size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, + fields: field_info, + } + }; + + match layout.variants { + Variants::Single { index } => { + if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive { + debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name); + let variant_def = &adt_def.variant(index); + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); + (vec![build_variant_info(Some(variant_def.name), &fields, layout)], None) + } else { + (vec![], None) + } + } + + Variants::Multiple { tag, ref tag_encoding, .. } => { + debug!( + "print-type-size `{:#?}` adt general variants def {}", + layout.ty, + adt_def.variants().len() + ); + let variant_infos: Vec<_> = adt_def + .variants() + .iter_enumerated() + .map(|(i, variant_def)| { + let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect(); + build_variant_info(Some(variant_def.name), &fields, layout.for_variant(cx, i)) + }) + .collect(); + + ( + variant_infos, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + ) + } + } +} + +fn variant_info_for_generator<'tcx>( + cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, + layout: TyAndLayout<'tcx>, + def_id: DefId, + substs: ty::SubstsRef<'tcx>, +) -> (Vec, Option) { + let Variants::Multiple { tag, ref tag_encoding, .. } = layout.variants else { + return (vec![], None); + }; + + let (generator, state_specific_names) = cx.tcx.generator_layout_and_saved_local_names(def_id); + let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id); + + let mut upvars_size = Size::ZERO; + let upvar_fields: Vec<_> = substs + .as_generator() + .upvar_tys() + .zip(upvar_names) + .enumerate() + .map(|(field_idx, (_, name))| { + let field_layout = layout.field(cx, field_idx); + let offset = layout.fields.offset(field_idx); + upvars_size = upvars_size.max(offset + field_layout.size); + FieldInfo { + name: Symbol::intern(&name), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .collect(); + + let variant_infos: Vec<_> = generator + .variant_fields + .iter_enumerated() + .map(|(variant_idx, variant_def)| { + let variant_layout = layout.for_variant(cx, variant_idx); + let mut variant_size = Size::ZERO; + let fields = variant_def + .iter() + .enumerate() + .map(|(field_idx, local)| { + let field_layout = variant_layout.field(cx, field_idx); + let offset = variant_layout.fields.offset(field_idx); + // The struct is as large as the last field's end + variant_size = variant_size.max(offset + field_layout.size); + FieldInfo { + name: state_specific_names.get(*local).copied().flatten().unwrap_or( + Symbol::intern(&format!(".generator_field{}", local.as_usize())), + ), + offset: offset.bytes(), + size: field_layout.size.bytes(), + align: field_layout.align.abi.bytes(), + } + }) + .chain(upvar_fields.iter().copied()) + .collect(); + + // If the variant has no state-specific fields, then it's the size of the upvars. + if variant_size == Size::ZERO { + variant_size = upvars_size; + } + // We need to add the discriminant size back into min_size, since it is subtracted + // later during printing. + variant_size += match tag_encoding { + TagEncoding::Direct => tag.size(cx), + _ => Size::ZERO, + }; + + VariantInfo { + name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name(variant_idx))), + kind: SizeKind::Exact, + size: variant_size.bytes(), + align: variant_layout.align.abi.bytes(), + fields, + } + }) + .collect(); + ( + variant_infos, + match tag_encoding { + TagEncoding::Direct => Some(tag.size(cx)), + _ => None, + }, + ) +}