Clang should only emit an available_externally vtable when there are no unused virtual inline functions. Currently, if such such a function is declared without inline inside the class, but is defined inline outside the class, Clang might emit the vtable as available_externally. This happens because Clang only considers the declarations of vtable entries, but not the definitions. This patch addresses this by inspecting the definitions in addition to the declarations.
560 lines
14 KiB
C++
560 lines
14 KiB
C++
// 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>
|
|
|
|
// 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();
|
|
};
|
|
|
|
// Inline definition outside body, so we can't emit vtable available_externally
|
|
// (see previous).
|
|
// CHECK-TEST10-DAG: @_ZTVN6Test101FE = external unnamed_addr constant
|
|
struct F : A {
|
|
void foo();
|
|
virtual void cat(); // inline outside body
|
|
};
|
|
inline void F::cat() {}
|
|
|
|
// 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 h(A& a) {
|
|
a.foo();
|
|
a.bar();
|
|
}
|
|
|
|
void g() {
|
|
A a;
|
|
h(a);
|
|
B b;
|
|
h(b);
|
|
C c;
|
|
h(c);
|
|
D d;
|
|
h(d);
|
|
E e;
|
|
h(e);
|
|
F f;
|
|
h(f);
|
|
}
|
|
|
|
} // 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 noundef 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 noundef i32 @_ZN13TestTemplates8TemplateIiE22thisShouldBeEmittedTooEi
|
|
|
|
template<class T>
|
|
struct Template {
|
|
Template();
|
|
virtual T foo(T val);
|
|
// CHECK-FORCE-EMIT-DAG: define linkonce_odr noundef 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 noundef 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 noundef 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 noundef 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 noundef 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 noundef 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>();
|
|
}
|
|
}
|