@@ -64,7 +64,7 @@ class FinalOverriders {
64
64
// / Method - The method decl of the overrider.
65
65
const CXXMethodDecl *Method;
66
66
67
- // / VirtualBase - The virtual base class subobject of this overridder .
67
+ // / VirtualBase - The virtual base class subobject of this overrider .
68
68
// / Note that this records the closest derived virtual base class subobject.
69
69
const CXXRecordDecl *VirtualBase;
70
70
@@ -2779,6 +2779,103 @@ VFTableBuilder::ComputeThisOffset(FinalOverriders::OverriderInfo Overrider) {
2779
2779
return Ret;
2780
2780
}
2781
2781
2782
+ // Things are getting even more complex when the "this" adjustment has to
2783
+ // use a dynamic offset instead of a static one, or even two dynamic offsets.
2784
+ // This is sometimes required when a virtual call happens in the middle of
2785
+ // a non-most-derived class construction or destruction.
2786
+ //
2787
+ // Let's take a look at the following example:
2788
+ // struct A {
2789
+ // virtual void f();
2790
+ // };
2791
+ //
2792
+ // void foo(A *a) { a->f(); } // Knows nothing about siblings of A.
2793
+ //
2794
+ // struct B : virtual A {
2795
+ // virtual void f();
2796
+ // B() {
2797
+ // foo(this);
2798
+ // }
2799
+ // };
2800
+ //
2801
+ // struct C : virtual B {
2802
+ // virtual void f();
2803
+ // };
2804
+ //
2805
+ // Record layouts for these classes are:
2806
+ // struct A
2807
+ // 0 | (A vftable pointer)
2808
+ //
2809
+ // struct B
2810
+ // 0 | (B vbtable pointer)
2811
+ // 4 | (vtordisp for vbase A)
2812
+ // 8 | struct A (virtual base)
2813
+ // 8 | (A vftable pointer)
2814
+ //
2815
+ // struct C
2816
+ // 0 | (C vbtable pointer)
2817
+ // 4 | (vtordisp for vbase A)
2818
+ // 8 | struct A (virtual base) // A precedes B!
2819
+ // 8 | (A vftable pointer)
2820
+ // 12 | struct B (virtual base)
2821
+ // 12 | (B vbtable pointer)
2822
+ //
2823
+ // When one creates an object of type C, the C constructor:
2824
+ // - initializes all the vbptrs, then
2825
+ // - calls the A subobject constructor
2826
+ // (initializes A's vfptr with an address of A vftable), then
2827
+ // - calls the B subobject constructor
2828
+ // (initializes A's vfptr with an address of B vftable and vtordisp for A),
2829
+ // that in turn calls foo(), then
2830
+ // - initializes A's vfptr with an address of C vftable and zeroes out the
2831
+ // vtordisp
2832
+ // FIXME: if a structor knows it belongs to MDC, why doesn't it use a vftable
2833
+ // without vtordisp thunks?
2834
+ //
2835
+ // When foo() is called, an object with a layout of class C has a vftable
2836
+ // referencing B::f() that assumes a B layout, so the "this" adjustments are
2837
+ // incorrect, unless an extra adjustment is done. This adjustment is called
2838
+ // "vtordisp adjustment". Vtordisp basically holds the difference between the
2839
+ // actual location of a vbase in the layout class and the location assumed by
2840
+ // the vftable of the class being constructed/destructed. Vtordisp is only
2841
+ // needed if "this" escapes a
2842
+ // structor (or we can't prove otherwise).
2843
+ // [i.e. vtordisp is a dynamic adjustment for a static adjustment, which is an
2844
+ // estimation of a dynamic adjustment]
2845
+ //
2846
+ // foo() gets a pointer to the A vbase and doesn't know anything about B or C,
2847
+ // so it just passes that pointer as "this" in a virtual call.
2848
+ // If there was no vtordisp, that would just dispatch to B::f().
2849
+ // However, B::f() assumes B+8 is passed as "this",
2850
+ // yet the pointer foo() passes along is B-4 (i.e. C+8).
2851
+ // An extra adjustment is needed, so we emit a thunk into the B vftable.
2852
+ // This vtordisp thunk subtracts the value of vtordisp
2853
+ // from the "this" argument (-12) before making a tailcall to B::f().
2854
+ //
2855
+ // Let's consider an even more complex example:
2856
+ // struct D : virtual B, virtual C {
2857
+ // D() {
2858
+ // foo(this);
2859
+ // }
2860
+ // };
2861
+ //
2862
+ // struct D
2863
+ // 0 | (D vbtable pointer)
2864
+ // 4 | (vtordisp for vbase A)
2865
+ // 8 | struct A (virtual base) // A precedes both B and C!
2866
+ // 8 | (A vftable pointer)
2867
+ // 12 | struct B (virtual base) // B precedes C!
2868
+ // 12 | (B vbtable pointer)
2869
+ // 16 | struct C (virtual base)
2870
+ // 16 | (C vbtable pointer)
2871
+ //
2872
+ // When D::D() calls foo(), we find ourselves in a thunk that should tailcall
2873
+ // to C::f(), which assumes C+8 as its "this" parameter. This time, foo()
2874
+ // passes along A, which is C-8. The A vtordisp holds
2875
+ // "D.vbptr[index_of_A] - offset_of_A_in_D"
2876
+ // and we statically know offset_of_A_in_D, so can get a pointer to D.
2877
+ // When we know it, we can make an extra vbtable lookup to locate the C vbase
2878
+ // and one extra static adjustment to calculate the expected value of C+8.
2782
2879
void VFTableBuilder::CalculateVtordispAdjustment (
2783
2880
FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset,
2784
2881
ThisAdjustment &TA) {
0 commit comments