
This commit - changes names to use di_node instead of metadata - uniformly names all functions that build new debuginfo nodes build_xyz_di_node - renames CrateDebugContext to CodegenUnitDebugContext (which is more accurate) - moves TypeMap and functions that work directly work with it to a new type_map module - moves and reimplements enum related builder functions to a new enums module - splits enum debuginfo building for the native and cpp-like cases, since they are mostly separate - uses SmallVec instead of Vec in many places - removes the old infrastructure for dealing with recursion cycles (create_and_register_recursive_type_forward_declaration(), RecursiveTypeDescription, set_members_of_composite_type(), MemberDescription, MemberDescriptionFactory, prepare_xyz_metadata(), etc) - adds type_map::build_type_with_children() as a replacement for dealing with recursion cycles - adds many (doc-)comments explaining what's going on - changes cpp-like naming for C-Style enums so they don't get a enum$<...> name (because the NatVis visualizer does not apply to them) - fixes detection of what is a C-style enum because some enums where classified as C-style even though they have fields - changes the position of discriminant debuginfo node so it is consistently nested inside the top-level union instead of, sometimes, next to it
428 lines
16 KiB
Rust
428 lines
16 KiB
Rust
use rustc_codegen_ssa::debuginfo::{
|
|
type_names::{compute_debuginfo_type_name, cpp_like_debuginfo},
|
|
wants_c_like_enum_debuginfo,
|
|
};
|
|
use rustc_hir::def::CtorKind;
|
|
use rustc_index::vec::IndexVec;
|
|
use rustc_middle::{
|
|
bug,
|
|
mir::{Field, GeneratorLayout, GeneratorSavedLocal},
|
|
ty::{
|
|
self,
|
|
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
|
|
util::Discr,
|
|
AdtDef, GeneratorSubsts, Ty, VariantDef,
|
|
},
|
|
};
|
|
use rustc_span::Symbol;
|
|
use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants};
|
|
use std::borrow::Cow;
|
|
|
|
use crate::{
|
|
common::CodegenCx,
|
|
debuginfo::{
|
|
metadata::{
|
|
build_field_di_node, build_generic_type_param_di_nodes, type_di_node,
|
|
type_map::{self, Stub},
|
|
unknown_file_metadata, UNKNOWN_LINE_NUMBER,
|
|
},
|
|
utils::{create_DIArray, get_namespace_for_item, DIB},
|
|
},
|
|
llvm::{
|
|
self,
|
|
debuginfo::{DIFlags, DIType},
|
|
},
|
|
};
|
|
|
|
use super::{
|
|
size_and_align_of,
|
|
type_map::{DINodeCreationResult, UniqueTypeId},
|
|
SmallVec,
|
|
};
|
|
|
|
mod cpp_like;
|
|
mod native;
|
|
|
|
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);
|
|
|
|
if wants_c_like_enum_debuginfo(enum_type_and_layout) {
|
|
return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
|
|
}
|
|
|
|
if cpp_like_debuginfo(cx.tcx) {
|
|
cpp_like::build_enum_type_di_node(cx, unique_type_id)
|
|
} else {
|
|
native::build_enum_type_di_node(cx, unique_type_id)
|
|
}
|
|
}
|
|
|
|
pub(super) fn build_generator_di_node<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
unique_type_id: UniqueTypeId<'tcx>,
|
|
) -> DINodeCreationResult<'ll> {
|
|
if cpp_like_debuginfo(cx.tcx) {
|
|
cpp_like::build_generator_di_node(cx, unique_type_id)
|
|
} else {
|
|
native::build_generator_di_node(cx, unique_type_id)
|
|
}
|
|
}
|
|
|
|
/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields.
|
|
///
|
|
/// The resulting debuginfo will be a DW_TAG_enumeration_type.
|
|
fn build_c_style_enum_di_node<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
enum_adt_def: AdtDef<'tcx>,
|
|
enum_type_and_layout: TyAndLayout<'tcx>,
|
|
) -> DINodeCreationResult<'ll> {
|
|
let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
|
|
DINodeCreationResult {
|
|
di_node: build_enumeration_type_di_node(
|
|
cx,
|
|
&compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
|
|
tag_base_type(cx, enum_type_and_layout),
|
|
&mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
|
|
(discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
|
|
}),
|
|
containing_scope,
|
|
),
|
|
already_stored_in_typemap: false,
|
|
}
|
|
}
|
|
|
|
/// Extract the type with which we want to describe the tag of the given enum or generator.
|
|
fn tag_base_type<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
enum_type_and_layout: TyAndLayout<'tcx>,
|
|
) -> Ty<'tcx> {
|
|
debug_assert!(match enum_type_and_layout.ty.kind() {
|
|
ty::Generator(..) => true,
|
|
ty::Adt(adt_def, _) => adt_def.is_enum(),
|
|
_ => false,
|
|
});
|
|
|
|
// FIXME(mw): Why are niche and regular tags treated differently? Because we want to preserve
|
|
// the sign?
|
|
match enum_type_and_layout.layout.variants() {
|
|
// A single-variant enum has no discriminant.
|
|
Variants::Single { .. } => {
|
|
bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
|
|
}
|
|
|
|
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
|
|
match tag.value {
|
|
Primitive::Int(t, _) => t,
|
|
Primitive::F32 => Integer::I32,
|
|
Primitive::F64 => Integer::I64,
|
|
Primitive::Pointer => {
|
|
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
|
|
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
|
|
// pointer so we fix this up to just be `usize`.
|
|
cx.data_layout().ptr_sized_integer()
|
|
}
|
|
}
|
|
.to_ty(cx.tcx, false)
|
|
}
|
|
|
|
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
|
|
tag.value.to_ty(cx.tcx)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This is a helper function. FIXME: elaborate docs.
|
|
fn build_enumeration_type_di_node<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
type_name: &str,
|
|
base_type: Ty<'tcx>,
|
|
variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>,
|
|
containing_scope: &'ll DIType,
|
|
) -> &'ll DIType {
|
|
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
|
|
.map(|(discr, variant_name)| {
|
|
let is_unsigned = match discr.ty.kind() {
|
|
ty::Int(_) => false,
|
|
ty::Uint(_) => true,
|
|
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
|
|
};
|
|
unsafe {
|
|
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
|
|
DIB(cx),
|
|
variant_name.as_ptr().cast(),
|
|
variant_name.len(),
|
|
// FIXME: what if enumeration has i128 discriminant?
|
|
discr.val as i64,
|
|
is_unsigned,
|
|
))
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let (size, align) = cx.size_and_align_of(base_type);
|
|
|
|
unsafe {
|
|
llvm::LLVMRustDIBuilderCreateEnumerationType(
|
|
DIB(cx),
|
|
containing_scope,
|
|
type_name.as_ptr().cast(),
|
|
type_name.len(),
|
|
unknown_file_metadata(cx),
|
|
UNKNOWN_LINE_NUMBER,
|
|
size.bits(),
|
|
align.bits() as u32,
|
|
create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
|
|
type_di_node(cx, base_type),
|
|
true,
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Build the debuginfo node for the struct type describing a single variant of an enum.
|
|
///
|
|
/// ```txt
|
|
/// DW_TAG_structure_type (top-level type for enum)
|
|
/// DW_TAG_variant_part (variant part)
|
|
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
|
/// DW_TAG_member (discriminant member)
|
|
/// DW_TAG_variant (variant 1)
|
|
/// DW_TAG_variant (variant 2)
|
|
/// DW_TAG_variant (variant 3)
|
|
/// ---> DW_TAG_structure_type (type of variant 1)
|
|
/// ---> DW_TAG_structure_type (type of variant 2)
|
|
/// ---> DW_TAG_structure_type (type of variant 3)
|
|
/// ```
|
|
///
|
|
/// In CPP-like mode, we have the exact same descriptions for each variant too:
|
|
///
|
|
/// ```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)
|
|
/// ```
|
|
///
|
|
/// The node looks like:
|
|
///
|
|
/// ```txt
|
|
/// DW_TAG_structure_type
|
|
/// DW_AT_name <name-of-variant>
|
|
/// DW_AT_byte_size 0x00000010
|
|
/// DW_AT_alignment 0x00000008
|
|
/// DW_TAG_member
|
|
/// DW_AT_name <name-of-field-0>
|
|
/// DW_AT_type <0x0000018e>
|
|
/// DW_AT_alignment 0x00000004
|
|
/// DW_AT_data_member_location 4
|
|
/// DW_TAG_member
|
|
/// DW_AT_name <name-of-field-1>
|
|
/// DW_AT_type <0x00000195>
|
|
/// DW_AT_alignment 0x00000008
|
|
/// DW_AT_data_member_location 8
|
|
/// ...
|
|
/// ```
|
|
///
|
|
/// The type of a variant is always a struct type with the name of the variant
|
|
/// and a DW_TAG_member for each field (but not the discriminant).
|
|
fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
enum_type: Ty<'tcx>,
|
|
enum_type_di_node: &'ll DIType,
|
|
variant_index: VariantIdx,
|
|
variant_def: &VariantDef,
|
|
variant_layout: TyAndLayout<'tcx>,
|
|
) -> &'ll DIType {
|
|
debug_assert_eq!(variant_layout.ty, enum_type);
|
|
|
|
type_map::build_type_with_children(
|
|
cx,
|
|
type_map::stub(
|
|
cx,
|
|
Stub::Struct,
|
|
UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index),
|
|
variant_def.name.as_str(),
|
|
// NOTE: We use size and align of enum_type, not from variant_layout:
|
|
cx.size_and_align_of(enum_type),
|
|
Some(enum_type_di_node),
|
|
DIFlags::FlagZero,
|
|
),
|
|
|cx, struct_type_di_node| {
|
|
(0..variant_layout.fields.count())
|
|
.map(|field_index| {
|
|
let field_name = if variant_def.ctor_kind != CtorKind::Fn {
|
|
// Fields have names
|
|
Cow::from(variant_def.fields[field_index].name.as_str())
|
|
} else {
|
|
// Tuple-like
|
|
super::tuple_field_name(field_index)
|
|
};
|
|
|
|
let field_layout = variant_layout.field(cx, field_index);
|
|
|
|
build_field_di_node(
|
|
cx,
|
|
struct_type_di_node,
|
|
&field_name,
|
|
(field_layout.size, field_layout.align.abi),
|
|
variant_layout.fields.offset(field_index),
|
|
DIFlags::FlagZero,
|
|
type_di_node(cx, field_layout.ty),
|
|
)
|
|
})
|
|
.collect()
|
|
},
|
|
|cx| build_generic_type_param_di_nodes(cx, enum_type),
|
|
)
|
|
.di_node
|
|
}
|
|
|
|
/// Build the struct type for describing a single generator state.
|
|
/// See [build_generator_variant_struct_type_di_node].
|
|
///
|
|
/// ```txt
|
|
///
|
|
/// DW_TAG_structure_type (top-level type for enum)
|
|
/// DW_TAG_variant_part (variant part)
|
|
/// DW_AT_discr (reference to discriminant DW_TAG_member)
|
|
/// DW_TAG_member (discriminant member)
|
|
/// DW_TAG_variant (variant 1)
|
|
/// DW_TAG_variant (variant 2)
|
|
/// DW_TAG_variant (variant 3)
|
|
/// ---> DW_TAG_structure_type (type of variant 1)
|
|
/// ---> DW_TAG_structure_type (type of variant 2)
|
|
/// ---> DW_TAG_structure_type (type of variant 3)
|
|
///
|
|
/// ```
|
|
pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
variant_index: VariantIdx,
|
|
generator_type_and_layout: TyAndLayout<'tcx>,
|
|
generator_type_di_node: &'ll DIType,
|
|
generator_layout: &GeneratorLayout<'tcx>,
|
|
state_specific_upvar_names: &IndexVec<GeneratorSavedLocal, Option<Symbol>>,
|
|
common_upvar_names: &[String],
|
|
) -> &'ll DIType {
|
|
let variant_name = GeneratorSubsts::variant_name(variant_index);
|
|
let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
|
|
cx.tcx,
|
|
generator_type_and_layout.ty,
|
|
variant_index,
|
|
);
|
|
|
|
let variant_layout = generator_type_and_layout.for_variant(cx, variant_index);
|
|
|
|
let generator_substs = match generator_type_and_layout.ty.kind() {
|
|
ty::Generator(_, substs, _) => substs.as_generator(),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
type_map::build_type_with_children(
|
|
cx,
|
|
type_map::stub(
|
|
cx,
|
|
Stub::Struct,
|
|
unique_type_id,
|
|
&variant_name,
|
|
size_and_align_of(generator_type_and_layout),
|
|
Some(generator_type_di_node),
|
|
DIFlags::FlagZero,
|
|
),
|
|
|cx, variant_struct_type_di_node| {
|
|
// Fields that just belong to this variant/state
|
|
let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
|
|
.map(|field_index| {
|
|
let generator_saved_local = generator_layout.variant_fields[variant_index]
|
|
[Field::from_usize(field_index)];
|
|
let field_name_maybe = state_specific_upvar_names[generator_saved_local];
|
|
let field_name = field_name_maybe
|
|
.as_ref()
|
|
.map(|s| Cow::from(s.as_str()))
|
|
.unwrap_or_else(|| super::tuple_field_name(field_index));
|
|
|
|
let field_type = variant_layout.field(cx, field_index).ty;
|
|
|
|
build_field_di_node(
|
|
cx,
|
|
variant_struct_type_di_node,
|
|
&field_name,
|
|
cx.size_and_align_of(field_type),
|
|
variant_layout.fields.offset(field_index),
|
|
DIFlags::FlagZero,
|
|
type_di_node(cx, field_type),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
// Fields that are common to all states
|
|
let common_fields: SmallVec<_> = generator_substs
|
|
.prefix_tys()
|
|
.enumerate()
|
|
.map(|(index, upvar_ty)| {
|
|
build_field_di_node(
|
|
cx,
|
|
variant_struct_type_di_node,
|
|
&common_upvar_names[index],
|
|
cx.size_and_align_of(upvar_ty),
|
|
generator_type_and_layout.fields.offset(index),
|
|
DIFlags::FlagZero,
|
|
type_di_node(cx, upvar_ty),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
state_specific_fields.into_iter().chain(common_fields.into_iter()).collect()
|
|
},
|
|
|cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty),
|
|
)
|
|
.di_node
|
|
}
|
|
|
|
/// Returns the discriminant value corresponding to the variant index.
|
|
///
|
|
/// Will return `None` if there is less than two variants (because then the enum won't have)
|
|
/// a tag, and if this is the dataful variant of a niche-layout enum (because then there is no
|
|
/// single discriminant value).
|
|
fn compute_discriminant_value<'ll, 'tcx>(
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
enum_type_and_layout: TyAndLayout<'tcx>,
|
|
variant_index: VariantIdx,
|
|
) -> Option<u64> {
|
|
match enum_type_and_layout.layout.variants() {
|
|
&Variants::Single { .. } => None,
|
|
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some(
|
|
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val
|
|
as u64,
|
|
),
|
|
&Variants::Multiple {
|
|
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
|
|
tag,
|
|
..
|
|
} => {
|
|
if variant_index == dataful_variant {
|
|
None
|
|
} else {
|
|
let value = (variant_index.as_u32() as u128)
|
|
.wrapping_sub(niche_variants.start().as_u32() as u128)
|
|
.wrapping_add(niche_start);
|
|
let value = tag.value.size(cx).truncate(value);
|
|
// NOTE(eddyb) do *NOT* remove this assert, until
|
|
// we pass the full 128-bit value to LLVM, otherwise
|
|
// truncation will be silent and remain undetected.
|
|
assert_eq!(value as u64 as u128, value);
|
|
Some(value as u64)
|
|
}
|
|
}
|
|
}
|
|
}
|