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