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

Conversation

kikairoya
Copy link
Contributor

Add test for:

  • shows effect of inlining member functions
  • shows effect of template instantiation methods

Make cleaner a bit:

  • drops unnecessary REQUIRES clause
  • uses %clang_cc1 instead of %clang -Xclang
  • uses simply -O1 instead of -disable-O0-optnone -disable-llvm-passes
    • LTO test remains unchanged since replacing by -O1 changes output even if variables are anchored

Add test for:
- shows effect of inlining member functions
- shows effect of template instantiation methods

Make cleaner a bit:
- drops unnecessary REQUIRES clause
- uses %clang_cc1 instead of %clang -Xclang
- uses simply -O1 instead of -disable-O0-optnone -disable-llvm-passes
  * LTO test remains unchanged since replacing by -O1 changes output even if variables are anchored
@llvmbot llvmbot added the clang Clang issues not falling into any other category label Aug 2, 2025
@kikairoya
Copy link
Contributor Author

I'd like to add test coverage and verify the intended behavior on edge cases before submitting the long-term fix for #149639.

@jmorse @CarlosAlbertoEnciso
Could you take a look at this and check if the new tests reflect the intended behavior?

@llvmbot
Copy link
Member

llvmbot commented Aug 2, 2025

@llvm/pr-subscribers-clang

Author: Tomohiro Kashiwada (kikairoya)

Changes

Add test for:

  • shows effect of inlining member functions
  • shows effect of template instantiation methods

Make cleaner a bit:

  • drops unnecessary REQUIRES clause
  • uses %clang_cc1 instead of %clang -Xclang
  • uses simply -O1 instead of -disable-O0-optnone -disable-llvm-passes
    • LTO test remains unchanged since replacing by -O1 changes output even if variables are anchored

Full diff: https://github.com/llvm/llvm-project/pull/151818.diff

7 Files Affected:

  • (added) clang/test/CodeGenCXX/vtable-debug-info-external.cpp (+93)
  • (modified) clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp (+8-7)
  • (modified) clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp (+6-6)
  • (modified) clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple-main.cpp (-2)
  • (modified) clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple.cpp (+6-3)
  • (modified) clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp (+8-7)
  • (added) clang/test/CodeGenCXX/vtable-debug-info-template-instantiation.cpp (+88)
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-external.cpp b/clang/test/CodeGenCXX/vtable-debug-info-external.cpp
new file mode 100644
index 0000000000000..c4138e720ccae
--- /dev/null
+++ b/clang/test/CodeGenCXX/vtable-debug-info-external.cpp
@@ -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"
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp
index 5ed1353eebb10..53a24a2d1b15e 100644
--- a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp
+++ b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-diamond.cpp
@@ -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).
@@ -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"
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp
index 23973a35d0e17..69fa096324651 100644
--- a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp
+++ b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-multiple.cpp
@@ -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).
@@ -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"
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple-main.cpp b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple-main.cpp
index d64e711dddfa0..5d93719b60699 100644
--- a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple-main.cpp
+++ b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple-main.cpp
@@ -1,5 +1,3 @@
-// REQUIRES: target={{x86_64.*-linux.*}}
-
 // Simple inheritance case:
 // For CBase and CDerived we check:
 // - Generation of their vtables (including attributes).
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple.cpp b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple.cpp
index 249586f5991f1..a0485f48ab8b8 100644
--- a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple.cpp
+++ b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-simple.cpp
@@ -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"
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp
index b01f156b7f654..fc8c15aabd616 100644
--- a/clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp
+++ b/clang/test/CodeGenCXX/vtable-debug-info-inheritance-virtual.cpp
@@ -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).
@@ -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"
diff --git a/clang/test/CodeGenCXX/vtable-debug-info-template-instantiation.cpp b/clang/test/CodeGenCXX/vtable-debug-info-template-instantiation.cpp
new file mode 100644
index 0000000000000..2f02861c7f465
--- /dev/null
+++ b/clang/test/CodeGenCXX/vtable-debug-info-template-instantiation.cpp
@@ -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)
+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants