| use std::borrow::Cow; | |
| use libc::c_uint; | |
| use rustc_abi::{Align, Endian, FieldIdx, Size, TagEncoding, VariantIdx, Variants}; | |
| use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; | |
| use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; | |
| use rustc_codegen_ssa::traits::{ConstCodegenMethods, MiscCodegenMethods}; | |
| use rustc_index::IndexVec; | |
| use rustc_middle::bug; | |
| use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; | |
| use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; | |
| use smallvec::smallvec; | |
| use crate::common::CodegenCx; | |
| use crate::debuginfo::dwarf_const::DW_TAG_const_type; | |
| use crate::debuginfo::metadata::enums::DiscrResult; | |
| use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; | |
| use crate::debuginfo::metadata::{ | |
| DINodeCreationResult, NO_GENERICS, NO_SCOPE_METADATA, SmallVec, UNKNOWN_LINE_NUMBER, | |
| build_field_di_node, create_member_type, file_metadata, file_metadata_from_def_id, | |
| size_and_align_of, type_di_node, unknown_file_metadata, visibility_di_flags, | |
| }; | |
| use crate::debuginfo::utils::DIB; | |
| use crate::llvm; | |
| use crate::llvm::debuginfo::{DIFile, DIFlags, DIType}; | |
| // The names of the associated constants in each variant wrapper struct. | |
| // These have to match up with the names being used in `intrinsic.natvis`. | |
| const ASSOC_CONST_DISCR_NAME: &str = "NAME"; | |
| const ASSOC_CONST_DISCR_EXACT: &str = "DISCR_EXACT"; | |
| const ASSOC_CONST_DISCR_BEGIN: &str = "DISCR_BEGIN"; | |
| const ASSOC_CONST_DISCR_END: &str = "DISCR_END"; | |
| const ASSOC_CONST_DISCR128_EXACT_LO: &str = "DISCR128_EXACT_LO"; | |
| const ASSOC_CONST_DISCR128_EXACT_HI: &str = "DISCR128_EXACT_HI"; | |
| const ASSOC_CONST_DISCR128_BEGIN_LO: &str = "DISCR128_BEGIN_LO"; | |
| const ASSOC_CONST_DISCR128_BEGIN_HI: &str = "DISCR128_BEGIN_HI"; | |
| const ASSOC_CONST_DISCR128_END_LO: &str = "DISCR128_END_LO"; | |
| const ASSOC_CONST_DISCR128_END_HI: &str = "DISCR128_END_HI"; | |
| // The name of the tag field in the top-level union | |
| const TAG_FIELD_NAME: &str = "tag"; | |
| const TAG_FIELD_NAME_128_LO: &str = "tag128_lo"; | |
| const TAG_FIELD_NAME_128_HI: &str = "tag128_hi"; | |
| // We assign a "virtual" discriminant value to the sole variant of | |
| // a single-variant enum. | |
| const SINGLE_VARIANT_VIRTUAL_DISR: u64 = 0; | |
| /// In CPP-like mode, we generate a union with a field for each variant and an | |
| /// explicit tag field. The field of each variant has a struct type | |
| /// that encodes the discriminant of the variant and it's data layout. | |
| /// The union also has a nested enumeration type that is only used for encoding | |
| /// variant names in an efficient way. Its enumerator values do _not_ correspond | |
| /// to the enum's discriminant values. | |
| /// It's roughly equivalent to the following C/C++ code: | |
| /// | |
| /// ```c | |
| /// union enum2$<{fully-qualified-name}> { | |
| /// struct Variant0 { | |
| /// struct {name-of-variant-0} { | |
| /// <variant 0 fields> | |
| /// } value; | |
| /// | |
| /// static VariantNames NAME = {name-of-variant-0}; | |
| /// static int_type DISCR_EXACT = {discriminant-of-variant-0}; | |
| /// } variant0; | |
| /// | |
| /// <other variant structs> | |
| /// | |
| /// int_type tag; | |
| /// | |
| /// enum VariantNames { | |
| /// <name-of-variant-0> = 0, // The numeric values are variant index, | |
| /// <name-of-variant-1> = 1, // not discriminant values. | |
| /// <name-of-variant-2> = 2, | |
| /// ... | |
| /// } | |
| /// } | |
| /// ``` | |
| /// | |
| /// As you can see, the type name is wrapped in `enum2$<_>`. This way we can | |
| /// have a single NatVis rule for handling all enums. The `2` in `enum2$<_>` | |
| /// is an encoding version tag, so that debuggers can decide to decode this | |
| /// differently than the previous `enum$<_>` encoding emitted by earlier | |
| /// compiler versions. | |
| /// | |
| /// Niche-tag enums have one special variant, usually called the | |
| /// "untagged variant". This variant has a field that | |
| /// doubles as the tag of the enum. The variant is active when the value of | |
| /// that field is within a pre-defined range. Therefore the variant struct | |
| /// has a `DISCR_BEGIN` and `DISCR_END` field instead of `DISCR_EXACT` in | |
| /// that case. Both `DISCR_BEGIN` and `DISCR_END` are inclusive bounds. | |
| /// Note that these ranges can wrap around, so that `DISCR_END < DISCR_BEGIN`. | |
| /// | |
| /// Single-variant enums don't actually have a tag field. In this case we | |
| /// emit a static tag field (that always has the value 0) so we can use the | |
| /// same representation (and NatVis). | |
| /// | |
| /// For niche-layout enums it's possible to have a 128-bit tag. NatVis, VS, and | |
| /// WinDbg (the main targets for CPP-like debuginfo at the moment) don't support | |
| /// 128-bit integers, so all values involved get split into two 64-bit fields. | |
| /// Instead of the `tag` field, we generate two fields `tag128_lo` and `tag128_hi`, | |
| /// Instead of `DISCR_EXACT`, we generate `DISCR128_EXACT_LO` and `DISCR128_EXACT_HI`, | |
| /// and so on. | |
| /// | |
| /// | |
| /// The following pseudocode shows how to decode an enum value in a debugger: | |
| /// | |
| /// ```text | |
| /// | |
| /// fn find_active_variant(enum_value) -> (VariantName, VariantValue) { | |
| /// let is_128_bit = enum_value.has_field("tag128_lo"); | |
| /// | |
| /// if !is_128_bit { | |
| /// // Note: `tag` can be a static field for enums with only one | |
| /// // inhabited variant. | |
| /// let tag = enum_value.field("tag").value; | |
| /// | |
| /// // For each variant, check if it is a match. Only one of them will match, | |
| /// // so if we find it we can return it immediately. | |
| /// for variant_field in enum_value.fields().filter(|f| f.name.starts_with("variant")) { | |
| /// if variant_field.has_field("DISCR_EXACT") { | |
| /// // This variant corresponds to a single tag value | |
| /// if variant_field.field("DISCR_EXACT").value == tag { | |
| /// return (variant_field.field("NAME"), variant_field.value); | |
| /// } | |
| /// } else { | |
| /// // This is a range variant | |
| /// let begin = variant_field.field("DISCR_BEGIN"); | |
| /// let end = variant_field.field("DISCR_END"); | |
| /// | |
| /// if is_in_range(tag, begin, end) { | |
| /// return (variant_field.field("NAME"), variant_field.value); | |
| /// } | |
| /// } | |
| /// } | |
| /// } else { | |
| /// // Basically the same as with smaller tags, we just have to | |
| /// // stitch the values together. | |
| /// let tag: u128 = (enum_value.field("tag128_lo").value as u128) | | |
| /// (enum_value.field("tag128_hi").value as u128 << 64); | |
| /// | |
| /// for variant_field in enum_value.fields().filter(|f| f.name.starts_with("variant")) { | |
| /// if variant_field.has_field("DISCR128_EXACT_LO") { | |
| /// let discr_exact = (variant_field.field("DISCR128_EXACT_LO" as u128) | | |
| /// (variant_field.field("DISCR128_EXACT_HI") as u128 << 64); | |
| /// | |
| /// // This variant corresponds to a single tag value | |
| /// if discr_exact.value == tag { | |
| /// return (variant_field.field("NAME"), variant_field.value); | |
| /// } | |
| /// } else { | |
| /// // This is a range variant | |
| /// let begin = (variant_field.field("DISCR128_BEGIN_LO").value as u128) | | |
| /// (variant_field.field("DISCR128_BEGIN_HI").value as u128 << 64); | |
| /// let end = (variant_field.field("DISCR128_END_LO").value as u128) | | |
| /// (variant_field.field("DISCR128_END_HI").value as u128 << 64); | |
| /// | |
| /// if is_in_range(tag, begin, end) { | |
| /// return (variant_field.field("NAME"), variant_field.value); | |
| /// } | |
| /// } | |
| /// } | |
| /// } | |
| /// | |
| /// // We should have found an active variant at this point. | |
| /// unreachable!(); | |
| /// } | |
| /// | |
| /// // Check if a value is within the given range | |
| /// // (where the range might wrap around the value space) | |
| /// fn is_in_range(value, start, end) -> bool { | |
| /// if start < end { | |
| /// value >= start && value <= end | |
| /// } else { | |
| /// value >= start || value <= end | |
| /// } | |
| /// } | |
| /// | |
| /// ``` | |
| 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); | |
| let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false); | |
| assert!(!wants_c_like_enum_debuginfo(cx.tcx, enum_type_and_layout)); | |
| let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(file_metadata_from_def_id(cx, Some(enum_adt_def.did()))) | |
| } else { | |
| None | |
| }; | |
| type_map::build_type_with_children( | |
| cx, | |
| type_map::stub( | |
| cx, | |
| type_map::Stub::Union, | |
| unique_type_id, | |
| &enum_type_name, | |
| def_location, | |
| cx.size_and_align_of(enum_type), | |
| NO_SCOPE_METADATA, | |
| visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()), | |
| ), | |
| |cx, enum_type_di_node| { | |
| match enum_type_and_layout.variants { | |
| Variants::Empty => { | |
| // We don't generate any members for uninhabited types. | |
| return smallvec![]; | |
| } | |
| Variants::Single { index: variant_index } => build_single_variant_union_fields( | |
| cx, | |
| enum_adt_def, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variant_index, | |
| ), | |
| Variants::Multiple { | |
| tag_encoding: TagEncoding::Direct, | |
| ref variants, | |
| tag_field, | |
| .. | |
| } => build_union_fields_for_enum( | |
| cx, | |
| enum_adt_def, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variants.indices(), | |
| tag_field, | |
| None, | |
| ), | |
| Variants::Multiple { | |
| tag_encoding: TagEncoding::Niche { untagged_variant, .. }, | |
| ref variants, | |
| tag_field, | |
| .. | |
| } => build_union_fields_for_enum( | |
| cx, | |
| enum_adt_def, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variants.indices(), | |
| tag_field, | |
| Some(untagged_variant), | |
| ), | |
| } | |
| }, | |
| NO_GENERICS, | |
| ) | |
| } | |
| /// A coroutine debuginfo node looks the same as a that of an enum type. | |
| /// | |
| /// See [build_enum_type_di_node] for more information. | |
| 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 def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| let &ty::Coroutine(coroutine_def_id, _) = coroutine_type.kind() else { | |
| bug!("build_coroutine_di_node() called with non-coroutine type: `{:?}`", coroutine_type) | |
| }; | |
| Some(file_metadata_from_def_id(cx, Some(coroutine_def_id))) | |
| } else { | |
| None | |
| }; | |
| let coroutine_type_and_layout = cx.layout_of(coroutine_type); | |
| let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false); | |
| assert!(!wants_c_like_enum_debuginfo(cx.tcx, coroutine_type_and_layout)); | |
| type_map::build_type_with_children( | |
| cx, | |
| type_map::stub( | |
| cx, | |
| type_map::Stub::Union, | |
| unique_type_id, | |
| &coroutine_type_name, | |
| def_location, | |
| size_and_align_of(coroutine_type_and_layout), | |
| NO_SCOPE_METADATA, | |
| DIFlags::FlagZero, | |
| ), | |
| |cx, coroutine_type_di_node| match coroutine_type_and_layout.variants { | |
| Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => { | |
| build_union_fields_for_direct_tag_coroutine( | |
| cx, | |
| coroutine_type_and_layout, | |
| coroutine_type_di_node, | |
| ) | |
| } | |
| Variants::Single { .. } | |
| | Variants::Empty | |
| | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => { | |
| bug!( | |
| "Encountered coroutine with non-direct-tag layout: {:?}", | |
| coroutine_type_and_layout | |
| ) | |
| } | |
| }, | |
| NO_GENERICS, | |
| ) | |
| } | |
| fn build_single_variant_union_fields<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_adt_def: AdtDef<'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| enum_type_di_node: &'ll DIType, | |
| variant_index: VariantIdx, | |
| ) -> SmallVec<&'ll DIType> { | |
| let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); | |
| let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()); | |
| let 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), | |
| variant_layout, | |
| visibility_flags, | |
| ); | |
| let tag_base_type = cx.tcx.types.u32; | |
| let tag_base_type_di_node = type_di_node(cx, tag_base_type); | |
| let tag_base_type_align = cx.align_of(tag_base_type); | |
| let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(enum_adt_def.did()) | |
| } else { | |
| None | |
| }; | |
| let variant_names_type_di_node = build_variant_names_type_di_node( | |
| cx, | |
| enum_type_di_node, | |
| std::iter::once(( | |
| variant_index, | |
| Cow::from(enum_adt_def.variant(variant_index).name.as_str()), | |
| )), | |
| enum_adt_def_id, | |
| ); | |
| let variant_struct_type_wrapper_di_node = build_variant_struct_wrapper_type_di_node( | |
| cx, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variant_index, | |
| None, | |
| variant_struct_type_di_node, | |
| variant_names_type_di_node, | |
| tag_base_type_di_node, | |
| tag_base_type, | |
| DiscrResult::NoDiscriminant, | |
| None, | |
| ); | |
| smallvec![ | |
| build_field_di_node( | |
| cx, | |
| enum_type_di_node, | |
| &variant_union_field_name(variant_index), | |
| // NOTE: We use the layout of the entire type, not from variant_layout | |
| // since the later is sometimes smaller (if it has fewer fields). | |
| enum_type_and_layout, | |
| Size::ZERO, | |
| visibility_flags, | |
| variant_struct_type_wrapper_di_node, | |
| None, | |
| ), | |
| create_static_member_type( | |
| cx, | |
| enum_type_di_node, | |
| TAG_FIELD_NAME, | |
| unknown_file_metadata(cx), | |
| UNKNOWN_LINE_NUMBER, | |
| variant_names_type_di_node, | |
| visibility_flags, | |
| Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), | |
| tag_base_type_align, | |
| ), | |
| ] | |
| } | |
| fn build_union_fields_for_enum<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_adt_def: AdtDef<'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| enum_type_di_node: &'ll DIType, | |
| variant_indices: impl Iterator<Item = VariantIdx> + Clone, | |
| tag_field: FieldIdx, | |
| untagged_variant_index: Option<VariantIdx>, | |
| ) -> SmallVec<&'ll DIType> { | |
| let tag_base_type = tag_base_type(cx.tcx, enum_type_and_layout); | |
| let enum_adt_def_id = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(enum_adt_def.did()) | |
| } else { | |
| None | |
| }; | |
| let variant_names_type_di_node = build_variant_names_type_di_node( | |
| cx, | |
| enum_type_di_node, | |
| variant_indices.clone().map(|variant_index| { | |
| let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str()); | |
| (variant_index, variant_name) | |
| }), | |
| enum_adt_def_id, | |
| ); | |
| let visibility_flags = visibility_di_flags(cx, enum_adt_def.did(), enum_adt_def.did()); | |
| let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_indices | |
| .map(|variant_index| { | |
| let variant_layout = enum_type_and_layout.for_variant(cx, variant_index); | |
| let variant_def = enum_adt_def.variant(variant_index); | |
| let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node( | |
| cx, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variant_index, | |
| variant_def, | |
| variant_layout, | |
| visibility_flags, | |
| ); | |
| VariantFieldInfo { | |
| variant_index, | |
| variant_struct_type_di_node, | |
| source_info: None, | |
| discr: super::compute_discriminant_value(cx, enum_type_and_layout, variant_index), | |
| } | |
| }) | |
| .collect(); | |
| build_union_fields_for_direct_tag_enum_or_coroutine( | |
| cx, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| &variant_field_infos, | |
| variant_names_type_di_node, | |
| tag_base_type, | |
| tag_field, | |
| untagged_variant_index, | |
| visibility_flags, | |
| ) | |
| } | |
| // The base type of the VariantNames DW_AT_enumeration_type is always the same. | |
| // It has nothing to do with the tag of the enum and just has to be big enough | |
| // to hold all variant names. | |
| fn variant_names_enum_base_type<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> Ty<'tcx> { | |
| cx.tcx.types.u32 | |
| } | |
| /// This function builds a DW_AT_enumeration_type that contains an entry for | |
| /// each variant. Note that this has nothing to do with the discriminant. The | |
| /// numeric value of each enumerator corresponds to the variant index. The | |
| /// type is only used for efficiently encoding the name of each variant in | |
| /// debuginfo. | |
| fn build_variant_names_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| containing_scope: &'ll DIType, | |
| variants: impl Iterator<Item = (VariantIdx, Cow<'tcx, str>)>, | |
| enum_def_id: Option<rustc_span::def_id::DefId>, | |
| ) -> &'ll DIType { | |
| // Create an enumerator for each variant. | |
| super::build_enumeration_type_di_node( | |
| cx, | |
| "VariantNames", | |
| variant_names_enum_base_type(cx), | |
| variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32().into())), | |
| enum_def_id, | |
| containing_scope, | |
| ) | |
| } | |
| fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_or_coroutine_type_and_layout: TyAndLayout<'tcx>, | |
| enum_or_coroutine_type_di_node: &'ll DIType, | |
| variant_index: VariantIdx, | |
| untagged_variant_index: Option<VariantIdx>, | |
| variant_struct_type_di_node: &'ll DIType, | |
| variant_names_type_di_node: &'ll DIType, | |
| tag_base_type_di_node: &'ll DIType, | |
| tag_base_type: Ty<'tcx>, | |
| discr: DiscrResult, | |
| source_info: Option<(&'ll DIFile, c_uint)>, | |
| ) -> &'ll DIType { | |
| type_map::build_type_with_children( | |
| cx, | |
| type_map::stub( | |
| cx, | |
| Stub::Struct, | |
| UniqueTypeId::for_enum_variant_struct_type_wrapper( | |
| cx.tcx, | |
| enum_or_coroutine_type_and_layout.ty, | |
| variant_index, | |
| ), | |
| &variant_struct_wrapper_type_name(variant_index), | |
| source_info, | |
| // NOTE: We use size and align of enum_type, not from variant_layout: | |
| size_and_align_of(enum_or_coroutine_type_and_layout), | |
| Some(enum_or_coroutine_type_di_node), | |
| DIFlags::FlagZero, | |
| ), | |
| |cx, wrapper_struct_type_di_node| { | |
| enum DiscrKind { | |
| Exact(u64), | |
| Exact128(u128), | |
| Range(u64, u64), | |
| Range128(u128, u128), | |
| } | |
| let (tag_base_type_size, tag_base_type_align) = cx.size_and_align_of(tag_base_type); | |
| let is_128_bits = tag_base_type_size.bits() > 64; | |
| let discr = match discr { | |
| DiscrResult::NoDiscriminant => DiscrKind::Exact(SINGLE_VARIANT_VIRTUAL_DISR), | |
| DiscrResult::Value(discr_val) => { | |
| if is_128_bits { | |
| DiscrKind::Exact128(discr_val) | |
| } else { | |
| assert_eq!(discr_val, discr_val as u64 as u128); | |
| DiscrKind::Exact(discr_val as u64) | |
| } | |
| } | |
| DiscrResult::Range(min, max) => { | |
| assert_eq!(Some(variant_index), untagged_variant_index); | |
| if is_128_bits { | |
| DiscrKind::Range128(min, max) | |
| } else { | |
| assert_eq!(min, min as u64 as u128); | |
| assert_eq!(max, max as u64 as u128); | |
| DiscrKind::Range(min as u64, max as u64) | |
| } | |
| } | |
| }; | |
| let mut fields = SmallVec::new(); | |
| // We always have a field for the value | |
| fields.push(build_field_di_node( | |
| cx, | |
| wrapper_struct_type_di_node, | |
| "value", | |
| enum_or_coroutine_type_and_layout, | |
| Size::ZERO, | |
| DIFlags::FlagZero, | |
| variant_struct_type_di_node, | |
| None, | |
| )); | |
| let build_assoc_const = |name: &str, | |
| type_di_node_: &'ll DIType, | |
| value: u64, | |
| align: Align| | |
| -> &'ll llvm::Metadata { | |
| // FIXME: Currently we force all DISCR_* values to be u64's as LLDB seems to have | |
| // problems inspecting other value types. Since DISCR_* is typically only going to be | |
| // directly inspected via the debugger visualizer - which compares it to the `tag` value | |
| // (whose type is not modified at all) it shouldn't cause any real problems. | |
| let (t_di, align) = if name == ASSOC_CONST_DISCR_NAME { | |
| (type_di_node_, align) | |
| } else { | |
| let ty_u64 = Ty::new_uint(cx.tcx, ty::UintTy::U64); | |
| (type_di_node(cx, ty_u64), Align::EIGHT) | |
| }; | |
| // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member | |
| let field_type = unsafe { | |
| llvm::LLVMDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di) | |
| }; | |
| create_static_member_type( | |
| cx, | |
| wrapper_struct_type_di_node, | |
| name, | |
| unknown_file_metadata(cx), | |
| UNKNOWN_LINE_NUMBER, | |
| field_type, | |
| DIFlags::FlagZero, | |
| Some(cx.const_u64(value)), | |
| align, | |
| ) | |
| }; | |
| // We also always have an associated constant for the discriminant value | |
| // of the variant. | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR_NAME, | |
| variant_names_type_di_node, | |
| variant_index.as_u32() as u64, | |
| cx.align_of(variant_names_enum_base_type(cx)), | |
| )); | |
| // Emit the discriminant value (or range) corresponding to the variant. | |
| match discr { | |
| DiscrKind::Exact(discr_val) => { | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR_EXACT, | |
| tag_base_type_di_node, | |
| discr_val, | |
| tag_base_type_align, | |
| )); | |
| } | |
| DiscrKind::Exact128(discr_val) => { | |
| let align = cx.align_of(cx.tcx.types.u64); | |
| let type_di_node = type_di_node(cx, cx.tcx.types.u64); | |
| let Split128 { hi, lo } = split_128(discr_val); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_EXACT_LO, | |
| type_di_node, | |
| lo, | |
| align, | |
| )); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_EXACT_HI, | |
| type_di_node, | |
| hi, | |
| align, | |
| )); | |
| } | |
| DiscrKind::Range(begin, end) => { | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR_BEGIN, | |
| tag_base_type_di_node, | |
| begin, | |
| tag_base_type_align, | |
| )); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR_END, | |
| tag_base_type_di_node, | |
| end, | |
| tag_base_type_align, | |
| )); | |
| } | |
| DiscrKind::Range128(begin, end) => { | |
| let align = cx.align_of(cx.tcx.types.u64); | |
| let type_di_node = type_di_node(cx, cx.tcx.types.u64); | |
| let Split128 { hi: begin_hi, lo: begin_lo } = split_128(begin); | |
| let Split128 { hi: end_hi, lo: end_lo } = split_128(end); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_BEGIN_HI, | |
| type_di_node, | |
| begin_hi, | |
| align, | |
| )); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_BEGIN_LO, | |
| type_di_node, | |
| begin_lo, | |
| align, | |
| )); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_END_HI, | |
| type_di_node, | |
| end_hi, | |
| align, | |
| )); | |
| fields.push(build_assoc_const( | |
| ASSOC_CONST_DISCR128_END_LO, | |
| type_di_node, | |
| end_lo, | |
| align, | |
| )); | |
| } | |
| } | |
| fields | |
| }, | |
| NO_GENERICS, | |
| ) | |
| .di_node | |
| } | |
| struct Split128 { | |
| hi: u64, | |
| lo: u64, | |
| } | |
| fn split_128(value: u128) -> Split128 { | |
| Split128 { hi: (value >> 64) as u64, lo: value as u64 } | |
| } | |
| fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| coroutine_type_and_layout: TyAndLayout<'tcx>, | |
| coroutine_type_di_node: &'ll DIType, | |
| ) -> SmallVec<&'ll DIType> { | |
| let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } = | |
| coroutine_type_and_layout.variants | |
| else { | |
| bug!("This function only supports layouts with directly encoded tags.") | |
| }; | |
| let (coroutine_def_id, coroutine_args) = match coroutine_type_and_layout.ty.kind() { | |
| &ty::Coroutine(def_id, args) => (def_id, args.as_coroutine()), | |
| _ => unreachable!(), | |
| }; | |
| let coroutine_layout = cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.args).unwrap(); | |
| let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); | |
| let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); | |
| let variant_count = (variant_range.start.as_u32()..variant_range.end.as_u32()).len(); | |
| let tag_base_type = tag_base_type(cx.tcx, coroutine_type_and_layout); | |
| let variant_names_type_di_node = build_variant_names_type_di_node( | |
| cx, | |
| coroutine_type_di_node, | |
| variant_range | |
| .clone() | |
| .map(|variant_index| (variant_index, CoroutineArgs::variant_name(variant_index))), | |
| if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| Some(coroutine_def_id) | |
| } else { | |
| None | |
| }, | |
| ); | |
| let discriminants: IndexVec<VariantIdx, DiscrResult> = { | |
| let discriminants_iter = coroutine_args.discriminants(coroutine_def_id, cx.tcx); | |
| let mut discriminants: IndexVec<VariantIdx, DiscrResult> = | |
| IndexVec::with_capacity(variant_count); | |
| for (variant_index, discr) in discriminants_iter { | |
| // Assert that the index in the IndexMap matches up with the given VariantIdx. | |
| assert_eq!(variant_index, discriminants.next_index()); | |
| discriminants.push(DiscrResult::Value(discr.val)); | |
| } | |
| discriminants | |
| }; | |
| // Build the type node for each field. | |
| let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_range | |
| .map(|variant_index| { | |
| let 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, | |
| ); | |
| 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 as c_uint)) | |
| } else { | |
| None | |
| }; | |
| VariantFieldInfo { | |
| variant_index, | |
| variant_struct_type_di_node, | |
| source_info, | |
| discr: discriminants[variant_index], | |
| } | |
| }) | |
| .collect(); | |
| build_union_fields_for_direct_tag_enum_or_coroutine( | |
| cx, | |
| coroutine_type_and_layout, | |
| coroutine_type_di_node, | |
| &variant_field_infos[..], | |
| variant_names_type_di_node, | |
| tag_base_type, | |
| tag_field, | |
| None, | |
| DIFlags::FlagZero, | |
| ) | |
| } | |
| /// This is a helper function shared between enums and coroutines that makes sure fields have the | |
| /// expect names. | |
| fn build_union_fields_for_direct_tag_enum_or_coroutine<'ll, 'tcx>( | |
| cx: &CodegenCx<'ll, 'tcx>, | |
| enum_type_and_layout: TyAndLayout<'tcx>, | |
| enum_type_di_node: &'ll DIType, | |
| variant_field_infos: &[VariantFieldInfo<'ll>], | |
| discr_type_di_node: &'ll DIType, | |
| tag_base_type: Ty<'tcx>, | |
| tag_field: FieldIdx, | |
| untagged_variant_index: Option<VariantIdx>, | |
| di_flags: DIFlags, | |
| ) -> SmallVec<&'ll DIType> { | |
| let tag_base_type_di_node = type_di_node(cx, tag_base_type); | |
| let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1); | |
| // We create a field in the union for each variant ... | |
| unions_fields.extend(variant_field_infos.into_iter().map(|variant_member_info| { | |
| let (file_di_node, line_number) = variant_member_info | |
| .source_info | |
| .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER)); | |
| let field_name = variant_union_field_name(variant_member_info.variant_index); | |
| let variant_struct_type_wrapper = build_variant_struct_wrapper_type_di_node( | |
| cx, | |
| enum_type_and_layout, | |
| enum_type_di_node, | |
| variant_member_info.variant_index, | |
| untagged_variant_index, | |
| variant_member_info.variant_struct_type_di_node, | |
| discr_type_di_node, | |
| tag_base_type_di_node, | |
| tag_base_type, | |
| variant_member_info.discr, | |
| if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { | |
| variant_member_info.source_info | |
| } else { | |
| None | |
| }, | |
| ); | |
| // We use create_member_type() member type directly because | |
| // the build_field_di_node() function does not support specifying a source location, | |
| // which is something that we don't do anywhere else. | |
| create_member_type( | |
| cx, | |
| enum_type_di_node, | |
| &field_name, | |
| file_di_node, | |
| line_number, | |
| // NOTE: We use the layout of the entire type, not from variant_layout | |
| // since the later is sometimes smaller (if it has fewer fields). | |
| enum_type_and_layout, | |
| // Union fields are always at offset zero | |
| Size::ZERO, | |
| di_flags, | |
| variant_struct_type_wrapper, | |
| ) | |
| })); | |
| assert_eq!( | |
| cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field.as_usize()).ty), | |
| cx.size_and_align_of(self::tag_base_type(cx.tcx, enum_type_and_layout)) | |
| ); | |
| // ... and a field for the tag. If the tag is 128 bits wide, this will actually | |
| // be two 64-bit fields. | |
| let is_128_bits = cx.size_of(tag_base_type).bits() > 64; | |
| if is_128_bits { | |
| let type_di_node = type_di_node(cx, cx.tcx.types.u64); | |
| let u64_layout = cx.layout_of(cx.tcx.types.u64); | |
| let (lo_offset, hi_offset) = match cx.tcx.data_layout.endian { | |
| Endian::Little => (0, 8), | |
| Endian::Big => (8, 0), | |
| }; | |
| let tag_field_offset = enum_type_and_layout.fields.offset(tag_field.as_usize()).bytes(); | |
| let lo_offset = Size::from_bytes(tag_field_offset + lo_offset); | |
| let hi_offset = Size::from_bytes(tag_field_offset + hi_offset); | |
| unions_fields.push(build_field_di_node( | |
| cx, | |
| enum_type_di_node, | |
| TAG_FIELD_NAME_128_LO, | |
| u64_layout, | |
| lo_offset, | |
| di_flags, | |
| type_di_node, | |
| None, | |
| )); | |
| unions_fields.push(build_field_di_node( | |
| cx, | |
| enum_type_di_node, | |
| TAG_FIELD_NAME_128_HI, | |
| u64_layout, | |
| hi_offset, | |
| DIFlags::FlagZero, | |
| type_di_node, | |
| None, | |
| )); | |
| } else { | |
| unions_fields.push(build_field_di_node( | |
| cx, | |
| enum_type_di_node, | |
| TAG_FIELD_NAME, | |
| enum_type_and_layout.field(cx, tag_field.as_usize()), | |
| enum_type_and_layout.fields.offset(tag_field.as_usize()), | |
| di_flags, | |
| tag_base_type_di_node, | |
| None, | |
| )); | |
| } | |
| unions_fields | |
| } | |
| /// Information about a single field of the top-level DW_TAG_union_type. | |
| struct VariantFieldInfo<'ll> { | |
| variant_index: VariantIdx, | |
| variant_struct_type_di_node: &'ll DIType, | |
| source_info: Option<(&'ll DIFile, c_uint)>, | |
| discr: DiscrResult, | |
| } | |
| fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> { | |
| const PRE_ALLOCATED: [&str; 16] = [ | |
| "variant0", | |
| "variant1", | |
| "variant2", | |
| "variant3", | |
| "variant4", | |
| "variant5", | |
| "variant6", | |
| "variant7", | |
| "variant8", | |
| "variant9", | |
| "variant10", | |
| "variant11", | |
| "variant12", | |
| "variant13", | |
| "variant14", | |
| "variant15", | |
| ]; | |
| PRE_ALLOCATED | |
| .get(variant_index.as_usize()) | |
| .map(|&s| Cow::from(s)) | |
| .unwrap_or_else(|| format!("variant{}", variant_index.as_usize()).into()) | |
| } | |
| fn variant_struct_wrapper_type_name(variant_index: VariantIdx) -> Cow<'static, str> { | |
| const PRE_ALLOCATED: [&str; 16] = [ | |
| "Variant0", | |
| "Variant1", | |
| "Variant2", | |
| "Variant3", | |
| "Variant4", | |
| "Variant5", | |
| "Variant6", | |
| "Variant7", | |
| "Variant8", | |
| "Variant9", | |
| "Variant10", | |
| "Variant11", | |
| "Variant12", | |
| "Variant13", | |
| "Variant14", | |
| "Variant15", | |
| ]; | |
| PRE_ALLOCATED | |
| .get(variant_index.as_usize()) | |
| .map(|&s| Cow::from(s)) | |
| .unwrap_or_else(|| format!("Variant{}", variant_index.as_usize()).into()) | |
| } | |
| fn create_static_member_type<'ll>( | |
| cx: &CodegenCx<'ll, '_>, | |
| scope: &'ll llvm::Metadata, | |
| name: &str, | |
| file: &'ll llvm::Metadata, | |
| line_number: c_uint, | |
| ty: &'ll llvm::Metadata, | |
| flags: DIFlags, | |
| value: Option<&'ll llvm::Value>, | |
| align: Align, | |
| ) -> &'ll llvm::Metadata { | |
| unsafe { | |
| llvm::LLVMDIBuilderCreateStaticMemberType( | |
| DIB(cx), | |
| scope, | |
| name.as_ptr(), | |
| name.len(), | |
| file, | |
| line_number, | |
| ty, | |
| flags, | |
| value, | |
| align.bits() as c_uint, | |
| ) | |
| } | |
| } |