Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debuginfo: Generalize C++-like encoding for enums. #98393

Merged
merged 15 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
622da5d
debuginfo: Change C++-like encoding for enums.
michaelwoerister Jun 20, 2022
063ebfa
Use enum2<_> instead of enum<_> for Cpp-like debuginfo enum type names.
michaelwoerister Jul 7, 2022
725ceae
Support wrapping 128-bit tag ranges for cpp-like enum debuginfo.
michaelwoerister Jul 7, 2022
6875f12
Remove out-dated NatVis visualizer.
michaelwoerister Jul 7, 2022
1bbda88
intrinsic.natvis: Don't access fields from context object in <Intrins…
michaelwoerister Jul 7, 2022
3a72ea0
[debuginfo] Add more test cases cpp-like enum debuginfo.
michaelwoerister Jul 7, 2022
8ef0301
intrinsic.natvis: Add comments, make names more consistent.
michaelwoerister Jul 8, 2022
dfb2c89
[compiletest] Add compiler-bundled NatVis files to test input stamp.
michaelwoerister Jul 8, 2022
8433e2a
[debuginfo] Remove the notion of a 'fallback variant' from the CPP-li…
michaelwoerister Jul 8, 2022
171d8a3
[debuginfo] Don't mark fields and types as artificial in CPP-like enu…
michaelwoerister Jul 8, 2022
95d7591
[debuginfo] Update cpp-like enum decoding docs to account for wrappin…
michaelwoerister Jul 20, 2022
b0e3ed6
[debuginfo] Use IndexMap instead of FxHashMap while generating cpp-li…
michaelwoerister Aug 11, 2022
ee634fb
[debuginfo] Update codegen tests for new cpp-like enum debuginfo enco…
michaelwoerister Aug 12, 2022
6d030a5
[debuginfo] Update src/test/debuginfo/mutex.rs for new cpp-like enum …
michaelwoerister Aug 12, 2022
03b93d0
[debuginfo] Fix msvc-pretty-enums debuginfo test for i686.
michaelwoerister Aug 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ macro_rules! return_if_di_node_created_in_meantime {
}

/// Extract size and alignment from a TyAndLayout.
#[inline]
fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) {
(ty_and_layout.size, ty_and_layout.align.abi)
}
Expand Down
738 changes: 574 additions & 164 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs

Large diffs are not rendered by default.

90 changes: 57 additions & 33 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use rustc_middle::{
ty::{
self,
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
util::Discr,
AdtDef, GeneratorSubsts, Ty, VariantDef,
},
};
Expand Down Expand Up @@ -90,8 +89,11 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
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()))
enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
// Is there anything we can do to support 128-bit C-Style enums?
wesleywiser marked this conversation as resolved.
Show resolved Hide resolved
let value = discr.val as u64;
(name, value)
}),
containing_scope,
),
Expand Down Expand Up @@ -152,7 +154,7 @@ 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>)>,
enumerators: impl Iterator<Item = (Cow<'tcx, str>, u64)>,
containing_scope: &'ll DIType,
) -> &'ll DIType {
let is_unsigned = match base_type.kind() {
Expand All @@ -161,18 +163,15 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
};

let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
.map(|(discr, variant_name)| {
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,
))
}
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators
.map(|(name, value)| unsafe {
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
DIB(cx),
name.as_ptr().cast(),
name.len(),
value as i64,
is_unsigned,
))
})
.collect();

Expand Down Expand Up @@ -247,23 +246,27 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
/// 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_and_layout: TyAndLayout<'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);
debug_assert_eq!(variant_layout.ty, enum_type_and_layout.ty);

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),
UniqueTypeId::for_enum_variant_struct_type(
cx.tcx,
enum_type_and_layout.ty,
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),
size_and_align_of(enum_type_and_layout),
Some(enum_type_di_node),
DIFlags::FlagZero,
),
Expand All @@ -290,9 +293,9 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
type_di_node(cx, field_layout.ty),
)
})
.collect()
.collect::<SmallVec<_>>()
},
|cx| build_generic_type_param_di_nodes(cx, enum_type),
|cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
)
.di_node
}
Expand Down Expand Up @@ -398,6 +401,19 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
.di_node
}

#[derive(Copy, Clone)]
enum DiscrResult {
NoDiscriminant,
Value(u128),
Range(u128, u128),
}

impl DiscrResult {
fn opt_single_val(&self) -> Option<u128> {
if let Self::Value(d) = *self { Some(d) } else { None }
}
}

/// 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)
Expand All @@ -407,30 +423,38 @@ fn compute_discriminant_value<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
enum_type_and_layout: TyAndLayout<'tcx>,
variant_index: VariantIdx,
) -> Option<u64> {
) -> DiscrResult {
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::Single { .. } => DiscrResult::NoDiscriminant,
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
),
&Variants::Multiple {
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
tag,
..
} => {
if variant_index == dataful_variant {
None
let valid_range = enum_type_and_layout
.for_variant(cx, variant_index)
.largest_niche
.as_ref()
.unwrap()
.valid_range;

let min = valid_range.start.min(valid_range.end);
let min = tag.size(cx).truncate(min);

let max = valid_range.start.max(valid_range.end);
let max = tag.size(cx).truncate(max);

DiscrResult::Range(min, max)
} 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.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)
DiscrResult::Value(value)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
cx,
enum_type,
enum_type_and_layout,
enum_type_di_node,
variant_index,
enum_adt_def.variant(variant_index),
Expand Down Expand Up @@ -413,7 +413,13 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>(
enum_type_and_layout.size.bits(),
enum_type_and_layout.align.abi.bits() as u32,
Size::ZERO.bits(),
discr_value.map(|v| cx.const_u64(v)),
discr_value.opt_single_val().map(|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);
cx.const_u64(value as u64)
}),
DIFlags::FlagZero,
variant_member_info.variant_struct_type_di_node,
)
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub(super) enum UniqueTypeId<'tcx> {
VariantPart(Ty<'tcx>, private::HiddenZst),
/// The ID for the artificial struct type describing a single enum variant.
VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst),
/// The ID for the additional wrapper struct type describing an enum variant in CPP-like mode.
VariantStructTypeCppLikeWrapper(Ty<'tcx>, VariantIdx, private::HiddenZst),
/// The ID of the artificial type we create for VTables.
VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst),
}
Expand All @@ -71,6 +73,15 @@ impl<'tcx> UniqueTypeId<'tcx> {
UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst)
}

pub fn for_enum_variant_struct_type_wrapper(
tcx: TyCtxt<'tcx>,
enum_ty: Ty<'tcx>,
variant_idx: VariantIdx,
) -> Self {
debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
UniqueTypeId::VariantStructTypeCppLikeWrapper(enum_ty, variant_idx, private::HiddenZst)
}

pub fn for_vtable_ty(
tcx: TyCtxt<'tcx>,
self_type: Ty<'tcx>,
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2079,6 +2079,19 @@ extern "C" {
Ty: &'a DIType,
) -> &'a DIType;

pub fn LLVMRustDIBuilderCreateStaticMemberType<'a>(
Builder: &DIBuilder<'a>,
Scope: &'a DIDescriptor,
Name: *const c_char,
NameLen: size_t,
File: &'a DIFile,
LineNo: c_uint,
Ty: &'a DIType,
Flags: DIFlags,
val: Option<&'a Value>,
AlignInBits: u32,
) -> &'a DIDerivedType;

pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>(
Builder: &DIBuilder<'a>,
Scope: &'a DIScope,
Expand Down
56 changes: 6 additions & 50 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Integer, TagEncoding, Variants};
use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::Integer;
use smallvec::SmallVec;

use std::borrow::Cow;
use std::fmt::Write;

use crate::debuginfo::wants_c_like_enum_debuginfo;
Expand Down Expand Up @@ -98,7 +97,6 @@ fn push_debuginfo_type_name<'tcx>(

if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
msvc_enum_fallback(
tcx,
ty_and_layout,
&|output, visited| {
push_item_name(tcx, def.did(), true, output);
Expand Down Expand Up @@ -391,11 +389,10 @@ fn push_debuginfo_type_name<'tcx>(
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
// "{async_fn_env#0}<T1, T2, ...>", etc.
// In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
// an artificial `enum$<>` type, as defined in msvc_enum_fallback().
// an artificial `enum2$<>` type, as defined in msvc_enum_fallback().
if cpp_like_debuginfo && t.is_generator() {
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
msvc_enum_fallback(
tcx,
ty_and_layout,
&|output, visited| {
push_closure_or_generator_name(tcx, def_id, substs, true, output, visited);
Expand Down Expand Up @@ -428,58 +425,17 @@ fn push_debuginfo_type_name<'tcx>(

/// MSVC names enums differently than other platforms so that the debugging visualization
// format (natvis) is able to understand enums and render the active variant correctly in the
// debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
// `EnumMemberDescriptionFactor::create_member_descriptions`.
// debugger. For more information, look in
// rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
fn msvc_enum_fallback<'tcx>(
tcx: TyCtxt<'tcx>,
ty_and_layout: TyAndLayout<'tcx>,
push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
output: &mut String,
visited: &mut FxHashSet<Ty<'tcx>>,
) {
debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
let ty = ty_and_layout.ty;

output.push_str("enum$<");
output.push_str("enum2$<");
push_inner(output, visited);

let variant_name = |variant_index| match ty.kind() {
ty::Adt(adt_def, _) => {
debug_assert!(adt_def.is_enum());
Cow::from(adt_def.variant(variant_index).name.as_str())
}
ty::Generator(..) => GeneratorSubsts::variant_name(variant_index),
_ => unreachable!(),
};

if let Variants::Multiple {
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
tag,
variants,
..
} = &ty_and_layout.variants
{
let dataful_variant_layout = &variants[*dataful_variant];

// calculate the range of values for the dataful variant
let dataful_discriminant_range =
dataful_variant_layout.largest_niche().unwrap().valid_range;

let min = dataful_discriminant_range.start;
let min = tag.size(&tcx).truncate(min);

let max = dataful_discriminant_range.end;
let max = tag.size(&tcx).truncate(max);

let dataful_variant_name = variant_name(*dataful_variant);
write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap();
} else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants {
// Uninhabited enums can't be constructed and should never need to be visualized so
// skip this step for them.
if !ty_and_layout.abi.is_uninhabited() {
write!(output, ", {}", variant_name(*variant_idx)).unwrap();
}
}
push_close_angle_bracket(true, output);
}

Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_index/src/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ impl<I: Idx, T> IndexVec<I, T> {
}

#[inline]
pub fn indices(&self) -> impl DoubleEndedIterator<Item = I> + ExactSizeIterator + 'static {
pub fn indices(
&self,
) -> impl DoubleEndedIterator<Item = I> + ExactSizeIterator + Clone + 'static {
(0..self.len()).map(|n| I::new(n))
}

Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,30 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
fromRust(Flags), unwrapDI<DIType>(Ty)));
}

extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType(
LLVMRustDIBuilderRef Builder,
LLVMMetadataRef Scope,
const char *Name,
size_t NameLen,
LLVMMetadataRef File,
unsigned LineNo,
LLVMMetadataRef Ty,
LLVMRustDIFlags Flags,
LLVMValueRef val,
uint32_t AlignInBits
) {
return wrap(Builder->createStaticMemberType(
unwrapDI<DIDescriptor>(Scope),
StringRef(Name, NameLen),
unwrapDI<DIFile>(File),
LineNo,
unwrapDI<DIType>(Ty),
fromRust(Flags),
unwrap<llvm::ConstantInt>(val),
AlignInBits
));
}

extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock(
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
LLVMMetadataRef File, unsigned Line, unsigned Col) {
Expand Down
Loading