Vcall offsets and mangling

Mark Mitchell mark at codesourcery.com
Wed Jun 21 23:35:19 UTC 2000


Folks --

We learned some interesting things about what worked and what didn't
when we implemented the ABI mangling in G++.  Alex has a rewritten
specification that is very complete.  He'll post it shortly.  There
were a lot of things unspecified, and some things specified in ways
that were notably inconvenient for the implementation.
  
Also, I noticed this today, trying to finish up the vcall offset
implementation in g++:

  struct A {
    virtual void f {};
  };

  struct B : virtual public A {
    int i;
  };

  struct C : virtual public A {
    int j;
  };

  struct D: public B, public C {};

The basic idea of the thunk thing is that all thunks are knowable
statically, allowing the fall-through implementation of thunks.  So,
the ABI actually mandates where you emit thunks, and says that you
emit them with the overriding virtual function.

Consider the graph above.  Here, we need a thunk for the C-in-D
vtable.  (Why?  A is a primary base of C.  So, C's vtable contains an
entry for f.  But, in D, A is also a primary base of B, and therefore
located at a non-zero offset from C.  So, C's vtable must contain a
thunk adjusting from C* to A* before calling f.)

We have a vcall offset for A::f in the C-in-D vtable.  We don't have a
thunk for A::f, though.  When A was emitted (without knowledge of C,
let alone D), there was no reason to expect any thunks to A::f.  We
could have emitted an A-as-a-virtual-base thunk with A::f, just in
case, but that's wasteful if A never is a virtual base.  Doing so
would violate the you-don't-pay-for-what-you-don't-use principle.

(This can only happen because A is a primary base.  If A were not
primary, then C's vtable wouldn't contain a reference to A::f.)

I think the right solution is, in this situation, to fall back on
generating thunks, in COMDAT groups, where you need them to define
vtables, just like the good old days.  These thunks will pay the
double-jump penalty.

On thinking about this further, I actually think a better solution
would have been to handle thunk emission in the linker.  The linker
could insert the thunks that are needed at link-time.  You could have
COMDAT thunks defined in case the linker didn't want to do that; an
optimizing linker would ignore the COMDAT thunks and put in the right
instructions in front of the virtual functions so that the
fall-through thing worked right.  

Can we fall back to making thunks weak, and generating them when you
need them for the vtable?  This doesn't stop an implementation from
emitting strong definitions right in front of the virtual functions,
as we'd planned, as an optimization.  And it doesn't stop the linker
from playing the games mentioned above.  All it means is that
implementation is easier -- and that object files (not executables)
will be a little bigger since they will contain extra copies of the
thunks.

Unfortunately, neither I nor Alex can be at the meeting tomorrow.
But, I will call in at 10:00 AM.  I have to be off by 10:55 AM, or so,
for an 11:00 conference call; Alex is unavailable at 11:00 as well.
He may or may not be able to join me on the phone some of the time.

--
Mark Mitchell                   mark at codesourcery.com
CodeSourcery, LLC               http://www.codesourcery.com




More information about the cxx-abi-dev mailing list