Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
53 changes: 53 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,59 @@ StringRef CGDebugInfo::getVTableName(const CXXRecordDecl *RD) {
return internString("_vptr$", RD->getNameAsString());
}

// Emit symbol for the debugger that points to the vtable address for
// the given class. The symbol is named as '_vtable$'.
// The debugger does not need to know any details about the contents of the
// vtable as it can work this out using its knowledge of the ABI and the
// existing information in the DWARF. The type is assumed to be 'void *'.
void CGDebugInfo::emitVTableSymbol(llvm::GlobalVariable *VTable,
const CXXRecordDecl *RD) {
ASTContext &Context = CGM.getContext();
SmallString<64> Buffer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SmallString<64> Buffer;

unused?

Twine SymbolName = internString("_vtable$");
StringRef SymbolNameRef = SymbolName.toStringRef(Buffer);
Copy link
Member

@Michael137 Michael137 Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need the call to internString here? AFAIU it's just used when we require the copy of a string and don't want to heap allocate? Can we just make it a local?

llvm::StringRef SymbolName = "_vtable$";

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Changed to be local.

DeclContext *DC = static_cast<DeclContext *>(const_cast<CXXRecordDecl *>(RD));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to cast away const here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to: const DeclContext *DC = static_cast<const DeclContext *>(RD);

SourceLocation Loc;
QualType VoidPtr = Context.getPointerType(Context.VoidTy);

// We deal with two different contexts:
// - The type for the variable, which is part of the class that has the
// vtable, is placed in the context of the DICompositeType metadata.
// - The DIGlobalVariable for the vtable is put in the DICompileUnitScope.

// The created non-member should be mark as 'artificial'. It will be
// placed it inside the scope of the C++ class/structure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// placed it inside the scope of the C++ class/structure.
// placed inside the scope of the C++ class/structure.

llvm::DIScope *DContext = getContextDescriptor(cast<Decl>(DC), TheCU);
auto *Ctxt = cast<llvm::DICompositeType>(DContext);
llvm::DIFile *Unit = getOrCreateFile(Loc);
llvm::DIType *VTy = getOrCreateType(VoidPtr, Unit);
llvm::DINode::DIFlags Flags = getAccessFlag(AccessSpecifier::AS_private, RD);
auto Tag = CGM.getCodeGenOpts().DwarfVersion >= 5
? llvm::dwarf::DW_TAG_variable
: llvm::dwarf::DW_TAG_member;
llvm::DIDerivedType *OldDT = DBuilder.createStaticMemberType(
Ctxt, SymbolNameRef, Unit, /*LineNumber=*/0, VTy, Flags,
/*Val=*/nullptr, Tag);
llvm::DIDerivedType *DT =
static_cast<llvm::DIDerivedType *>(DBuilder.createArtificialType(OldDT));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling createArtificialType could we just add llvm::DINode::FlagArtificial to the Flags parameter we pass to createStaticMemberType?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point.


// Use the same vtable pointer to global alignment for the symbol.
LangAS AS = CGM.GetGlobalVarAddressSpace(nullptr);
unsigned PAlign = CGM.getItaniumVTableContext().isRelativeLayout()
? 32
: CGM.getTarget().getPointerAlign(AS);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this is how ItaniumCXXABI::getAddrOfVTable does it? Might be worth splitting this out into a common helper that can be shared between the two? (there's a couple more copies of this around Clang).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably also need to guard this behind CGM.getTarget().getCXXABI().isItaniumFamily() or something (or even just short-circuit this entire function if we're not generating for Itanium?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a helper function just to calculate the alignment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole function emitVTableSymbol now is guarded.


// The global variable is in the CU scope, and links back to the type it's
// "within" via the declaration field.
llvm::DIGlobalVariableExpression *GVE =
DBuilder.createGlobalVariableExpression(
TheCU, SymbolNameRef, VTable->getName(), Unit, /*LineNo=*/0,
getOrCreateType(VoidPtr, Unit), VTable->hasLocalLinkage(),
/*isDefined=*/true, nullptr, DT, /*TemplateParameters=*/nullptr,
PAlign);
VTable->addDebugInfo(GVE);
}

StringRef CGDebugInfo::getDynamicInitializerName(const VarDecl *VD,
DynamicInitKind StubKind,
llvm::Function *InitFn) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,9 @@ class CGDebugInfo {
StringRef Category,
StringRef FailureMsg);

/// Emit symbol for debugger that holds the pointer to the vtable.
void emitVTableSymbol(llvm::GlobalVariable *VTable, const CXXRecordDecl *RD);

private:
/// Emit call to llvm.dbg.declare for a variable declaration.
/// Returns a pointer to the DILocalVariable associated with the
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,10 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
if (!VTable->isDSOLocal())
CGVT.GenerateRelativeVTableAlias(VTable, VTable->getName());
}

// Emit symbol for debugger only if requested debug info.
if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
DI->emitVTableSymbol(VTable, RD);
}

bool ItaniumCXXABI::isVirtualOffsetNeededForVTableField(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "vtable-debug-info-inheritance-simple-base.h"

void NSP::CBase::zero() {}
int NSP::CBase::one() { return 1; }
int NSP::CBase::two() { return 2; };
int NSP::CBase::three() { return 3; }

#ifdef SYMBOL_AT_FILE_SCOPE
static NSP::CBase Base;
#else
void fooBase() {
NSP::CBase Base;
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef BASE_H
#define BASE_H

namespace NSP {
struct CBase {
unsigned B = 1;
virtual void zero();
virtual int one();
virtual int two();
virtual int three();
};
}

extern void fooBase();
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "vtable-debug-info-inheritance-simple-derived.h"

void CDerived::zero() {}
int CDerived::two() { return 22; };
int CDerived::three() { return 33; }

#ifdef SYMBOL_AT_FILE_SCOPE
static CDerived Derived;
#else
void fooDerived() {
CDerived Derived;
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "vtable-debug-info-inheritance-simple-base.h"

#ifndef DERIVED_H
#define DERIVED_H

struct CDerived : NSP::CBase {
unsigned D = 2;
void zero() override;
int two() override;
int three() override;
};

extern void fooDerived();
#endif
26 changes: 14 additions & 12 deletions clang/test/CodeGenCXX/debug-info-class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,6 @@ int main(int argc, char **argv) {
// CHECK-SAME: ){{$}}

// CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int"
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B"
// CHECK-NOT: DIFlagFwdDecl
// CHECK-SAME: ){{$}}
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$B",
// CHECK-SAME: DIFlagArtificial

// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C",
// CHECK-NOT: DIFlagFwdDecl
Expand All @@ -145,6 +137,20 @@ int main(int argc, char **argv) {
// CHECK-SAME: DIFlagStaticMember
// CHECK: [[C_DTOR]] = !DISubprogram(name: "~C"

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "K"
// CHECK-SAME: identifier: "_ZTS1K"
// CHECK-SAME: ){{$}}

// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B"
// CHECK-NOT: DIFlagFwdDecl
// CHECK-SAME: ){{$}}
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$B",
// CHECK-SAME: DIFlagArtificial

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "bar"
// CHECK: !DICompositeType(tag: DW_TAG_union_type, name: "baz"

// CHECK: [[D:![0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "D"
// CHECK-SAME: size:
// CHECK-SAME: DIFlagFwdDecl
Expand All @@ -156,10 +162,6 @@ int main(int argc, char **argv) {
// CHECK-NOT: identifier:
// CHECK-SAME: ){{$}}

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "K"
// CHECK-SAME: identifier: "_ZTS1K"
// CHECK-SAME: ){{$}}

// CHECK: [[L:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "L"
// CHECK-SAME: ){{$}}
// CHECK: [[L_FUNC_DECL:![0-9]*]] = !DISubprogram(name: "func",{{.*}} scope: [[L]]
Expand Down
52 changes: 26 additions & 26 deletions clang/test/CodeGenCXX/debug-info-template-member.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,6 @@ inline int add3(int x) {
// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:.*]], expr: !DIExpression())
// CHECK: [[XV]] = distinct !DIGlobalVariable(name: "x",
// CHECK-SAME: type: ![[OUTER_FOO_INNER_ID:[0-9]+]]
//
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "var"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateTypeParameter(name: "T", type: [[TY:![0-9]+]])
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "var"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateTypeParameter(name: "T", type: {{![0-9]+}})
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "varray"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateValueParameter(name: "N", type: [[TY]], value: i32 1)

// CHECK: ![[OUTER_FOO_INNER_ID:[0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier:
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK-SAME: elements: [[FOO_MEM:![0-9]*]]
// CHECK-SAME: identifier: "_ZTS3foo"
// CHECK: [[FOO_MEM]] = !{[[FOO_FUNC:![0-9]*]]}
// CHECK: [[FOO_FUNC]] = !DISubprogram(name: "func", linkageName: "_ZN3foo4funcEN5outerIS_E5innerE",
// CHECK-SAME: type: [[FOO_FUNC_TYPE:![0-9]*]]
// CHECK: [[FOO_FUNC_TYPE]] = !DISubroutineType(types: [[FOO_FUNC_PARAMS:![0-9]*]])
// CHECK: [[FOO_FUNC_PARAMS]] = !{null, !{{[0-9]*}}, ![[OUTER_FOO_INNER_ID]]}

// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass"
// CHECK-SAME: elements: [[C_MEM:![0-9]*]]
Expand All @@ -55,9 +32,6 @@ inline int add3(int x) {

// CHECK: [[C_FUNC]] = !DISubprogram(name: "func",{{.*}} line: 9,

// CHECK: !DISubprogram(name: "add<2>"
// CHECK-SAME: scope: [[C]]
//
// CHECK: [[VIRT_TEMP:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "virt<elem>"
// CHECK-SAME: elements: [[VIRT_MEM:![0-9]*]]
// CHECK-SAME: vtableHolder: [[VIRT_TEMP]]
Expand All @@ -74,6 +48,32 @@ inline int add3(int x) {
// CHECK: [[VIRT_TEMP_PARAM]] = !{[[VIRT_T:![0-9]*]]}
// CHECK: [[VIRT_T]] = !DITemplateTypeParameter(name: "T", type: [[ELEM]])

// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "var"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateTypeParameter(name: "T", type: [[TY:![0-9]+]])
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "var"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateTypeParameter(name: "T", type: {{![0-9]+}})
// CHECK: {{![0-9]+}} = distinct !DIGlobalVariable(
// CHECK-SAME: name: "varray"
// CHECK-SAME: templateParams: {{![0-9]+}}
// CHECK: !DITemplateValueParameter(name: "N", type: [[TY]], value: i32 1)

// CHECK: ![[OUTER_FOO_INNER_ID:[0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier:
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
// CHECK-SAME: elements: [[FOO_MEM:![0-9]*]]
// CHECK-SAME: identifier: "_ZTS3foo"
// CHECK: [[FOO_MEM]] = !{[[FOO_FUNC:![0-9]*]]}
// CHECK: [[FOO_FUNC]] = !DISubprogram(name: "func", linkageName: "_ZN3foo4funcEN5outerIS_E5innerE",
// CHECK-SAME: type: [[FOO_FUNC_TYPE:![0-9]*]]
// CHECK: [[FOO_FUNC_TYPE]] = !DISubroutineType(types: [[FOO_FUNC_PARAMS:![0-9]*]])
// CHECK: [[FOO_FUNC_PARAMS]] = !{null, !{{[0-9]*}}, ![[OUTER_FOO_INNER_ID]]}

// CHECK: !DISubprogram(name: "add<2>"
// CHECK-SAME: scope: [[C]]

template<typename T>
struct outer {
struct inner {
Expand Down
87 changes: 87 additions & 0 deletions clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// REQUIRES: target={{x86_64.*-linux.*}}

// Diamond inheritance case:
// For CBase, CLeft, CRight and CDerived we check:
// - Generation of their vtables (including attributes).
// - Generation of their '_vtable$' data members:
// * Correct scope and attributes

namespace NSP {
struct CBase {
int B = 0;
virtual char fooBase() { return 'B'; }
};
}

namespace NSP_1 {
struct CLeft : NSP::CBase {
int M1 = 1;
char fooBase() override { return 'O'; };
virtual int fooLeft() { return 1; }
};
}

namespace NSP_2 {
struct CRight : NSP::CBase {
int M2 = 2;
char fooBase() override { return 'T'; };
virtual int fooRight() { return 2; }
};
}

struct CDerived : NSP_1::CLeft, NSP_2::CRight {
int D = 3;
char fooBase() override { return 'D'; };
int fooDerived() { return 3; };
};

int main() {
NSP::CBase Base;
NSP_1::CLeft Left;
NSP_2::CRight Right;
CDerived Derived;

return 0;
}

// RUN: %clang --target=x86_64-linux -Xclang -disable-O0-optnone -Xclang -disable-llvm-passes -emit-llvm -S -g %s -o - | FileCheck %s

// CHECK: $_ZTVN3NSP5CBaseE = comdat any
// CHECK: $_ZTVN5NSP_15CLeftE = comdat any
// CHECK: $_ZTVN5NSP_26CRightE = comdat any
// CHECK: $_ZTV8CDerived = comdat any

// CHECK: @_ZTVN3NSP5CBaseE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_15CLeftE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[LEFT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_26CRightE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[RIGHT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTV8CDerived = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[DERIVED_VTABLE_VAR:![0-9]*]]

// CHECK: [[BASE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[BASE_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK-NEXT: [[BASE_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN3NSP5CBaseE"

// CHECK: [[LEFT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[LEFT_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK-NEXT: [[LEFT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_15CLeftE"

// CHECK: [[TYPE:![0-9]*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)

// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[LEFT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK: [[LEFT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CLeft"

// CHECK: [[BASE:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CBase"

// CHECK: [[RIGHT_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[RIGHT_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK-NEXT: [[RIGHT_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_26CRightE"

// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[RIGHT:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK: [[RIGHT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CRight"

// CHECK: [[DERIVED_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[DERIVED_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK-NEXT: [[DERIVED_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CDerived"

// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[DERIVED:![0-9]*]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK: [[DERIVED]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CDerived"

// CHECK: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[BASE]], file: {{.*}}, baseType: [[TYPE]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
Loading