| // RUN: %clang_cc1 -std=c++98 %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s |
| // RUN: %clang_cc1 -std=c++11 %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s |
| // RUN: %clang_cc1 -std=c++1z %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s |
| |
| struct A { |
| virtual void f(); |
| virtual void f_const() const; |
| virtual void g(); |
| |
| A h(); |
| }; |
| |
| A g(); |
| |
| void f(A a, A *ap, A& ar) { |
| // This should not be a virtual function call. |
| |
| // CHECK: call void @_ZN1A1fEv(%struct.A* %a) |
| a.f(); |
| |
| // CHECK: call void % |
| ap->f(); |
| |
| // CHECK: call void % |
| ar.f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| A().f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| g().f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| a.h().f(); |
| |
| // CHECK: call void @_ZNK1A7f_constEv |
| a.f_const(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| (a).f(); |
| } |
| |
| struct D : A { virtual void g(); }; |
| struct XD { D d; }; |
| |
| D gd(); |
| |
| void fd(D d, XD xd, D *p) { |
| // CHECK: call void @_ZN1A1fEv(%struct.A* |
| d.f(); |
| |
| // CHECK: call void @_ZN1D1gEv(%struct.D* |
| d.g(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| D().f(); |
| |
| // CHECK: call void @_ZN1D1gEv |
| D().g(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| gd().f(); |
| |
| // CHECK: call void @_ZNK1A7f_constEv |
| d.f_const(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| (d).f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| (true, d).f(); |
| |
| // CHECK: call void @_ZN1D1gEv |
| (true, d).g(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| xd.d.f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| XD().d.f(); |
| |
| // CHECK: call void @_ZN1A1fEv |
| D XD::*mp; |
| (xd.*mp).f(); |
| |
| // CHECK: call void @_ZN1D1gEv |
| (xd.*mp).g(); |
| |
| // Can't devirtualize this; we have no guarantee that p points to a D here, |
| // due to the "single object is considered to be an array of one element" |
| // rule. |
| // CHECK: call void % |
| p[0].f(); |
| |
| // FIXME: We can devirtualize this, by C++1z [expr.add]/6 (if the array |
| // element type and the pointee type are not similar, behavior is undefined). |
| // CHECK: call void % |
| p[1].f(); |
| } |
| |
| struct B { |
| virtual void f(); |
| ~B(); |
| |
| B h(); |
| }; |
| |
| |
| void f() { |
| // CHECK: call void @_ZN1B1fEv |
| B().f(); |
| |
| // CHECK: call void @_ZN1B1fEv |
| B().h().f(); |
| } |
| |
| namespace test2 { |
| struct foo { |
| virtual void f(); |
| virtual ~foo(); |
| }; |
| |
| struct bar : public foo { |
| virtual void f(); |
| virtual ~bar(); |
| }; |
| |
| void f(bar *b) { |
| // CHECK: call void @_ZN5test23foo1fEv |
| // CHECK: call %"struct.test2::foo"* @_ZN5test23fooD1Ev |
| b->foo::f(); |
| b->foo::~foo(); |
| } |
| } |
| |
| namespace test3 { |
| // Test that we don't crash in this case. |
| struct B { |
| }; |
| struct D : public B { |
| }; |
| void f(D d) { |
| // CHECK-LABEL: define void @_ZN5test31fENS_1DE |
| d.B::~B(); |
| } |
| } |
| |
| namespace test4 { |
| struct Animal { |
| virtual void eat(); |
| }; |
| struct Fish : Animal { |
| virtual void eat(); |
| }; |
| struct Wrapper { |
| Fish fish; |
| }; |
| extern Wrapper *p; |
| void test() { |
| // CHECK: call void @_ZN5test44Fish3eatEv |
| p->fish.eat(); |
| } |
| } |
| |
| // Do not devirtualize to pure virtual function calls. |
| namespace test5 { |
| struct X { |
| virtual void f() = 0; |
| }; |
| struct Y {}; |
| // CHECK-LABEL: define {{.*}} @_ZN5test51f |
| void f(Y &y, X Y::*p) { |
| // CHECK-NOT: call {{.*}} @_ZN5test51X1fEv |
| // CHECK: call void % |
| (y.*p).f(); |
| }; |
| |
| struct Z final { |
| virtual void f() = 0; |
| }; |
| // CHECK-LABEL: define {{.*}} @_ZN5test51g |
| void g(Z &z) { |
| // CHECK-NOT: call {{.*}} @_ZN5test51Z1fEv |
| // CHECK: call void % |
| z.f(); |
| } |
| |
| struct Q { |
| virtual void f() final = 0; |
| }; |
| // CHECK-LABEL: define {{.*}} @_ZN5test51h |
| void h(Q &q) { |
| // CHECK-NOT: call {{.*}} @_ZN5test51Q1fEv |
| // CHECK: call void % |
| q.f(); |
| } |
| } |