Add MicrosoftVFTableContext to AST
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@187409 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp
new file mode 100644
index 0000000..bd0666b
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp
@@ -0,0 +1,431 @@
+// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o - >%t 2>&1
+
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test1 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test2 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test3 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test4 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test5 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test6 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test7 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test8 %s < %t
+// RUN: FileCheck --check-prefix=NO-THUNKS-Test9 %s < %t
+// RUN: FileCheck --check-prefix=PURE-VIRTUAL-Test1 %s < %t
+// RUN: FileCheck --check-prefix=THIS-THUNKS-Test1 %s < %t
+// RUN: FileCheck --check-prefix=THIS-THUNKS-Test2 %s < %t
+// RUN: FileCheck --check-prefix=THIS-THUNKS-Test3 %s < %t
+// RUN: FileCheck --check-prefix=RET-THUNKS-Test1 %s < %t
+// RUN: FileCheck --check-prefix=RET-THUNKS-Test2 %s < %t
+// RUN: FileCheck --check-prefix=RET-THUNKS-Test3 %s < %t
+// RUN: FileCheck --check-prefix=RET-THUNKS-Test4 %s < %t
+// RUN: FileCheck --check-prefix=RET-THUNKS-Test5 %s < %t
+
+struct Empty {
+ // Doesn't have a vftable!
+};
+
+struct A {
+ virtual void f();
+};
+
+struct B {
+ virtual void g();
+ // Add an extra virtual method so it's easier to check for the absence of thunks.
+ virtual void h();
+};
+
+struct C {
+ virtual void g(); // Might "collide" with B::g if both are bases of some class.
+};
+
+
+namespace no_thunks {
+
+struct Test1: A, B {
+ // NO-THUNKS-Test1: VFTable for 'A' in 'no_thunks::Test1' (1 entries)
+ // NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f()
+
+ // NO-THUNKS-Test1: VFTable for 'B' in 'no_thunks::Test1' (2 entries)
+ // NO-THUNKS-Test1-NEXT: 0 | void B::g()
+ // NO-THUNKS-Test1-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test1: VFTable indices for 'no_thunks::Test1' (1 entries)
+ // NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f()
+
+ // Overrides only the left child's method (A::f), needs no thunks.
+ virtual void f();
+};
+
+Test1 t1;
+
+struct Test2: A, B {
+ // NO-THUNKS-Test2: VFTable for 'A' in 'no_thunks::Test2' (1 entries)
+ // NO-THUNKS-Test2-NEXT: 0 | void A::f()
+
+ // NO-THUNKS-Test2: VFTable for 'B' in 'no_thunks::Test2' (2 entries)
+ // NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g()
+ // NO-THUNKS-Test2-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test2: VFTable indices for 'no_thunks::Test2' (1 entries).
+ // NO-THUNKS-Test2-NEXT: via vfptr at offset 4
+ // NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g()
+
+ // Overrides only the right child's method (B::g), needs this adjustment but
+ // not thunks.
+ virtual void g();
+};
+
+Test2 t2;
+
+struct Test3: A, B {
+ // NO-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test3' (2 entries)
+ // NO-THUNKS-Test3-NEXT: 0 | void A::f()
+ // NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i()
+
+ // NO-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test3' (2 entries)
+ // NO-THUNKS-Test3-NEXT: 0 | void B::g()
+ // NO-THUNKS-Test3-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test3: VFTable indices for 'no_thunks::Test3' (1 entries).
+ // NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i()
+
+ // Only adds a new method.
+ virtual void i();
+};
+
+Test3 t3;
+
+// Only the right base has a vftable, so it's laid out before the left one!
+struct Test4 : Empty, A {
+ // NO-THUNKS-Test4: VFTable for 'A' in 'no_thunks::Test4' (1 entries)
+ // NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f()
+
+ // NO-THUNKS-Test4: VFTable indices for 'no_thunks::Test4' (1 entries).
+ // NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f()
+
+ virtual void f();
+};
+
+Test4 t4;
+
+// 2-level structure with repeating subobject types, but no thunks needed.
+struct Test5: Test1, Test2 {
+ // NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries)
+ // NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test1::f()
+ // NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z()
+
+ // NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries)
+ // NO-THUNKS-Test5-NEXT: 0 | void B::g()
+ // NO-THUNKS-Test5-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test5' (1 entries)
+ // NO-THUNKS-Test5-NEXT: 0 | void A::f()
+
+ // NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test5' (2 entries)
+ // NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test2::g()
+ // NO-THUNKS-Test5-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test5: VFTable indices for 'no_thunks::Test5' (1 entries).
+ // NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z()
+
+ virtual void z();
+};
+
+Test5 t5;
+
+struct Test6: Test1 {
+ // NO-THUNKS-Test6: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test6' (1 entries).
+ // NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f()
+
+ // NO-THUNKS-Test6: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test6' (2 entries).
+ // NO-THUNKS-Test6-NEXT: 0 | void B::g()
+ // NO-THUNKS-Test6-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test6: VFTable indices for 'no_thunks::Test6' (1 entries).
+ // NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f()
+
+ // Overrides both no_thunks::Test1::f and A::f.
+ virtual void f();
+};
+
+Test6 t6;
+
+struct Test7: Test2 {
+ // NO-THUNKS-Test7: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test7' (1 entries).
+ // NO-THUNKS-Test7-NEXT: 0 | void A::f()
+
+ // NO-THUNKS-Test7: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test7' (2 entries).
+ // NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g()
+ // NO-THUNKS-Test7-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test7: VFTable indices for 'no_thunks::Test7' (1 entries).
+ // NO-THUNKS-Test7-NEXT: via vfptr at offset 4
+ // NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g()
+
+ // Overrides both no_thunks::Test2::g and B::g.
+ virtual void g();
+};
+
+Test7 t7;
+
+struct Test8: Test3 {
+ // NO-THUNKS-Test8: VFTable for 'A' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries).
+ // NO-THUNKS-Test8-NEXT: 0 | void A::f()
+ // NO-THUNKS-Test8-NEXT: 1 | void no_thunks::Test3::i()
+
+ // NO-THUNKS-Test8: VFTable for 'B' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries).
+ // NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g()
+ // NO-THUNKS-Test8-NEXT: 1 | void B::h()
+
+ // NO-THUNKS-Test8: VFTable indices for 'no_thunks::Test8' (1 entries).
+ // NO-THUNKS-Test8-NEXT: via vfptr at offset 4
+ // NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g()
+
+ // Overrides grandparent's B::g.
+ virtual void g();
+};
+
+Test8 t8;
+
+struct D : A {
+ virtual void g();
+};
+
+// Repeating subobject.
+struct Test9: A, D {
+ // NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::Test9' (2 entries).
+ // NO-THUNKS-Test9-NEXT: 0 | void A::f()
+ // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h()
+
+ // NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::D' in 'no_thunks::Test9' (2 entries).
+ // NO-THUNKS-Test9-NEXT: 0 | void A::f()
+ // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::D::g()
+
+ // NO-THUNKS-Test9: VFTable indices for 'no_thunks::Test9' (1 entries).
+ // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h()
+
+ virtual void h();
+};
+
+Test9 t9;
+}
+
+namespace pure_virtual {
+struct D {
+ virtual void g() = 0;
+ virtual void h();
+};
+
+
+struct Test1: A, D {
+ // PURE-VIRTUAL-Test1: VFTable for 'A' in 'pure_virtual::Test1' (1 entries)
+ // PURE-VIRTUAL-Test1-NEXT: 0 | void A::f()
+
+ // PURE-VIRTUAL-Test1: VFTable for 'pure_virtual::D' in 'pure_virtual::Test1' (2 entries)
+ // PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g()
+ // PURE-VIRTUAL-Test1-NEXT: 1 | void pure_virtual::D::h()
+
+ // PURE-VIRTUAL-Test1: VFTable indices for 'pure_virtual::Test1' (1 entries).
+ // PURE-VIRTUAL-Test1-NEXT: via vfptr at offset 4
+ // PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g()
+
+ // Overrides only the right child's method (pure_virtual::D::g), needs this adjustment but
+ // not thunks.
+ virtual void g();
+};
+
+Test1 t1;
+}
+
+namespace this_adjustment {
+
+// Overrides methods of two bases at the same time, thus needing thunks.
+struct Test1 : B, C {
+ // THIS-THUNKS-Test1: VFTable for 'B' in 'this_adjustment::Test1' (2 entries).
+ // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
+ // THIS-THUNKS-Test1-NEXT: 1 | void B::h()
+
+ // THIS-THUNKS-Test1: VFTable for 'C' in 'this_adjustment::Test1' (1 entries).
+ // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
+ // THIS-THUNKS-Test1-NEXT: [this adjustment: -4 non-virtual]
+
+ // THIS-THUNKS-Test1: Thunks for 'void this_adjustment::Test1::g()' (1 entry).
+ // THIS-THUNKS-Test1-NEXT: 0 | this adjustment: -4 non-virtual
+
+ // THIS-THUNKS-Test1: VFTable indices for 'this_adjustment::Test1' (1 entries).
+ // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
+
+ virtual void g();
+};
+
+Test1 t1;
+
+struct Test2 : A, B, C {
+ // THIS-THUNKS-Test2: VFTable for 'A' in 'this_adjustment::Test2' (1 entries).
+ // THIS-THUNKS-Test2-NEXT: 0 | void A::f()
+
+ // THIS-THUNKS-Test2: VFTable for 'B' in 'this_adjustment::Test2' (2 entries).
+ // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
+ // THIS-THUNKS-Test2-NEXT: 1 | void B::h()
+
+ // THIS-THUNKS-Test2: VFTable for 'C' in 'this_adjustment::Test2' (1 entries).
+ // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
+ // THIS-THUNKS-Test2-NEXT: [this adjustment: -4 non-virtual]
+
+ // THIS-THUNKS-Test2: Thunks for 'void this_adjustment::Test2::g()' (1 entry).
+ // THIS-THUNKS-Test2-NEXT: 0 | this adjustment: -4 non-virtual
+
+ // THIS-THUNKS-Test2: VFTable indices for 'this_adjustment::Test2' (1 entries).
+ // THIS-THUNKS-Test2-NEXT: via vfptr at offset 4
+ // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
+
+ virtual void g();
+};
+
+Test2 t2;
+
+// Overrides methods of two bases at the same time, thus needing thunks.
+struct Test3: no_thunks::Test1, no_thunks::Test2 {
+ // THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test1' in 'this_adjustment::Test3' (1 entries).
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
+
+ // THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test1' in 'this_adjustment::Test3' (2 entries).
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
+ // THIS-THUNKS-Test3-NEXT: 1 | void B::h()
+
+ // THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test2' in 'this_adjustment::Test3' (1 entries).
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
+ // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual]
+
+ // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::f()' (1 entry).
+ // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual
+
+ // THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test2' in 'this_adjustment::Test3' (2 entries).
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
+ // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual]
+ // THIS-THUNKS-Test3-NEXT: 1 | void B::h()
+
+ // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::g()' (1 entry).
+ // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual
+
+ // THIS-THUNKS-Test3: VFTable indices for 'this_adjustment::Test3' (2 entries).
+ // THIS-THUNKS-Test3-NEXT: via vfptr at offset 0
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
+ // THIS-THUNKS-Test3-NEXT: via vfptr at offset 4
+ // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
+
+ virtual void f();
+ virtual void g();
+};
+
+Test3 t3;
+}
+
+namespace return_adjustment {
+
+struct Ret1 {
+ virtual C* foo();
+ virtual void z();
+};
+
+struct Test1 : Ret1 {
+ // RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries).
+ // RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo()
+ // RET-THUNKS-Test1-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
+
+ // RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entries).
+ // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
+
+ virtual this_adjustment::Test1* foo();
+};
+
+Test1 t1;
+
+struct Ret2 : B, this_adjustment::Test1 { };
+
+struct Test2 : Test1 {
+ // RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries).
+ // RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
+ // RET-THUNKS-Test2-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
+ // RET-THUNKS-Test2-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
+
+ // RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entries).
+ // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
+
+ virtual Ret2* foo();
+};
+
+Test2 t2;
+
+struct Test3: B, Ret1 {
+ // RET-THUNKS-Test3: VFTable for 'B' in 'return_adjustment::Test3' (2 entries).
+ // RET-THUNKS-Test3-NEXT: 0 | void B::g()
+ // RET-THUNKS-Test3-NEXT: 1 | void B::h()
+
+ // RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries).
+ // RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo()
+ // RET-THUNKS-Test3-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
+
+ // RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entries).
+ // RET-THUNKS-Test3-NEXT: via vfptr at offset 4
+ // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
+
+ virtual this_adjustment::Test1* foo();
+};
+
+Test3 t3;
+
+struct Test4 : Test3 {
+ // RET-THUNKS-Test4: VFTable for 'B' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (2 entries).
+ // RET-THUNKS-Test4-NEXT: 0 | void B::g()
+ // RET-THUNKS-Test4-NEXT: 1 | void B::h()
+
+ // RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries).
+ // RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+ // RET-THUNKS-Test4-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+ // RET-THUNKS-Test4-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+
+ // RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entries).
+ // RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 --
+ // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+
+ virtual Ret2* foo();
+};
+
+Test4 t4;
+
+struct Test5 : Ret1, Test1 {
+ // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries).
+ // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+
+ // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries).
+ // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
+ // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+
+ // RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entries).
+ // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+
+ virtual Ret2* foo();
+};
+
+Test5 t5;
+}
diff --git a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
index 04b5fc1..1bebec9 100644
--- a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
+++ b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
@@ -8,12 +8,25 @@
// RUN: FileCheck --check-prefix=CHECK-E %s < %t
// RUN: FileCheck --check-prefix=CHECK-F %s < %t
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
+// RUN: FileCheck --check-prefix=CHECK-I %s < %t
+
+// FIXME: Currently, we only test VFTableContext in the AST, but still use
+// VTableContext for CodeGen. We should remove the "Vtable" checks below when we
+// completely switch from VTableContext to VFTableContext.
+// Currently, the order of Vtable vs VFTable output depends on whether the
+// v*table info was required by a constructor or a method definition.
struct A {
// CHECK-A: Vtable for 'A' (3 entries)
// CHECK-A-NEXT: 0 | void A::f()
// CHECK-A-NEXT: 1 | void A::g()
// CHECK-A-NEXT: 2 | void A::h()
+
+ // CHECK-A: VFTable for 'A' (3 entries)
+ // CHECK-A-NEXT: 0 | void A::f()
+ // CHECK-A-NEXT: 1 | void A::g()
+ // CHECK-A-NEXT: 2 | void A::h()
+
virtual void f();
virtual void g();
virtual void h();
@@ -29,6 +42,14 @@
// CHECK-B-NEXT: 2 | void A::h()
// CHECK-B-NEXT: 3 | void B::i()
// CHECK-B-NEXT: 4 | void B::j()
+
+ // CHECK-B: VFTable for 'A' in 'B' (5 entries)
+ // CHECK-B-NEXT: 0 | void B::f()
+ // CHECK-B-NEXT: 1 | void A::g()
+ // CHECK-B-NEXT: 2 | void A::h()
+ // CHECK-B-NEXT: 3 | void B::i()
+ // CHECK-B-NEXT: 4 | void B::j()
+
virtual void f(); // overrides A::f()
virtual void i();
virtual void j();
@@ -37,14 +58,21 @@
// EMITS-VTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct C {
+ // CHECK-C: VFTable for 'C' (2 entries)
+ // CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-C-NEXT: 1 | void C::f()
+ // CHECK-C: VFTable indices for 'C' (2 entries).
+ // CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
+ // CHECK-C-NEXT: 1 | void C::f()
+
// CHECK-C: Vtable for 'C' (2 entries)
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: VTable indices for 'C' (2 entries).
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
- virtual ~C();
+ virtual ~C();
virtual void f();
};
void C::f() {}
@@ -54,14 +82,28 @@
// CHECK-D: Vtable for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
- virtual void f();
+ // CHECK-D: VFTable for 'D' (2 entries)
+ // CHECK-D-NEXT: 0 | void D::f()
+ // CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
+
+ virtual void f();
virtual ~D();
};
D d;
// EMITS-VTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
struct E : A {
+ // CHECK-E: VFTable for 'A' in 'E' (5 entries)
+ // CHECK-E-NEXT: 0 | void A::f()
+ // CHECK-E-NEXT: 1 | void A::g()
+ // CHECK-E-NEXT: 2 | void A::h()
+ // CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
+ // CHECK-E-NEXT: 4 | void E::i()
+ // CHECK-E: VFTable indices for 'E' (2 entries).
+ // CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
+ // CHECK-E-NEXT: 4 | void E::i()
+
// CHECK-E: Vtable for 'E' (5 entries)
// CHECK-E-NEXT: 0 | void A::f()
// CHECK-E-NEXT: 1 | void A::g()
@@ -90,6 +132,17 @@
// CHECK-F: VTable indices for 'F' (2 entries).
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
+
+ // CHECK-F: VFTable for 'A' in 'F' (5 entries)
+ // CHECK-F-NEXT: 0 | void A::f()
+ // CHECK-F-NEXT: 1 | void A::g()
+ // CHECK-F-NEXT: 2 | void A::h()
+ // CHECK-F-NEXT: 3 | void F::i()
+ // CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
+ // CHECK-F: VFTable indices for 'F' (2 entries).
+ // CHECK-F-NEXT: 3 | void F::i()
+ // CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
+
virtual void i();
virtual ~F();
};
@@ -97,6 +150,18 @@
// EMITS-VTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct G : E {
+ // CHECK-G: VFTable for 'A' in 'E' in 'G' (6 entries)
+ // CHECK-G-NEXT: 0 | void G::f()
+ // CHECK-G-NEXT: 1 | void A::g()
+ // CHECK-G-NEXT: 2 | void A::h()
+ // CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
+ // CHECK-G-NEXT: 4 | void E::i()
+ // CHECK-G-NEXT: 5 | void G::j()
+ // CHECK-G: VFTable indices for 'G' (3 entries).
+ // CHECK-G-NEXT: 0 | void G::f()
+ // CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
+ // CHECK-G-NEXT: 5 | void G::j()
+
// CHECK-G: Vtable for 'G' (6 entries)
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 1 | void A::g()
@@ -108,6 +173,7 @@
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 5 | void G::j()
+
virtual void f(); // overrides A::f()
virtual ~G();
virtual void j();
@@ -121,3 +187,15 @@
};
void H::f() {}
// NO-VTABLE-NOT: @"\01??_7H@@6B@"
+
+struct Empty { };
+
+struct I : Empty {
+ // CHECK-I: VFTable for 'I' (2 entries)
+ // CHECK-I-NEXT: 0 | void I::f()
+ // CHECK-I-NEXT: 1 | void I::g()
+ virtual void f();
+ virtual void g();
+};
+
+I i;
diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
new file mode 100644
index 0000000..cf9e4c0
--- /dev/null
+++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp
@@ -0,0 +1,391 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
+
+// RUN: FileCheck --check-prefix=VTABLE-C %s < %t
+// RUN: FileCheck --check-prefix=VTABLE-D %s < %t
+// RUN: FileCheck --check-prefix=TEST1 %s < %t
+// RUN: FileCheck --check-prefix=TEST2 %s < %t
+// RUN: FileCheck --check-prefix=TEST3 %s < %t
+// RUN: FileCheck --check-prefix=TEST4 %s < %t
+// RUN: FileCheck --check-prefix=TEST5 %s < %t
+// RUN: FileCheck --check-prefix=TEST6 %s < %t
+// RUN: FileCheck --check-prefix=TEST7 %s < %t
+// RUN: FileCheck --check-prefix=TEST8 %s < %t
+// RUN: FileCheck --check-prefix=TEST9-Y %s < %t
+// RUN: FileCheck --check-prefix=TEST9-Z %s < %t
+// RUN: FileCheck --check-prefix=TEST9-W %s < %t
+// RUN: FileCheck --check-prefix=TEST9-T %s < %t
+// RUN: FileCheck --check-prefix=TEST10 %s < %t
+// RUN: FileCheck --check-prefix=RET-W %s < %t
+// RUN: FileCheck --check-prefix=RET-T %s < %t
+
+struct Empty { };
+
+struct A {
+ virtual void f();
+ virtual void z(); // Useful to check there are no thunks for f() when appropriate.
+};
+
+struct B {
+ virtual void g();
+};
+
+struct C: virtual A {
+ // VTABLE-C: VFTable for 'A' in 'C' (2 entries)
+ // VTABLE-C-NEXT: 0 | void C::f()
+ // VTABLE-C-NEXT: 1 | void A::z()
+
+ // VTABLE-C: VFTable indices for 'C' (1 entries)
+ // VTABLE-C-NEXT: vbtable index 1, vfptr at offset 0
+ // VTABLE-C-NEXT: 0 | void C::f()
+
+ ~C(); // Currently required to have correct record layout, see PR16406
+ virtual void f();
+};
+
+C c;
+
+struct D: virtual A {
+ // VTABLE-D: VFTable for 'D' (1 entries).
+ // VTABLE-D-NEXT: 0 | void D::h()
+
+ // VTABLE-D: VFTable for 'A' in 'D' (2 entries).
+ // VTABLE-D-NEXT: 0 | void D::f()
+ // VTABLE-D-NEXT: 1 | void A::z()
+
+ // VTABLE-D: VFTable indices for 'D' (2 entries).
+ // VTABLE-D-NEXT: via vfptr at offset 0
+ // VTABLE-D-NEXT: 0 | void D::h()
+ // VTABLE-D-NEXT: via vbtable index 1, vfptr at offset 0
+ // VTABLE-D-NEXT: 0 | void D::f()
+
+ virtual void f();
+ virtual void h();
+};
+
+void D::h() {}
+D d;
+
+namespace Test1 {
+
+struct X { int x; };
+
+// X and A get reordered in the layout since X doesn't have a vfptr while A has.
+struct Y : X, A { };
+
+struct Z : virtual Y {
+ // TEST1: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries).
+ // TEST1-NEXT: 0 | void A::f()
+ // TEST1-NEXT: 1 | void A::z()
+
+ // TEST1-NOT: VFTable indices for 'Test1::Z'
+};
+
+Z z;
+}
+
+namespace Test2 {
+
+struct X: virtual A, virtual B {
+ // TEST2: VFTable for 'Test2::X' (1 entries).
+ // TEST2-NEXT: 0 | void Test2::X::h()
+
+ // TEST2: VFTable for 'A' in 'Test2::X' (2 entries).
+ // TEST2-NEXT: 0 | void A::f()
+ // TEST2-NEXT: 1 | void A::z()
+
+ // TEST2: VFTable for 'B' in 'Test2::X' (1 entries).
+ // TEST2-NEXT: 0 | void B::g()
+
+ // TEST2: VFTable indices for 'Test2::X' (1 entries).
+ // TEST2-NEXT: 0 | void Test2::X::h()
+
+ virtual void h();
+};
+
+X x;
+}
+
+namespace Test3 {
+
+struct X : virtual A { };
+
+struct Y: virtual X {
+ // TEST3: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries).
+ // TEST3-NEXT: 0 | void A::f()
+ // TEST3-NEXT: 1 | void A::z()
+
+ // TEST3-NOT: VFTable indices for 'Test3::Y'
+};
+
+Y y;
+}
+
+namespace Test4 {
+
+struct X: virtual C {
+ // This one's interesting. C::f expects (A*) to be passed as 'this' and does
+ // ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk
+ // should pass a pointer to the end of X in order
+ // for ECX-=4 to point at the C part.
+
+ // TEST4: VFTable for 'A' in 'C' in 'Test4::X' (2 entries).
+ // TEST4-NEXT: 0 | void C::f()
+ // TEST4-NEXT: [this adjustment: 12 non-virtual]
+ // TEST4-NEXT: 1 | void A::z()
+
+ // TEST4-NOT: VFTable indices for 'Test4::X'
+};
+
+X x;
+}
+
+namespace Test5 {
+
+// New methods are added to the base's vftable.
+struct X : A {
+ virtual void g();
+};
+
+struct Y : virtual X {
+ // TEST5: VFTable for 'Test5::Y' (1 entries).
+ // TEST5-NEXT: 0 | void Test5::Y::h()
+
+ // TEST5: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries).
+ // TEST5-NEXT: 0 | void A::f()
+ // TEST5-NEXT: 1 | void A::z()
+ // TEST5-NEXT: 2 | void Test5::X::g()
+
+ // TEST5: VFTable indices for 'Test5::Y' (1 entries).
+ // TEST5-NEXT: 0 | void Test5::Y::h()
+
+ virtual void h();
+};
+
+Y y;
+}
+
+namespace Test6 {
+
+struct X : A, virtual Empty {
+ // TEST6: VFTable for 'A' in 'Test6::X' (2 entries).
+ // TEST6-NEXT: 0 | void A::f()
+ // TEST6-NEXT: 1 | void A::z()
+
+ // TEST6-NOT: VFTable indices for 'Test6::X'
+};
+
+X x;
+}
+
+namespace Test7 {
+
+struct X : C { };
+
+struct Y : virtual X {
+ // TEST7: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries).
+ // TEST7-NEXT: 0 | void C::f()
+ // TEST7-NEXT: [this adjustment: 12 non-virtual]
+ // TEST7-NEXT: 1 | void A::z()
+
+ // TEST7: Thunks for 'void C::f()' (1 entry).
+ // TEST7-NEXT: 0 | this adjustment: 12 non-virtual
+
+ // TEST7-NOT: VFTable indices for 'Test7::Y'
+};
+
+Y y;
+}
+
+namespace Test8 {
+
+// This is a typical diamond inheritance with a shared 'A' vbase.
+struct X : D, C {
+ // TEST8: VFTable for 'D' in 'Test8::X' (1 entries).
+ // TEST8-NEXT: 0 | void D::h()
+
+ // TEST8: VFTable for 'A' in 'D' in 'Test8::X' (2 entries).
+ // TEST8-NEXT: 0 | void Test8::X::f()
+ // TEST8-NEXT: 1 | void A::z()
+
+ // TEST8: VFTable indices for 'Test8::X' (1 entries).
+ // TEST8-NEXT: via vbtable index 1, vfptr at offset 0
+
+ virtual void f();
+};
+
+X x;
+}
+
+namespace Test9 {
+
+struct X : A { };
+
+struct Y : virtual X {
+ // TEST9-Y: VFTable for 'Test9::Y' (1 entries).
+ // TEST9-Y-NEXT: 0 | void Test9::Y::h()
+
+ // TEST9-Y: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries).
+ // TEST9-Y-NEXT: 0 | void A::f()
+ // TEST9-Y-NEXT: 1 | void A::z()
+
+ // TEST9-Y: VFTable indices for 'Test9::Y' (1 entries).
+ // TEST9-Y-NEXT: 0 | void Test9::Y::h()
+
+ virtual void h();
+};
+
+Y y;
+
+struct Z : Y, virtual B {
+ // TEST9-Z: VFTable for 'Test9::Y' in 'Test9::Z' (1 entries).
+ // TEST9-Z-NEXT: 0 | void Test9::Y::h()
+
+ // TEST9-Z: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries).
+ // TEST9-Z-NEXT: 0 | void A::f()
+ // TEST9-Z-NEXT: 1 | void A::z()
+
+ // TEST9-Z: VFTable for 'B' in 'Test9::Z' (1 entries).
+ // TEST9-Z-NEXT: 0 | void B::g()
+
+ // TEST9-Z-NOT: VFTable indices for 'Test9::Z'
+};
+
+Z z;
+
+struct W : Z, D, virtual A, virtual B {
+ // TEST9-W: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entries).
+ // TEST9-W-NEXT: 0 | void Test9::Y::h()
+
+ // TEST9-W: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries).
+ // TEST9-W-NEXT: 0 | void A::f()
+ // TEST9-W-NEXT: 1 | void A::z()
+
+ // TEST9-W: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entries).
+ // TEST9-W-NEXT: 0 | void B::g()
+
+ // TEST9-W: VFTable for 'D' in 'Test9::W' (1 entries).
+ // TEST9-W-NEXT: 0 | void D::h()
+
+ // TEST9-W: VFTable for 'A' in 'D' in 'Test9::W' (2 entries).
+ // TEST9-W-NEXT: 0 | void D::f()
+ // TEST9-W-NEXT: [this adjustment: -8 non-virtual]
+ // TEST9-W-NEXT: 1 | void A::z()
+
+ // TEST9-W: Thunks for 'void D::f()' (1 entry).
+ // TEST9-W-NEXT: 0 | this adjustment: -8 non-virtual
+
+ // TEST9-W-NOT: VFTable indices for 'Test9::W'
+};
+
+W w;
+
+struct T : Z, D, virtual A, virtual B {
+ ~T(); // Currently required to have correct record layout, see PR16406
+
+ // TEST9-T: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entries).
+ // TEST9-T-NEXT: 0 | void Test9::T::h()
+
+ // TEST9-T: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries).
+ // TEST9-T-NEXT: 0 | void Test9::T::f()
+ // TEST9-T-NEXT: 1 | void Test9::T::z()
+
+ // TEST9-T: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entries).
+ // TEST9-T-NEXT: 0 | void Test9::T::g()
+
+ // TEST9-T: VFTable for 'D' in 'Test9::T' (1 entries).
+ // TEST9-T-NEXT: 0 | void Test9::T::h()
+ // TEST9-T-NEXT: [this adjustment: -8 non-virtual]
+
+ // TEST9-T: Thunks for 'void Test9::T::h()' (1 entry).
+ // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual
+
+ // TEST9-T: VFTable for 'A' in 'D' in 'Test9::T' (2 entries).
+ // TEST9-T-NEXT: 0 | void Test9::T::f()
+ // TEST9-T-NEXT: [this adjustment: -16 non-virtual]
+ // TEST9-T-NEXT: 1 | void Test9::T::z()
+ // TEST9-T-NEXT: [this adjustment: -16 non-virtual]
+
+ // TEST9-T: Thunks for 'void Test9::T::f()' (1 entry).
+ // TEST9-T-NEXT: 0 | this adjustment: -16 non-virtual
+
+ // TEST9-T: Thunks for 'void Test9::T::z()' (1 entry).
+ // TEST9-T-NEXT: 0 | this adjustment: -16 non-virtual
+
+ // TEST9-T: VFTable indices for 'Test9::T' (4 entries).
+ // TEST9-T-NEXT: via vfptr at offset 0
+ // TEST9-T-NEXT: 0 | void Test9::T::h()
+ // TEST9-T-NEXT: via vbtable index 1, vfptr at offset 0
+ // TEST9-T-NEXT: 0 | void Test9::T::f()
+ // TEST9-T-NEXT: 1 | void Test9::T::z()
+ // TEST9-T-NEXT: via vbtable index 2, vfptr at offset 0
+ // TEST9-T-NEXT: 0 | void Test9::T::g()
+
+ virtual void f();
+ virtual void g();
+ virtual void h();
+ virtual void z();
+};
+
+T t;
+}
+
+namespace Test10 {
+struct X : virtual C, virtual A {
+ // TEST10: VFTable for 'A' in 'C' in 'Test10::X' (2 entries).
+ // TEST10-NEXT: 0 | void Test10::X::f()
+ // TEST10-NEXT: 1 | void A::z()
+
+ // TEST10: VFTable indices for 'Test10::X' (1 entries).
+ // TEST10-NEXT: via vbtable index 1, vfptr at offset 0
+ // TEST10-NEXT: 0 | void Test10::X::f()
+ virtual void f();
+};
+
+void X::f() {}
+X x;
+}
+
+namespace return_adjustment {
+
+struct X : virtual A {
+ virtual void f();
+};
+
+struct Y : virtual A, virtual X {
+ virtual void f();
+};
+
+struct Z {
+ virtual A* foo();
+};
+
+struct W : Z {
+ // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
+ // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
+ // RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual]
+ // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
+
+ // RET-W: VFTable indices for 'return_adjustment::W' (1 entries).
+ // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
+
+ virtual X* foo();
+};
+
+W y;
+
+struct T : W {
+ // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
+ // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
+ // RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual]
+ // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
+ // RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual]
+ // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
+
+ // RET-T: VFTable indices for 'return_adjustment::T' (1 entries).
+ // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
+
+ virtual Y* foo();
+};
+
+T t;
+}