Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
51 changes: 51 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,57 @@ 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) {
if (!CGM.getTarget().getCXXABI().isItaniumFamily())
return;

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?

StringRef SymbolName = "_vtable$";
const DeclContext *DC = static_cast<const DeclContext *>(RD);
Copy link
Member

Choose a reason for hiding this comment

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

I think this cast is redundant. You should be able to just pass RD into getContextDescriptor

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 correct. Removed the redundant cast.

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 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) |
llvm::DINode::FlagArtificial;
auto Tag = CGM.getCodeGenOpts().DwarfVersion >= 5
? llvm::dwarf::DW_TAG_variable
: llvm::dwarf::DW_TAG_member;
llvm::DIDerivedType *DT = DBuilder.createStaticMemberType(
Ctxt, SymbolName, Unit, /*LineNumber=*/0, VTy, Flags,
/*Val=*/nullptr, Tag);

// Use the same vtable pointer to global alignment for the symbol.
unsigned PAlign = CGM.getGlobalVarAlignment();

// 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, SymbolName, 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
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,15 @@ class CodeGenModule : public CodeGenTypeCache {
void requireVectorDestructorDefinition(const CXXRecordDecl *RD);
bool classNeedsVectorDestructor(const CXXRecordDecl *RD);

// Helper to get the alignment for a variable.
unsigned getGlobalVarAlignment(const VarDecl *D = nullptr) {
LangAS AS = GetGlobalVarAddressSpace(D);
unsigned PAlign = getItaniumVTableContext().isRelativeLayout()
? 32
: getTarget().getPointerAlign(AS);
return PAlign;
}

Copy link
Member

Choose a reason for hiding this comment

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

It makes sense to refactor this out; I feel the name of the function should contain "vtable" somewhere though, it's fundamentally tied to producing vtable information as there's a call to getItaniumVTableContext, yes? There's a small risk that someone uses it for a different purpose, which we can fix by putting "vtable" in the name.

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 function name to getVtableGlobalVarAlignment.

private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;

Expand Down
9 changes: 5 additions & 4 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 Expand Up @@ -2164,10 +2168,7 @@ llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
// Use pointer to global alignment for the vtable. Otherwise we would align
// them based on the size of the initializer which doesn't make sense as only
// single values are read.
LangAS AS = CGM.GetGlobalVarAddressSpace(nullptr);
unsigned PAlign = CGM.getItaniumVTableContext().isRelativeLayout()
? 32
: CGM.getTarget().getPointerAlign(AS);
unsigned PAlign = CGM.getGlobalVarAlignment();

VTable = CGM.CreateOrReplaceCXXRuntimeVariable(
Name, VTableType, llvm::GlobalValue::ExternalLinkage,
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
Loading