llvm-project/clang/test/CodeGenCXX/vtable-available-externally...

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

549 lines
14 KiB
C++
Raw Normal View History

// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -emit-llvm -o %t
// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -O2 -disable-llvm-passes -emit-llvm -o %t.opt
// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -std=c++98 -O2 -disable-llvm-passes -emit-llvm -o %t.vtable -fforce-emit-vtables -fstrict-vtable-pointers -mconstructor-aliases
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST1 %s < %t
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST2 %s < %t
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST5 %s < %t
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST8 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST9 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST10 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST11 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST12 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST13 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST14 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST15 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST16 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-TEST17 %s < %t.opt
// RUN: FileCheck -allow-deprecated-dag-overlap --check-prefix=CHECK-FORCE-EMIT %s < %t.vtable
#include <typeinfo>
Don't emit an available_externally vtable pointing to linkonce_odr funcs. This fixes pr13124. From the discussion at http://lists.cs.uiuc.edu/pipermail/cfe-dev/2012-June/022606.html we know that we cannot make funcions in a weak_odr vtable also weak_odr. They should remain linkonce_odr. The side effect is that we cannot emit a available_externally vtable unless we also emit a copy of the function. This also has an issue: If codegen is going to output a function, sema has to mark it used. Given llvm.org/pr9114, it looks like sema cannot be more aggressive at marking functions used because of vtables. This leaves us with a few unpleasant options: * Marking functions in vtables used if possible. This sounds a bit sloppy, so we should avoid it. * Producing available_externally vtables only when all the functions in it are already used or weak_odr. This would cover cases like -------------------- struct foo { virtual ~foo(); }; struct bar : public foo { virtual void zed(); }; void f() { foo *x(new bar); delete x; } void g(bar *x) { x->~bar(); // force the destructor to be used } -------------------------- and ---------------------------------- template<typename T> struct bar { virtual ~bar(); }; template<typename T> bar<T>::~bar() { } // make the destructor weak_odr instead of linkonce_odr extern template class bar<int>; void f() { bar<int> *x(new bar<int>); delete x; } ---------------------------- These look like corner cases, so it is unclear if it is worth it. * And finally: Just nuke this optimization. That is what this patch implements. llvm-svn: 189852
2013-09-04 05:05:13 +08:00
// CHECK-TEST1: @_ZTVN5Test11AE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN5Test11AE = available_externally unnamed_addr constant
namespace Test1 {
struct A {
A();
virtual void f();
virtual ~A() { }
};
A::A() { }
void f(A* a) {
a->f();
};
// CHECK-LABEL: define void @_ZN5Test11gEv
// CHECK: call void @_ZN5Test11A1fEv
void g() {
A a;
f(&a);
}
}
// Test2::A's key function (f) is defined in this translation unit, but when
// we're doing codegen for the typeid(A) call, we don't know that yet.
// This tests mainly that the typeinfo and typename constants have their linkage
// updated correctly.
// CHECK-TEST2: @_ZTSN5Test21AE = constant
// CHECK-TEST2: @_ZTIN5Test21AE = constant
// CHECK-TEST2: @_ZTVN5Test21AE = unnamed_addr constant
namespace Test2 {
struct A {
virtual void f();
};
const std::type_info &g() {
return typeid(A);
};
void A::f() { }
}
// Test that we don't assert on this test.
namespace Test3 {
struct A {
virtual void f();
virtual ~A() { }
};
struct B : A {
B();
virtual void f();
};
B::B() { }
void g(A* a) {
a->f();
};
}
// PR9114, test that we don't try to instantiate RefPtr<Node>.
namespace Test4 {
template <class T> struct RefPtr {
T* p;
~RefPtr() {
p->deref();
}
};
struct A {
virtual ~A();
};
struct Node;
struct B : A {
virtual void deref();
RefPtr<Node> m;
};
void f() {
RefPtr<B> b;
}
}
// PR9130, test that we emit a definition of A::f.
// CHECK-TEST5-LABEL: define linkonce_odr void @_ZN5Test51A1fEv
namespace Test5 {
struct A {
virtual void f() { }
};
struct B : A {
virtual ~B();
};
B::~B() { }
}
// Check that we don't assert on this test.
namespace Test6 {
struct A {
virtual ~A();
int a;
};
struct B {
virtual ~B();
int b;
};
struct C : A, B {
C();
};
struct D : C {
virtual void f();
D();
};
D::D() { }
}
namespace Test7 {
struct c1 {};
struct c10 : c1{
virtual void foo ();
};
struct c11 : c10, c1{
virtual void f6 ();
};
struct c28 : virtual c11{
void f6 ();
};
}
namespace Test8 {
// CHECK-TEST8: @_ZTVN5Test81YE = available_externally unnamed_addr constant
// vtable for X is not generated because there are no stores here
struct X {
X();
virtual void foo();
};
struct Y : X {
void foo();
};
void g(X* p) { p->foo(); }
void f() {
Y y;
g(&y);
X x;
g(&x);
}
} // Test8
namespace Test9 {
// All virtual functions are outline, so we can assume that it will
// be generated in translation unit where foo is defined.
// CHECK-TEST9-DAG: @_ZTVN5Test91AE = available_externally unnamed_addr constant
// CHECK-TEST9-DAG: @_ZTVN5Test91BE = available_externally unnamed_addr constant
struct A {
virtual void foo();
virtual void bar();
};
void A::bar() {}
struct B : A {
void foo();
};
void g() {
A a;
a.foo();
B b;
b.foo();
}
} // Test9
namespace Test10 {
// because A's key function is defined here, vtable is generated in this TU
// CHECK-TEST10-DAG: @_ZTVN6Test101AE = unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101AE = unnamed_addr constant
struct A {
virtual void foo();
virtual void bar();
};
void A::foo() {}
// Because key function is inline we will generate vtable as linkonce_odr.
// CHECK-TEST10-DAG: @_ZTVN6Test101DE = linkonce_odr unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101DE = linkonce_odr unnamed_addr constant
struct D : A {
void bar();
};
inline void D::bar() {}
// Because B has outline all virtual functions, we can refer to them.
// CHECK-TEST10-DAG: @_ZTVN6Test101BE = available_externally unnamed_addr constant
struct B : A {
void foo();
void bar();
};
// C's key function (car) is outline, but C has inline virtual function so we
// can't guarantee that we will be able to refer to bar from name
// so (at the moment) we can't emit vtable available_externally.
// CHECK-TEST10-DAG: @_ZTVN6Test101CE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101CE = available_externally unnamed_addr constant
struct C : A {
void bar() {} // defined in body - not key function
virtual inline void gar(); // inline in body - not key function
virtual void car();
};
// no key function, vtable will be generated everywhere it will be used
// CHECK-TEST10-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
struct E : A {};
void g(A& a) {
a.foo();
a.bar();
}
void f() {
A a;
g(a);
B b;
g(b);
C c;
g(c);
D d;
g(d);
E e;
g(e);
}
} // Test10
namespace Test11 {
struct D;
// Can emit C's vtable available_externally.
// CHECK-TEST11: @_ZTVN6Test111CE = available_externally unnamed_addr constant
struct C {
virtual D& operator=(const D&);
};
// Can emit D's vtable available_externally.
// CHECK-TEST11: @_ZTVN6Test111DE = available_externally unnamed_addr constant
struct D : C {
virtual void key();
};
D f();
void g(D& a) {
C c;
c = a;
a.key();
a.key();
}
void g() {
D d;
d = f();
g(d);
}
} // Test 11
namespace Test12 {
// CHECK-TEST12: @_ZTVN6Test121AE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test121AE = available_externally unnamed_addr constant
struct A {
virtual void foo();
virtual ~A() {}
};
// CHECK-TEST12: @_ZTVN6Test121BE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test121BE = available_externally unnamed_addr constant
struct B : A {
void foo();
};
void g() {
A a;
a.foo();
B b;
b.foo();
}
}
namespace Test13 {
// CHECK-TEST13-DAG: @_ZTVN6Test131AE = available_externally unnamed_addr constant
// CHECK-TEST13-DAG: @_ZTVN6Test131BE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test131AE = available_externally unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test131BE = available_externally unnamed_addr constant
struct A {
virtual ~A();
};
struct B : A {
virtual void f();
void operator delete(void *);
~B() {}
};
void g() {
A *b = new B;
}
}
namespace Test14 {
// CHECK-TEST14: @_ZTVN6Test141AE = available_externally unnamed_addr constant
struct A {
virtual void f();
void operator delete(void *);
~A();
};
void g() {
A *b = new A;
delete b;
}
}
namespace Test15 {
// In this test D's vtable has two slots for function f(), but uses only one,
// so the second slot is set to null.
// CHECK-TEST15: @_ZTVN6Test151DE = available_externally unnamed_addr constant
struct A { virtual void f() {} };
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {
virtual void g();
void f();
};
void test() {
D * d = new D;
d->f();
}
}
namespace Test16 {
// S has virtual method that is hidden, because of it we can't
// generate available_externally vtable for it.
// CHECK-TEST16-DAG: @_ZTVN6Test161SE = external unnamed_addr constant
// CHECK-TEST16-DAG: @_ZTVN6Test162S2E = available_externally
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test161SE = external unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test162S2E = available_externally
struct S {
__attribute__((visibility("hidden"))) virtual void doStuff();
};
struct S2 {
virtual void doStuff();
__attribute__((visibility("hidden"))) void unused();
};
void test() {
S *s = new S;
s->doStuff();
S2 *s2 = new S2;
s2->doStuff();
}
}
namespace Test17 {
// This test checks if we emit vtables opportunistically.
// CHECK-TEST17-DAG: @_ZTVN6Test171AE = available_externally
// CHECK-TEST17-DAG: @_ZTVN6Test171BE = external
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test171AE = available_externally
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test171BE = available_externally
// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test171BD2Ev(
// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test171BD0Ev(
struct A {
virtual void key();
virtual void bar() {}
};
// We won't gonna use deleting destructor for this type, which will disallow
// emitting vtable as available_externally
struct B {
virtual void key();
virtual ~B() {}
};
void testcaseA() {
A a;
a.bar(); // this forces to emit definition of bar
}
void testcaseB() {
B b; // This only forces emitting of complete object destructor
}
} // namespace Test17
namespace Test18 {
// Here vtable will be only emitted because it is referenced by assume-load
// after the Derived construction.
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test187DerivedE = linkonce_odr unnamed_addr constant {{.*}} @_ZTIN6Test187DerivedE {{.*}} @_ZN6Test184Base3funEv {{.*}} @_ZN6Test184BaseD2Ev {{.*}} @_ZN6Test187DerivedD0Ev
// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test187DerivedD0Ev
// CHECK-FORCE-EMIT-DAG: define linkonce_odr void @_ZN6Test184BaseD2Ev
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN6Test184Base3funEv
// CHECK-FORCE-EMIT-DAG: @_ZTIN6Test187DerivedE = linkonce_odr constant
struct Base {
virtual int fun() { return 42; }
virtual ~Base() { }
};
struct Derived : Base {
Derived();
};
int foo() {
Derived *der = new Derived();
return der->fun();
}
}
namespace TestTemplates {
// CHECK-FORCE-EMIT-DAG: @_ZTVN13TestTemplates8TemplateIiEE = linkonce_odr unnamed_addr constant {{.*}} @_ZTIN13TestTemplates8TemplateIiEE {{.*}} @_ZN13TestTemplates8TemplateIiE3fooEi {{.*}}@_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi {{.*}}@_ZN13TestTemplates8TemplateIiED1Ev {{.*}}@_ZN13TestTemplates8TemplateIiED0Ev
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi
template<class T>
struct Template {
Template();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~Template();
};
struct NonTemplate {
typedef int T;
NonTemplate();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates11NonTemplate22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~NonTemplate();
};
// CHECK-FORCE-EMIT-DAG: @_ZTVN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiEE = linkonce_odr {{.*}} @_ZTIN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiEE {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE3fooEi {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE22thisShouldBeEmittedTooEi {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiED1Ev {{.*}} @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiED0Ev
struct OuterNonTemplate {
template<class T>
struct NestedTemplateInNonTemplate {
NestedTemplateInNonTemplate();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates16OuterNonTemplate27NestedTemplateInNonTemplateIiE22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~NestedTemplateInNonTemplate();
};
struct NestedNonTemplateInNonTemplate {
typedef int T;
NestedNonTemplateInNonTemplate();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates16OuterNonTemplate30NestedNonTemplateInNonTemplate22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~NestedNonTemplateInNonTemplate();
};
};
template<class>
struct OuterTemplate {
template<class T>
struct NestedTemplateInTemplate {
NestedTemplateInTemplate();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates13OuterTemplateIlE24NestedTemplateInTemplateIiE22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~NestedTemplateInTemplate();
};
struct NestedNonTemplateInTemplate {
typedef int T;
NestedNonTemplateInTemplate();
virtual T foo(T val);
// CHECK-FORCE-EMIT-DAG: define linkonce_odr i32 @_ZN13TestTemplates13OuterTemplateIlE27NestedNonTemplateInTemplate22thisShouldBeEmittedTooEi
virtual T thisShouldBeEmittedToo(T val) { return val; }
virtual ~NestedNonTemplateInTemplate();
};
};
template<class T>
int use() {
T *ptr = new T();
return ptr->foo(42);
}
void test() {
use<Template<int> >();
use<OuterTemplate<long>::NestedTemplateInTemplate<int> >();
use<OuterNonTemplate::NestedTemplateInNonTemplate<int> >();
use<NonTemplate>();
use<OuterTemplate<long>::NestedNonTemplateInTemplate>();
use<OuterNonTemplate::NestedNonTemplateInNonTemplate>();
}
}