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 { 55 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). 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 { 80 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. 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 { 107 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. 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 { 133 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. 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 { 175 virtual void getId() const {} 176 __declspec(dllimport) virtual void Bump(); 177 virtual void Decrement(); // Not imported. 178 int mCounter; 179 }; 180 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 { 202 virtual void getId() const {} 203 __declspec(dllexport) virtual void Bump(); 204 virtual void Decrement(); // Not exported. 205 int mCounter; 206 }; 207 208 void FooNoExport::Bump() 209 { 210 typeid(FooNoExport).name(); 211 } 212