| // RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o %t.ll -O1 -disable-llvm-optzns -fms-extensions -fstrict-vtable-pointers |
| // RUN: %clang_cc1 %s -triple i686-pc-win32 -emit-llvm -o %t.ms.ll -O1 -disable-llvm-optzns -fms-extensions -fstrict-vtable-pointers |
| // FIXME: Assume load should not require -fstrict-vtable-pointers |
| |
| // RUN: FileCheck --check-prefix=CHECK1 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK2 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK3 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK4 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK-MS --input-file=%t.ms.ll %s |
| // RUN: FileCheck --check-prefix=CHECK6 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK7 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK8 --input-file=%t.ll %s |
| // RUN: FileCheck --check-prefix=CHECK9 --input-file=%t.ll %s |
| namespace test1 { |
| |
| struct A { |
| A(); |
| virtual void foo(); |
| }; |
| |
| struct B : A { |
| virtual void foo(); |
| }; |
| |
| void g(A *a) { a->foo(); } |
| |
| // CHECK1-LABEL: define void @_ZN5test14fooAEv() |
| // CHECK1: call void @_ZN5test11AC1Ev(%"struct.test1::A"* |
| // CHECK1: %[[VTABLE:.*]] = load i8**, i8*** %{{.*}} |
| // CHECK1: %[[CMP:.*]] = icmp eq i8** %[[VTABLE]], getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTVN5test11AE, i64 0, i64 2) |
| // CHECK1: call void @llvm.assume(i1 %[[CMP]]) |
| // CHECK1-LABEL: } |
| |
| void fooA() { |
| A a; |
| g(&a); |
| } |
| |
| // CHECK1-LABEL: define void @_ZN5test14fooBEv() |
| // CHECK1: call void @_ZN5test11BC1Ev(%"struct.test1::B"* %{{.*}}) |
| // CHECK1: %[[VTABLE:.*]] = load i8**, i8*** %{{.*}} |
| // CHECK1: %[[CMP:.*]] = icmp eq i8** %[[VTABLE]], getelementptr inbounds ([3 x i8*], [3 x i8*]* @_ZTVN5test11BE, i64 0, i64 2) |
| // CHECK1: call void @llvm.assume(i1 %[[CMP]]) |
| // CHECK1-LABEL: } |
| |
| void fooB() { |
| B b; |
| g(&b); |
| } |
| // there should not be any assumes in the ctor that calls base ctor |
| // CHECK1-LABEL: define linkonce_odr void @_ZN5test11BC2Ev(%"struct.test1::B"* |
| // CHECK1-NOT: @llvm.assume( |
| // CHECK1-LABEL: } |
| } |
| namespace test2 { |
| struct A { |
| A(); |
| virtual void foo(); |
| }; |
| |
| struct B { |
| B(); |
| virtual void bar(); |
| }; |
| |
| struct C : A, B { |
| C(); |
| virtual void foo(); |
| }; |
| void g(A *a) { a->foo(); } |
| void h(B *b) { b->bar(); } |
| |
| // CHECK2-LABEL: define void @_ZN5test24testEv() |
| // CHECK2: call void @_ZN5test21CC1Ev(%"struct.test2::C"* |
| // CHECK2: %[[VTABLE:.*]] = load i8**, i8*** {{.*}} |
| // CHECK2: %[[CMP:.*]] = icmp eq i8** %[[VTABLE]], getelementptr inbounds ([6 x i8*], [6 x i8*]* @_ZTVN5test21CE, i64 0, i64 2) |
| // CHECK2: call void @llvm.assume(i1 %[[CMP]]) |
| |
| // CHECK2: %[[V2:.*]] = bitcast %"struct.test2::C"* %{{.*}} to i8* |
| // CHECK2: %[[ADD_PTR:.*]] = getelementptr inbounds i8, i8* %[[V2]], i64 8 |
| // CHECK2: %[[V3:.*]] = bitcast i8* %[[ADD_PTR]] to i8*** |
| // CHECK2: %[[VTABLE2:.*]] = load i8**, i8*** %[[V3]] |
| // CHECK2: %[[CMP2:.*]] = icmp eq i8** %[[VTABLE2]], getelementptr inbounds ([6 x i8*], [6 x i8*]* @_ZTVN5test21CE, i64 0, i64 5) |
| // CHECK2: call void @llvm.assume(i1 %[[CMP2]]) |
| |
| // CHECK2: call void @_ZN5test21gEPNS_1AE( |
| // CHECK2-LABEL: } |
| |
| void test() { |
| C c; |
| g(&c); |
| h(&c); |
| } |
| } |
| |
| namespace test3 { |
| struct A { |
| A(); |
| }; |
| |
| struct B : A { |
| B(); |
| virtual void foo(); |
| }; |
| |
| struct C : virtual A, B { |
| C(); |
| virtual void foo(); |
| }; |
| void g(B *a) { a->foo(); } |
| |
| // CHECK3-LABEL: define void @_ZN5test34testEv() |
| // CHECK3: call void @_ZN5test31CC1Ev(%"struct.test3::C"* |
| // CHECK3: %[[CMP:.*]] = icmp eq i8** %{{.*}}, getelementptr inbounds ([4 x i8*], [4 x i8*]* @_ZTVN5test31CE, i64 0, i64 3) |
| // CHECK3: call void @llvm.assume(i1 %[[CMP]]) |
| // CHECK3-LABLEL: } |
| void test() { |
| C c; |
| g(&c); |
| } |
| } // test3 |
| |
| namespace test4 { |
| struct A { |
| A(); |
| virtual void foo(); |
| }; |
| |
| struct B : virtual A { |
| B(); |
| virtual void foo(); |
| }; |
| struct C : B { |
| C(); |
| virtual void foo(); |
| }; |
| |
| void g(C *c) { c->foo(); } |
| |
| // CHECK4-LABEL: define void @_ZN5test44testEv() |
| // CHECK4: call void @_ZN5test41CC1Ev(%"struct.test4::C"* |
| // CHECK4: %[[VTABLE:.*]] = load i8**, i8*** %{{.*}} |
| // CHECK4: %[[CMP:.*]] = icmp eq i8** %[[VTABLE]], getelementptr inbounds ([5 x i8*], [5 x i8*]* @_ZTVN5test41CE, i64 0, i64 4) |
| // CHECK4: call void @llvm.assume(i1 %[[CMP]] |
| |
| // CHECK4: %[[VTABLE2:.*]] = load i8**, i8*** %{{.*}} |
| // CHECK4: %[[CMP2:.*]] = icmp eq i8** %[[VTABLE2]], getelementptr inbounds ([5 x i8*], [5 x i8*]* @_ZTVN5test41CE, i64 0, i64 4) |
| // CHECK4: call void @llvm.assume(i1 %[[CMP2]]) |
| // CHECK4-LABEL: } |
| |
| void test() { |
| C c; |
| g(&c); |
| } |
| } // test4 |
| |
| namespace testMS { |
| |
| struct __declspec(novtable) S { |
| virtual void foo(); |
| }; |
| |
| void g(S &s) { s.foo(); } |
| |
| // if struct has novtable specifier, then we can't generate assumes |
| // CHECK-MS-LABEL: define void @"\01?test@testMS@@YAXXZ"() |
| // CHECK-MS: call x86_thiscallcc %"struct.testMS::S"* @"\01??0S@testMS@@QAE@XZ"( |
| // CHECK-MS-NOT: @llvm.assume |
| // CHECK-MS-LABEL: } |
| |
| void test() { |
| S s; |
| g(s); |
| } |
| |
| } // testMS |
| |
| namespace test6 { |
| struct A { |
| A(); |
| virtual void foo(); |
| virtual ~A() {} |
| }; |
| struct B : A { |
| B(); |
| }; |
| // FIXME: Because A's vtable is external, and no virtual functions are hidden, |
| // it's safe to generate assumption loads. |
| // CHECK6-LABEL: define void @_ZN5test61gEv() |
| // CHECK6: call void @_ZN5test61AC1Ev( |
| // CHECK6-NOT: call void @llvm.assume( |
| |
| // We can't emit assumption loads for B, because if we would refer to vtable |
| // it would refer to functions that will not be able to find (like implicit |
| // inline destructor). |
| |
| // CHECK6-LABEL: call void @_ZN5test61BC1Ev( |
| // CHECK6-NOT: call void @llvm.assume( |
| // CHECK6-LABEL: } |
| void g() { |
| A *a = new A; |
| B *b = new B; |
| } |
| } |
| |
| namespace test7 { |
| // Because A's key function is defined here, vtable is generated in this TU |
| // CHECK7: @_ZTVN5test71AE = unnamed_addr constant |
| struct A { |
| A(); |
| virtual void foo(); |
| virtual void bar(); |
| }; |
| void A::foo() {} |
| |
| // CHECK7-LABEL: define void @_ZN5test71gEv() |
| // CHECK7: call void @_ZN5test71AC1Ev( |
| // CHECK7: call void @llvm.assume( |
| // CHECK7-LABEL: } |
| void g() { |
| A *a = new A(); |
| a->bar(); |
| } |
| } |
| |
| namespace test8 { |
| |
| struct A { |
| virtual void foo(); |
| virtual void bar(); |
| }; |
| |
| // CHECK8-DAG: @_ZTVN5test81BE = available_externally unnamed_addr constant |
| struct B : A { |
| B(); |
| void foo(); |
| void bar(); |
| }; |
| |
| // CHECK8-DAG: @_ZTVN5test81CE = linkonce_odr unnamed_addr constant |
| struct C : A { |
| C(); |
| void bar(); |
| void foo() {} |
| }; |
| inline void C::bar() {} |
| |
| struct D : A { |
| D(); |
| void foo(); |
| void inline bar(); |
| }; |
| void D::bar() {} |
| |
| // CHECK8-DAG: @_ZTVN5test81EE = linkonce_odr unnamed_addr constant |
| struct E : A { |
| E(); |
| }; |
| |
| // CHECK8-LABEL: define void @_ZN5test81bEv() |
| // CHECK8: call void @llvm.assume( |
| // CHECK8-LABEL: } |
| void b() { |
| B b; |
| b.bar(); |
| } |
| |
| // FIXME: C has inline virtual functions which prohibits as from generating |
| // assumption loads, but because vtable is generated in this TU (key function |
| // defined here) it would be correct to refer to it. |
| // CHECK8-LABEL: define void @_ZN5test81cEv() |
| // CHECK8-NOT: call void @llvm.assume( |
| // CHECK8-LABEL: } |
| void c() { |
| C c; |
| c.bar(); |
| } |
| |
| // FIXME: We could generate assumption loads here. |
| // CHECK8-LABEL: define void @_ZN5test81dEv() |
| // CHECK8-NOT: call void @llvm.assume( |
| // CHECK8-LABEL: } |
| void d() { |
| D d; |
| d.bar(); |
| } |
| |
| // CHECK8-LABEL: define void @_ZN5test81eEv() |
| // CHECK8: call void @llvm.assume( |
| // CHECK8-LABEL: } |
| void e() { |
| E e; |
| e.bar(); |
| } |
| } |
| |
| namespace test9 { |
| |
| struct S { |
| S(); |
| __attribute__((visibility("hidden"))) virtual void doStuff(); |
| }; |
| |
| // CHECK9-LABEL: define void @_ZN5test94testEv() |
| // CHECK9-NOT: @llvm.assume( |
| // CHECK9: } |
| void test() { |
| S *s = new S(); |
| s->doStuff(); |
| delete s; |
| } |
| } |
| |