| use std::borrow::Cow; |
| |
| use libc::c_uint; |
| use rustc_codegen_ssa::{ |
| debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo}, |
| traits::ConstMethods, |
| }; |
| |
| use rustc_index::IndexVec; |
| use rustc_middle::{ |
| bug, |
| ty::{ |
| self, |
| layout::{LayoutOf, TyAndLayout}, |
| AdtDef, CoroutineArgs, Ty, |
| }, |
| }; |
| use rustc_target::abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants}; |
| use smallvec::smallvec; |
| |
| use crate::{ |
| common::CodegenCx, |
| debuginfo::{ |
| metadata::{ |
| build_field_di_node, |
| enums::{tag_base_type, DiscrResult}, |
| 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, |
| }, |
| utils::DIB, |
| }, |
| llvm::{ |
| self, |
| 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); |
| |
| debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout)); |
| |
| type_map::build_type_with_children( |
| cx, |
| type_map::stub( |
| cx, |
| type_map::Stub::Union, |
| unique_type_id, |
| &enum_type_name, |
| cx.size_and_align_of(enum_type), |
| NO_SCOPE_METADATA, |
| DIFlags::FlagZero, |
| ), |
| |cx, enum_type_di_node| { |
| match enum_type_and_layout.variants { |
| Variants::Single { index: variant_index } => { |
| if enum_adt_def.variants().is_empty() { |
| // Uninhabited enums have Variants::Single. We don't generate |
| // any members for them. |
| return smallvec![]; |
| } |
| |
| 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 coroutine_type_and_layout = cx.layout_of(coroutine_type); |
| let coroutine_type_name = compute_debuginfo_type_name(cx.tcx, coroutine_type, false); |
| |
| debug_assert!(!wants_c_like_enum_debuginfo(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, |
| 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::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 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, |
| ); |
| |
| 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 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()), |
| )), |
| ); |
| |
| 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, |
| ); |
| |
| smallvec![ |
| build_field_di_node( |
| cx, |
| enum_type_di_node, |
| &variant_union_field_name(variant_index), |
| // NOTE: We use the size and align of the entire type, not from variant_layout |
| // since the later is sometimes smaller (if it has fewer fields). |
| size_and_align_of(enum_type_and_layout), |
| Size::ZERO, |
| DIFlags::FlagZero, |
| variant_struct_type_wrapper_di_node, |
| ), |
| unsafe { |
| llvm::LLVMRustDIBuilderCreateStaticMemberType( |
| DIB(cx), |
| enum_type_di_node, |
| TAG_FIELD_NAME.as_ptr().cast(), |
| TAG_FIELD_NAME.len(), |
| unknown_file_metadata(cx), |
| UNKNOWN_LINE_NUMBER, |
| variant_names_type_di_node, |
| DIFlags::FlagZero, |
| Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), |
| tag_base_type_align.bits() as u32, |
| ) |
| } |
| ] |
| } |
| |
| 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: usize, |
| untagged_variant_index: Option<VariantIdx>, |
| ) -> SmallVec<&'ll DIType> { |
| let tag_base_type = super::tag_base_type(cx, enum_type_and_layout); |
| |
| 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) |
| }), |
| ); |
| |
| 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, |
| ); |
| |
| 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, |
| ) |
| } |
| |
| // 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>)>, |
| ) -> &'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())), |
| 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, |
| ) -> &'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), |
| // 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 { |
| debug_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 { |
| debug_assert_eq!(min, min as u64 as u128); |
| debug_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", |
| size_and_align_of(enum_or_coroutine_type_and_layout), |
| Size::ZERO, |
| DIFlags::FlagZero, |
| variant_struct_type_di_node, |
| )); |
| |
| let build_assoc_const = |
| |name: &str, type_di_node: &'ll DIType, value: u64, align: Align| unsafe { |
| llvm::LLVMRustDIBuilderCreateStaticMemberType( |
| DIB(cx), |
| wrapper_struct_type_di_node, |
| name.as_ptr().cast(), |
| name.len(), |
| unknown_file_metadata(cx), |
| UNKNOWN_LINE_NUMBER, |
| type_di_node, |
| DIFlags::FlagZero, |
| Some(cx.const_u64(value)), |
| align.bits() as u32, |
| ) |
| }; |
| |
| // 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.optimized_mir(coroutine_def_id).coroutine_layout().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, 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))), |
| ); |
| |
| 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, |
| ) |
| } |
| |
| /// 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: usize, |
| untagged_variant_index: Option<VariantIdx>, |
| ) -> 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 (size, align) = size_and_align_of(enum_type_and_layout); |
| |
| 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, |
| ); |
| |
| // We use LLVMRustDIBuilderCreateMemberType() 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. |
| unsafe { |
| llvm::LLVMRustDIBuilderCreateMemberType( |
| DIB(cx), |
| enum_type_di_node, |
| field_name.as_ptr().cast(), |
| field_name.len(), |
| file_di_node, |
| line_number, |
| // NOTE: We use the size and align of the entire type, not from variant_layout |
| // since the later is sometimes smaller (if it has fewer fields). |
| size.bits(), |
| align.bits() as u32, |
| // Union fields are always at offset zero |
| Size::ZERO.bits(), |
| DIFlags::FlagZero, |
| variant_struct_type_wrapper, |
| ) |
| } |
| })); |
| |
| debug_assert_eq!( |
| cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), |
| cx.size_and_align_of(super::tag_base_type(cx, 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 size_and_align = cx.size_and_align_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).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, |
| size_and_align, |
| lo_offset, |
| DIFlags::FlagZero, |
| type_di_node, |
| )); |
| |
| unions_fields.push(build_field_di_node( |
| cx, |
| enum_type_di_node, |
| TAG_FIELD_NAME_128_HI, |
| size_and_align, |
| hi_offset, |
| DIFlags::FlagZero, |
| type_di_node, |
| )); |
| } else { |
| unions_fields.push(build_field_di_node( |
| cx, |
| enum_type_di_node, |
| TAG_FIELD_NAME, |
| cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty), |
| enum_type_and_layout.fields.offset(tag_field), |
| DIFlags::FlagZero, |
| tag_base_type_di_node, |
| )); |
| } |
| |
| 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()) |
| } |