1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -triple=i386-pc-win32 > %t 2 // RUN: FileCheck %s < %t 3 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll 4 5 // For now, just make sure x86_64 doesn't crash. 6 // RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -triple=x86_64-pc-win32 > /dev/null 7 8 struct V1 { 9 virtual void f(); 10 virtual ~V1(); 11 }; 12 13 struct V2 { 14 virtual void f(); 15 virtual ~V2(); 16 int v; 17 }; 18 19 struct Z { 20 virtual void g(); 21 virtual ~Z(); 22 int x; 23 }; 24 25 struct V3 : Z, V2 { 26 }; 27 28 struct V4 : Z, V1, V2 { 29 int y; 30 }; 31 32 void use_somewhere_else(void*); 33 34 namespace simple { 35 // In case of a single-layer virtual inheritance, the "this" adjustment for a 36 // virtual method is done statically: 37 // struct A { 38 // virtual void f(); // Expects "(A*)this" in ECX 39 // }; 40 // struct B : virtual A { 41 // virtual void f(); // Expects "(char*)(B*)this + 12" in ECX 42 // virtual ~B(); // Might call f() 43 // }; 44 // 45 // If a class overrides a virtual function of its base and has a non-trivial 46 // ctor/dtor that call(s) the virtual function (or may escape "this" to some 47 // code that might call it), a virtual adjustment might be needed in case the 48 // current class layout and the most derived class layout are different. 49 // This is done using vtordisp thunks. 50 // 51 // A simple vtordisp{x,y} thunk for Method@Class is something like: 52 // sub ecx, [ecx+x] // apply the vtordisp adjustment 53 // sub ecx, y // apply the subobject adjustment, if needed. 54 // jmp Method@Class 55 56 struct A : virtual V1 { 57 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries). 58 // CHECK-NEXT: 0 | void simple::A::f() 59 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 60 // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting] 61 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 62 63 // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry). 64 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] 65 66 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 67 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] 68 69 virtual void f(); 70 // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ" 71 72 virtual ~A(); 73 // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z" 74 }; 75 76 A a; 77 void use(A *obj) { obj->f(); } 78 79 struct B : virtual V3 { 80 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries). 81 // CHECK-NEXT: 0 | void Z::g() 82 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting] 83 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 84 85 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry). 86 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] 87 88 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries). 89 // CHECK-NEXT: 0 | void simple::B::f() 90 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] 91 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting] 92 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 93 94 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry). 95 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] 96 97 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry). 98 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual] 99 100 // FIXME: The vtordisp thunk should only get emitted for a constructor 101 // if "this" leaves scope. 102 B() { use_somewhere_else(this); } 103 104 virtual void f(); 105 // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ" 106 107 // Has an implicit destructor. 108 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z" 109 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z" 110 }; 111 112 B b; 113 void use(B *obj) { obj->f(); } 114 115 struct C : virtual V4 { 116 // CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries). 117 // CHECK-NEXT: 0 | void Z::g() 118 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] 119 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 120 121 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). 122 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] 123 124 // CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries). 125 // CHECK-NEXT: 0 | void simple::C::f() 126 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] 127 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] 128 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 129 130 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). 131 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] 132 133 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry). 134 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual] 135 136 // CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries). 137 // CHECK-NEXT: 0 | void simple::C::f() 138 // CHECK-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual] 139 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] 140 // CHECK-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual] 141 142 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). 143 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual] 144 145 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry). 146 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual] 147 148 int x; 149 virtual void f(); 150 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ" 151 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ" 152 virtual ~C(); 153 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z" 154 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z" 155 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z" 156 }; 157 158 C c; 159 void use(C *obj) { obj->f(); } 160 161 class D : B { 162 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries). 163 // CHECK-NEXT: 0 | void simple::B::f() 164 // CHECK-NEXT: [this adjustment: vtordisp at -12, -4 non-virtual] 165 // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting] 166 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 167 D(); 168 int z; 169 170 // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@3AEXXZ" 171 }; 172 173 D::D() {} 174 175 struct E : V3 { 176 virtual void f(); 177 }; 178 179 struct F : virtual E { 180 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries). 181 // CHECK-NEXT: 0 | void simple::F::g() 182 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 183 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting] 184 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 185 186 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries). 187 // CHECK-NEXT: 0 | void simple::E::f() 188 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting] 189 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 190 191 F(); 192 virtual void g(); // Force a vtordisp. 193 int f; 194 195 // MANGLING-DAG: @"\01?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z 196 // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z 197 }; 198 199 F::F() {} 200 201 struct G : F { 202 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries). 203 // CHECK-NEXT: 0 | void simple::F::g() 204 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual] 205 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting] 206 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 207 208 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries). 209 // CHECK-NEXT: 0 | void simple::E::f() 210 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting] 211 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 212 213 G(); 214 int g; 215 216 // MANGLING-DAG: @"\01?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z" 217 // MANGLING-DAG: @"\01?f@E@simple@@UAEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z" 218 }; 219 220 G::G() {} 221 } 222 223 namespace extended { 224 // If a virtual function requires vtordisp adjustment and the final overrider 225 // is defined in another virtual base of the most derived class, 226 // we need to know two vbase offsets. 227 // In this case, we should use the extended form of vtordisp thunks, called 228 // vtordispex thunks. 229 // 230 // vtordispex{x,y,z,w} thunk for Method@Class is something like: 231 // sub ecx, [ecx+z] // apply the vtordisp adjustment 232 // sub ecx, x // jump to the vbptr of the most derived class 233 // mov eax, [ecx] // load the vbtable address 234 // add ecx, [eax+y] // lookup the final overrider's vbase offset 235 // add ecx, w // apphy the subobject offset if needed 236 // jmp Method@Class 237 238 struct A : virtual simple::A { 239 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries). 240 // CHECK-NEXT: 0 | void simple::A::f() 241 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 242 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 243 // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting] 244 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 245 246 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 247 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, 248 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 249 250 // `vtordispex{8,8,4294967292,8}' 251 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" 252 253 virtual ~A(); 254 // vtordisp{4294967292,0} 255 // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z" 256 }; 257 258 A a; 259 void use(A *obj) { delete obj; } 260 261 struct B : virtual simple::A { 262 // This class has an implicit dtor. Vdtors don't require vtordispex thunks 263 // as the most derived class always has an implicit dtor, 264 // which is a final overrider. 265 266 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries). 267 // ... 268 // CHECK: 1 | extended::B::~B() [scalar deleting] 269 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 270 271 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 272 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, 273 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 274 275 // vtordisp{4294967292,0} 276 // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z" 277 }; 278 279 B b; 280 void use(B *obj) { delete obj; } 281 282 struct C : virtual simple::A { 283 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries). 284 // CHECK-NEXT: 0 | void simple::A::f() 285 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left, 286 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 287 288 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 289 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left, 290 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 291 292 // `vtordispex{12,8,4294967292,8}' 293 // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ" 294 int x; 295 virtual ~C(); 296 // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z" 297 }; 298 299 C c; 300 void use(C *obj) { delete obj; } 301 302 struct D : virtual V2 { 303 virtual void f(); 304 virtual ~D(); 305 int x; 306 }; 307 308 struct E : virtual D { 309 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries). 310 // CHECK-NEXT: 0 | void extended::D::f() 311 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 312 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual] 313 314 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry). 315 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, 316 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual] 317 318 // `vtordispex{8,8,4294967292,12}' 319 // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ" 320 321 virtual ~E(); 322 // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z" 323 }; 324 325 E e; 326 void use(E *obj) { delete obj; } 327 328 struct F : virtual Z, virtual D { 329 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries). 330 // CHECK-NEXT: 0 | void extended::D::f() 331 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left, 332 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual] 333 334 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry). 335 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left, 336 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual] 337 338 // `vtordispex{20,12,4294967292,12}' 339 // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ" 340 int x; 341 virtual ~F(); 342 // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z" 343 }; 344 345 F f; 346 void use(F *obj) { delete obj; } 347 348 struct G : virtual simple::A { 349 // CHECK-LABEL: VFTable for 'extended::G' (1 entry). 350 // CHECK-NEXT: 0 | void extended::G::g() 351 352 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries). 353 // CHECK-NEXT: 0 | void simple::A::f() 354 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 355 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 356 // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting] 357 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 358 359 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 360 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, 361 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 362 363 // Emits a G's own vfptr, thus moving the vbptr in the layout. 364 virtual void g(); 365 366 virtual ~G(); 367 // vtordisp{4294967292,0} 368 // MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z" 369 }; 370 371 G g; 372 void use(G *obj) { obj->g(); } 373 374 struct H : Z, A { 375 // CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries). 376 // CHECK-NEXT: 0 | void Z::g() 377 // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting] 378 379 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries). 380 // CHECK-NEXT: 0 | void simple::A::f() 381 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 382 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 383 384 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). 385 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, 386 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 387 388 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" 389 // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z" 390 }; 391 392 H h; 393 void use(H *obj) { delete obj; } 394 } 395 396 namespace pr17738 { 397 // These classes should have vtordispex thunks but MSVS CL miscompiles them. 398 // Just do the right thing. 399 400 struct A : virtual simple::B { 401 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries). 402 // CHECK-NEXT: 0 | void simple::B::f() 403 // CHECK-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left, 404 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual] 405 406 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry). 407 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left, 408 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual] 409 410 // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ" 411 int a; 412 virtual ~A(); 413 }; 414 415 A a; 416 void use(A *obj) { delete obj; } 417 } 418 419 namespace pr19408 { 420 // In this test, the vptr used to vcall D::f() is located in the A vbase. 421 // The offset of A in different in C and D, so the D vtordisp thunk should 422 // adjust "this" so C::f gets the right value. 423 struct A { 424 A(); 425 virtual void f(); 426 int a; 427 }; 428 429 struct B : virtual A { 430 B(); 431 int b; 432 }; 433 434 struct C : B { 435 C(); 436 virtual void f(); 437 int c; 438 }; 439 440 struct D : C { 441 // CHECK-LABEL: VFTable for 'pr19408::A' in 'pr19408::B' in 'pr19408::C' in 'pr19408::D' (1 entry). 442 // CHECK-NEXT: 0 | void pr19408::C::f() 443 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual] 444 445 // MANGLING-DAG: @"\01?f@C@pr19408@@$4PPPPPPPM@3AEXXZ" 446 D(); 447 int d; 448 }; 449 450 D::D() {} 451 } 452 453 namespace access { 454 struct A { 455 virtual ~A(); 456 protected: 457 virtual void prot(); 458 private: 459 virtual void priv(); 460 }; 461 462 struct B : virtual A { 463 virtual ~B(); 464 protected: 465 virtual void prot(); 466 // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ" 467 private: 468 virtual void priv(); 469 // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ" 470 }; 471 472 B b; 473 474 struct C : virtual B { 475 virtual ~C(); 476 477 // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ" 478 // MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ" 479 }; 480 481 C c; 482 } 483 484 namespace pr19505 { 485 struct A { 486 virtual void f(); 487 virtual void z(); 488 }; 489 490 struct B : A { 491 virtual void f(); 492 }; 493 494 struct C : A, B { 495 virtual void g(); 496 }; 497 498 struct X : B, virtual C { 499 X() {} 500 virtual void g(); 501 502 // CHECK-LABEL: VFTable for 'pr19505::A' in 'pr19505::B' in 'pr19505::C' in 'pr19505::X' (2 entries). 503 // CHECK-NEXT: 0 | void pr19505::B::f() 504 // CHECK-NEXT: 1 | void pr19505::A::z() 505 506 // MANGLING-DAG: @"\01??_7X@pr19505@@6BB@1@@" = {{.*}}@"\01?f@B@pr19505@@UAEXXZ" 507 } x; 508 509 void build_vftable(X *obj) { obj->g(); } 510 } 511 512 namespace pr19506 { 513 struct A { 514 virtual void f(); 515 virtual void g(); 516 }; 517 518 struct B : A { 519 virtual void f(); 520 }; 521 522 struct C : B {}; 523 524 struct X : C, virtual B { 525 virtual void g(); 526 X() {} 527 528 // CHECK-LABEL: VFTable for 'pr19506::A' in 'pr19506::B' in 'pr19506::X' (2 entries). 529 // CHECK-NEXT: 0 | void pr19506::B::f() 530 // CHECK-NEXT: 1 | void pr19506::X::g() 531 // CHECK-NEXT: [this adjustment: vtordisp at -4, -12 non-virtual] 532 533 // MANGLING-DAG: @"\01??_7X@pr19506@@6BB@1@@" = {{.*}}@"\01?f@B@pr19506@@UAEXXZ" 534 } x; 535 536 void build_vftable(X *obj) { obj->g(); } 537 } 538 539 namespace pr19519 { 540 // VS2013 CL miscompiles this, just make sure we don't regress. 541 542 struct A { 543 virtual void f(); 544 virtual void g(); 545 }; 546 547 struct B : virtual A { 548 virtual void f(); 549 B(); 550 }; 551 552 struct C : virtual A { 553 virtual void g(); 554 }; 555 556 struct X : B, C { 557 X(); 558 559 // CHECK-LABEL: VFTable for 'pr19519::A' in 'pr19519::B' in 'pr19519::X' (2 entries). 560 // CHECK-NEXT: 0 | void pr19519::B::f() 561 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual] 562 // CHECK-NEXT: 1 | void pr19519::C::g() 563 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual] 564 565 // MANGLING-DAG: @"\01??_7X@pr19519@@6B@" = {{.*}}@"\01?g@C@pr19519@@$4PPPPPPPM@3AEXXZ" 566 }; 567 568 X::X() {} 569 } 570