1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1 2 3 // RUN: FileCheck --check-prefix=VTABLE-C %s < %t 4 // RUN: FileCheck --check-prefix=VTABLE-D %s < %t 5 // RUN: FileCheck --check-prefix=TEST1 %s < %t 6 // RUN: FileCheck --check-prefix=TEST2 %s < %t 7 // RUN: FileCheck --check-prefix=TEST3 %s < %t 8 // RUN: FileCheck --check-prefix=TEST4 %s < %t 9 // RUN: FileCheck --check-prefix=TEST5 %s < %t 10 // RUN: FileCheck --check-prefix=TEST6 %s < %t 11 // RUN: FileCheck --check-prefix=TEST7 %s < %t 12 // RUN: FileCheck --check-prefix=TEST8 %s < %t 13 // RUN: FileCheck --check-prefix=TEST9-Y %s < %t 14 // RUN: FileCheck --check-prefix=TEST9-Z %s < %t 15 // RUN: FileCheck --check-prefix=TEST9-W %s < %t 16 // RUN: FileCheck --check-prefix=TEST9-T %s < %t 17 // RUN: FileCheck --check-prefix=TEST10 %s < %t 18 // RUN: FileCheck --check-prefix=VDTORS-Y %s < %t 19 // RUN: FileCheck --check-prefix=VDTORS-U %s < %t 20 // RUN: FileCheck --check-prefix=VDTORS-V %s < %t 21 // RUN: FileCheck --check-prefix=VDTORS-P %s < %t 22 // RUN: FileCheck --check-prefix=RET-W %s < %t 23 // RUN: FileCheck --check-prefix=RET-T %s < %t 24 25 // RUN: FileCheck --check-prefix=MANGLING %s < %t 26 27 struct Empty { }; 28 29 struct A { 30 virtual void f(); 31 virtual void z(); // Useful to check there are no thunks for f() when appropriate. 32 }; 33 34 struct B { 35 virtual void g(); 36 }; 37 38 struct C: virtual A { 39 // VTABLE-C: VFTable for 'A' in 'C' (2 entries) 40 // VTABLE-C-NEXT: 0 | void C::f() 41 // VTABLE-C-NEXT: 1 | void A::z() 42 43 // VTABLE-C: VFTable indices for 'C' (1 entries) 44 // VTABLE-C-NEXT: vbtable index 1, vfptr at offset 0 45 // VTABLE-C-NEXT: 0 | void C::f() 46 47 // MANGLING-DAG: @"\01??_7C@@6B@" 48 49 virtual void f(); 50 }; 51 52 C c; 53 54 struct D: virtual A { 55 // VTABLE-D: VFTable for 'D' (1 entries). 56 // VTABLE-D-NEXT: 0 | void D::h() 57 58 // VTABLE-D: VFTable for 'A' in 'D' (2 entries). 59 // VTABLE-D-NEXT: 0 | void D::f() 60 // VTABLE-D-NEXT: 1 | void A::z() 61 62 // VTABLE-D: VFTable indices for 'D' (2 entries). 63 // VTABLE-D-NEXT: via vfptr at offset 0 64 // VTABLE-D-NEXT: 0 | void D::h() 65 // VTABLE-D-NEXT: via vbtable index 1, vfptr at offset 0 66 // VTABLE-D-NEXT: 0 | void D::f() 67 68 // MANGLING-DAG: @"\01??_7D@@6B0@@" 69 // MANGLING-DAG: @"\01??_7D@@6BA@@@" 70 71 virtual void f(); 72 virtual void h(); 73 }; 74 75 void D::h() {} 76 D d; 77 78 namespace Test1 { 79 80 struct X { int x; }; 81 82 // X and A get reordered in the layout since X doesn't have a vfptr while A has. 83 struct Y : X, A { }; 84 // MANGLING-DAG: @"\01??_7Y@Test1@@6B@" 85 86 struct Z : virtual Y { 87 // TEST1: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries). 88 // TEST1-NEXT: 0 | void A::f() 89 // TEST1-NEXT: 1 | void A::z() 90 91 // TEST1-NOT: VFTable indices for 'Test1::Z' 92 93 // MANGLING-DAG: @"\01??_7Z@Test1@@6B@" 94 }; 95 96 Z z; 97 } 98 99 namespace Test2 { 100 101 struct X: virtual A, virtual B { 102 // TEST2: VFTable for 'Test2::X' (1 entries). 103 // TEST2-NEXT: 0 | void Test2::X::h() 104 105 // TEST2: VFTable for 'A' in 'Test2::X' (2 entries). 106 // TEST2-NEXT: 0 | void A::f() 107 // TEST2-NEXT: 1 | void A::z() 108 109 // TEST2: VFTable for 'B' in 'Test2::X' (1 entries). 110 // TEST2-NEXT: 0 | void B::g() 111 112 // TEST2: VFTable indices for 'Test2::X' (1 entries). 113 // TEST2-NEXT: 0 | void Test2::X::h() 114 115 // MANGLING-DAG: @"\01??_7X@Test2@@6B01@@" 116 // MANGLING-DAG: @"\01??_7X@Test2@@6BA@@@" 117 // MANGLING-DAG: @"\01??_7X@Test2@@6BB@@@" 118 119 virtual void h(); 120 }; 121 122 X x; 123 } 124 125 namespace Test3 { 126 127 struct X : virtual A { 128 // MANGLING-DAG: @"\01??_7X@Test3@@6B@" 129 }; 130 131 struct Y: virtual X { 132 // TEST3: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries). 133 // TEST3-NEXT: 0 | void A::f() 134 // TEST3-NEXT: 1 | void A::z() 135 136 // TEST3-NOT: VFTable indices for 'Test3::Y' 137 138 // MANGLING-DAG: @"\01??_7Y@Test3@@6B@" 139 }; 140 141 Y y; 142 } 143 144 namespace Test4 { 145 146 struct X: virtual C { 147 // This one's interesting. C::f expects (A*) to be passed as 'this' and does 148 // ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk 149 // should pass a pointer to the end of X in order 150 // for ECX-=4 to point at the C part. 151 152 // TEST4: VFTable for 'A' in 'C' in 'Test4::X' (2 entries). 153 // TEST4-NEXT: 0 | void C::f() 154 // TEST4-NEXT: [this adjustment: 8 non-virtual] 155 // TEST4-NEXT: 1 | void A::z() 156 157 // TEST4-NOT: VFTable indices for 'Test4::X' 158 159 // MANGLING-DAG: @"\01??_7X@Test4@@6B@" 160 161 // Also check the mangling of the thunk. 162 // MANGLING-DAG: define weak x86_thiscallcc void @"\01?f@C@@WPPPPPPPI@AEXXZ" 163 }; 164 165 X x; 166 } 167 168 namespace Test5 { 169 170 // New methods are added to the base's vftable. 171 struct X : A { 172 // MANGLING-DAG: @"\01??_7X@Test5@@6B@" 173 virtual void g(); 174 }; 175 176 struct Y : virtual X { 177 // TEST5: VFTable for 'Test5::Y' (1 entries). 178 // TEST5-NEXT: 0 | void Test5::Y::h() 179 180 // TEST5: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries). 181 // TEST5-NEXT: 0 | void A::f() 182 // TEST5-NEXT: 1 | void A::z() 183 // TEST5-NEXT: 2 | void Test5::X::g() 184 185 // TEST5: VFTable indices for 'Test5::Y' (1 entries). 186 // TEST5-NEXT: 0 | void Test5::Y::h() 187 188 // MANGLING-DAG: @"\01??_7Y@Test5@@6B01@@" 189 // MANGLING-DAG: @"\01??_7Y@Test5@@6BX@1@@" 190 191 virtual void h(); 192 }; 193 194 Y y; 195 } 196 197 namespace Test6 { 198 199 struct X : A, virtual Empty { 200 // TEST6: VFTable for 'A' in 'Test6::X' (2 entries). 201 // TEST6-NEXT: 0 | void A::f() 202 // TEST6-NEXT: 1 | void A::z() 203 204 // TEST6-NOT: VFTable indices for 'Test6::X' 205 206 // MANGLING-DAG: @"\01??_7X@Test6@@6B@" 207 }; 208 209 X x; 210 } 211 212 namespace Test7 { 213 214 struct X : C { 215 // MANGLING-DAG: @"\01??_7X@Test7@@6B@" 216 }; 217 218 struct Y : virtual X { 219 // TEST7: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries). 220 // TEST7-NEXT: 0 | void C::f() 221 // TEST7-NEXT: [this adjustment: 8 non-virtual] 222 // TEST7-NEXT: 1 | void A::z() 223 224 // TEST7: Thunks for 'void C::f()' (1 entry). 225 // TEST7-NEXT: 0 | this adjustment: 8 non-virtual 226 227 // TEST7-NOT: VFTable indices for 'Test7::Y' 228 229 // MANGLING-DAG: @"\01??_7Y@Test7@@6B@" 230 }; 231 232 Y y; 233 } 234 235 namespace Test8 { 236 237 // This is a typical diamond inheritance with a shared 'A' vbase. 238 struct X : D, C { 239 // TEST8: VFTable for 'D' in 'Test8::X' (1 entries). 240 // TEST8-NEXT: 0 | void D::h() 241 242 // TEST8: VFTable for 'A' in 'D' in 'Test8::X' (2 entries). 243 // TEST8-NEXT: 0 | void Test8::X::f() 244 // TEST8-NEXT: 1 | void A::z() 245 246 // TEST8: VFTable indices for 'Test8::X' (1 entries). 247 // TEST8-NEXT: via vbtable index 1, vfptr at offset 0 248 249 // MANGLING-DAG: @"\01??_7X@Test8@@6BA@@@" 250 // MANGLING-DAG: @"\01??_7X@Test8@@6BD@@@" 251 252 virtual void f(); 253 }; 254 255 X x; 256 } 257 258 namespace Test9 { 259 260 struct X : A { }; 261 262 struct Y : virtual X { 263 // TEST9-Y: VFTable for 'Test9::Y' (1 entries). 264 // TEST9-Y-NEXT: 0 | void Test9::Y::h() 265 266 // TEST9-Y: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries). 267 // TEST9-Y-NEXT: 0 | void A::f() 268 // TEST9-Y-NEXT: 1 | void A::z() 269 270 // TEST9-Y: VFTable indices for 'Test9::Y' (1 entries). 271 // TEST9-Y-NEXT: 0 | void Test9::Y::h() 272 273 // MANGLING-DAG: @"\01??_7Y@Test9@@6B01@@" 274 // MANGLING-DAG: @"\01??_7Y@Test9@@6BX@1@@" 275 276 virtual void h(); 277 }; 278 279 Y y; 280 281 struct Z : Y, virtual B { 282 // TEST9-Z: VFTable for 'Test9::Y' in 'Test9::Z' (1 entries). 283 // TEST9-Z-NEXT: 0 | void Test9::Y::h() 284 285 // TEST9-Z: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries). 286 // TEST9-Z-NEXT: 0 | void A::f() 287 // TEST9-Z-NEXT: 1 | void A::z() 288 289 // TEST9-Z: VFTable for 'B' in 'Test9::Z' (1 entries). 290 // TEST9-Z-NEXT: 0 | void B::g() 291 292 // TEST9-Z-NOT: VFTable indices for 'Test9::Z' 293 294 // MANGLING-DAG: @"\01??_7Z@Test9@@6BX@1@@" 295 // MANGLING-DAG: @"\01??_7Z@Test9@@6BY@1@@" 296 297 // FIXME this one is wrong: 298 // INCORRECT MANGLING-DAG: @"\01??_7Z@Test9@@6BB@@@" 299 // MANGLING-DAG-SHOULD-BE: @"\01??_7Z@Test9@@6B@" 300 }; 301 302 Z z; 303 304 struct W : Z, D, virtual A, virtual B { 305 // TEST9-W: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entries). 306 // TEST9-W-NEXT: 0 | void Test9::Y::h() 307 308 // TEST9-W: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries). 309 // TEST9-W-NEXT: 0 | void A::f() 310 // TEST9-W-NEXT: 1 | void A::z() 311 312 // TEST9-W: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entries). 313 // TEST9-W-NEXT: 0 | void B::g() 314 315 // TEST9-W: VFTable for 'D' in 'Test9::W' (1 entries). 316 // TEST9-W-NEXT: 0 | void D::h() 317 318 // TEST9-W: VFTable for 'A' in 'D' in 'Test9::W' (2 entries). 319 // TEST9-W-NEXT: 0 | void D::f() 320 // TEST9-W-NEXT: [this adjustment: -8 non-virtual] 321 // TEST9-W-NEXT: 1 | void A::z() 322 323 // TEST9-W: Thunks for 'void D::f()' (1 entry). 324 // TEST9-W-NEXT: 0 | this adjustment: -8 non-virtual 325 326 // TEST9-W-NOT: VFTable indices for 'Test9::W' 327 328 // MANGLING-DAG: @"\01??_7W@Test9@@6BA@@@" 329 // MANGLING-DAG: @"\01??_7W@Test9@@6BD@@@" 330 // MANGLING-DAG: @"\01??_7W@Test9@@6BX@1@@" 331 332 // FIXME: these two are wrong: 333 // INCORRECT MANGLING-DAG: @"\01??_7W@Test9@@6BB@@@" 334 // MANGLING-DAG-SHOULD-BE: @"\01??_7W@Test9@@6B@" 335 // INCORRECT MANGLING-DAG: @"\01??_7W@Test9@@6BY@1@Z@1@@" 336 // MANGLING-DAG-SHOULD-BE: @"\01??_7W@Test9@@6BY@1@@" 337 }; 338 339 W w; 340 341 struct T : Z, D, virtual A, virtual B { 342 // TEST9-T: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entries). 343 // TEST9-T-NEXT: 0 | void Test9::T::h() 344 345 // TEST9-T: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries). 346 // TEST9-T-NEXT: 0 | void Test9::T::f() 347 // TEST9-T-NEXT: 1 | void Test9::T::z() 348 349 // TEST9-T: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entries). 350 // TEST9-T-NEXT: 0 | void Test9::T::g() 351 352 // TEST9-T: VFTable for 'D' in 'Test9::T' (1 entries). 353 // TEST9-T-NEXT: 0 | void Test9::T::h() 354 // TEST9-T-NEXT: [this adjustment: -8 non-virtual] 355 356 // TEST9-T: Thunks for 'void Test9::T::h()' (1 entry). 357 // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual 358 359 // TEST9-T: VFTable for 'A' in 'D' in 'Test9::T' (2 entries). 360 // TEST9-T-NEXT: 0 | void Test9::T::f() 361 // TEST9-T-NEXT: [this adjustment: -8 non-virtual] 362 // TEST9-T-NEXT: 1 | void Test9::T::z() 363 // TEST9-T-NEXT: [this adjustment: -8 non-virtual] 364 365 // TEST9-T: Thunks for 'void Test9::T::f()' (1 entry). 366 // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual 367 368 // TEST9-T: Thunks for 'void Test9::T::z()' (1 entry). 369 // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual 370 371 // TEST9-T: VFTable indices for 'Test9::T' (4 entries). 372 // TEST9-T-NEXT: via vfptr at offset 0 373 // TEST9-T-NEXT: 0 | void Test9::T::h() 374 // TEST9-T-NEXT: via vbtable index 1, vfptr at offset 0 375 // TEST9-T-NEXT: 0 | void Test9::T::f() 376 // TEST9-T-NEXT: 1 | void Test9::T::z() 377 // TEST9-T-NEXT: via vbtable index 2, vfptr at offset 0 378 // TEST9-T-NEXT: 0 | void Test9::T::g() 379 380 // MANGLING-DAG: @"\01??_7T@Test9@@6BA@@@" 381 // MANGLING-DAG: @"\01??_7T@Test9@@6BD@@@" 382 // MANGLING-DAG: @"\01??_7T@Test9@@6BX@1@@" 383 384 // FIXME: these two are wrong: 385 // INCORRECT MANGLING-DAG: @"\01??_7T@Test9@@6BB@@@" 386 // MANGLING-DAG-SHOULD-BE: @"\01??_7T@Test9@@6B@" 387 // INCORRECT MANGLING-DAG: @"\01??_7T@Test9@@6BY@1@Z@1@@" 388 // MANGLING-DAG-SHOULD-BE: @"\01??_7T@Test9@@6BY@1@@" 389 390 virtual void f(); 391 virtual void g(); 392 virtual void h(); 393 virtual void z(); 394 }; 395 396 T t; 397 } 398 399 namespace Test10 { 400 struct X : virtual C, virtual A { 401 // TEST10: VFTable for 'A' in 'C' in 'Test10::X' (2 entries). 402 // TEST10-NEXT: 0 | void Test10::X::f() 403 // TEST10-NEXT: 1 | void A::z() 404 405 // TEST10: VFTable indices for 'Test10::X' (1 entries). 406 // TEST10-NEXT: via vbtable index 1, vfptr at offset 0 407 // TEST10-NEXT: 0 | void Test10::X::f() 408 virtual void f(); 409 }; 410 411 void X::f() {} 412 X x; 413 } 414 415 namespace vdtors { 416 struct X { 417 virtual ~X(); 418 virtual void zzz(); 419 }; 420 421 struct Y : virtual X { 422 // VDTORS-Y: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries). 423 // VDTORS-Y-NEXT: 0 | vdtors::Y::~Y() [scalar deleting] 424 // VDTORS-Y-NEXT: 1 | void vdtors::X::zzz() 425 426 // VDTORS-Y-NOT: Thunks for 'vdtors::Y::~Y()' 427 virtual ~Y(); 428 }; 429 430 Y y; 431 432 struct Z { 433 virtual void z(); 434 }; 435 436 struct W : Z, X { 437 // Implicit virtual dtor. 438 }; 439 440 struct U : virtual W { 441 // VDTORS-U: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::U' (1 entries). 442 // VDTORS-U-NEXT: 0 | void vdtors::Z::z() 443 444 // VDTORS-U: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries). 445 // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting] 446 // VDTORS-U-NEXT: [this adjustment: -4 non-virtual] 447 // VDTORS-U-NEXT: 1 | void vdtors::X::zzz() 448 449 // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry). 450 // VDTORS-U-NEXT: 0 | this adjustment: -4 non-virtual 451 452 // VDTORS-U: VFTable indices for 'vdtors::U' (1 entries). 453 // VDTORS-U-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- 454 // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting] 455 virtual ~U(); 456 }; 457 458 U u; 459 460 struct V : virtual W { 461 // VDTORS-V: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::V' (1 entries). 462 // VDTORS-V-NEXT: 0 | void vdtors::Z::z() 463 464 // VDTORS-V: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries). 465 // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting] 466 // VDTORS-V-NEXT: [this adjustment: -4 non-virtual] 467 // VDTORS-V-NEXT: 1 | void vdtors::X::zzz() 468 469 // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry). 470 // VDTORS-V-NEXT: 0 | this adjustment: -4 non-virtual 471 472 // VDTORS-V: VFTable indices for 'vdtors::V' (1 entries). 473 // VDTORS-V-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- 474 // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting] 475 }; 476 477 V v; 478 479 struct T : virtual X { 480 virtual ~T(); 481 }; 482 483 struct P : T, Y { 484 // VDTORS-P: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries). 485 // VDTORS-P-NEXT: 0 | vdtors::P::~P() [scalar deleting] 486 // VDTORS-P-NEXT: 1 | void vdtors::X::zzz() 487 488 // VDTORS-P-NOT: Thunks for 'vdtors::P::~P()' 489 virtual ~P(); 490 }; 491 492 P p; 493 494 } 495 496 namespace return_adjustment { 497 498 struct X : virtual A { 499 virtual void f(); 500 }; 501 502 struct Y : virtual A, virtual X { 503 virtual void f(); 504 }; 505 506 struct Z { 507 virtual A* foo(); 508 }; 509 510 struct W : Z { 511 // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries). 512 // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo() 513 // RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual] 514 // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() 515 516 // RET-W: VFTable indices for 'return_adjustment::W' (1 entries). 517 // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() 518 519 virtual X* foo(); 520 }; 521 522 W y; 523 524 struct T : W { 525 // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries). 526 // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo() 527 // RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual] 528 // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo() 529 // RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual] 530 // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() 531 532 // RET-T: VFTable indices for 'return_adjustment::T' (1 entries). 533 // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() 534 535 virtual Y* foo(); 536 }; 537 538 T t; 539 } 540