Skip to content

[Clang](NFC) Add coverage for VTable debug info #151818

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
93 changes: 93 additions & 0 deletions clang/test/CodeGenCXX/vtable-debug-info-external.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// The debug info of vtable is attached conditionally to whether
// - Member functions are inlined or not
// - Definition of destructor is visible or not

struct CInlined {
virtual void f1() noexcept {}
virtual void f2() noexcept {}
virtual ~CInlined() noexcept;
};
#ifndef NO_DTOR_BODY
inline CInlined::~CInlined() noexcept {}
#endif

struct CNoInline {
virtual void g1() noexcept;
virtual void g2() noexcept;
virtual ~CNoInline() noexcept;
};

void CNoInline::g1() noexcept {}
void CNoInline::g2() noexcept {}
#ifndef NO_DTOR_BODY
CNoInline::~CNoInline() noexcept {}
#endif

struct CNoFnDef {
virtual void h1() noexcept;
virtual void h2() noexcept;
virtual ~CNoFnDef() noexcept;
};

#ifndef NO_DTOR_BODY
CNoFnDef::~CNoFnDef() noexcept {}
#endif

void use(void *, ...);

int main() {
CInlined Inlined;
CNoInline NoInline;
CNoFnDef NoFnDef;
use(&Inlined, &NoInline, &NoFnDef);

return 0;
}

// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-HAS-DTOR
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 -DNO_DTOR_BODY %s -o - | FileCheck %s -check-prefixes CHECK-NO-DTOR

// CHECK-HAS-DTOR: $_ZTV8CInlined = comdat any
// CHECK-HAS-DTOR-NOT: $_ZTV9CNoInline
// CHECK-HAS-DTOR-NOT: $_ZTV8CNoFnDef

// CHECK-HAS-DTOR-DAG: @_ZTV8CInlined = linkonce_odr {{.*}}constant {{{ \[[^]]*\] } { \[[^]]*\] \[[^]]*\] }}}, comdat, align 8, !dbg [[INLINED_VTABLE_VAR:![0-9]+]]
// CHECK-HAS-DTOR-DAG: @_ZTV9CNoInline = {{.*}}constant {{{ \[[^]]*\] } { \[[^]]*\] \[[^]]*\] }}}, align 8, !dbg [[NOINLINE_VTABLE_VAR:![0-9]+]]
// CHECK-HAS-DTOR-DAG: @_ZTV8CNoFnDef = external {{.*}}constant {{{ \[[^]]*\] }}}, align 8

// CHECK-HAS-DTOR: !llvm.dbg.cu

// CHECK-HAS-DTOR-DAG: [[INLINED_VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CInlined"
// CHECK-HAS-DTOR-DAG: [[INLINED_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[INLINED_VTABLE]], expr: !DIExpression())
// CHECK-HAS-DTOR-DAG: [[INLINED:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CInlined"
// CHECK-HAS-DTOR-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[INLINED]], file: {{.*}}, baseType: {{![0-9]+}}, flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK-HAS-DTOR-DAG: [[NOINLINE_VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CNoInline"
// CHECK-HAS-DTOR-DAG: [[NOINLINE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[NOINLINE_VTABLE]], expr: !DIExpression())
// CHECK-HAS-DTOR-DAG: [[NOINLINE:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CNoInline"
// CHECK-HAS-DTOR-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[NOINLINE]], file: {{.*}}, baseType: {{![0-9]+}}, flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK-HAS-DTOR-DAG: !llvm.ident

// CHECK-HAS-DTOR-NOT: !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CNoFnDef"


// CHECK-NO-DTOR-NOT: $_ZTV8CInlined
// CHECK-NO-DTOR-NOT: $_ZTV9CNoInline
// CHECK-NO-DTOR-NOT: $_ZTV8CNoFnDef

// CHECK-NO-DTOR-DAG: @_ZTV8CInlined = external {{.*}}constant {{.*}}, align 8{{$}}
// CHECK-NO-DTOR-DAG: @_ZTV9CNoInline = {{.*}}constant {{{ \[[^]]*\] } { \[[^]]*\] \[[^]]*\] }}}, align 8, !dbg [[NOINLINE_VTABLE_VAR:![0-9]+]]
// CHECK-NO-DTOR-DAG: @_ZTV8CNoFnDef = external {{.*}}constant {{{ \[[^]]*\] }}}, align 8, !dbg [[NOFNDEF_VTABLE_VAR:![0-9]+]]

// CHECK-NO-DTOR: !llvm.dbg.cu

// CHECK-NO-DTOR-DAG: [[NOINLINE_VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CNoInline"
// CHECK-NO-DTOR-DAG: [[NOINLINE_VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[NOINLINE_VTABLE]], expr: !DIExpression())
// CHECK-NO-DTOR-DAG: [[NOINLINE:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CNoInline"
// CHECK-NO-DTOR-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[NOINLINE]], file: {{.*}}, baseType: {{![0-9]+}}, flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)

// CHECK-NO-DTOR-DAG: !llvm.ident

// CHECK-NO-DTOR-NOT: !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CInlined"
// CHECK-NO-DTOR-NOT: !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV8CNoFnDef"
15 changes: 8 additions & 7 deletions clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// REQUIRES: target={{x86_64.*-linux.*}}

// Diamond inheritance case:
// For CBase, CLeft, CRight and CDerived we check:
// - Generation of their vtables (including attributes).
Expand Down Expand Up @@ -35,26 +33,29 @@ struct CDerived : NSP_1::CLeft, NSP_2::CRight {
int fooDerived() { return 3; };
};

void use(void *, ...);

int main() {
NSP::CBase Base;
NSP_1::CLeft Left;
NSP_2::CRight Right;
CDerived Derived;
use(&Base, &Left, &Right, &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
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %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: @_ZTVN3NSP5CBaseE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_15CLeftE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[LEFT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_26CRightE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[RIGHT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTV8CDerived = linkonce_odr {{.*}}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"
Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// REQUIRES: target={{x86_64.*-linux.*}}

// Multiple inheritance case:
// For CBaseOne, CBaseTwo and CDerived we check:
// - Generation of their vtables (including attributes).
Expand Down Expand Up @@ -30,23 +28,25 @@ struct CDerived : NSP_1::CBaseOne, NSP_2::CBaseTwo {
int six() override { return 66; }
};

void use(void *, ...);
int main() {
NSP_1::CBaseOne BaseOne;
NSP_2::CBaseTwo BaseTwo;
CDerived Derived;
use(&BaseOne, &BaseTwo, &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
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %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: @_ZTVN5NSP_18CBaseOneE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_ONE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_28CBaseTwoE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_TWO_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTV8CDerived = linkonce_odr {{.*}}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"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// REQUIRES: target={{x86_64.*-linux.*}}

// Simple inheritance case:
// For CBase and CDerived we check:
// - Generation of their vtables (including attributes).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,23 @@ struct CDerived : NSP::CBase {
int three() override { return 33; }
};

void use(void *, ...);

int main() {
NSP::CBase Base;
CDerived Derived;
use(&Base, &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
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - | FileCheck %s

// CHECK: $_ZTVN3NSP5CBaseE = 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: @_ZTV8CDerived = linkonce_odr {{dso_local|hidden}} unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[DERIVED_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN3NSP5CBaseE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTV8CDerived = linkonce_odr {{.*}}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"
Expand Down
15 changes: 8 additions & 7 deletions clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// REQUIRES: target={{x86_64.*-linux.*}}

// Virtual inheritance case:
// For CBase, CLeft, CRight and CDerived we check:
// - Generation of their vtables (including attributes).
Expand Down Expand Up @@ -35,26 +33,29 @@ struct CDerived : NSP_1::CLeft, NSP_2::CRight {
int fooDerived() { return 3; };
};

void use(void *, ...);

int main() {
NSP::CBase Base;
NSP_1::CLeft Left;
NSP_2::CRight Right;
CDerived Derived;
use(&Base, &Left, &Right, &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
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %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: @_ZTVN3NSP5CBaseE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[BASE_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_15CLeftE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[LEFT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTVN5NSP_26CRightE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[RIGHT_VTABLE_VAR:![0-9]*]]
// CHECK: @_ZTV8CDerived = linkonce_odr {{.*}}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"
Expand Down
88 changes: 88 additions & 0 deletions clang/test/CodeGenCXX/vtable-debug-info-template-instantiation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// For CTemplate we check in case of:
// - Implicitly instantiate whole class by up-casting:
// * The vtable is generated with comdat
// * Its '_vtable$' is generated
// - Implicitly instantiate member function only:
// * The vtable is NOT generated
// * Its '_vtable$' is generated
// - Define explicitly instantiation:
// * The vtable is generated with comdat
// * Its '_vtable$' is generated
// - Declare explicitly instantiation as extern:
// # for COFF targets:
// * The vtable is declared but NOT associated with '_vtable$'
// # for non-COFF targets:
// * The vtable is declared
// * Its '_vtable$' is generated

struct CBase {
virtual void f() noexcept {}
};

template <typename T>
struct CTemplate: CBase {
void f() noexcept override;
virtual ~CTemplate() noexcept;
};
template <typename T>
void CTemplate<T>::f() noexcept {}
template <typename T>
CTemplate<T>::~CTemplate() noexcept {}

#ifdef EXPLICIT
template struct CTemplate<void>;
#endif
#ifdef EXTERN
extern template struct CTemplate<void>;
#endif

CTemplate<void> *get(CBase *) noexcept;

int main() {
CTemplate<void> Template;
#ifdef NOCAST
get(nullptr)->f();
#else
get(&Template)->f();
#endif

return 0;
}

// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix IMPLICIT
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - -DNOCAST | FileCheck %s -check-prefix NOCAST
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - -DEXPLICIT | FileCheck %s -check-prefix EXPLICIT
// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -debug-info-kind=limited -dwarf-version=5 -O1 %s -o - -DEXTERN | FileCheck %s -check-prefix EXTERN

// IMPLICIT: $_ZTV9CTemplateIvE = comdat any
// IMPLICIT: @_ZTV9CTemplateIvE = linkonce_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[VTABLE_VAR:![0-9]*]]
// IMPLICIT-DAG: [[VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CTemplateIvE"
// IMPLICIT-DAG: !DIGlobalVariableExpression(var: [[VTABLE]], expr: !DIExpression())
// IMPLICIT-DAG: [[TYPE:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CTemplate<void>"
// IMPLICIT-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[TYPE]], file: {{.*}}, baseType: [[PVOID:![0-9]+]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
// IMPLICIT-DAG: [[PVOID]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)

// NOCAST-NOT: $_ZTV9CTemplateIvE
// NOCAST-NOT: @_ZTV9CTemplateIvE
// NOCAST-DAG: [[VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CTemplateIvE"
// NOCAST-DAG: !DIGlobalVariableExpression(var: [[VTABLE]], expr: !DIExpression())
// NOCAST-DAG: [[TYPE:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CTemplate<void>"
// NOCAST-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[TYPE]], file: {{.*}}, baseType: [[PVOID:![0-9]+]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
// NOCAST-DAG: [[PVOID]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)

// EXPLICIT: $_ZTV9CTemplateIvE = comdat any
// EXPLICIT: @_ZTV9CTemplateIvE = weak_odr {{.*}}unnamed_addr constant {{.*}}, comdat, align 8, !dbg [[VTABLE_VAR:![0-9]*]]
// EXPLICIT-DAG: [[VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CTemplateIvE"
// EXPLICIT-DAG: [[VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[VTABLE]], expr: !DIExpression())
// EXPLICIT-DAG: [[TYPE:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "CTemplate<void>"
// EXPLICIT-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[TYPE]], file: {{.*}}, baseType: [[PVOID:![0-9]+]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
// EXPLICIT-DAG: [[PVOID]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)

// EXTERN-NOT: $_ZTV9CTemplateIvE
// EXTERN: @_ZTV9CTemplateIvE = external {{.*}}unnamed_addr constant {{.*}}, align 8, !dbg [[VTABLE_VAR:![0-9]*]]
// EXTERN-DAG: [[VTABLE:![0-9]+]] = distinct !DIGlobalVariable(name: "_vtable$", linkageName: "_ZTV9CTemplateIvE"
// EXTERN-DAG: [[VTABLE_VAR]] = !DIGlobalVariableExpression(var: [[VTABLE]], expr: !DIExpression())
// EXTERN-DAG: [[TYPE:![0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "CTemplate<void>"
// EXTERN-DAG: !DIDerivedType(tag: DW_TAG_variable, name: "_vtable$", scope: [[TYPE]], file: {{.*}}, baseType: [[PVOID:![0-9]+]], flags: DIFlagPrivate | DIFlagArtificial | DIFlagStaticMember)
// EXTERN-DAG: [[PVOID]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)