1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -cxx-abi microsoft -triple=i386-pc-win32 >%t 2 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t 3 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t 4 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t 5 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t 6 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t 7 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t 8 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t 9 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t 10 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t 11 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t 12 // RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t 13 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll 14 15 // For now, just make sure x86_64 doesn't crash. 16 // RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -cxx-abi microsoft -triple=x86_64-pc-win32 >/dev/null 17 18 struct V1 { 19 virtual void f(); 20 virtual ~V1(); 21 }; 22 23 struct V2 { 24 virtual void f(); 25 virtual ~V2(); 26 int v; 27 }; 28 29 struct Z { 30 virtual void g(); 31 virtual ~Z(); 32 int x; 33 }; 34 35 struct V3 : Z, V2 { 36 }; 37 38 struct V4 : Z, V1, V2 { 39 int y; 40 }; 41 42 void use_somewhere_else(void*); 43 44 namespace simple { 45 // In case of a single-layer virtual inheritance, the "this" adjustment is done 46 // staically: 47 // struct A { 48 // virtual void f(); // Expects "(A*)this" in ECX 49 // }; 50 // struct B : virtual A { 51 // virtual void f(); // Expects "(char*)(B*)this + 12" in ECX 52 // virtual ~B(); // Might call f() 53 // }; 54 // 55 // If a class overrides a virtual function of its base and has a non-trivial 56 // ctor/dtor that call(s) the virtual function (or may escape "this" to some 57 // code that might call it), a virtual adjustment might be needed in case the 58 // current class layout and the most derived class layout are different. 59 // This is done using vtordisp thunks. 60 // 61 // A simple vtordisp{A,B} thunk for Method@Class is something like: 62 // sub ecx, [ecx+A] // apply the vtordisp adjustment 63 // sub ecx, B // apply the subobject adjustment, if needed. 64 // jmp Method@Class 65 66 struct A : virtual V1 { 67 // VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries). 68 // VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f() 69 // VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 70 // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting] 71 // VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 72 73 virtual void f(); 74 // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ" 75 76 virtual ~A(); 77 // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z" 78 }; 79 80 A a; 81 82 struct B : virtual V3 { 83 // VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries). 84 // VTABLE-SIMPLE-B-NEXT: 0 | void Z::g() 85 // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting] 86 // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 87 88 // VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries). 89 // VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f() 90 // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] 91 // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting] 92 // VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 93 94 // FIXME: The vtordisp thunk should only get emitted for a constructor 95 // if "this" leaves scope. 96 B() { use_somewhere_else(this); } 97 98 virtual void f(); 99 // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ" 100 101 // Has an implicit destructor. 102 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z" 103 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z" 104 }; 105 106 B b; 107 108 struct C : virtual V4 { 109 // VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries). 110 // VTABLE-SIMPLE-C-NEXT: 0 | void Z::g() 111 // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] 112 // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 113 114 // VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries). 115 // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f() 116 // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual] 117 // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] 118 // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual] 119 120 // VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries). 121 // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f() 122 // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual] 123 // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting] 124 // VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual] 125 126 int x; 127 virtual void f(); 128 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ" 129 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ" 130 virtual ~C(); 131 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z" 132 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z" 133 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z" 134 }; 135 136 C c; 137 } 138 139 namespace extended { 140 // If a virtual function requires vtordisp adjustment and the final overrider 141 // is defined in another vitual base of the most derived class, 142 // we need to know two vbase offsets. 143 // In this case, we should use the extended form of vtordisp thunks, called 144 // vtordispex thunks. 145 // 146 // vtordispex{A,B,C,D} thunk for Method@Class is something like: 147 // sub ecx, [ecx+C] // apply the vtordisp adjustment 148 // sub ecx, A // jump to the vbtable of the most derived class 149 // mov eax, [ecx] // load the vbtable address 150 // add ecx, [eax+B] // lookup the final overrider's vbase offset 151 // add ecx, D // apphy the subobject offset if needed 152 // jmp Method@Class 153 154 struct A : virtual simple::A { 155 // VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries). 156 // VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f() 157 // VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 158 // VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 159 // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting] 160 // VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 161 162 // `vtordispex{8,8,4294967292,8}' 163 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" 164 165 virtual ~A(); 166 // vtordisp{4294967292,0} 167 // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z" 168 }; 169 170 A a; 171 172 struct B : virtual simple::A { 173 // This class has an implicit dtor. Vdtors don't require vtordispex thunks 174 // as the most derived class always has an implicit dtor, 175 // which is a final overrider. 176 177 // VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries). 178 // ... 179 // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting] 180 // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 181 182 // vtordisp{4294967292,0} 183 // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z" 184 }; 185 186 B b; 187 188 struct C : virtual simple::A { 189 // VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries). 190 // VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f() 191 // VTABLE-EXTENDED-C-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left, 192 // VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 193 194 // `vtordispex{12,8,4294967292,8}' 195 // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ" 196 int x; 197 virtual ~C(); 198 // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z" 199 }; 200 201 C c; 202 203 struct D : virtual V2 { 204 virtual void f(); 205 virtual ~D(); 206 int x; 207 }; 208 209 struct E : virtual D { 210 // VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries). 211 // VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f() 212 // VTABLE-EXTENDED-E-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 213 // VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual] 214 215 // `vtordispex{8,8,4294967292,12}' 216 // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ" 217 218 virtual ~E(); 219 // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z" 220 }; 221 222 E e; 223 224 struct F : virtual Z, virtual D { 225 // VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries). 226 // VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f() 227 // VTABLE-EXTENDED-F-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left, 228 // VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual] 229 230 // `vtordispex{20,12,4294967292,12}' 231 // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ" 232 int x; 233 virtual ~F(); 234 // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z" 235 }; 236 237 F f; 238 239 struct G : virtual simple::A { 240 // VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries). 241 // VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g() 242 243 // VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries). 244 // VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f() 245 // VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 246 // VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 247 // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting] 248 // VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] 249 250 // Emits a G's own vfptr, thus moving the vbptr in the layout. 251 virtual void g(); 252 253 virtual ~G(); 254 // vtordisp{4294967292,0} 255 // MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z" 256 }; 257 258 G g; 259 260 struct H : Z, A { 261 // VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries). 262 // VTABLE-EXTENDED-H-NEXT: 0 | void Z::g() 263 // VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting] 264 265 // VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries). 266 // VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f() 267 // VTABLE-EXTENDED-H-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, 268 // VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] 269 270 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" 271 // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z" 272 }; 273 274 H h; 275 } 276 277 namespace pr17738 { 278 // These classes should have vtordispex thunks but MSVS CL miscompiles them. 279 // Just do the right thing. 280 281 struct A : virtual simple::B { 282 // VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries). 283 // VTABLE-PR17738-A-NEXT: 0 | void simple::B::f() 284 // VTABLE-PR17738-A-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left, 285 // VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual] 286 287 // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ" 288 int a; 289 virtual ~A(); 290 }; 291 292 A a; 293 } 294 295 namespace access { 296 struct A { 297 virtual ~A(); 298 protected: 299 virtual void prot(); 300 private: 301 virtual void priv(); 302 }; 303 304 struct B : virtual A { 305 virtual ~B(); 306 protected: 307 virtual void prot(); 308 // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ" 309 private: 310 virtual void priv(); 311 // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ" 312 }; 313 314 B b; 315 316 struct C : virtual B { 317 virtual ~C(); 318 319 // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ" 320 // MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ" 321 }; 322 323 C c; 324 } 325