summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs')
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs740
1 files changed, 575 insertions, 165 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
index d6e2c8ccd..129e336c7 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -1,19 +1,21 @@
use std::borrow::Cow;
use libc::c_uint;
-use rustc_codegen_ssa::debuginfo::{
- type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo,
+use rustc_codegen_ssa::{
+ debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo},
+ traits::ConstMethods,
};
+
+use rustc_index::vec::IndexVec;
use rustc_middle::{
bug,
ty::{
self,
layout::{LayoutOf, TyAndLayout},
- util::Discr,
- AdtDef, GeneratorSubsts,
+ AdtDef, GeneratorSubsts, Ty,
},
};
-use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants};
+use rustc_target::abi::{Align, Endian, Size, TagEncoding, VariantIdx, Variants};
use smallvec::smallvec;
use crate::{
@@ -21,9 +23,9 @@ use crate::{
debuginfo::{
metadata::{
build_field_di_node, closure_saved_names_of_captured_variables,
- enums::tag_base_type,
- file_metadata, generator_layout_and_saved_local_names, size_and_align_of,
- type_map::{self, UniqueTypeId},
+ enums::{tag_base_type, DiscrResult},
+ file_metadata, generator_layout_and_saved_local_names, 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,
},
@@ -35,59 +37,161 @@ use crate::{
},
};
-/// In CPP-like mode, we generate a union of structs for each variant and an
-/// explicit discriminant field roughly equivalent to the following C/C++ code:
+// 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 discrimiant 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 enum$<{fully-qualified-name}> {
-/// struct {variant 0 name} {
-/// <variant 0 fields>
+/// 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>
-/// {name} discriminant;
+///
+/// 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 `enum$`. This way we can have a
-/// single NatVis rule for handling all enums.
+/// 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.
///
-/// At the LLVM IR level this looks like
+/// 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`.
///
-/// ```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)
-/// ```
+/// 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).
///
-/// The above encoding applies for enums with a direct tag. For niche-tag we have to do things
-/// differently in order to allow a NatVis visualizer to extract all the information needed:
-/// We generate a union of two fields, one for the dataful variant
-/// and one that just points to the discriminant (which is some field within the dataful variant).
-/// We also create a DW_TAG_enumeration_type DIE that contains tag values for the non-dataful
-/// variants and make the discriminant field that type. We then use NatVis to render the enum type
-/// correctly in Windbg/VS. This will generate debuginfo roughly equivalent to the following C:
+/// 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.
///
-/// ```c
-/// union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> {
-/// struct <dataful variant name> {
-/// <fields in dataful variant>
-/// } dataful_variant;
-/// enum Discriminant$ {
-/// <non-dataful variants>
-/// } discriminant;
+///
+/// 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!();
/// }
-/// ```
///
-/// The NatVis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>`
-/// and evaluates `this.discriminant`. If the value is between the min niche and max
-/// niche, then the enum is in the dataful variant and `this.dataful_variant` is
-/// rendered. Otherwise, the enum is in one of the non-dataful variants. In that
-/// case, we just need to render the name of the `this.discriminant` enum.
+/// // 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>,
@@ -135,27 +239,28 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
ref variants,
tag_field,
..
- } => build_union_fields_for_direct_tag_enum(
+ } => build_union_fields_for_enum(
cx,
enum_adt_def,
enum_type_and_layout,
enum_type_di_node,
- &mut variants.indices(),
+ variants.indices(),
tag_field,
+ None,
),
Variants::Multiple {
- tag_encoding: TagEncoding::Niche { dataful_variant, .. },
+ tag_encoding: TagEncoding::Niche { untagged_variant, .. },
ref variants,
tag_field,
..
- } => build_union_fields_for_niche_tag_enum(
+ } => build_union_fields_for_enum(
cx,
enum_adt_def,
enum_type_and_layout,
enum_type_di_node,
- dataful_variant,
- &mut variants.indices(),
+ variants.indices(),
tag_field,
+ Some(untagged_variant),
),
}
},
@@ -217,137 +322,344 @@ fn build_single_variant_union_fields<'ll, 'tcx>(
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.ty,
+ enum_type_and_layout,
enum_type_di_node,
variant_index,
enum_adt_def.variant(variant_index),
variant_layout,
);
- // NOTE: The field name of the union is the same as the variant name, not "variant0".
- let variant_name = enum_adt_def.variant(variant_index).name.as_str();
+ 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()),
+ )),
+ );
- smallvec![build_field_di_node(
+ let variant_struct_type_wrapper_di_node = build_variant_struct_wrapper_type_di_node(
cx,
+ enum_type_and_layout,
enum_type_di_node,
- variant_name,
- // 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_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_direct_tag_enum<'ll, 'tcx>(
+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: &mut dyn Iterator<Item = VariantIdx>,
+ 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: super::build_enum_variant_struct_type_di_node(
- cx,
- enum_type_and_layout.ty,
- enum_type_di_node,
- variant_index,
- enum_adt_def.variant(variant_index),
- variant_layout,
- ),
+ variant_struct_type_di_node,
source_info: None,
+ discr: super::compute_discriminant_value(cx, enum_type_and_layout, variant_index),
}
})
.collect();
- let discr_type_name = cx.tcx.item_name(enum_adt_def.did());
- let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
- let discr_type_di_node = super::build_enumeration_type_di_node(
- cx,
- discr_type_name.as_str(),
- tag_base_type,
- &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
- (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
- }),
- enum_type_di_node,
- );
-
build_union_fields_for_direct_tag_enum_or_generator(
cx,
enum_type_and_layout,
enum_type_di_node,
&variant_field_infos,
- discr_type_di_node,
+ variant_names_type_di_node,
+ tag_base_type,
tag_field,
+ untagged_variant_index,
)
}
-fn build_union_fields_for_niche_tag_enum<'ll, 'tcx>(
+// 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>,
- enum_adt_def: AdtDef<'tcx>,
- enum_type_and_layout: TyAndLayout<'tcx>,
- enum_type_di_node: &'ll DIType,
- dataful_variant_index: VariantIdx,
- variant_indices: &mut dyn Iterator<Item = VariantIdx>,
- tag_field: usize,
-) -> SmallVec<&'ll DIType> {
- let dataful_variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
+ 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,
- enum_type_and_layout.ty,
- enum_type_di_node,
- dataful_variant_index,
- &enum_adt_def.variant(dataful_variant_index),
- enum_type_and_layout.for_variant(cx, dataful_variant_index),
- );
+ "VariantNames",
+ variant_names_enum_base_type(cx),
+ variants.map(|(variant_index, variant_name)| (variant_name, variant_index.as_u32() as u64)),
+ containing_scope,
+ )
+}
- let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
- // Create an DW_TAG_enumerator for each variant except the dataful one.
- let discr_type_di_node = super::build_enumeration_type_di_node(
+fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>(
+ cx: &CodegenCx<'ll, 'tcx>,
+ enum_or_generator_type_and_layout: TyAndLayout<'tcx>,
+ enum_or_generator_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,
- "Discriminant$",
- tag_base_type,
- &mut variant_indices.filter_map(|variant_index| {
- if let Some(discr_val) =
- super::compute_discriminant_value(cx, enum_type_and_layout, variant_index)
- {
- let discr = Discr { val: discr_val as u128, ty: tag_base_type };
- let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
- Some((discr, variant_name))
- } else {
- debug_assert_eq!(variant_index, dataful_variant_index);
- None
- }
- }),
- enum_type_di_node,
- );
-
- smallvec![
- build_field_di_node(
- cx,
- enum_type_di_node,
- "dataful_variant",
- size_and_align_of(enum_type_and_layout),
- Size::ZERO,
- DIFlags::FlagZero,
- dataful_variant_struct_type_di_node,
- ),
- build_field_di_node(
+ type_map::stub(
cx,
- enum_type_di_node,
- "discriminant",
- cx.size_and_align_of(tag_base_type),
- enum_type_and_layout.fields.offset(tag_field),
+ Stub::Struct,
+ UniqueTypeId::for_enum_variant_struct_type_wrapper(
+ cx.tcx,
+ enum_or_generator_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_generator_type_and_layout),
+ Some(enum_or_generator_type_di_node),
DIFlags::FlagZero,
- discr_type_di_node,
),
- ]
+ |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_generator_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_generator<'ll, 'tcx>(
@@ -369,6 +681,29 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
let variant_range = generator_substs.variant_range(generator_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, generator_type_and_layout);
+
+ let variant_names_type_di_node = build_variant_names_type_di_node(
+ cx,
+ generator_type_di_node,
+ variant_range
+ .clone()
+ .map(|variant_index| (variant_index, GeneratorSubsts::variant_name(variant_index))),
+ );
+
+ let discriminants: IndexVec<VariantIdx, DiscrResult> = {
+ let discriminants_iter = generator_substs.discriminants(generator_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
@@ -391,29 +726,24 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
None
};
- VariantFieldInfo { variant_index, variant_struct_type_di_node, source_info }
+ VariantFieldInfo {
+ variant_index,
+ variant_struct_type_di_node,
+ source_info,
+ discr: discriminants[variant_index],
+ }
})
.collect();
- let tag_base_type = tag_base_type(cx, generator_type_and_layout);
- let discr_type_name = "Discriminant$";
- let discr_type_di_node = super::build_enumeration_type_di_node(
- cx,
- discr_type_name,
- tag_base_type,
- &mut generator_substs
- .discriminants(generator_def_id, cx.tcx)
- .map(|(variant_index, discr)| (discr, GeneratorSubsts::variant_name(variant_index))),
- generator_type_di_node,
- );
-
build_union_fields_for_direct_tag_enum_or_generator(
cx,
generator_type_and_layout,
generator_type_di_node,
&variant_field_infos[..],
- discr_type_di_node,
+ variant_names_type_di_node,
+ tag_base_type,
tag_field,
+ None,
)
}
@@ -425,8 +755,11 @@ fn build_union_fields_for_direct_tag_enum_or_generator<'ll, '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 ...
@@ -438,6 +771,19 @@ fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>(
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.
@@ -456,7 +802,7 @@ fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>(
// Union fields are always at offset zero
Size::ZERO.bits(),
DIFlags::FlagZero,
- variant_member_info.variant_struct_type_di_node,
+ variant_struct_type_wrapper,
)
}
}));
@@ -466,16 +812,53 @@ fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>(
cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout))
);
- // ... and a field for the discriminant.
- unions_fields.push(build_field_di_node(
- cx,
- enum_type_di_node,
- "discriminant",
- cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
- enum_type_and_layout.fields.offset(tag_field),
- DIFlags::FlagZero,
- discr_type_di_node,
- ));
+ // ... 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
}
@@ -485,6 +868,7 @@ 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> {
@@ -512,3 +896,29 @@ fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> {
.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())
+}