Virtual function call stuff, again

Jason Merrill jason at cygnus.com
Tue Feb 22 23:40:45 UTC 2000


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

 >>>>>> 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:

Oops; I missed the basic misunderstanding in my previous reply.

Actually, the algorithm for this case is very simple: use the same entry
point as is used in the 'A-in-C' secondary vtable.  Since 'B' does not
override 'f', it does not introduce a new entry point.  That's the primary
design goal.

So the interesting question is, what do you put in the 'A-in-C' secondary
vtable?

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

...from 'C' to 'A'...for 'C'.

 >>     - 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'.

...when the 'A-in-C' vtable is created.

 >>     - 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'.

 > How is that defined?  We're looking at paths between `B' and `C'.  But
 > maybe I got that wrong, too?

Yes; see above.

 >>       (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.

 > Something is under-specified there.  (We're looking for something that
 > is a virtual base, right?)  How, exactly, do we find `V'?  That's the
 > part that still seems most unclear to me.

Walk down the inheritance chain from A to C, and stop when you see the
first use of virtual inheritance.

 >>       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'

'A-in-C'

 >>       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'.

'V-in-B' is correct here.  When B is defined, the only thing that needs to
change in the secondary vtable for V is the vcall offset, not the entry
point.

 >   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

 > How does that work if there is more than one `A'?  I understand that
 > more than one `V' could be handled by having different vcall offsets
 > for each `V'.

If there is more than one A, then you can't just fall through, you have to
branch.  So you would add (at the beginning of the above):

A1 vtable entry point
  adjusts the A1* to a V* by a constant offset and jumps to the V entry point

But you can use a PC-relative branch in this situation, so the performance
hit is small.

 > Our first priority is to provide a conforming implementation.  I
 > expect that will mean that we don't take full advantage of the new
 > ABI.  But, the good news is that, because it's an ABI, we can take
 > more advantage of it later and still link with things produced by
 > earlier versions of the compiler.

Fair enough.

Jason




More information about the cxx-abi-dev mailing list