diff options
| author | Timur Iskhodzhanov <timurrrr@google.com> | 2014-11-17 15:11:05 +0000 |
|---|---|---|
| committer | Timur Iskhodzhanov <timurrrr@google.com> | 2014-11-17 15:11:05 +0000 |
| commit | 7d19fc1c375cc883b216ce0033671aa20de5f35d (patch) | |
| tree | b61a32e976b80929d3d29ee67c48b6efd65c4655 /clang/lib/AST | |
| parent | 5a8c46653f15350abf326fbf46d7b91cc976671d (diff) | |
| download | bcm5719-llvm-7d19fc1c375cc883b216ce0033671aa20de5f35d.tar.gz bcm5719-llvm-7d19fc1c375cc883b216ce0033671aa20de5f35d.zip | |
Add a couple more examples illustrating why we need vtordisps and how they work
llvm-svn: 222133
Diffstat (limited to 'clang/lib/AST')
| -rw-r--r-- | clang/lib/AST/VTableBuilder.cpp | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp index 48135461385..b78c080fc94 100644 --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -64,7 +64,7 @@ public: /// Method - The method decl of the overrider. const CXXMethodDecl *Method; - /// VirtualBase - The virtual base class subobject of this overridder. + /// VirtualBase - The virtual base class subobject of this overrider. /// Note that this records the closest derived virtual base class subobject. const CXXRecordDecl *VirtualBase; @@ -2779,6 +2779,103 @@ VFTableBuilder::ComputeThisOffset(FinalOverriders::OverriderInfo Overrider) { return Ret; } +// Things are getting even more complex when the "this" adjustment has to +// use a dynamic offset instead of a static one, or even two dynamic offsets. +// This is sometimes required when a virtual call happens in the middle of +// a non-most-derived class construction or destruction. +// +// Let's take a look at the following example: +// struct A { +// virtual void f(); +// }; +// +// void foo(A *a) { a->f(); } // Knows nothing about siblings of A. +// +// struct B : virtual A { +// virtual void f(); +// B() { +// foo(this); +// } +// }; +// +// struct C : virtual B { +// virtual void f(); +// }; +// +// Record layouts for these classes are: +// struct A +// 0 | (A vftable pointer) +// +// struct B +// 0 | (B vbtable pointer) +// 4 | (vtordisp for vbase A) +// 8 | struct A (virtual base) +// 8 | (A vftable pointer) +// +// struct C +// 0 | (C vbtable pointer) +// 4 | (vtordisp for vbase A) +// 8 | struct A (virtual base) // A precedes B! +// 8 | (A vftable pointer) +// 12 | struct B (virtual base) +// 12 | (B vbtable pointer) +// +// When one creates an object of type C, the C constructor: +// - initializes all the vbptrs, then +// - calls the A subobject constructor +// (initializes A's vfptr with an address of A vftable), then +// - calls the B subobject constructor +// (initializes A's vfptr with an address of B vftable and vtordisp for A), +// that in turn calls foo(), then +// - initializes A's vfptr with an address of C vftable and zeroes out the +// vtordisp +// FIXME: if a structor knows it belongs to MDC, why doesn't it use a vftable +// without vtordisp thunks? +// +// When foo() is called, an object with a layout of class C has a vftable +// referencing B::f() that assumes a B layout, so the "this" adjustments are +// incorrect, unless an extra adjustment is done. This adjustment is called +// "vtordisp adjustment". Vtordisp basically holds the difference between the +// actual location of a vbase in the layout class and the location assumed by +// the vftable of the class being constructed/destructed. Vtordisp is only +// needed if "this" escapes a +// structor (or we can't prove otherwise). +// [i.e. vtordisp is a dynamic adjustment for a static adjustment, which is an +// estimation of a dynamic adjustment] +// +// foo() gets a pointer to the A vbase and doesn't know anything about B or C, +// so it just passes that pointer as "this" in a virtual call. +// If there was no vtordisp, that would just dispatch to B::f(). +// However, B::f() assumes B+8 is passed as "this", +// yet the pointer foo() passes along is B-4 (i.e. C+8). +// An extra adjustment is needed, so we emit a thunk into the B vftable. +// This vtordisp thunk subtracts the value of vtordisp +// from the "this" argument (-12) before making a tailcall to B::f(). +// +// Let's consider an even more complex example: +// struct D : virtual B, virtual C { +// D() { +// foo(this); +// } +// }; +// +// struct D +// 0 | (D vbtable pointer) +// 4 | (vtordisp for vbase A) +// 8 | struct A (virtual base) // A precedes both B and C! +// 8 | (A vftable pointer) +// 12 | struct B (virtual base) // B precedes C! +// 12 | (B vbtable pointer) +// 16 | struct C (virtual base) +// 16 | (C vbtable pointer) +// +// When D::D() calls foo(), we find ourselves in a thunk that should tailcall +// to C::f(), which assumes C+8 as its "this" parameter. This time, foo() +// passes along A, which is C-8. The A vtordisp holds +// "D.vbptr[index_of_A] - offset_of_A_in_D" +// and we statically know offset_of_A_in_D, so can get a pointer to D. +// When we know it, we can make an extra vbtable lookup to locate the C vbase +// and one extra static adjustment to calculate the expected value of C+8. void VFTableBuilder::CalculateVtordispAdjustment( FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset, ThisAdjustment &TA) { |

