]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / debuginfo / metadata / enums / cpp_like.rs
index d6e2c8ccdf44a2344cbf74004c0dec9f0f0b85db..129e336c7e431c58dd68b2280188bce1ee199cfa 100644 (file)
@@ -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())
+}