1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
// For a class that has a vtable (and hence, also has a typeinfo symbol for
// RTTI), if a user marks either:
//
// (a) the entire class as dllexport (dllimport), or
// (b) all non-inline virtual methods of the class as dllexport (dllimport)
//
// then Clang must export the vtable and typeinfo symbol from the TU where they
// are defined (the TU containing the definition of the Itanium C++ ABI "key
// function"), and must import them in other modules where they are referenced.
//
// Conversely to point (b), if some (but not all) of the non-inline virtual
// methods of a class are marked as dllexport (dllimport), then the vtable and
// typeinfo symbols must not be exported (imported). This will result in a
// link-time failure when linking the importing module. This link-time failure
// is the desired behavior, because the Microsoft toolchain also gets a
// link-time failure in these cases (and since __declspec(dllexport)
// (__declspec(dllimport)) is a Microsoft extension, our intention is to mimic
// that Microsoft behavior).
//
// Side note: It is within the bodies of constructors (and in some cases,
// destructors) that the vtable is explicitly referenced. In case (a) above,
// where the entire class is exported (imported), then all constructors (among
// other things) are exported (imported). So for that situation, an importing
// module for a well-formed program will not actually reference the vtable,
// since constructor calls will all be to functions external to that module
// (and imported into it, from the exporting module). I.e., all vtable
// references will be in that module where the constructor and destructor
// bodies are, therefore, there will not be a need to import the vtable in
// that case.
//
// This test contains 6 test classes:
// 2 for point (a),
// 2 for point (b),
// and 2 negative tests for the converse of point (b).
//
// The two tests for each of these points are one for importing, and one for
// exporting.
// RUN: %clang_cc1 -no-opaque-pointers -I%S -fdeclspec -triple x86_64-unknown-windows-itanium -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s -check-prefix=WI
// RUN: %clang_cc1 -no-opaque-pointers -I%S -fdeclspec -triple x86_64-scei-windows-itanium -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s --check-prefixes=PS4,SCEI_WI
// RUN: %clang_cc1 -no-opaque-pointers -I%S -fdeclspec -triple x86_64-scei-ps4 -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s --check-prefixes=PS4,SCEI_PS4
// RUN: %clang_cc1 -no-opaque-pointers -I%S -fdeclspec -triple x86_64-sie-ps5 -emit-llvm -o - %s -fhalf-no-semantic-interposition | FileCheck %s --check-prefixes=PS4,SCEI_PS4
#include <typeinfo>
// Case (a) -- Import Aspect
// The entire class is imported. The typeinfo symbol must also be imported,
// but the vtable will not be referenced, and so does not need to be imported
// (as described in the "Side note", above).
//
// PS4-DAG: @_ZTI10FullImport = {{.*}}dllimport
// WI-DAG: @_ZTI10FullImport = external dllimport constant i8*
struct __declspec(dllimport) FullImport
{
virtual void getId() {}
virtual void Bump();
virtual void Decrement();
};
// 'FullImport::Bump()' is the key function, so the vtable and typeinfo symbol
// of 'FullImport' will be defined in the TU that contains the definition of
// 'Bump()' (and they must be exported from there).
void FullImportTest()
{
typeid(FullImport).name();
}
///////////////////////////////////////////////////////////////////
// Case (a) -- Export Aspect
// The entire class is exported. The vtable and typeinfo symbols must also be
// exported,
//
// PS4-DAG: @_ZTV10FullExport ={{.*}}dllexport
// WI-DAG: @_ZTV10FullExport ={{.*}}dllexport
// PS4-DAG: @_ZTI10FullExport ={{.*}}dllexport
// WI-DAG: @_ZTI10FullExport = dso_local dllexport constant {
struct __declspec(dllexport) FullExport // Easy case: Entire class is exported.
{
virtual void getId() {}
virtual void Bump();
virtual void Decrement();
};
// This is the key function of the class 'FullExport', so the vtable and
// typeinfo symbols of 'FullExport' will be defined in this TU, and so they
// must be exported from this TU.
void FullExport::Bump()
{
typeid(FullExport).name();
}
///////////////////////////////////////////////////////////////////
// Case (b) -- Import Aspect
// The class as a whole is not imported, but all non-inline virtual methods of
// the class are, so the vtable and typeinfo symbol must be imported.
//
// PS4-DAG: @_ZTV9FooImport ={{.*}}dllimport
// WI-DAG: @_ZTV9FooImport = linkonce_odr dso_local unnamed_addr constant {
// PS4-DAG: @_ZTI9FooImport ={{.*}}dllimport
// WI-DAG: @_ZTI9FooImport = linkonce_odr dso_local constant {
struct FooImport
{
virtual void getId() const {}
__declspec(dllimport) virtual void Bump();
__declspec(dllimport) virtual void Decrement();
};
// 'FooImport::Bump()' is the key function, so the vtable and typeinfo symbol
// of 'FooImport' will be defined in the TU that contains the definition of
// 'Bump()' (and they must be exported from there). Here, we will reference
// the vtable and typeinfo symbol, so we must also import them.
void importTest()
{
typeid(FooImport).name();
}
///////////////////////////////////////////////////////////////////
// Case (b) -- Export Aspect
// The class as a whole is not exported, but all non-inline virtual methods of
// the class are, so the vtable and typeinfo symbol must be exported.
//
// PS4-DAG: @_ZTV9FooExport ={{.*}}dllexport
// WI-DAG: @_ZTV9FooExport = dso_local unnamed_addr constant {
// PS4-DAG: @_ZTI9FooExport ={{.*}}dllexport
// WI-DAG: @_ZTI9FooExport = dso_local constant {
struct FooExport
{
virtual void getId() const {}
__declspec(dllexport) virtual void Bump();
__declspec(dllexport) virtual void Decrement();
};
// This is the key function of the class 'FooExport', so the vtable and
// typeinfo symbol of 'FooExport' will be defined in this TU, and so they must
// be exported from this TU.
void FooExport::Bump()
{
FooImport f;
typeid(FooExport).name();
}
///////////////////////////////////////////////////////////////////
// The tests below verify that the associated vtable and typeinfo symbols are
// not imported/exported. These are the converse of case (b).
//
// Note that ultimately, if the module doing the importing calls a constructor
// of the class with the vtable, or makes a reference to the typeinfo symbol of
// the class, then this will result in an unresolved reference (to the vtable
// or typeinfo symbol) when linking the importing module, and thus a link-time
// failure.
//
// Note that with the Microsoft toolchain there will also be a link-time
// failure when linking the module doing the importing. With the Microsoft
// toolchain, it will be an unresolved reference to the method 'Decrement()'
// of the approriate class, rather than to the vtable or typeinfo symbol of
// the class, because Microsoft defines the vtable and typeinfo symbol (weakly)
// everywhere they are used.
// Converse of case (b) -- Import Aspect
// The class as a whole is not imported, and not all non-inline virtual methods
// are imported, so the vtable and typeinfo symbol are not to be imported.
//
// CHECK-PS4: @_ZTV11FooNoImport = external dso_local unnamed_addr constant {
// CHECK-WI: @_ZTV11FooNoImport = linkonce_odr dso_local unnamed_addr constant {
// CHECK-PS4: @_ZTI11FooNoImport = external dso_local constant i8*{{$}}
// CHECK-WI: @_ZTI11FooNoImport = linkonce_odr dso_local constant {
struct FooNoImport
{
virtual void getId() const {}
__declspec(dllimport) virtual void Bump();
virtual void Decrement(); // Not imported.
int mCounter;
};
void importNegativeTest()
{
FooNoImport f;
typeid(FooNoImport).name();
}
///////////////////////////////////////////////////////////////////
// Converse of case (b) -- Export Aspect
// The class as a whole is not exported, and not all non-inline virtual methods
// are exported, so the vtable and typeinfo symbol are not to be exported.
//
// SCEI_PS4-DAG: @_ZTV11FooNoImport = external unnamed_addr constant {
// SCEI_WI-DAG: @_ZTV11FooNoExport = dso_local unnamed_addr constant {
// WI-DAG: @_ZTV11FooNoExport = dso_local unnamed_addr constant {
// SCEI_PS4-DAG: @_ZTI11FooNoExport = constant {
// SCEI_WI-DAG: @_ZTI11FooNoExport = dso_local constant {
// WI-DAG: @_ZTI11FooNoExport = dso_local constant {
struct FooNoExport
{
virtual void getId() const {}
__declspec(dllexport) virtual void Bump();
virtual void Decrement(); // Not exported.
int mCounter;
};
void FooNoExport::Bump()
{
typeid(FooNoExport).name();
}
|