Skip to content

[clang][DebugInfo] Add symbol for debugger with VTable information. #130255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
49 changes: 49 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,55 @@ 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();
StringRef SymbolName = "_vtable$";
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(RD, 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.getVtableGlobalVarAlignment();

// 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 getVtableGlobalVarAlignment(const VarDecl *D = nullptr) {
LangAS AS = GetGlobalVarAddressSpace(D);
unsigned PAlign = getItaniumVTableContext().isRelativeLayout()
? 32
: getTarget().getPointerAlign(AS);
return PAlign;
}

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.getVtableGlobalVarAlignment();

VTable = CGM.CreateOrReplaceCXXRuntimeVariable(
Name, VTableType, llvm::GlobalValue::ExternalLinkage,
Expand Down
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)
72 changes: 72 additions & 0 deletions clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// REQUIRES: target={{x86_64.*-linux.*}}

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

namespace NSP_1 {
struct CBaseOne {
int B1 = 1;
virtual int one() { return 1; }
virtual int two() { return 2; }
virtual int three() { return 3; }
};
}

namespace NSP_2 {
struct CBaseTwo {
int B2 = 1;
virtual int four() { return 4; }
virtual int five() { return 5; }
virtual int six() { return 6; }
};
}

struct CDerived : NSP_1::CBaseOne, NSP_2::CBaseTwo {
int D = 1;
int two() override { return 22; };
int six() override { return 66; }
};

int main() {
NSP_1::CBaseOne BaseOne;
NSP_2::CBaseTwo BaseTwo;
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: $_ZTVN5NSP_18CBaseOneE = comdat any
// CHECK: $_ZTVN5NSP_28CBaseTwoE = comdat any
// CHECK: $_ZTV8CDerived = comdat any

// CHECK: @_ZTVN5NSP_18CBaseOneE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_ONE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_28CBaseTwoE = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_TWO_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_ONE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[BASE_ONE_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK-NEXT: [[BASE_ONE_VTABLE]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTVN5NSP_18CBaseOneE"

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

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

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

// check: [[BASE_TWO]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CBaseTwo"

// CHECK: [[DERIVED_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[DERIVED_VTABLE:![0-9]*]], expr: !DIExpression())
// CHECK: [[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: [[BASE_ONE:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CBaseOne"

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