| use std::borrow::Cow; | |
| use rustc_abi::{FieldIdx, TagEncoding, VariantIdx, Variants}; | |
| use rustc_codegen_ssa::debuginfo::type_names::{compute_debuginfo_type_name, cpp_like_debuginfo}; | |
| use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; | |
| use rustc_codegen_ssa::traits::MiscCodegenMethods; | |
| use rustc_hir::def::CtorKind; | |
| use rustc_index::IndexSlice; | |
| use rustc_middle::bug; | |
| use rustc_middle::mir::CoroutineLayout; | |
| use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; | |
| use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty, VariantDef}; | |
| use rustc_span::{Span, Symbol}; | |
| use super::type_map::{DINodeCreationResult, UniqueTypeId}; | |
| use super::{SmallVec, size_and_align_of}; | |
| use crate::common::{AsCCharPtr, CodegenCx}; | |
| use crate::debuginfo::metadata::type_map::{self, Stub}; | |
| use crate::debuginfo::metadata::{ | |
| UNKNOWN_LINE_NUMBER, build_field_di_node, build_generic_type_param_di_nodes, | |
| file_metadata_from_def_id, type_di_node, unknown_file_metadata, | |
| }; | |
| use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; | |
| use crate::llvm; | |
| use crate::llvm::debuginfo::{DIFlags, DIType}; | |
| mod cpp_like; | |
| mod native; | |
| pub(super) fn build_enum_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| unique_type_id: UniqueTypeId<'tcx>, | |
| span: Span, | |
| ) -> DINodeCreationResult<'ll> { | |
| let enum_type = unique_type_id.expect_ty(); | |
| let &ty::Adt(enum_adt_def, _) = enum_type.kind() else { | |
| bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type) | |
| }; | |
| let enum_type_and_layout = cx.spanned_layout_of(enum_type, span); | |
| if wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout) { | |
| return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout); | |
| } | |
| if cpp_like_debuginfo(cx.tcx) { | |
| cpp_like::build_enum_type_di_node(cx, unique_type_id) | |
| } else { | |
| native::build_enum_type_di_node(cx, unique_type_id) | |
| } | |
| } | |
| pub(super) fn build_coroutine_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| unique_type_id: UniqueTypeId<'tcx>, | |
| ) -> DINodeCreationResult<'ll> { | |
| if cpp_like_debuginfo(cx.tcx) { | |
| cpp_like::build_coroutine_di_node(cx, unique_type_id) | |
| } else { | |
| native::build_coroutine_di_node(cx, unique_type_id) | |
| } | |
| } | |
| /// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields. | |
| /// | |
| /// The resulting debuginfo will be a DW_TAG_enumeration_type. | |
| fn build_c_style_enum_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_adt_def: AdtDef<'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| ) -> DINodeCreationResult<'ll> { | |
| let containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); | |
| let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(enum_adt_def.did()) | |
| } else { | |
| None | |
| }; | |
| DINodeCreationResult { | |
| di_node: build_enumeration_type_di_node( | |
| cx, | |
| &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false), | |
| tag_base_type(cx.tcx, enum_type_and_layout), | |
| enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { | |
| let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); | |
| (name, discr.val) | |
| }), | |
| enum_adt_def_id, | |
| containing_scope, | |
| ), | |
| already_stored_in_typemap: false, | |
| } | |
| } | |
| /// Build a DW_TAG_enumeration_type debuginfo node, with the given base type and variants. | |
| /// This is a helper function and does not register anything in the type map by itself. | |
| /// | |
| /// `variants` is an iterator of (discr-value, variant-name). | |
| fn build_enumeration_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| type_name: &str, | |
| base_type: Ty<'tcx>, | |
| enumerators: impl Iterator<Item = (Cow<'tcx, str>, u128)>, | |
| def_id: Option<rustc_span::def_id::DefId>, | |
| containing_scope: &'ll DIType, | |
| ) -> &'ll DIType { | |
| let is_unsigned = match base_type.kind() { | |
| ty::Int(_) => false, | |
| ty::Uint(_) => true, | |
| _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."), | |
| }; | |
| let (size, align) = cx.size_and_align_of(base_type); | |
| let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators | |
| .map(|(name, value)| unsafe { | |
| let value = [value as u64, (value >> 64) as u64]; | |
| Some(llvm::LLVMRustDIBuilderCreateEnumerator( | |
| DIB(cx), | |
| name.as_c_char_ptr(), | |
| name.len(), | |
| value.as_ptr(), | |
| size.bits() as libc::c_uint, | |
| is_unsigned, | |
| )) | |
| }) | |
| .collect(); | |
| let (file_metadata, line_number) = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers | |
| { | |
| file_metadata_from_def_id(cx, def_id) | |
| } else { | |
| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) | |
| }; | |
| unsafe { | |
| llvm::LLVMRustDIBuilderCreateEnumerationType( | |
| DIB(cx), | |
| containing_scope, | |
| type_name.as_c_char_ptr(), | |
| type_name.len(), | |
| file_metadata, | |
| line_number, | |
| size.bits(), | |
| align.bits() as u32, | |
| create_DIArray(DIB(cx), &enumerator_di_nodes[..]), | |
| type_di_node(cx, base_type), | |
| true, | |
| ) | |
| } | |
| } | |
| /// Build the debuginfo node for the struct type describing a single variant of an enum. | |
| /// | |
| /// ```txt | |
| /// DW_TAG_structure_type (top-level type for enum) | |
| /// DW_TAG_variant_part (variant part) | |
| /// DW_AT_discr (reference to discriminant DW_TAG_member) | |
| /// DW_TAG_member (discriminant member) | |
| /// DW_TAG_variant (variant 1) | |
| /// DW_TAG_variant (variant 2) | |
| /// DW_TAG_variant (variant 3) | |
| /// ---> DW_TAG_structure_type (type of variant 1) | |
| /// ---> DW_TAG_structure_type (type of variant 2) | |
| /// ---> DW_TAG_structure_type (type of variant 3) | |
| /// ``` | |
| /// | |
| /// In CPP-like mode, we have the exact same descriptions for each variant too: | |
| /// | |
| /// ```txt | |
| /// DW_TAG_union_type (top-level type for enum) | |
| /// DW_TAG_member (member for variant 1) | |
| /// DW_TAG_member (member for variant 2) | |
| /// DW_TAG_member (member for variant 3) | |
| /// ---> DW_TAG_structure_type (type of variant 1) | |
| /// ---> DW_TAG_structure_type (type of variant 2) | |
| /// ---> DW_TAG_structure_type (type of variant 3) | |
| /// DW_TAG_enumeration_type (type of tag) | |
| /// ``` | |
| /// | |
| /// The node looks like: | |
| /// | |
| /// ```txt | |
| /// DW_TAG_structure_type | |
| /// DW_AT_name <name-of-variant> | |
| /// DW_AT_byte_size 0x00000010 | |
| /// DW_AT_alignment 0x00000008 | |
| /// DW_TAG_member | |
| /// DW_AT_name <name-of-field-0> | |
| /// DW_AT_type <0x0000018e> | |
| /// DW_AT_alignment 0x00000004 | |
| /// DW_AT_data_member_location 4 | |
| /// DW_TAG_member | |
| /// DW_AT_name <name-of-field-1> | |
| /// DW_AT_type <0x00000195> | |
| /// DW_AT_alignment 0x00000008 | |
| /// DW_AT_data_member_location 8 | |
| /// ... | |
| /// ``` | |
| /// | |
| /// The type of a variant is always a struct type with the name of the variant | |
| /// and a DW_TAG_member for each field (but not the discriminant). | |
| fn build_enum_variant_struct_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| enum_type_di_node: &'ll DIType, | |
| variant_index: VariantIdx, | |
| variant_def: &VariantDef, | |
| variant_layout: TyAndLayout<'tcx>, | |
| di_flags: DIFlags, | |
| ) -> &'ll DIType { | |
| assert_eq!(variant_layout.ty, enum_type_and_layout.ty); | |
| let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(file_metadata_from_def_id(cx, Some(variant_def.def_id))) | |
| } else { | |
| None | |
| }; | |
| type_map::build_type_with_children( | |
| cx, | |
| type_map::stub( | |
| cx, | |
| Stub::Struct, | |
| UniqueTypeId::for_enum_variant_struct_type( | |
| cx.tcx, | |
| enum_type_and_layout.ty, | |
| variant_index, | |
| ), | |
| variant_def.name.as_str(), | |
| def_location, | |
| // NOTE: We use size and align of enum_type, not from variant_layout: | |
| size_and_align_of(enum_type_and_layout), | |
| Some(enum_type_di_node), | |
| di_flags, | |
| ), | |
| |cx, struct_type_di_node| { | |
| (0..variant_layout.fields.count()) | |
| .map(|field_index| { | |
| let field_name = if variant_def.ctor_kind() != Some(CtorKind::Fn) { | |
| // Fields have names | |
| let field = &variant_def.fields[FieldIdx::from_usize(field_index)]; | |
| Cow::from(field.name.as_str()) | |
| } else { | |
| // Tuple-like | |
| super::tuple_field_name(field_index) | |
| }; | |
| let field_layout = variant_layout.field(cx, field_index); | |
| build_field_di_node( | |
| cx, | |
| struct_type_di_node, | |
| &field_name, | |
| field_layout, | |
| variant_layout.fields.offset(field_index), | |
| di_flags, | |
| type_di_node(cx, field_layout.ty), | |
| None, | |
| ) | |
| }) | |
| .collect::<SmallVec<_>>() | |
| }, | |
| |cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty), | |
| ) | |
| .di_node | |
| } | |
| /// Build the struct type for describing a single coroutine state. | |
| /// See [build_coroutine_variant_struct_type_di_node]. | |
| /// | |
| /// ```txt | |
| /// | |
| /// DW_TAG_structure_type (top-level type for enum) | |
| /// DW_TAG_variant_part (variant part) | |
| /// DW_AT_discr (reference to discriminant DW_TAG_member) | |
| /// DW_TAG_member (discriminant member) | |
| /// DW_TAG_variant (variant 1) | |
| /// DW_TAG_variant (variant 2) | |
| /// DW_TAG_variant (variant 3) | |
| /// ---> DW_TAG_structure_type (type of variant 1) | |
| /// ---> DW_TAG_structure_type (type of variant 2) | |
| /// ---> DW_TAG_structure_type (type of variant 3) | |
| /// | |
| /// ``` | |
| fn build_coroutine_variant_struct_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| variant_index: VariantIdx, | |
| coroutine_type_and_layout: TyAndLayout<'tcx>, | |
| coroutine_type_di_node: &'ll DIType, | |
| coroutine_layout: &CoroutineLayout<'tcx>, | |
| common_upvar_names: &IndexSlice<FieldIdx, Symbol>, | |
| ) -> &'ll DIType { | |
| let variant_name = CoroutineArgs::variant_name(variant_index); | |
| let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( | |
| cx.tcx, | |
| coroutine_type_and_layout.ty, | |
| variant_index, | |
| ); | |
| let variant_layout = coroutine_type_and_layout.for_variant(cx, variant_index); | |
| let coroutine_args = match coroutine_type_and_layout.ty.kind() { | |
| ty::Coroutine(_, args) => args.as_coroutine(), | |
| _ => unreachable!(), | |
| }; | |
| type_map::build_type_with_children( | |
| cx, | |
| type_map::stub( | |
| cx, | |
| Stub::Struct, | |
| unique_type_id, | |
| &variant_name, | |
| None, | |
| size_and_align_of(coroutine_type_and_layout), | |
| Some(coroutine_type_di_node), | |
| DIFlags::FlagZero, | |
| ), | |
| |cx, variant_struct_type_di_node| { | |
| // Fields that just belong to this variant/state | |
| let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count()) | |
| .map(|field_index| { | |
| let coroutine_saved_local = coroutine_layout.variant_fields[variant_index] | |
| [FieldIdx::from_usize(field_index)]; | |
| let field_name_maybe = coroutine_layout.field_names[coroutine_saved_local]; | |
| let field_name = field_name_maybe | |
| .as_ref() | |
| .map(|s| Cow::from(s.as_str())) | |
| .unwrap_or_else(|| super::tuple_field_name(field_index)); | |
| let field_type = variant_layout.field(cx, field_index).ty; | |
| build_field_di_node( | |
| cx, | |
| variant_struct_type_di_node, | |
| &field_name, | |
| cx.layout_of(field_type), | |
| variant_layout.fields.offset(field_index), | |
| DIFlags::FlagZero, | |
| type_di_node(cx, field_type), | |
| None, | |
| ) | |
| }) | |
| .collect(); | |
| // Fields that are common to all states | |
| let common_fields: SmallVec<_> = coroutine_args | |
| .prefix_tys() | |
| .iter() | |
| .zip(common_upvar_names) | |
| .enumerate() | |
| .map(|(index, (upvar_ty, upvar_name))| { | |
| build_field_di_node( | |
| cx, | |
| variant_struct_type_di_node, | |
| upvar_name.as_str(), | |
| cx.layout_of(upvar_ty), | |
| coroutine_type_and_layout.fields.offset(index), | |
| DIFlags::FlagZero, | |
| type_di_node(cx, upvar_ty), | |
| None, | |
| ) | |
| }) | |
| .collect(); | |
| state_specific_fields.into_iter().chain(common_fields).collect() | |
| }, | |
| |cx| build_generic_type_param_di_nodes(cx, coroutine_type_and_layout.ty), | |
| ) | |
| .di_node | |
| } | |
| #[derive(Copy, Clone)] | |
| enum DiscrResult { | |
| NoDiscriminant, | |
| Value(u128), | |
| Range(u128, u128), | |
| } | |
| impl DiscrResult { | |
| fn opt_single_val(&self) -> Option<u128> { | |
| if let Self::Value(d) = *self { Some(d) } else { None } | |
| } | |
| } | |
| /// Returns the discriminant value corresponding to the variant index. | |
| /// | |
| /// Will return `None` if there is less than two variants (because then the enum won't have) | |
| /// a tag, and if this is the untagged variant of a niche-layout enum (because then there is no | |
| /// single discriminant value). | |
| fn compute_discriminant_value<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| variant_index: VariantIdx, | |
| ) -> DiscrResult { | |
| match enum_type_and_layout.layout.variants() { | |
| &Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant, | |
| &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value( | |
| enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val, | |
| ), | |
| &Variants::Multiple { | |
| tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, untagged_variant }, | |
| tag, | |
| .. | |
| } => { | |
| if variant_index == untagged_variant { | |
| let valid_range = enum_type_and_layout | |
| .for_variant(cx, variant_index) | |
| .largest_niche | |
| .as_ref() | |
| .unwrap() | |
| .valid_range; | |
| let min = valid_range.start.min(valid_range.end); | |
| let min = tag.size(cx).truncate(min); | |
| let max = valid_range.start.max(valid_range.end); | |
| let max = tag.size(cx).truncate(max); | |
| DiscrResult::Range(min, max) | |
| } else { | |
| let value = (variant_index.as_u32() as u128) | |
| .wrapping_sub(niche_variants.start().as_u32() as u128) | |
| .wrapping_add(niche_start); | |
| let value = tag.size(cx).truncate(value); | |
| DiscrResult::Value(value) | |
| } | |
| } | |
| } | |
| } |