Virtual function call stuff, again

Jason Merrill jason at cygnus.com
Tue Feb 22 21:27:14 UTC 2000


>>>>> Mark Mitchell <mark at codesourcery.com> writes:

 >   o Suppose a class `A' defines a virtual function `A::f'.  The 
 >     primary vtable for `A' contains a pointer to an entry point
 >     that performs no adjustment.

 >   o Suppose that a class `A' declares a virtual function `A::f', 
 >     and suppose that `A' is a base class in a hierarchy dominated 
 >     by another class `B'.  Suppose that the unique final overrider for
 >     `A::f' in `B' is `C::f'.  We must determine what entry point
 >     is used for `f' in the `A-in-B' secondary vtable.  Here is the
 >     algorithm:

 >     - Find any path from `B' to `C' in the inheritance graph for `B'.

 >     - If there is no virtual base along the path, then create
 >       an entry point which adjusts the `this' pointer from `A' to `C'.
 >       This value can be computed statically when the `A-in-B' vtable
 >       is created.  Then transfer control to the non-adjusting entry
 >       point for `C::f'.

 >     - If there is a virtual base along this path, let `V' be the
 >       virtual base nearest to `C' along the path.  (In fact, `V'
 >       will be `C' itself if `C' is a virtual base.)

Rather, nearest to 'A'.

 >       (Note that the choice of `V' is independent of the choice of path.
 >       If there was more than one path, then there must have been a
 >       virtual base along all of the paths, and there is a unique one
 >       closest to `C'.)

No; we're looking for the most-derived base subobject of which our A is a
non-virtual base, which is unique.

 >       Now, create an entry point which first performs the adjustment
 >       from `A' to `V'.  (This value can be computed statically, when
 >       the `A-in-B' vtable is created.)  Then, adjust the `this'
 >       pointer by the vcall offset stored in the secondary vtable for
 >       `V' (i.e., the `V-in-B' vtable).  (This adjustment will adjust
 >       the `this' pointer from `V' to `C'.)  Finally, transfer control
 >       to the non-adjusting entry point for `C::f'.

This is correct.

 >   It seems like the scheme specified in the ABI is advantageous in a
 > situation where `C' is the same as `A', and `A' is the same as `V'.
 > (In other words, if `A' is a virtual base and `A::f' is not overriden
 > in `B'.)  Then, by emitting the vcall-adjusting entry point right
 > before the main entry point for `C::f', calling `f' requires only one
 > branch (to the entry point specified in the vtable), rather than two
 > (to a thunk, and then from the thunk to the main function).  Right?

Hmm?  If C is the same as A, no adjustment is necessary at all, and the
vtable can point directly to the main function.

If C is different from A, but A is the same as V, then we need one entry
point directly before the main function, which adds the vcall offset and
falls through.

If C != A != V, and V overrides f, then we need one more entry point, so we
get:

A vtable entry point
   adjusts the A* to a V* by a constant offset and falls through to
V vtable entry point
   adjusts the V* to a C* by the vcall offset in the vtable and falls
   through to
C::f

 >   I still can't see why it is a win to use vcall offsets in the case
 > where `A' and `V' are not the same class.  You already have to do one
 > static adjustment in the entry point -- why not just adjust all the
 > way to `B' directly, without bothering to look up the vcall offset?

Because the offset from A* to B* (or to C*) may change in the presence of
further derivation, requiring a third-party thunk (i.e. a thunk emitted
apart from the main function).  That's what we're going to all this trouble
to avoid.  We don't want to pay the price of that indirect jump, which is
quite high on modern architectures due to branch prediction and cache
locality problems.

 >   Furthermore, the actual algorithm used to perform the adjustments
 > does not seem necessarily to be part of the ABI.  The layout of the
 > vtables is certainly part of the ABI.  But, if one compiler wants to
 > completely ignore the vcall offset entries in the vtables, and compute
 > the entire adjustment statically, shouldn't that be permitted by the
 > ABI, even though it might require one extra branch?  Surely that's
 > just a quality-of-implementation issue?

I suppose so, though I hope you won't do that for g++; users have been
complaining about the thunk penalty for some time, and the whole point of
this scheme is to eliminate it.

Jason




More information about the cxx-abi-dev mailing list