1 // RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o - >%t 2>&1 2 3 // RUN: FileCheck --check-prefix=NO-THUNKS-Test1 %s < %t 4 // RUN: FileCheck --check-prefix=NO-THUNKS-Test2 %s < %t 5 // RUN: FileCheck --check-prefix=NO-THUNKS-Test3 %s < %t 6 // RUN: FileCheck --check-prefix=NO-THUNKS-Test4 %s < %t 7 // RUN: FileCheck --check-prefix=NO-THUNKS-Test5 %s < %t 8 // RUN: FileCheck --check-prefix=NO-THUNKS-Test6 %s < %t 9 // RUN: FileCheck --check-prefix=NO-THUNKS-Test7 %s < %t 10 // RUN: FileCheck --check-prefix=NO-THUNKS-Test8 %s < %t 11 // RUN: FileCheck --check-prefix=NO-THUNKS-Test9 %s < %t 12 // RUN: FileCheck --check-prefix=PURE-VIRTUAL-Test1 %s < %t 13 // RUN: FileCheck --check-prefix=THIS-THUNKS-Test1 %s < %t 14 // RUN: FileCheck --check-prefix=THIS-THUNKS-Test2 %s < %t 15 // RUN: FileCheck --check-prefix=THIS-THUNKS-Test3 %s < %t 16 // RUN: FileCheck --check-prefix=RET-THUNKS-Test1 %s < %t 17 // RUN: FileCheck --check-prefix=RET-THUNKS-Test2 %s < %t 18 // RUN: FileCheck --check-prefix=RET-THUNKS-Test3 %s < %t 19 // RUN: FileCheck --check-prefix=RET-THUNKS-Test4 %s < %t 20 // RUN: FileCheck --check-prefix=RET-THUNKS-Test5 %s < %t 21 22 struct Empty { 23 // Doesn't have a vftable! 24 }; 25 26 struct A { 27 virtual void f(); 28 }; 29 30 struct B { 31 virtual void g(); 32 // Add an extra virtual method so it's easier to check for the absence of thunks. 33 virtual void h(); 34 }; 35 36 struct C { 37 virtual void g(); // Might "collide" with B::g if both are bases of some class. 38 }; 39 40 41 namespace no_thunks { 42 43 struct Test1: A, B { 44 // NO-THUNKS-Test1: VFTable for 'A' in 'no_thunks::Test1' (1 entries) 45 // NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f() 46 47 // NO-THUNKS-Test1: VFTable for 'B' in 'no_thunks::Test1' (2 entries) 48 // NO-THUNKS-Test1-NEXT: 0 | void B::g() 49 // NO-THUNKS-Test1-NEXT: 1 | void B::h() 50 51 // NO-THUNKS-Test1: VFTable indices for 'no_thunks::Test1' (1 entries) 52 // NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f() 53 54 // Overrides only the left child's method (A::f), needs no thunks. 55 virtual void f(); 56 }; 57 58 Test1 t1; 59 60 struct Test2: A, B { 61 // NO-THUNKS-Test2: VFTable for 'A' in 'no_thunks::Test2' (1 entries) 62 // NO-THUNKS-Test2-NEXT: 0 | void A::f() 63 64 // NO-THUNKS-Test2: VFTable for 'B' in 'no_thunks::Test2' (2 entries) 65 // NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g() 66 // NO-THUNKS-Test2-NEXT: 1 | void B::h() 67 68 // NO-THUNKS-Test2: VFTable indices for 'no_thunks::Test2' (1 entries). 69 // NO-THUNKS-Test2-NEXT: via vfptr at offset 4 70 // NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g() 71 72 // Overrides only the right child's method (B::g), needs this adjustment but 73 // not thunks. 74 virtual void g(); 75 }; 76 77 Test2 t2; 78 79 struct Test3: A, B { 80 // NO-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test3' (2 entries) 81 // NO-THUNKS-Test3-NEXT: 0 | void A::f() 82 // NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i() 83 84 // NO-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test3' (2 entries) 85 // NO-THUNKS-Test3-NEXT: 0 | void B::g() 86 // NO-THUNKS-Test3-NEXT: 1 | void B::h() 87 88 // NO-THUNKS-Test3: VFTable indices for 'no_thunks::Test3' (1 entries). 89 // NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i() 90 91 // Only adds a new method. 92 virtual void i(); 93 }; 94 95 Test3 t3; 96 97 // Only the right base has a vftable, so it's laid out before the left one! 98 struct Test4 : Empty, A { 99 // NO-THUNKS-Test4: VFTable for 'A' in 'no_thunks::Test4' (1 entries) 100 // NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f() 101 102 // NO-THUNKS-Test4: VFTable indices for 'no_thunks::Test4' (1 entries). 103 // NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f() 104 105 virtual void f(); 106 }; 107 108 Test4 t4; 109 110 // 2-level structure with repeating subobject types, but no thunks needed. 111 struct Test5: Test1, Test2 { 112 // NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries) 113 // NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test1::f() 114 // NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z() 115 116 // NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries) 117 // NO-THUNKS-Test5-NEXT: 0 | void B::g() 118 // NO-THUNKS-Test5-NEXT: 1 | void B::h() 119 120 // NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test5' (1 entries) 121 // NO-THUNKS-Test5-NEXT: 0 | void A::f() 122 123 // NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test5' (2 entries) 124 // NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test2::g() 125 // NO-THUNKS-Test5-NEXT: 1 | void B::h() 126 127 // NO-THUNKS-Test5: VFTable indices for 'no_thunks::Test5' (1 entries). 128 // NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z() 129 130 virtual void z(); 131 }; 132 133 Test5 t5; 134 135 struct Test6: Test1 { 136 // NO-THUNKS-Test6: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test6' (1 entries). 137 // NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f() 138 139 // NO-THUNKS-Test6: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test6' (2 entries). 140 // NO-THUNKS-Test6-NEXT: 0 | void B::g() 141 // NO-THUNKS-Test6-NEXT: 1 | void B::h() 142 143 // NO-THUNKS-Test6: VFTable indices for 'no_thunks::Test6' (1 entries). 144 // NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f() 145 146 // Overrides both no_thunks::Test1::f and A::f. 147 virtual void f(); 148 }; 149 150 Test6 t6; 151 152 struct Test7: Test2 { 153 // NO-THUNKS-Test7: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test7' (1 entries). 154 // NO-THUNKS-Test7-NEXT: 0 | void A::f() 155 156 // NO-THUNKS-Test7: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test7' (2 entries). 157 // NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g() 158 // NO-THUNKS-Test7-NEXT: 1 | void B::h() 159 160 // NO-THUNKS-Test7: VFTable indices for 'no_thunks::Test7' (1 entries). 161 // NO-THUNKS-Test7-NEXT: via vfptr at offset 4 162 // NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g() 163 164 // Overrides both no_thunks::Test2::g and B::g. 165 virtual void g(); 166 }; 167 168 Test7 t7; 169 170 struct Test8: Test3 { 171 // NO-THUNKS-Test8: VFTable for 'A' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries). 172 // NO-THUNKS-Test8-NEXT: 0 | void A::f() 173 // NO-THUNKS-Test8-NEXT: 1 | void no_thunks::Test3::i() 174 175 // NO-THUNKS-Test8: VFTable for 'B' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries). 176 // NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g() 177 // NO-THUNKS-Test8-NEXT: 1 | void B::h() 178 179 // NO-THUNKS-Test8: VFTable indices for 'no_thunks::Test8' (1 entries). 180 // NO-THUNKS-Test8-NEXT: via vfptr at offset 4 181 // NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g() 182 183 // Overrides grandparent's B::g. 184 virtual void g(); 185 }; 186 187 Test8 t8; 188 189 struct D : A { 190 virtual void g(); 191 }; 192 193 // Repeating subobject. 194 struct Test9: A, D { 195 // NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::Test9' (2 entries). 196 // NO-THUNKS-Test9-NEXT: 0 | void A::f() 197 // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h() 198 199 // NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::D' in 'no_thunks::Test9' (2 entries). 200 // NO-THUNKS-Test9-NEXT: 0 | void A::f() 201 // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::D::g() 202 203 // NO-THUNKS-Test9: VFTable indices for 'no_thunks::Test9' (1 entries). 204 // NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h() 205 206 virtual void h(); 207 }; 208 209 Test9 t9; 210 } 211 212 namespace pure_virtual { 213 struct D { 214 virtual void g() = 0; 215 virtual void h(); 216 }; 217 218 219 struct Test1: A, D { 220 // PURE-VIRTUAL-Test1: VFTable for 'A' in 'pure_virtual::Test1' (1 entries) 221 // PURE-VIRTUAL-Test1-NEXT: 0 | void A::f() 222 223 // PURE-VIRTUAL-Test1: VFTable for 'pure_virtual::D' in 'pure_virtual::Test1' (2 entries) 224 // PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g() 225 // PURE-VIRTUAL-Test1-NEXT: 1 | void pure_virtual::D::h() 226 227 // PURE-VIRTUAL-Test1: VFTable indices for 'pure_virtual::Test1' (1 entries). 228 // PURE-VIRTUAL-Test1-NEXT: via vfptr at offset 4 229 // PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g() 230 231 // Overrides only the right child's method (pure_virtual::D::g), needs this adjustment but 232 // not thunks. 233 virtual void g(); 234 }; 235 236 Test1 t1; 237 } 238 239 namespace this_adjustment { 240 241 // Overrides methods of two bases at the same time, thus needing thunks. 242 struct Test1 : B, C { 243 // THIS-THUNKS-Test1: VFTable for 'B' in 'this_adjustment::Test1' (2 entries). 244 // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g() 245 // THIS-THUNKS-Test1-NEXT: 1 | void B::h() 246 247 // THIS-THUNKS-Test1: VFTable for 'C' in 'this_adjustment::Test1' (1 entries). 248 // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g() 249 // THIS-THUNKS-Test1-NEXT: [this adjustment: -4 non-virtual] 250 251 // THIS-THUNKS-Test1: Thunks for 'void this_adjustment::Test1::g()' (1 entry). 252 // THIS-THUNKS-Test1-NEXT: 0 | this adjustment: -4 non-virtual 253 254 // THIS-THUNKS-Test1: VFTable indices for 'this_adjustment::Test1' (1 entries). 255 // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g() 256 257 virtual void g(); 258 }; 259 260 Test1 t1; 261 262 struct Test2 : A, B, C { 263 // THIS-THUNKS-Test2: VFTable for 'A' in 'this_adjustment::Test2' (1 entries). 264 // THIS-THUNKS-Test2-NEXT: 0 | void A::f() 265 266 // THIS-THUNKS-Test2: VFTable for 'B' in 'this_adjustment::Test2' (2 entries). 267 // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g() 268 // THIS-THUNKS-Test2-NEXT: 1 | void B::h() 269 270 // THIS-THUNKS-Test2: VFTable for 'C' in 'this_adjustment::Test2' (1 entries). 271 // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g() 272 // THIS-THUNKS-Test2-NEXT: [this adjustment: -4 non-virtual] 273 274 // THIS-THUNKS-Test2: Thunks for 'void this_adjustment::Test2::g()' (1 entry). 275 // THIS-THUNKS-Test2-NEXT: 0 | this adjustment: -4 non-virtual 276 277 // THIS-THUNKS-Test2: VFTable indices for 'this_adjustment::Test2' (1 entries). 278 // THIS-THUNKS-Test2-NEXT: via vfptr at offset 4 279 // THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g() 280 281 virtual void g(); 282 }; 283 284 Test2 t2; 285 286 // Overrides methods of two bases at the same time, thus needing thunks. 287 struct Test3: no_thunks::Test1, no_thunks::Test2 { 288 // THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test1' in 'this_adjustment::Test3' (1 entries). 289 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f() 290 291 // THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test1' in 'this_adjustment::Test3' (2 entries). 292 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g() 293 // THIS-THUNKS-Test3-NEXT: 1 | void B::h() 294 295 // THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test2' in 'this_adjustment::Test3' (1 entries). 296 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f() 297 // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] 298 299 // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::f()' (1 entry). 300 // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual 301 302 // THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test2' in 'this_adjustment::Test3' (2 entries). 303 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g() 304 // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] 305 // THIS-THUNKS-Test3-NEXT: 1 | void B::h() 306 307 // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::g()' (1 entry). 308 // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual 309 310 // THIS-THUNKS-Test3: VFTable indices for 'this_adjustment::Test3' (2 entries). 311 // THIS-THUNKS-Test3-NEXT: via vfptr at offset 0 312 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f() 313 // THIS-THUNKS-Test3-NEXT: via vfptr at offset 4 314 // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g() 315 316 virtual void f(); 317 virtual void g(); 318 }; 319 320 Test3 t3; 321 } 322 323 namespace return_adjustment { 324 325 struct Ret1 { 326 virtual C* foo(); 327 virtual void z(); 328 }; 329 330 struct Test1 : Ret1 { 331 // RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries). 332 // RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo() 333 // RET-THUNKS-Test1-NEXT: [return adjustment: 4 non-virtual] 334 // RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z() 335 // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo() 336 337 // RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entries). 338 // RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo() 339 340 virtual this_adjustment::Test1* foo(); 341 }; 342 343 Test1 t1; 344 345 struct Ret2 : B, this_adjustment::Test1 { }; 346 347 struct Test2 : Test1 { 348 // RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries). 349 // RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo() 350 // RET-THUNKS-Test2-NEXT: [return adjustment: 8 non-virtual] 351 // RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z() 352 // RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo() 353 // RET-THUNKS-Test2-NEXT: [return adjustment: 4 non-virtual] 354 // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo() 355 356 // RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entries). 357 // RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo() 358 359 virtual Ret2* foo(); 360 }; 361 362 Test2 t2; 363 364 struct Test3: B, Ret1 { 365 // RET-THUNKS-Test3: VFTable for 'B' in 'return_adjustment::Test3' (2 entries). 366 // RET-THUNKS-Test3-NEXT: 0 | void B::g() 367 // RET-THUNKS-Test3-NEXT: 1 | void B::h() 368 369 // RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries). 370 // RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo() 371 // RET-THUNKS-Test3-NEXT: [return adjustment: 4 non-virtual] 372 // RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z() 373 // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo() 374 375 // RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entries). 376 // RET-THUNKS-Test3-NEXT: via vfptr at offset 4 377 // RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo() 378 379 virtual this_adjustment::Test1* foo(); 380 }; 381 382 Test3 t3; 383 384 struct Test4 : Test3 { 385 // RET-THUNKS-Test4: VFTable for 'B' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (2 entries). 386 // RET-THUNKS-Test4-NEXT: 0 | void B::g() 387 // RET-THUNKS-Test4-NEXT: 1 | void B::h() 388 389 // RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries). 390 // RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo() 391 // RET-THUNKS-Test4-NEXT: [return adjustment: 8 non-virtual] 392 // RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z() 393 // RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo() 394 // RET-THUNKS-Test4-NEXT: [return adjustment: 4 non-virtual] 395 // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo() 396 397 // RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entries). 398 // RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 -- 399 // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo() 400 401 virtual Ret2* foo(); 402 }; 403 404 Test4 t4; 405 406 struct Test5 : Ret1, Test1 { 407 // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries). 408 // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 409 // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual] 410 // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z() 411 // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 412 413 // RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries). 414 // RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 415 // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual] 416 // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] 417 // RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z() 418 // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 419 // RET-THUNKS-Test5-NEXT: [return adjustment: 4 non-virtual] 420 // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] 421 // RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 422 // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] 423 424 // RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entries). 425 // RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo() 426 427 virtual Ret2* foo(); 428 }; 429 430 Test5 t5; 431 } 432