| use std::borrow::Cow; |
| |
| use crate::{ |
| common::CodegenCx, |
| debuginfo::{ |
| metadata::{ |
| enums::tag_base_type, |
| file_metadata, size_and_align_of, type_di_node, |
| type_map::{self, Stub, StubInfo, UniqueTypeId}, |
| unknown_file_metadata, visibility_di_flags, DINodeCreationResult, SmallVec, |
| NO_GENERICS, UNKNOWN_LINE_NUMBER, |
| }, |
| utils::{create_DIArray, get_namespace_for_item, DIB}, |
| }, |
| llvm::{ |
| self, |
| debuginfo::{DIFile, DIFlags, DIType}, |
| }, |
| }; |
| use libc::c_uint; |
| use rustc_codegen_ssa::{ |
| debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo}, |
| traits::ConstMethods, |
| }; |
| use rustc_middle::{ |
| bug, |
| ty::{ |
| self, |
| layout::{LayoutOf, TyAndLayout}, |
| }, |
| }; |
| use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants}; |
| use smallvec::smallvec; |
| |
| /// Build the debuginfo node for an enum type. The listing below shows how such a |
| /// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type` |
| /// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant` |
| /// for each variant of the enum. The variant-part also contains a single member |
| /// describing the discriminant, and a nested struct type for each of the variants. |
| /// |
| /// ```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(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 containing_scope = get_namespace_for_item(cx, enum_adt_def.did()); |
| let enum_type_and_layout = cx.layout_of(enum_type); |
| let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); |
| |
| let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()); |
| |
| debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); |
| |
| type_map::build_type_with_children( |
| cx, |
| type_map::stub( |
| cx, |
| Stub::Struct, |
| unique_type_id, |
| &enum_type_name, |
| size_and_align_of(enum_type_and_layout), |
| Some(containing_scope), |
| visibility_flags, |
| ), |
| |cx, enum_type_di_node| { |
| // Build the struct type for each variant. These will be referenced by the |
| // DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE. |
| // We also called the names for the corresponding DW_TAG_variant DIEs here. |
| let variant_member_infos: SmallVec<_> = enum_adt_def |
| .variant_range() |
| .map(|variant_index| VariantMemberInfo { |
| variant_index, |
| variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()), |
| variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node( |
| cx, |
| enum_type_and_layout, |
| enum_type_di_node, |
| variant_index, |
| enum_adt_def.variant(variant_index), |
| enum_type_and_layout.for_variant(cx, variant_index), |
| visibility_flags, |
| ), |
| source_info: None, |
| }) |
| .collect(); |
| |
| smallvec![build_enum_variant_part_di_node( |
| cx, |
| enum_type_and_layout, |
| enum_type_di_node, |
| &variant_member_infos[..], |
| )] |
| }, |
| // We don't seem to be emitting generic args on the enum type, it seems. Rather |
| // they get attached to the struct type of each variant. |
| NO_GENERICS, |
| ) |
| } |
| |
| /// Build the debuginfo node for a coroutine environment. It looks the same as the debuginfo for |
| /// an enum. See [build_enum_type_di_node] for more information. |
| /// |
| /// ```txt |
| /// |
| /// ---> DW_TAG_structure_type (top-level type for the coroutine) |
| /// 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(super) fn build_coroutine_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| unique_type_id: UniqueTypeId<'tcx>, |
| ) -> DINodeCreationResult<'ll> { |
| let coroutine_type = unique_type_id.expect_ty(); |
| let &ty::Coroutine(coroutine_def_id, _) = coroutine_type.kind() else { |
| bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type) |
| }; |
| |
| let containing_scope = get_namespace_for_item(cx, coroutine_def_id); |
| let coroutine_type_and_layout = cx.layout_of(coroutine_type); |
| |
| debug_assert!(!wants_c_like_enum_debuginfo(coroutine_type_and_layout)); |
| |
| let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false); |
| |
| type_map::build_type_with_children( |
| cx, |
| type_map::stub( |
| cx, |
| Stub::Struct, |
| unique_type_id, |
| &coroutine_type_name, |
| size_and_align_of(coroutine_type_and_layout), |
| Some(containing_scope), |
| DIFlags::FlagZero, |
| ), |
| |cx, coroutine_type_di_node| { |
| let coroutine_layout = |
| cx.tcx.optimized_mir(coroutine_def_id).coroutine_layout().unwrap(); |
| |
| let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = |
| coroutine_type_and_layout.variants |
| else { |
| bug!( |
| "Encountered coroutine with non-direct-tag layout: {:?}", |
| coroutine_type_and_layout |
| ) |
| }; |
| |
| let common_upvar_names = |
| cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); |
| |
| // Build variant struct types |
| let variant_struct_type_di_nodes: SmallVec<_> = variants |
| .indices() |
| .map(|variant_index| { |
| // FIXME: This is problematic because just a number is not a valid identifier. |
| // CoroutineArgs::variant_name(variant_index), would be consistent |
| // with enums? |
| let variant_name = format!("{}", variant_index.as_usize()).into(); |
| |
| let span = coroutine_layout.variant_source_info[variant_index].span; |
| let source_info = if !span.is_dummy() { |
| let loc = cx.lookup_debug_loc(span.lo()); |
| Some((file_metadata(cx, &loc.file), loc.line)) |
| } else { |
| None |
| }; |
| |
| VariantMemberInfo { |
| variant_index, |
| variant_name, |
| variant_struct_type_di_node: |
| super::build_coroutine_variant_struct_type_di_node( |
| cx, |
| variant_index, |
| coroutine_type_and_layout, |
| coroutine_type_di_node, |
| coroutine_layout, |
| common_upvar_names, |
| ), |
| source_info, |
| } |
| }) |
| .collect(); |
| |
| smallvec![build_enum_variant_part_di_node( |
| cx, |
| coroutine_type_and_layout, |
| coroutine_type_di_node, |
| &variant_struct_type_di_nodes[..], |
| )] |
| }, |
| // We don't seem to be emitting generic args on the coroutine type, it seems. Rather |
| // they get attached to the struct type of each variant. |
| NO_GENERICS, |
| ) |
| } |
| |
| /// Builds the DW_TAG_variant_part of an enum or coroutine debuginfo 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_enum_variant_part_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| enum_type_and_layout: TyAndLayout<'tcx>, |
| enum_type_di_node: &'ll DIType, |
| variant_member_infos: &[VariantMemberInfo<'_, 'll>], |
| ) -> &'ll DIType { |
| let tag_member_di_node = |
| build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node); |
| |
| let variant_part_unique_type_id = |
| UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty); |
| |
| let stub = StubInfo::new( |
| cx, |
| variant_part_unique_type_id, |
| |cx, variant_part_unique_type_id_str| unsafe { |
| let variant_part_name = ""; |
| llvm::LLVMRustDIBuilderCreateVariantPart( |
| DIB(cx), |
| enum_type_di_node, |
| variant_part_name.as_ptr().cast(), |
| variant_part_name.len(), |
| unknown_file_metadata(cx), |
| UNKNOWN_LINE_NUMBER, |
| enum_type_and_layout.size.bits(), |
| enum_type_and_layout.align.abi.bits() as u32, |
| DIFlags::FlagZero, |
| tag_member_di_node, |
| create_DIArray(DIB(cx), &[]), |
| variant_part_unique_type_id_str.as_ptr().cast(), |
| variant_part_unique_type_id_str.len(), |
| ) |
| }, |
| ); |
| |
| type_map::build_type_with_children( |
| cx, |
| stub, |
| |cx, variant_part_di_node| { |
| variant_member_infos |
| .iter() |
| .map(|variant_member_info| { |
| build_enum_variant_member_di_node( |
| cx, |
| enum_type_and_layout, |
| variant_part_di_node, |
| variant_member_info, |
| ) |
| }) |
| .collect() |
| }, |
| NO_GENERICS, |
| ) |
| .di_node |
| } |
| |
| /// Builds the DW_TAG_member describing where we can find the tag of an enum. |
| /// Returns `None` if the enum does not have a tag. |
| /// |
| /// ```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_discr_member_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| enum_or_coroutine_type_and_layout: TyAndLayout<'tcx>, |
| enum_or_coroutine_type_di_node: &'ll DIType, |
| ) -> Option<&'ll DIType> { |
| let tag_name = match enum_or_coroutine_type_and_layout.ty.kind() { |
| ty::Coroutine(..) => "__state", |
| _ => "", |
| }; |
| |
| // NOTE: This is actually wrong. This will become a member of |
| // of the DW_TAG_variant_part. But, due to LLVM's API, that |
| // can only be constructed with this DW_TAG_member already in created. |
| // In LLVM IR the wrong scope will be listed but when DWARF is |
| // generated from it, the DW_TAG_member will be a child the |
| // DW_TAG_variant_part. |
| let containing_scope = enum_or_coroutine_type_di_node; |
| |
| match enum_or_coroutine_type_and_layout.layout.variants() { |
| // A single-variant enum has no discriminant. |
| &Variants::Single { .. } => None, |
| |
| &Variants::Multiple { tag_field, .. } => { |
| let tag_base_type = tag_base_type(cx, enum_or_coroutine_type_and_layout); |
| let (size, align) = cx.size_and_align_of(tag_base_type); |
| |
| unsafe { |
| Some(llvm::LLVMRustDIBuilderCreateMemberType( |
| DIB(cx), |
| containing_scope, |
| tag_name.as_ptr().cast(), |
| tag_name.len(), |
| unknown_file_metadata(cx), |
| UNKNOWN_LINE_NUMBER, |
| size.bits(), |
| align.bits() as u32, |
| enum_or_coroutine_type_and_layout.fields.offset(tag_field).bits(), |
| DIFlags::FlagArtificial, |
| type_di_node(cx, tag_base_type), |
| )) |
| } |
| } |
| } |
| } |
| |
| /// Build the debuginfo node for `DW_TAG_variant`: |
| /// |
| /// ```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) |
| /// ``` |
| /// |
| /// This node looks like: |
| /// |
| /// ```txt |
| /// DW_TAG_variant |
| /// DW_AT_discr_value 0 |
| /// DW_TAG_member |
| /// DW_AT_name None |
| /// DW_AT_type <0x000002a1> |
| /// DW_AT_alignment 0x00000002 |
| /// DW_AT_data_member_location 0 |
| /// ``` |
| /// |
| /// The DW_AT_discr_value is optional, and is omitted if |
| /// - This is the only variant of a univariant enum (i.e. their is no discriminant) |
| /// - This is the "untagged" variant of a niche-layout enum |
| /// (where only the other variants are identified by a single value) |
| /// |
| /// There is only ever a single member, the type of which is a struct that describes the |
| /// fields of the variant (excluding the discriminant). The name of the member is the name |
| /// of the variant as given in the source code. The DW_AT_data_member_location is always |
| /// zero. |
| /// |
| /// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree |
| /// (including the DW_TAG_member) is built by a single call to |
| /// `LLVMRustDIBuilderCreateVariantMemberType()`. |
| fn build_enum_variant_member_di_node<'ll, 'tcx>( |
| cx: &CodegenCx<'ll, 'tcx>, |
| enum_type_and_layout: TyAndLayout<'tcx>, |
| variant_part_di_node: &'ll DIType, |
| variant_member_info: &VariantMemberInfo<'_, 'll>, |
| ) -> &'ll DIType { |
| let variant_index = variant_member_info.variant_index; |
| let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index); |
| |
| let (file_di_node, line_number) = variant_member_info |
| .source_info |
| .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); |
| |
| unsafe { |
| llvm::LLVMRustDIBuilderCreateVariantMemberType( |
| DIB(cx), |
| variant_part_di_node, |
| variant_member_info.variant_name.as_ptr().cast(), |
| variant_member_info.variant_name.len(), |
| file_di_node, |
| line_number, |
| enum_type_and_layout.size.bits(), |
| enum_type_and_layout.align.abi.bits() as u32, |
| Size::ZERO.bits(), |
| discr_value.opt_single_val().map(|value| cx.const_u128(value)), |
| DIFlags::FlagZero, |
| variant_member_info.variant_struct_type_di_node, |
| ) |
| } |
| } |
| |
| /// Information needed for building a `DW_TAG_variant`: |
| /// |
| /// ```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) |
| /// ``` |
| struct VariantMemberInfo<'a, 'll> { |
| variant_index: VariantIdx, |
| variant_name: Cow<'a, str>, |
| variant_struct_type_di_node: &'ll DIType, |
| source_info: Option<(&'ll DIFile, c_uint)>, |
| } |