Skip to content

Commit 4916e2b

Browse files
committed
Auto merge of rust-lang#98393 - michaelwoerister:new-cpp-like-enum-debuginfo, r=wesleywiser
debuginfo: Generalize C++-like encoding for enums. The updated encoding should be able to handle niche layouts where more than one variant has fields (as introduced in rust-lang#94075). The new encoding is more uniform as there is no structural difference between direct-tag, niche-tag, and no-tag layouts anymore. The only difference between those cases is that the "dataful" variant in a niche-tag enum will have a `(start, end)` pair denoting the tag range instead of a single value. The new encoding now also supports 128-bit tags, which occur in at least some standard library types. These tags are represented as `u64` pairs so that debuggers (which don't always have support for 128-bit integers) can reliably deal with them. The downside is that this adds quite a bit of complexity to the encoding and especially to the corresponding NatVis. The new encoding seems to increase the size of (x86_64-pc-windows-msvc) debuginfo by 10-15%. The size of binaries is not affected (release builds were built with `-Cdebuginfo=2`, numbers are in kilobytes): EXE | before | after | relative -- | -- | -- | -- cargo (debug) | 40453 | 40450 | +0% ripgrep (debug) | 10275 | 10273 | +0% cargo (release) | 16186 | 16185 | +0% ripgrep (release) | 4727 | 4726 | +0% PDB | before | after | relative -- | -- | -- | -- cargo (debug) | 236524 | 261412 | +11% ripgrep (debug) | 53140 | 59060 | +11% cargo (release) | 148516 | 169620 | +14% ripgrep (release) | 10676 | 11804 | +11% Given that the new encoding is more general, this is to be expected. Only platforms using C++-like debuginfo are affected -- which currently is only `*-pc-windows-msvc`. *TODO* - [x] Properly update documentation - [x] Add regression tests for new optimized enum layouts as introduced by rust-lang#94075. r? `@wesleywiser`
2 parents 6ce7609 + 03b93d0 commit 4916e2b

File tree

23 files changed

+1118
-424
lines changed

23 files changed

+1118
-424
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ macro_rules! return_if_di_node_created_in_meantime {
114114
}
115115

116116
/// Extract size and alignment from a TyAndLayout.
117+
#[inline]
117118
fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) {
118119
(ty_and_layout.size, ty_and_layout.align.abi)
119120
}

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs

+574-164
Large diffs are not rendered by default.

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs

+57-33
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use rustc_middle::{
1010
ty::{
1111
self,
1212
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
13-
util::Discr,
1413
AdtDef, GeneratorSubsts, Ty, VariantDef,
1514
},
1615
};
@@ -90,8 +89,11 @@ fn build_c_style_enum_di_node<'ll, 'tcx>(
9089
cx,
9190
&compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
9291
tag_base_type(cx, enum_type_and_layout),
93-
&mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
94-
(discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
92+
enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
93+
let name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
94+
// Is there anything we can do to support 128-bit C-Style enums?
95+
let value = discr.val as u64;
96+
(name, value)
9597
}),
9698
containing_scope,
9799
),
@@ -152,7 +154,7 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
152154
cx: &CodegenCx<'ll, 'tcx>,
153155
type_name: &str,
154156
base_type: Ty<'tcx>,
155-
variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>,
157+
enumerators: impl Iterator<Item = (Cow<'tcx, str>, u64)>,
156158
containing_scope: &'ll DIType,
157159
) -> &'ll DIType {
158160
let is_unsigned = match base_type.kind() {
@@ -161,18 +163,15 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
161163
_ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
162164
};
163165

164-
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
165-
.map(|(discr, variant_name)| {
166-
unsafe {
167-
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
168-
DIB(cx),
169-
variant_name.as_ptr().cast(),
170-
variant_name.len(),
171-
// FIXME: what if enumeration has i128 discriminant?
172-
discr.val as i64,
173-
is_unsigned,
174-
))
175-
}
166+
let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = enumerators
167+
.map(|(name, value)| unsafe {
168+
Some(llvm::LLVMRustDIBuilderCreateEnumerator(
169+
DIB(cx),
170+
name.as_ptr().cast(),
171+
name.len(),
172+
value as i64,
173+
is_unsigned,
174+
))
176175
})
177176
.collect();
178177

@@ -247,23 +246,27 @@ fn build_enumeration_type_di_node<'ll, 'tcx>(
247246
/// and a DW_TAG_member for each field (but not the discriminant).
248247
fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
249248
cx: &CodegenCx<'ll, 'tcx>,
250-
enum_type: Ty<'tcx>,
249+
enum_type_and_layout: TyAndLayout<'tcx>,
251250
enum_type_di_node: &'ll DIType,
252251
variant_index: VariantIdx,
253252
variant_def: &VariantDef,
254253
variant_layout: TyAndLayout<'tcx>,
255254
) -> &'ll DIType {
256-
debug_assert_eq!(variant_layout.ty, enum_type);
255+
debug_assert_eq!(variant_layout.ty, enum_type_and_layout.ty);
257256

258257
type_map::build_type_with_children(
259258
cx,
260259
type_map::stub(
261260
cx,
262261
Stub::Struct,
263-
UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index),
262+
UniqueTypeId::for_enum_variant_struct_type(
263+
cx.tcx,
264+
enum_type_and_layout.ty,
265+
variant_index,
266+
),
264267
variant_def.name.as_str(),
265268
// NOTE: We use size and align of enum_type, not from variant_layout:
266-
cx.size_and_align_of(enum_type),
269+
size_and_align_of(enum_type_and_layout),
267270
Some(enum_type_di_node),
268271
DIFlags::FlagZero,
269272
),
@@ -290,9 +293,9 @@ fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
290293
type_di_node(cx, field_layout.ty),
291294
)
292295
})
293-
.collect()
296+
.collect::<SmallVec<_>>()
294297
},
295-
|cx| build_generic_type_param_di_nodes(cx, enum_type),
298+
|cx| build_generic_type_param_di_nodes(cx, enum_type_and_layout.ty),
296299
)
297300
.di_node
298301
}
@@ -398,6 +401,19 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
398401
.di_node
399402
}
400403

404+
#[derive(Copy, Clone)]
405+
enum DiscrResult {
406+
NoDiscriminant,
407+
Value(u128),
408+
Range(u128, u128),
409+
}
410+
411+
impl DiscrResult {
412+
fn opt_single_val(&self) -> Option<u128> {
413+
if let Self::Value(d) = *self { Some(d) } else { None }
414+
}
415+
}
416+
401417
/// Returns the discriminant value corresponding to the variant index.
402418
///
403419
/// Will return `None` if there is less than two variants (because then the enum won't have)
@@ -407,30 +423,38 @@ fn compute_discriminant_value<'ll, 'tcx>(
407423
cx: &CodegenCx<'ll, 'tcx>,
408424
enum_type_and_layout: TyAndLayout<'tcx>,
409425
variant_index: VariantIdx,
410-
) -> Option<u64> {
426+
) -> DiscrResult {
411427
match enum_type_and_layout.layout.variants() {
412-
&Variants::Single { .. } => None,
413-
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some(
414-
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val
415-
as u64,
428+
&Variants::Single { .. } => DiscrResult::NoDiscriminant,
429+
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
430+
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
416431
),
417432
&Variants::Multiple {
418433
tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
419434
tag,
420435
..
421436
} => {
422437
if variant_index == dataful_variant {
423-
None
438+
let valid_range = enum_type_and_layout
439+
.for_variant(cx, variant_index)
440+
.largest_niche
441+
.as_ref()
442+
.unwrap()
443+
.valid_range;
444+
445+
let min = valid_range.start.min(valid_range.end);
446+
let min = tag.size(cx).truncate(min);
447+
448+
let max = valid_range.start.max(valid_range.end);
449+
let max = tag.size(cx).truncate(max);
450+
451+
DiscrResult::Range(min, max)
424452
} else {
425453
let value = (variant_index.as_u32() as u128)
426454
.wrapping_sub(niche_variants.start().as_u32() as u128)
427455
.wrapping_add(niche_start);
428456
let value = tag.size(cx).truncate(value);
429-
// NOTE(eddyb) do *NOT* remove this assert, until
430-
// we pass the full 128-bit value to LLVM, otherwise
431-
// truncation will be silent and remain undetected.
432-
assert_eq!(value as u64 as u128, value);
433-
Some(value as u64)
457+
DiscrResult::Value(value)
434458
}
435459
}
436460
}

compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
8888
variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
8989
variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
9090
cx,
91-
enum_type,
91+
enum_type_and_layout,
9292
enum_type_di_node,
9393
variant_index,
9494
enum_adt_def.variant(variant_index),
@@ -413,7 +413,13 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>(
413413
enum_type_and_layout.size.bits(),
414414
enum_type_and_layout.align.abi.bits() as u32,
415415
Size::ZERO.bits(),
416-
discr_value.map(|v| cx.const_u64(v)),
416+
discr_value.opt_single_val().map(|value| {
417+
// NOTE(eddyb) do *NOT* remove this assert, until
418+
// we pass the full 128-bit value to LLVM, otherwise
419+
// truncation will be silent and remain undetected.
420+
assert_eq!(value as u64 as u128, value);
421+
cx.const_u64(value as u64)
422+
}),
417423
DIFlags::FlagZero,
418424
variant_member_info.variant_struct_type_di_node,
419425
)

compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs

+11
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub(super) enum UniqueTypeId<'tcx> {
4747
VariantPart(Ty<'tcx>, private::HiddenZst),
4848
/// The ID for the artificial struct type describing a single enum variant.
4949
VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst),
50+
/// The ID for the additional wrapper struct type describing an enum variant in CPP-like mode.
51+
VariantStructTypeCppLikeWrapper(Ty<'tcx>, VariantIdx, private::HiddenZst),
5052
/// The ID of the artificial type we create for VTables.
5153
VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst),
5254
}
@@ -71,6 +73,15 @@ impl<'tcx> UniqueTypeId<'tcx> {
7173
UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst)
7274
}
7375

76+
pub fn for_enum_variant_struct_type_wrapper(
77+
tcx: TyCtxt<'tcx>,
78+
enum_ty: Ty<'tcx>,
79+
variant_idx: VariantIdx,
80+
) -> Self {
81+
debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
82+
UniqueTypeId::VariantStructTypeCppLikeWrapper(enum_ty, variant_idx, private::HiddenZst)
83+
}
84+
7485
pub fn for_vtable_ty(
7586
tcx: TyCtxt<'tcx>,
7687
self_type: Ty<'tcx>,

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2079,6 +2079,19 @@ extern "C" {
20792079
Ty: &'a DIType,
20802080
) -> &'a DIType;
20812081

2082+
pub fn LLVMRustDIBuilderCreateStaticMemberType<'a>(
2083+
Builder: &DIBuilder<'a>,
2084+
Scope: &'a DIDescriptor,
2085+
Name: *const c_char,
2086+
NameLen: size_t,
2087+
File: &'a DIFile,
2088+
LineNo: c_uint,
2089+
Ty: &'a DIType,
2090+
Flags: DIFlags,
2091+
val: Option<&'a Value>,
2092+
AlignInBits: u32,
2093+
) -> &'a DIDerivedType;
2094+
20822095
pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>(
20832096
Builder: &DIBuilder<'a>,
20842097
Scope: &'a DIScope,

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+6-50
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
1818
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
1919
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
2020
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
21-
use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt};
22-
use rustc_target::abi::{Integer, TagEncoding, Variants};
21+
use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
22+
use rustc_target::abi::Integer;
2323
use smallvec::SmallVec;
2424

25-
use std::borrow::Cow;
2625
use std::fmt::Write;
2726

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

9998
if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
10099
msvc_enum_fallback(
101-
tcx,
102100
ty_and_layout,
103101
&|output, visited| {
104102
push_item_name(tcx, def.did(), true, output);
@@ -391,11 +389,10 @@ fn push_debuginfo_type_name<'tcx>(
391389
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
392390
// "{async_fn_env#0}<T1, T2, ...>", etc.
393391
// In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
394-
// an artificial `enum$<>` type, as defined in msvc_enum_fallback().
392+
// an artificial `enum2$<>` type, as defined in msvc_enum_fallback().
395393
if cpp_like_debuginfo && t.is_generator() {
396394
let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
397395
msvc_enum_fallback(
398-
tcx,
399396
ty_and_layout,
400397
&|output, visited| {
401398
push_closure_or_generator_name(tcx, def_id, substs, true, output, visited);
@@ -428,58 +425,17 @@ fn push_debuginfo_type_name<'tcx>(
428425

429426
/// MSVC names enums differently than other platforms so that the debugging visualization
430427
// format (natvis) is able to understand enums and render the active variant correctly in the
431-
// debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
432-
// `EnumMemberDescriptionFactor::create_member_descriptions`.
428+
// debugger. For more information, look in
429+
// rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
433430
fn msvc_enum_fallback<'tcx>(
434-
tcx: TyCtxt<'tcx>,
435431
ty_and_layout: TyAndLayout<'tcx>,
436432
push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
437433
output: &mut String,
438434
visited: &mut FxHashSet<Ty<'tcx>>,
439435
) {
440436
debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
441-
let ty = ty_and_layout.ty;
442-
443-
output.push_str("enum$<");
437+
output.push_str("enum2$<");
444438
push_inner(output, visited);
445-
446-
let variant_name = |variant_index| match ty.kind() {
447-
ty::Adt(adt_def, _) => {
448-
debug_assert!(adt_def.is_enum());
449-
Cow::from(adt_def.variant(variant_index).name.as_str())
450-
}
451-
ty::Generator(..) => GeneratorSubsts::variant_name(variant_index),
452-
_ => unreachable!(),
453-
};
454-
455-
if let Variants::Multiple {
456-
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
457-
tag,
458-
variants,
459-
..
460-
} = &ty_and_layout.variants
461-
{
462-
let dataful_variant_layout = &variants[*dataful_variant];
463-
464-
// calculate the range of values for the dataful variant
465-
let dataful_discriminant_range =
466-
dataful_variant_layout.largest_niche().unwrap().valid_range;
467-
468-
let min = dataful_discriminant_range.start;
469-
let min = tag.size(&tcx).truncate(min);
470-
471-
let max = dataful_discriminant_range.end;
472-
let max = tag.size(&tcx).truncate(max);
473-
474-
let dataful_variant_name = variant_name(*dataful_variant);
475-
write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap();
476-
} else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants {
477-
// Uninhabited enums can't be constructed and should never need to be visualized so
478-
// skip this step for them.
479-
if !ty_and_layout.abi.is_uninhabited() {
480-
write!(output, ", {}", variant_name(*variant_idx)).unwrap();
481-
}
482-
}
483439
push_close_angle_bracket(true, output);
484440
}
485441

compiler/rustc_index/src/vec.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ impl<I: Idx, T> IndexVec<I, T> {
172172
}
173173

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

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,30 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType(
924924
fromRust(Flags), unwrapDI<DIType>(Ty)));
925925
}
926926

927+
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType(
928+
LLVMRustDIBuilderRef Builder,
929+
LLVMMetadataRef Scope,
930+
const char *Name,
931+
size_t NameLen,
932+
LLVMMetadataRef File,
933+
unsigned LineNo,
934+
LLVMMetadataRef Ty,
935+
LLVMRustDIFlags Flags,
936+
LLVMValueRef val,
937+
uint32_t AlignInBits
938+
) {
939+
return wrap(Builder->createStaticMemberType(
940+
unwrapDI<DIDescriptor>(Scope),
941+
StringRef(Name, NameLen),
942+
unwrapDI<DIFile>(File),
943+
LineNo,
944+
unwrapDI<DIType>(Ty),
945+
fromRust(Flags),
946+
unwrap<llvm::ConstantInt>(val),
947+
AlignInBits
948+
));
949+
}
950+
927951
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlock(
928952
LLVMRustDIBuilderRef Builder, LLVMMetadataRef Scope,
929953
LLVMMetadataRef File, unsigned Line, unsigned Col) {

0 commit comments

Comments
 (0)