|  | // 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(); | 
|  | } | 
|  | } |