Skip to content

Commit 9a75781

Browse files
committed
Auto merge of #89597 - michaelwoerister:improve-vtable-debuginfo, r=wesleywiser
Create more accurate debuginfo for vtables. Before this PR all vtables would have the same name (`"vtable"`) in debuginfo. Now they get an unambiguous name that identifies the implementing type and the trait that is being implemented. This is only one of several possible improvements: - This PR describes vtables as arrays of `*const u8` pointers. It would nice to describe them as structs where function pointer is represented by a field with a name indicative of the method it maps to. However, this requires coming up with a naming scheme that avoids clashes between methods with the same name (which is possible if the vtable contains multiple traits). - The PR does not update the debuginfo we generate for the vtable-pointer field in a fat `dyn` pointer. Right now there does not seem to be an easy way of getting ahold of a vtable-layout without also knowing the concrete self-type of a trait object. r? `@wesleywiser`
2 parents 1ddd4e6 + 61c5a6d commit 9a75781

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
};
@@ -29,8 +29,9 @@ use rustc_index::vec::{Idx, IndexVec};
2929
use rustc_middle::mir::{self, GeneratorLayout};
3030
use rustc_middle::ty::layout::{self, IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout};
3131
use rustc_middle::ty::subst::GenericArgKind;
32-
use rustc_middle::ty::Instance;
33-
use rustc_middle::ty::{self, AdtKind, GeneratorSubsts, ParamEnv, Ty, TyCtxt};
32+
use rustc_middle::ty::{
33+
self, AdtKind, GeneratorSubsts, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES,
34+
};
3435
use rustc_middle::{bug, span_bug};
3536
use rustc_query_system::ich::NodeIdHashingMode;
3637
use rustc_session::config::{self, DebugInfo};
@@ -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)