Skip to content

Commit 0d61b6f

Browse files
debuginfo: Bring back DW_AT_containing_type for vtables after it has accidentally been
removed in rust-lang#89597. Also describe vtables as structs with a field for each entry.
1 parent 1934a69 commit 0d61b6f

File tree

4 files changed

+158
-39
lines changed

4 files changed

+158
-39
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+90-11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::value::Value;
2121

2222
use cstr::cstr;
2323
use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo;
24+
use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
2425
use rustc_codegen_ssa::traits::*;
2526
use rustc_data_structures::fingerprint::Fingerprint;
2627
use rustc_data_structures::fx::FxHashMap;
@@ -276,6 +277,12 @@ impl<'ll, 'tcx> TypeMap<'ll, 'tcx> {
276277
) -> String {
277278
format!("{}_variant_part", self.get_unique_type_id_as_string(enum_type_id))
278279
}
280+
281+
/// Gets the `UniqueTypeId` for the type of a vtable.
282+
fn get_unique_type_id_of_vtable_type(&mut self, vtable_type_name: &str) -> UniqueTypeId {
283+
let interner_key = self.unique_id_interner.intern(vtable_type_name);
284+
interner_key
285+
}
279286
}
280287

281288
/// A description of some recursive type. It can either be already finished (as
@@ -2586,6 +2593,14 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g
25862593
}
25872594

25882595
/// Generates LLVM debuginfo for a vtable.
2596+
///
2597+
/// The vtable type looks like a struct with a field for each function pointer and super-trait
2598+
/// pointer it contains (plus the `size` and `align` fields).
2599+
///
2600+
/// Except for `size`, `align`, and `drop_in_place`, the field names don't try to mirror
2601+
/// the name of the method they implement. This can be implemented in the future once there
2602+
/// is a proper disambiguation scheme for dealing with methods from different traits that have
2603+
/// the same name.
25892604
fn vtable_type_metadata<'ll, 'tcx>(
25902605
cx: &CodegenCx<'ll, 'tcx>,
25912606
ty: Ty<'tcx>,
@@ -2602,16 +2617,79 @@ fn vtable_type_metadata<'ll, 'tcx>(
26022617
COMMON_VTABLE_ENTRIES
26032618
};
26042619

2605-
// FIXME: We describe the vtable as an array of *const () pointers. The length of the array is
2606-
// correct - but we could create a more accurate description, e.g. by describing it
2607-
// as a struct where each field has a name that corresponds to the name of the method
2608-
// it points to.
2609-
// However, this is not entirely straightforward because there might be multiple
2610-
// methods with the same name if the vtable is for multiple traits. So for now we keep
2611-
// things simple instead of adding some ad-hoc disambiguation scheme.
2612-
let vtable_type = tcx.mk_array(tcx.mk_imm_ptr(tcx.types.unit), vtable_entries.len() as u64);
2620+
// All function pointers are described as opaque pointers. This could be improved in the future
2621+
// by describing them as actual function pointers.
2622+
let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit);
2623+
let void_pointer_type_debuginfo = type_metadata(cx, void_pointer_ty);
2624+
let usize_debuginfo = type_metadata(cx, tcx.types.usize);
2625+
let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty);
2626+
// If `usize` is not pointer-sized and -aligned then the size and alignment computations
2627+
// for the vtable as a whole would be wrong. Let's make sure this holds even on weird
2628+
// platforms.
2629+
assert_eq!(cx.size_and_align_of(tcx.types.usize), (pointer_size, pointer_align));
2630+
2631+
let vtable_type_name =
2632+
compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::Type);
2633+
let unique_type_id = debug_context(cx)
2634+
.type_map
2635+
.borrow_mut()
2636+
.get_unique_type_id_of_vtable_type(&vtable_type_name);
2637+
let size = pointer_size * vtable_entries.len() as u64;
2638+
2639+
// This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate
2640+
// the vtable to the type it is for.
2641+
let vtable_holder = type_metadata(cx, ty);
2642+
2643+
let vtable_type_metadata = create_struct_stub(
2644+
cx,
2645+
size,
2646+
pointer_align,
2647+
&vtable_type_name,
2648+
unique_type_id,
2649+
NO_SCOPE_METADATA,
2650+
DIFlags::FlagArtificial,
2651+
Some(vtable_holder),
2652+
);
2653+
2654+
// Create a field for each entry in the vtable.
2655+
let fields: Vec<_> = vtable_entries
2656+
.iter()
2657+
.enumerate()
2658+
.filter_map(|(index, vtable_entry)| {
2659+
let (field_name, field_type) = match vtable_entry {
2660+
ty::VtblEntry::MetadataDropInPlace => {
2661+
("drop_in_place".to_string(), void_pointer_type_debuginfo)
2662+
}
2663+
ty::VtblEntry::Method(_) => {
2664+
// Note: This code does not try to give a proper name to each method
2665+
// because their might be multiple methods with the same name
2666+
// (coming from different traits).
2667+
(format!("__method{}", index), void_pointer_type_debuginfo)
2668+
}
2669+
ty::VtblEntry::TraitVPtr(_) => {
2670+
(format!("__super_trait_ptr{}", index), void_pointer_type_debuginfo)
2671+
}
2672+
ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_debuginfo),
2673+
ty::VtblEntry::MetadataSize => ("size".to_string(), usize_debuginfo),
2674+
ty::VtblEntry::Vacant => return None,
2675+
};
2676+
2677+
Some(MemberDescription {
2678+
name: field_name,
2679+
type_metadata: field_type,
2680+
offset: pointer_size * index as u64,
2681+
size: pointer_size,
2682+
align: pointer_align,
2683+
flags: DIFlags::FlagZero,
2684+
discriminant: None,
2685+
source_info: None,
2686+
})
2687+
})
2688+
.collect();
26132689

2614-
type_metadata(cx, vtable_type)
2690+
let type_params = create_DIArray(DIB(cx), &[]);
2691+
set_members_of_composite_type(cx, vtable_type_metadata, fields, None, type_params);
2692+
vtable_type_metadata
26152693
}
26162694

26172695
/// Creates debug information for the given vtable, which is for the
@@ -2633,11 +2711,12 @@ pub fn create_vtable_metadata<'ll, 'tcx>(
26332711
return;
26342712
}
26352713

2636-
let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref);
2714+
let vtable_name =
2715+
compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable);
26372716
let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref);
2717+
let linkage_name = "";
26382718

26392719
unsafe {
2640-
let linkage_name = "";
26412720
llvm::LLVMRustDIBuilderCreateStaticVariable(
26422721
DIB(cx),
26432722
NO_SCOPE_METADATA,

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,14 @@ fn push_debuginfo_type_name<'tcx>(
469469
}
470470
}
471471

472-
/// Computes a name for the global variable storing a vtable.
472+
pub enum VTableNameKind {
473+
// Is the name for const/static holding the vtable?
474+
GlobalVariable,
475+
// Is the name for the type of the vtable?
476+
Type,
477+
}
478+
479+
/// Computes a name for the global variable storing a vtable (or the type of that global variable).
473480
///
474481
/// The name is of the form:
475482
///
@@ -478,10 +485,15 @@ fn push_debuginfo_type_name<'tcx>(
478485
/// or, when generating C++-like names:
479486
///
480487
/// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
488+
///
489+
/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
490+
/// `{vtable}`, so that the type and the corresponding global variable get assigned different
491+
/// names.
481492
pub fn compute_debuginfo_vtable_name<'tcx>(
482493
tcx: TyCtxt<'tcx>,
483494
t: Ty<'tcx>,
484495
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
496+
kind: VTableNameKind,
485497
) -> String {
486498
let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
487499

@@ -514,7 +526,12 @@ pub fn compute_debuginfo_vtable_name<'tcx>(
514526

515527
push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
516528

517-
let suffix = if cpp_like_debuginfo { "::vtable$" } else { "::{vtable}" };
529+
let suffix = match (cpp_like_debuginfo, kind) {
530+
(true, VTableNameKind::GlobalVariable) => "::vtable$",
531+
(false, VTableNameKind::GlobalVariable) => "::{vtable}",
532+
(true, VTableNameKind::Type) => "::vtable_type$",
533+
(false, VTableNameKind::Type) => "::{vtable_type}",
534+
};
518535

519536
vtable_name.reserve_exact(suffix.len());
520537
vtable_name.push_str(suffix);

src/test/codegen/debug-vtable.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,41 @@
99
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Csymbol-mangling-version=v0
1010
// ignore-tidy-linelength
1111

12+
// NONMSVC: ![[USIZE:[0-9]+]] = !DIBasicType(name: "usize"
13+
// MSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize"
14+
// NONMSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()"
15+
// MSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >"
16+
1217
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
1318
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"
14-
// NONMSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()",
15-
// MSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >",
16-
// CHECK: !DISubrange(count: 5
19+
20+
// NONMSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable_type}", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
21+
// MSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable_type$", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
22+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
23+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
24+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
25+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
26+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}})
27+
// CHECK: ![[FOO_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo",
1728

1829
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
1930
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"
20-
// CHECK: !DISubrange(count: 4
31+
32+
// NONMSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable_type}", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
33+
// MSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable_type$", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
34+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
35+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
36+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
37+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
2138

2239
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
2340
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"
24-
// CHECK: !DISubrange(count: 3
41+
42+
// NONMSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as _>::{vtable_type}", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
43+
// MSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, _>::vtable_type$", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
44+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
45+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
46+
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
2547

2648
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::bar::{closure_env#0} as core::ops::function::FnOnce<(core::option::Option<&dyn core::ops::function::Fn<(), Output=()>>)>>::{vtable}"
2749
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::bar::closure_env$0, core::ops::function::FnOnce<tuple$<enum$<core::option::Option<ref$<dyn$<core::ops::function::Fn<tuple$<>,assoc$<Output,tuple$<> > > > > >, {{.*}}, {{.*}}, Some> > > >::vtable$"
@@ -34,6 +56,9 @@
3456

3557
#![crate_type = "lib"]
3658

59+
// Force emission for debuginfo for usize and *const() early..
60+
pub static mut XYZ: Option<(usize, *const ())> = None;
61+
3762
pub struct Foo;
3863

3964
pub trait SomeTrait {

src/test/codegen/debuginfo-generic-closure-env-names.rs

+19-21
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,34 @@
1717

1818
// compile-flags: -Cdebuginfo=2 --edition 2021 -Copt-level=0 -Csymbol-mangling-version=v0
1919

20-
21-
// CHECK: [[non_generic_closure_NAMESPACE:!.*]] = !DINamespace(name: "non_generic_closure"
22-
// CHECK: [[function_containing_closure_NAMESPACE:!.*]] = !DINamespace(name: "function_containing_closure"
23-
// CHECK: [[generic_async_function_NAMESPACE:!.*]] = !DINamespace(name: "generic_async_function"
24-
// CHECK: [[generic_async_block_NAMESPACE:!.*]] = !DINamespace(name: "generic_async_block"
25-
2620
// non_generic_closure()
27-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: [[non_generic_closure_NAMESPACE]]
28-
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: [[non_generic_closure_NAMESPACE]]
21+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]],
22+
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]],
23+
// CHECK: ![[non_generic_closure_NAMESPACE]] = !DINamespace(name: "non_generic_closure"
24+
25+
// CHECK: ![[function_containing_closure_NAMESPACE:[0-9]+]] = !DINamespace(name: "function_containing_closure"
26+
// CHECK: ![[generic_async_function_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_function"
27+
// CHECK: ![[generic_async_block_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_block"
2928

3029
// function_containing_closure<u32>()
31-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<u32>", scope: [[function_containing_closure_NAMESPACE]]
32-
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<u32>", scope: [[function_containing_closure_NAMESPACE]]
30+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<u32>", scope: ![[function_containing_closure_NAMESPACE]]
31+
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<u32>", scope: ![[function_containing_closure_NAMESPACE]]
3332

3433
// generic_async_function<Foo>()
35-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[generic_async_function_NAMESPACE]]
34+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[generic_async_function_NAMESPACE]]
3635

3736
// generic_async_function<u32>()
38-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<u32>", scope: [[generic_async_function_NAMESPACE]]
37+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<u32>", scope: ![[generic_async_function_NAMESPACE]]
3938

4039
// generic_async_block<Foo>()
41-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[generic_async_block_NAMESPACE]]
40+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[generic_async_block_NAMESPACE]]
4241

4342
// generic_async_block<u32>()
44-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<u32>", scope: [[generic_async_block_NAMESPACE]]
43+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<u32>", scope: ![[generic_async_block_NAMESPACE]]
4544

4645
// function_containing_closure<Foo>()
47-
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[function_containing_closure_NAMESPACE]]
48-
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<debuginfo_generic_closure_env_names::Foo>", scope: [[function_containing_closure_NAMESPACE]]
46+
// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[function_containing_closure_NAMESPACE]]
47+
// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<debuginfo_generic_closure_env_names::Foo>", scope: ![[function_containing_closure_NAMESPACE]]
4948

5049

5150
#![crate_type = "lib"]
@@ -54,15 +53,14 @@ use std::future::Future;
5453
pub struct Foo;
5554

5655
pub fn non_generic_closure(x: Foo) -> Box<dyn FnOnce() -> Foo> {
57-
// This static only exists to trigger generating the namespace debuginfo for
58-
// `function_containing_closure` at a predictable, early point, which makes
59-
// writing the FileCheck tests above simpler.
60-
static _X: u8 = 0;
6156
return Box::new(move || x);
6257
}
6358

6459
fn function_containing_closure<T: 'static>(x: T) -> impl FnOnce() -> T {
65-
static _X: u8 = 0; // Same as above
60+
// This static only exists to trigger generating the namespace debuginfo for
61+
// `function_containing_closure` at a predictable, early point, which makes
62+
// writing the FileCheck tests above simpler.
63+
static _X: u8 = 0;
6664

6765
return move || x;
6866
}

0 commit comments

Comments
 (0)