// RUN: %clang_cc1 -triple x86_64-pc-linux -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir // RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR // RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -fclangir -emit-llvm -o %t-cir.ll // RUN: FileCheck %s --input-file=%t-cir.ll --check-prefixes=LLVM // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o %t-classic.ll // RUN: FileCheck %s --input-file=%t-classic.ll --check-prefixes=LLVM // // RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++03 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir // RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR // RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++03 -fclangir -emit-llvm -o %t-cir.ll // RUN: FileCheck %s --input-file=%t-cir.ll --check-prefixes=LLVM // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++03 -emit-llvm -o %t-classic.ll // RUN: FileCheck %s --input-file=%t-classic.ll --check-prefixes=LLVM // // RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++11 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir // RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR // RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++11 -fclangir -emit-llvm -o %t-cir.ll // RUN: FileCheck %s --input-file=%t-cir.ll --check-prefixes=LLVM // RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++11 -emit-llvm -o %t-classic.ll // RUN: FileCheck %s --input-file=%t-classic.ll --check-prefixes=LLVM namespace { struct A { virtual void f() { } }; } void f() { A b; } struct B { B(); virtual void f(); }; B::B() { } struct C : virtual B { C(); virtual void f() { } }; C::C() { } struct D { virtual void f(); }; void D::f() { } static struct : D { } e; // Force 'e' to be constructed and therefore have a vtable defined. void use_e() { e.f(); } // The destructor is the key function. template struct E { virtual ~E(); }; template E::~E() { } // Anchor is the key function template<> struct E { virtual void anchor(); }; void E::anchor() { } template struct E; extern template struct E; void use_E() { E ei; (void)ei; E el; (void)el; } // No key function template struct F { virtual void foo() { } }; // No key function template<> struct F { virtual void foo() { } }; template struct F; extern template struct F; void use_F() { F fc; fc.foo(); F fi; fi.foo(); F fl; (void)fl; } // B has a key function that is not defined in this translation unit so its vtable // has external linkage. // CIR-DAG: cir.global "private" external @_ZTV1B : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1B = external {{.*}}{ [3 x ptr] }, align 8 // C has no key function, so its vtable should have weak_odr linkage // and hidden visibility. // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1C = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1C> : !cir.ptr, #cir.global_view<@_ZN1C1fEv> : !cir.ptr]> : !cir.array x 5>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global linkonce_odr comdat @_ZTS1C = #cir.const_array<"1C" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant linkonce_odr comdat @_ZTI1C = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv121__vmi_class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1C> : !cir.ptr, #cir.int<0> : !u32i, #cir.int<1> : !u32i, #cir.global_view<@_ZTI1B> : !cir.ptr, #cir.int<-8189> : !s64i}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global linkonce_odr comdat @_ZTT1C = #cir.const_array<[#cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr, #cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr]> : !cir.array x 2> {alignment = 8 : i64} // LLVM-DAG: @_ZTV1C = linkonce_odr {{.*}}{ [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv] }, comdat, align 8 // LLVM-DAG: @_ZTS1C = linkonce_odr {{.*}}[{{[0-9]}} x i8] c"1C\00", comdat, align 1 // LLVM-DAG: @_ZTI1C = linkonce_odr {{.*}}{ ptr, ptr, i32, i32, ptr, i64 } { ptr getelementptr{{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 {{.*}}), ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -8189 }, comdat // LLVM-DAG: @_ZTT1C = linkonce_odr {{.*}}[2 x ptr] [ptr getelementptr inbounds{{.*}}({{.*}}, ptr @_ZTV1C, {{.*}}, ptr getelementptr inbounds {{.*}}({{.*}}, ptr @_ZTV1C{{.*}})] // D has a key function that is defined in this translation unit so its vtable is // defined in the translation unit. // CIR-DAG: cir.global "private" external @_ZTV1D = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1D> : !cir.ptr, #cir.global_view<@_ZN1D1fEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global external @_ZTS1D = #cir.const_array<"1D" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant external @_ZTI1D = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1D> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1D = {{(constant|global)}} { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1D, ptr @_ZN1D1fEv] }, align 8 // LLVM-DAG: @_ZTS1D = {{(constant|global)}} [{{[0-9]}} x i8] c"1D\00", align 1 // LLVM-DAG: @_ZTI1D = constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{[0-9]+}}), ptr @_ZTS1D }, align 8 // E is an explicit specialization with a key function defined // in this translation unit, so its vtable should have external // linkage. // CIR-DAG: cir.global "private" external @_ZTV1EIcE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1EIcE> : !cir.ptr, #cir.global_view<@_ZN1EIcE6anchorEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global external @_ZTS1EIcE = #cir.const_array<"1EIcE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant external @_ZTI1EIcE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1EIcE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1EIcE = {{(constant|global)}} { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1EIcE, ptr @_ZN1EIcE6anchorEv] }, align 8 // LLVM-DAG: @_ZTS1EIcE = {{(constant|global)}} [6 x i8] c"1EIcE\00", align 1 // LLVM-DAG: @_ZTI1EIcE = constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIcE }, align 8 // E is an explicit template instantiation with a key function // defined in this translation unit, so its vtable should have // weak_odr linkage. // CIR-DAG: cir.global "private" weak_odr comdat @_ZTV1EIsE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1EIsE> : !cir.ptr, #cir.global_view<@_ZN1EIsED1Ev> : !cir.ptr, #cir.global_view<@_ZN1EIsED0Ev> : !cir.ptr]> : !cir.array x 4>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global constant weak_odr comdat @_ZTI1EIsE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1EIsE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global weak_odr comdat @_ZTS1EIsE = #cir.const_array<"1EIsE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // LLVM-DAG: @_ZTV1EIsE = weak_odr {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1EIsE, ptr @_ZN1EIsED1Ev, ptr @_ZN1EIsED0Ev] }, comdat, align 8 // LLVM-DAG: @_ZTI1EIsE = weak_odr constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIsE }, comdat // LLVM-DAG: @_ZTS1EIsE = weak_odr {{.*}}[6 x i8] c"1EIsE\00", comdat // F is an explicit template instantiation without a key // function, so its vtable should have weak_odr linkage // CIR-DAG: cir.global "private" weak_odr comdat @_ZTV1FIsE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1FIsE> : !cir.ptr, #cir.global_view<@_ZN1FIsE3fooEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global weak_odr comdat @_ZTS1FIsE = #cir.const_array<"1FIsE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant weak_odr comdat @_ZTI1FIsE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1FIsE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1FIsE = weak_odr {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIsE, ptr @_ZN1FIsE3fooEv] }, comdat, align 8 // LLVM-DAG: @_ZTS1FIsE = weak_odr {{.*}}[6 x i8] c"1FIsE\00", comdat, align 1 // LLVM-DAG: @_ZTI1FIsE = weak_odr constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIsE }, comdat // E is an implicit template instantiation with a key function // defined in this translation unit, so its vtable should have // linkonce_odr linkage. // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1EIlE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1EIlE> : !cir.ptr, #cir.global_view<@_ZN1EIlED1Ev> : !cir.ptr, #cir.global_view<@_ZN1EIlED0Ev> : !cir.ptr]> : !cir.array x 4>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global linkonce_odr comdat @_ZTS1EIlE = #cir.const_array<"1EIlE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant linkonce_odr comdat @_ZTI1EIlE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1EIlE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1EIlE = linkonce_odr {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1EIlE, ptr @_ZN1EIlED1Ev, ptr @_ZN1EIlED0Ev] }, comdat, align 8 // LLVM-DAG: @_ZTS1EIlE = linkonce_odr {{.*}}[6 x i8] c"1EIlE\00", comdat, align 1 // LLVM-DAG: @_ZTI1EIlE = linkonce_odr constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIlE }, comdat // F is an implicit template instantiation with no key function, // so its vtable should have linkonce_odr linkage. // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1FIlE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1FIlE> : !cir.ptr, #cir.global_view<@_ZN1FIlE3fooEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global linkonce_odr comdat @_ZTS1FIlE = #cir.const_array<"1FIlE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant linkonce_odr comdat @_ZTI1FIlE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1FIlE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1FIlE = linkonce_odr {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIlE, ptr @_ZN1FIlE3fooEv] }, comdat, align 8 // LLVM-DAG: @_ZTS1FIlE = linkonce_odr {{.*}}[6 x i8] c"1FIlE\00", comdat, align 1 // LLVM-DAG: @_ZTI1FIlE = linkonce_odr constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIlE }, comdat // F is an explicit template instantiation declaration without a // key function, so its vtable should have external linkage. // CIR-DAG: cir.global "private" external @_ZTV1FIiE : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1FIiE = external {{.*}}{ [3 x ptr] }, align 8 // E is an explicit template instantiation declaration. It has a // key function is not instantiated, so we know that vtable definition // will be generated in TU where key function will be defined // so we can mark it as external (without optimizations) and // available_externally (with optimizations) because all of the inline // virtual functions have been emitted. // CIR-DAG: cir.global "private" external @_ZTV1EIiE : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1EIiE = external {{.*}}{ [4 x ptr] }, align 8 // The anonymous struct for e has no linkage, so the vtable should have // internal linkage. // CIR-DAG: cir.global "private" internal dso_local @_ZTV3$_0 = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI3$_0> : !cir.ptr, #cir.global_view<@_ZN1D1fEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global internal dso_local @_ZTS3$_0 = #cir.const_array<"3$_0" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant internal @_ZTI3$_0 = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv120__si_class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS3$_0> : !cir.ptr, #cir.global_view<@_ZTI1D> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @"_ZTV3$_0" = internal {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @"_ZTI3$_0", ptr @_ZN1D1fEv] }, align 8 // LLVM-DAG: @"_ZTS3$_0" = internal {{.*}}[5 x i8] c"3$_0\00", align 1 // LLVM-DAG: @"_ZTI3$_0" = internal constant { ptr, ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 {{.*}}), ptr @"_ZTS3$_0", ptr @_ZTI1D }, align 8 // The A vtable should have internal linkage since it is inside an anonymous // namespace. // CIR-DAG: cir.global "private" internal dso_local @_ZTVN12_GLOBAL__N_11AE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTIN12_GLOBAL__N_11AE> : !cir.ptr, #cir.global_view<@_ZN12_GLOBAL__N_11A1fEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global internal dso_local @_ZTSN12_GLOBAL__N_11AE = #cir.const_array<"N12_GLOBAL__N_11AE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant internal @_ZTIN12_GLOBAL__N_11AE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTSN12_GLOBAL__N_11AE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTVN12_GLOBAL__N_11AE = internal {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTIN12_GLOBAL__N_11AE, ptr @_ZN12_GLOBAL__N_11A1fEv] }, align 8 // LLVM-DAG: @_ZTSN12_GLOBAL__N_11AE = internal {{.*}}[19 x i8] c"N12_GLOBAL__N_11AE\00", align 1 // LLVM-DAG: @_ZTIN12_GLOBAL__N_11AE = internal constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTSN12_GLOBAL__N_11AE }, align 8 // F is an explicit specialization without a key function, so // its vtable should have linkonce_odr linkage. // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1FIcE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1FIcE> : !cir.ptr, #cir.global_view<@_ZN1FIcE3fooEv> : !cir.ptr]> : !cir.array x 3>}> : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global linkonce_odr comdat @_ZTS1FIcE = #cir.const_array<"1FIcE" : !cir.array, trailing_zeros> : !cir.array {alignment = 1 : i64} // CIR-DAG: cir.global constant linkonce_odr comdat @_ZTI1FIcE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr, #cir.global_view<@_ZTS1FIcE> : !cir.ptr}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1FIcE = linkonce_odr {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIcE, ptr @_ZN1FIcE3fooEv] }, comdat, align 8 // LLVM-DAG: @_ZTS1FIcE = linkonce_odr {{.*}}[6 x i8] c"1FIcE\00", comdat, align 1 // LLVM-DAG: @_ZTI1FIcE = linkonce_odr constant { ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIcE }, comdat // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1GIiE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1GIiE> : !cir.ptr, #cir.global_view<@_ZN1GIiE2f0Ev> : !cir.ptr, #cir.global_view<@_ZN1GIiE2f1Ev> : !cir.ptr]> : !cir.array x 4>}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1GIiE = linkonce_odr {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1GIiE, ptr @_ZN1GIiE2f0Ev, ptr @_ZN1GIiE2f1Ev] }, comdat, align 8 template class G { public: G() {} virtual void f0(); virtual void f1(); }; template <> void G::f1() {} template void G::f0() {} void G_f0() { new G(); } // H has a key function without a body but it's a template instantiation // so its VTable must be emitted. // CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1HIiE = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.global_view<@_ZTI1HIiE> : !cir.ptr, #cir.global_view<@_ZN1HIiED1Ev> : !cir.ptr, #cir.global_view<@_ZN1HIiED0Ev> : !cir.ptr]> : !cir.array x 4>}> : !{{.*}}{alignment = 8 : i64} // LLVM-DAG: @_ZTV1HIiE = linkonce_odr {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1HIiE, ptr @_ZN1HIiED1Ev, ptr @_ZN1HIiED0Ev] }, comdat, align 8 template class H { public: virtual ~H(); }; void use_H() { H h; } // I has an explicit instantiation declaration and needs a VTT and // construction vtables. // CIR-DAG: cir.global "private" external @_ZTV1IIiE : !{{.*}}{alignment = 8 : i64} // CIR-DAG: cir.global "private" external @_ZTT1IIiE : !cir.array x 4> {alignment = 8 : i64} // LLVM-DAG: @_ZTV1IIiE = external {{.*}}{ [5 x ptr] }, align 8 // LLVM-DAG: @_ZTT1IIiE = external {{.*}}[4 x ptr], align 8 struct VBase1 { virtual void f(); }; struct VBase2 : virtual VBase1 {}; template struct I : VBase2 {}; extern template struct I; I i;