| use rustc_codegen_ssa::debuginfo::{ |
| type_names::{compute_debuginfo_type_name, cpp_like_debuginfo}, |
| wants_c_like_enum_debuginfo, |
| }; |
| use rustc_hir::def::CtorKind; |
| use rustc_index::vec::IndexVec; |
| use rustc_middle::{ |
| bug, |
| mir::{Field, GeneratorLayout, GeneratorSavedLocal}, |
| ty::{ |
| self, |
| layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout}, |
| util::Discr, |
| AdtDef, GeneratorSubsts, Ty, VariantDef, |
| }, |
| }; |
| use rustc_span::Symbol; |
| use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants}; |
| use std::borrow::Cow; |
| |
| use crate::{ |
| common::CodegenCx, |
| debuginfo::{ |
| metadata::{ |
| build_field_di_node, build_generic_type_param_di_nodes, type_di_node, |
| type_map::{self, Stub}, |
| unknown_file_metadata, UNKNOWN_LINE_NUMBER, |
| }, |
| utils::{create_DIArray, get_namespace_for_item, DIB}, |
| }, |
| llvm::{ |
| self, |
| debuginfo::{DIFlags, DIType}, |
| }, |
| }; |
| |
| use super::{ |
| size_and_align_of, |
| type_map::{DINodeCreationResult, UniqueTypeId}, |
| SmallVec, |
| }; |
| |
| mod cpp_like; |
| mod native; |
| |
| pub(super) fn build_enum_type_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| unique_type_id: UniqueTypeId<'tcx>, |
| ) -> 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.layout_of(enum_type); |
| |
| if wants_c_like_enum_debuginfo(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_generator_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| unique_type_id: UniqueTypeId<'tcx>, |
| ) -> DINodeCreationResult<'ll> { |
| if cpp_like_debuginfo(cx.tcx) { |
| cpp_like::build_generator_di_node(cx, unique_type_id) |
| } else { |
| native::build_generator_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()); |
| 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, enum_type_and_layout), |
| &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| { |
| (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str())) |
| }), |
| containing_scope, |
| ), |
| already_stored_in_typemap: false, |
| } |
| } |
| |
| /// Extract the type with which we want to describe the tag of the given enum or generator. |
| fn tag_base_type<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| enum_type_and_layout: TyAndLayout<'tcx>, |
| ) -> Ty<'tcx> { |
| debug_assert!(match enum_type_and_layout.ty.kind() { |
| ty::Generator(..) => true, |
| ty::Adt(adt_def, _) => adt_def.is_enum(), |
| _ => false, |
| }); |
| |
| match enum_type_and_layout.layout.variants() { |
| // A single-variant enum has no discriminant. |
| Variants::Single { .. } => { |
| bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout) |
| } |
| |
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => { |
| // Niche tags are always normalized to unsized integers of the correct size. |
| match tag.primitive() { |
| Primitive::Int(t, _) => t, |
| Primitive::F32 => Integer::I32, |
| Primitive::F64 => Integer::I64, |
| Primitive::Pointer => { |
| // If the niche is the NULL value of a reference, then `discr_enum_ty` will be |
| // a RawPtr. CodeView doesn't know what to do with enums whose base type is a |
| // pointer so we fix this up to just be `usize`. |
| // DWARF might be able to deal with this but with an integer type we are on |
| // the safe side there too. |
| cx.data_layout().ptr_sized_integer() |
| } |
| } |
| .to_ty(cx.tcx, false) |
| } |
| |
| Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => { |
| // Direct tags preserve the sign. |
| tag.primitive().to_ty(cx.tcx) |
| } |
| } |
| } |
| |
| /// 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). |
| /// |
| // NOTE: Handling of discriminant values is somewhat inconsistent. They can appear as u128, |
| // u64, and i64. Here everything gets mapped to i64 because that's what LLVM's API expects. |
| fn build_enumeration_type_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| type_name: &str, |
| base_type: Ty<'tcx>, |
| variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>, |
| 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 enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants |
| .map(|(discr, variant_name)| { |
| unsafe { |
| Some(llvm::LLVMRustDIBuilderCreateEnumerator( |
| DIB(cx), |
| variant_name.as_ptr().cast(), |
| variant_name.len(), |
| // FIXME: what if enumeration has i128 discriminant? |
| discr.val as i64, |
| is_unsigned, |
| )) |
| } |
| }) |
| .collect(); |
| |
| let (size, align) = cx.size_and_align_of(base_type); |
| |
| unsafe { |
| llvm::LLVMRustDIBuilderCreateEnumerationType( |
| DIB(cx), |
| containing_scope, |
| type_name.as_ptr().cast(), |
| type_name.len(), |
| unknown_file_metadata(cx), |
| UNKNOWN_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: Ty<'tcx>, |
| enum_type_di_node: &'ll DIType, |
| variant_index: VariantIdx, |
| variant_def: &VariantDef, |
| variant_layout: TyAndLayout<'tcx>, |
| ) -> &'ll DIType { |
| debug_assert_eq!(variant_layout.ty, enum_type); |
| |
| type_map::build_type_with_children( |
| cx, |
| type_map::stub( |
| cx, |
| Stub::Struct, |
| UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index), |
| variant_def.name.as_str(), |
| // NOTE: We use size and align of enum_type, not from variant_layout: |
| cx.size_and_align_of(enum_type), |
| Some(enum_type_di_node), |
| DIFlags::FlagZero, |
| ), |
| |cx, struct_type_di_node| { |
| (0..variant_layout.fields.count()) |
| .map(|field_index| { |
| let field_name = if variant_def.ctor_kind != CtorKind::Fn { |
| // Fields have names |
| Cow::from(variant_def.fields[field_index].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.size, field_layout.align.abi), |
| variant_layout.fields.offset(field_index), |
| DIFlags::FlagZero, |
| type_di_node(cx, field_layout.ty), |
| ) |
| }) |
| .collect() |
| }, |
| |cx| build_generic_type_param_di_nodes(cx, enum_type), |
| ) |
| .di_node |
| } |
| |
| /// Build the struct type for describing a single generator state. |
| /// See [build_generator_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) |
| /// |
| /// ``` |
| pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| variant_index: VariantIdx, |
| generator_type_and_layout: TyAndLayout<'tcx>, |
| generator_type_di_node: &'ll DIType, |
| generator_layout: &GeneratorLayout<'tcx>, |
| state_specific_upvar_names: &IndexVec<GeneratorSavedLocal, Option<Symbol>>, |
| common_upvar_names: &[String], |
| ) -> &'ll DIType { |
| let variant_name = GeneratorSubsts::variant_name(variant_index); |
| let unique_type_id = UniqueTypeId::for_enum_variant_struct_type( |
| cx.tcx, |
| generator_type_and_layout.ty, |
| variant_index, |
| ); |
| |
| let variant_layout = generator_type_and_layout.for_variant(cx, variant_index); |
| |
| let generator_substs = match generator_type_and_layout.ty.kind() { |
| ty::Generator(_, substs, _) => substs.as_generator(), |
| _ => unreachable!(), |
| }; |
| |
| type_map::build_type_with_children( |
| cx, |
| type_map::stub( |
| cx, |
| Stub::Struct, |
| unique_type_id, |
| &variant_name, |
| size_and_align_of(generator_type_and_layout), |
| Some(generator_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 generator_saved_local = generator_layout.variant_fields[variant_index] |
| [Field::from_usize(field_index)]; |
| let field_name_maybe = state_specific_upvar_names[generator_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.size_and_align_of(field_type), |
| variant_layout.fields.offset(field_index), |
| DIFlags::FlagZero, |
| type_di_node(cx, field_type), |
| ) |
| }) |
| .collect(); |
| |
| // Fields that are common to all states |
| let common_fields: SmallVec<_> = generator_substs |
| .prefix_tys() |
| .enumerate() |
| .map(|(index, upvar_ty)| { |
| build_field_di_node( |
| cx, |
| variant_struct_type_di_node, |
| &common_upvar_names[index], |
| cx.size_and_align_of(upvar_ty), |
| generator_type_and_layout.fields.offset(index), |
| DIFlags::FlagZero, |
| type_di_node(cx, upvar_ty), |
| ) |
| }) |
| .collect(); |
| |
| state_specific_fields.into_iter().chain(common_fields.into_iter()).collect() |
| }, |
| |cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty), |
| ) |
| .di_node |
| } |
| |
| /// 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 dataful 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, |
| ) -> Option<u64> { |
| match enum_type_and_layout.layout.variants() { |
| &Variants::Single { .. } => None, |
| &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some( |
| enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val |
| as u64, |
| ), |
| &Variants::Multiple { |
| tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant }, |
| tag, |
| .. |
| } => { |
| if variant_index == dataful_variant { |
| None |
| } 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); |
| // NOTE(eddyb) do *NOT* remove this assert, until |
| // we pass the full 128-bit value to LLVM, otherwise |
| // truncation will be silent and remain undetected. |
| assert_eq!(value as u64 as u128, value); |
| Some(value as u64) |
| } |
| } |
| } |
| } |