Skip to content

Commit 61c5a6d

Browse files
committedOct 8, 2021
Create more accurate debuginfo for vtables.
Before this commit all vtables would have the same name "vtable" in debuginfo. Now they get a name that identifies the implementing type and the trait that is being implemented.
1 parent a479766 commit 61c5a6d

File tree

8 files changed

+163
-62
lines changed

8 files changed

+163
-62
lines changed
 

‎compiler/rustc_codegen_gcc/src/debuginfo.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use gccjit::RValue;
22
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
33
use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
44
use rustc_middle::mir;
5-
use rustc_middle::ty::{Instance, Ty};
5+
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
66
use rustc_span::{SourceFile, Span, Symbol};
77
use rustc_target::abi::Size;
88
use rustc_target::abi::call::FnAbi;
@@ -31,7 +31,7 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
3131
}
3232

3333
impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
34-
fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _vtable: Self::Value) {
34+
fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
3535
// TODO(antoyo)
3636
}
3737

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

+43-34
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use self::MemberDescriptionFactory::*;
22
use self::RecursiveTypeDescription::*;
33

44
use super::namespace::mangled_name_of_instance;
5-
use super::type_names::compute_debuginfo_type_name;
5+
use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name};
66
use super::utils::{
77
create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, DIB,
88
};
@@ -30,8 +30,9 @@ use rustc_middle::ich::NodeIdHashingMode;
3030
use rustc_middle::mir::{self, GeneratorLayout};
3131
use rustc_middle::ty::layout::{self, IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout};
3232
use rustc_middle::ty::subst::GenericArgKind;
33-
use rustc_middle::ty::Instance;
34-
use rustc_middle::ty::{self, AdtKind, GeneratorSubsts, ParamEnv, Ty, TyCtxt};
33+
use rustc_middle::ty::{
34+
self, AdtKind, GeneratorSubsts, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES,
35+
};
3536
use rustc_middle::{bug, span_bug};
3637
use rustc_session::config::{self, DebugInfo};
3738
use rustc_span::symbol::Symbol;
@@ -2591,11 +2592,45 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global
25912592
}
25922593
}
25932594

2595+
/// Generates LLVM debuginfo for a vtable.
2596+
fn vtable_type_metadata(
2597+
cx: &CodegenCx<'ll, 'tcx>,
2598+
ty: Ty<'tcx>,
2599+
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
2600+
) -> &'ll DIType {
2601+
let tcx = cx.tcx;
2602+
2603+
let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref {
2604+
let trait_ref = poly_trait_ref.with_self_ty(tcx, ty);
2605+
let trait_ref = tcx.erase_regions(trait_ref);
2606+
2607+
tcx.vtable_entries(trait_ref)
2608+
} else {
2609+
COMMON_VTABLE_ENTRIES
2610+
};
2611+
2612+
// FIXME: We describe the vtable as an array of *const () pointers. The length of the array is
2613+
// correct - but we could create a more accurate description, e.g. by describing it
2614+
// as a struct where each field has a name that corresponds to the name of the method
2615+
// it points to.
2616+
// However, this is not entirely straightforward because there might be multiple
2617+
// methods with the same name if the vtable is for multiple traits. So for now we keep
2618+
// things simple instead of adding some ad-hoc disambiguation scheme.
2619+
let vtable_type = tcx.mk_array(tcx.mk_imm_ptr(tcx.types.unit), vtable_entries.len() as u64);
2620+
2621+
type_metadata(cx, vtable_type, rustc_span::DUMMY_SP)
2622+
}
2623+
25942624
/// Creates debug information for the given vtable, which is for the
25952625
/// given type.
25962626
///
25972627
/// Adds the created metadata nodes directly to the crate's IR.
2598-
pub fn create_vtable_metadata(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, vtable: &'ll Value) {
2628+
pub fn create_vtable_metadata(
2629+
cx: &CodegenCx<'ll, 'tcx>,
2630+
ty: Ty<'tcx>,
2631+
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
2632+
vtable: &'ll Value,
2633+
) {
25992634
if cx.dbg_cx.is_none() {
26002635
return;
26012636
}
@@ -2605,42 +2640,16 @@ pub fn create_vtable_metadata(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, vtable: &
26052640
return;
26062641
}
26072642

2608-
let type_metadata = type_metadata(cx, ty, rustc_span::DUMMY_SP);
2643+
let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref);
2644+
let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref);
26092645

26102646
unsafe {
2611-
// `LLVMRustDIBuilderCreateStructType()` wants an empty array. A null
2612-
// pointer will lead to hard to trace and debug LLVM assertions
2613-
// later on in `llvm/lib/IR/Value.cpp`.
2614-
let empty_array = create_DIArray(DIB(cx), &[]);
2615-
let name = "vtable";
2616-
2617-
// Create a new one each time. We don't want metadata caching
2618-
// here, because each vtable will refer to a unique containing
2619-
// type.
2620-
let vtable_type = llvm::LLVMRustDIBuilderCreateStructType(
2621-
DIB(cx),
2622-
NO_SCOPE_METADATA,
2623-
name.as_ptr().cast(),
2624-
name.len(),
2625-
unknown_file_metadata(cx),
2626-
UNKNOWN_LINE_NUMBER,
2627-
Size::ZERO.bits(),
2628-
cx.tcx.data_layout.pointer_align.abi.bits() as u32,
2629-
DIFlags::FlagArtificial,
2630-
None,
2631-
empty_array,
2632-
0,
2633-
Some(type_metadata),
2634-
name.as_ptr().cast(),
2635-
name.len(),
2636-
);
2637-
26382647
let linkage_name = "";
26392648
llvm::LLVMRustDIBuilderCreateStaticVariable(
26402649
DIB(cx),
26412650
NO_SCOPE_METADATA,
2642-
name.as_ptr().cast(),
2643-
name.len(),
2651+
vtable_name.as_ptr().cast(),
2652+
vtable_name.len(),
26442653
linkage_name.as_ptr().cast(),
26452654
linkage_name.len(),
26462655
unknown_file_metadata(cx),

‎compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,13 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
550550
unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) }
551551
}
552552

553-
fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value) {
554-
metadata::create_vtable_metadata(self, ty, vtable)
553+
fn create_vtable_metadata(
554+
&self,
555+
ty: Ty<'tcx>,
556+
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
557+
vtable: Self::Value,
558+
) {
559+
metadata::create_vtable_metadata(self, ty, trait_ref, vtable)
555560
}
556561

557562
fn extend_scope_to_file(

‎compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+56
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,62 @@ fn push_debuginfo_type_name<'tcx>(
446446
}
447447
}
448448

449+
/// Computes a name for the global variable storing a vtable.
450+
///
451+
/// The name is of the form:
452+
///
453+
/// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
454+
///
455+
/// or, when generating C++-like names:
456+
///
457+
/// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
458+
pub fn compute_debuginfo_vtable_name<'tcx>(
459+
tcx: TyCtxt<'tcx>,
460+
t: Ty<'tcx>,
461+
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
462+
) -> String {
463+
let cpp_like_names = cpp_like_names(tcx);
464+
465+
let mut vtable_name = String::with_capacity(64);
466+
467+
if cpp_like_names {
468+
vtable_name.push_str("impl$<");
469+
} else {
470+
vtable_name.push('<');
471+
}
472+
473+
let mut visited = FxHashSet::default();
474+
push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
475+
476+
if cpp_like_names {
477+
vtable_name.push_str(", ");
478+
} else {
479+
vtable_name.push_str(" as ");
480+
}
481+
482+
if let Some(trait_ref) = trait_ref {
483+
push_item_name(tcx, trait_ref.skip_binder().def_id, true, &mut vtable_name);
484+
visited.clear();
485+
push_generic_params_internal(
486+
tcx,
487+
trait_ref.skip_binder().substs,
488+
&mut vtable_name,
489+
&mut visited,
490+
);
491+
} else {
492+
vtable_name.push_str("_");
493+
}
494+
495+
push_close_angle_bracket(cpp_like_names, &mut vtable_name);
496+
497+
let suffix = if cpp_like_names { "::vtable$" } else { "::{vtable}" };
498+
499+
vtable_name.reserve_exact(suffix.len());
500+
vtable_name.push_str(suffix);
501+
502+
vtable_name
503+
}
504+
449505
pub fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
450506
let def_key = tcx.def_key(def_id);
451507
if qualified {

‎compiler/rustc_codegen_ssa/src/meth.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
7878
let align = cx.data_layout().pointer_align.abi;
7979
let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
8080

81-
cx.create_vtable_metadata(ty, vtable);
81+
cx.create_vtable_metadata(ty, trait_ref, vtable);
8282
cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
8383
vtable
8484
}

‎compiler/rustc_codegen_ssa/src/traits/debuginfo.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
use super::BackendTypes;
22
use crate::mir::debuginfo::{FunctionDebugContext, VariableKind};
33
use rustc_middle::mir;
4-
use rustc_middle::ty::{Instance, Ty};
4+
use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
55
use rustc_span::{SourceFile, Span, Symbol};
66
use rustc_target::abi::call::FnAbi;
77
use rustc_target::abi::Size;
88

99
pub trait DebugInfoMethods<'tcx>: BackendTypes {
10-
fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value);
10+
fn create_vtable_metadata(
11+
&self,
12+
ty: Ty<'tcx>,
13+
trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
14+
vtable: Self::Value,
15+
);
1116

1217
/// Creates the function-specific debug context.
1318
///

‎src/test/codegen/debug-vtable.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Ccodegen-units=1
2+
// ignore-tidy-linelength
3+
4+
// This test checks the debuginfo for the expected 3 vtables is generated for correct names and number
5+
// of entries.
6+
7+
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
8+
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"
9+
// NONMSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()",
10+
// MSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >",
11+
// CHECK: !DISubrange(count: 5
12+
13+
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
14+
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"
15+
// CHECK: !DISubrange(count: 4
16+
17+
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
18+
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"
19+
// CHECK: !DISubrange(count: 3
20+
21+
#![crate_type = "lib"]
22+
23+
pub struct Foo;
24+
25+
pub trait SomeTrait {
26+
fn method1(&self) -> u32;
27+
fn method2(&self) -> u32;
28+
}
29+
30+
impl SomeTrait for Foo {
31+
fn method1(&self) -> u32 { 1 }
32+
fn method2(&self) -> u32 { 2 }
33+
}
34+
35+
pub trait SomeTraitWithGenerics<T, U> {
36+
fn method1(&self) -> (T, U);
37+
}
38+
39+
impl SomeTraitWithGenerics<u64, i8> for Foo {
40+
fn method1(&self) -> (u64, i8) { (1, 2) }
41+
}
42+
43+
pub fn foo(x: &Foo) -> (u32, (u64, i8), &dyn Send) {
44+
let y: &dyn SomeTrait = x;
45+
let z: &dyn SomeTraitWithGenerics<u64, i8> = x;
46+
(y.method1(), z.method1(), x as &dyn Send)
47+
}

‎src/test/codegen/vtabletype.rs

-21
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.