This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: layout of the g++ virtual tables
- To: comar at act-europe dot fr
- Subject: Re: layout of the g++ virtual tables
- From: mrs at wrs dot com (Mike Stump)
- Date: Tue, 4 Aug 1998 15:08:17 -0700
- Cc: egcs at cygnus dot com
Executive summary:
All you need do, is dispatch as you did before, just remove the this
offset code, and change the vtable entry size from 8 to 4.
> Date: Tue, 4 Aug 1998 12:22:16 +0200
> From: Cyrille Comar <comar@act-europe.fr>
> To: mrs@wrs.com
> I am one of the GNAT implementors and as you probably know, we
> maintain an interface between C++ and Ada that allows to derive Ada
> 95 tagged types from C++ classes. We support inter-language
> dispatching and other nice features like that but for this we need
> to have some detailed information of the virtual table layout.
> I met with your team when we started this project a few years back
> and they kindly gave me details on this layout. they also warned me
> that the layout may change in the future since you were going to use
> a thunk scheme for multiple inheritance. Well, it seems that the
> default layout has indeed changed between 2.7.2 and 2.8.1 and I am
> now looking for a description of the new layout. I glanced
> gxxint.texi without much success.
> I already found out how to deal with the simple case where only
> simple inheritance is used but in the old model, we went as far as
> computing the displacement for 'This' for virtual functions not
> coming from the main inheritance branch. Now there is apprently no
> 'displacement' anymore in the virtual table and I would need some
> more information to acheive the same level of binding. I would also
> be delighted to know more how the type specific information is
> stored.
> I would appreciate any information you can give me.
I hope you don't mind me copying egcs on this, but I thought I would
so that if others that want to comment can have a chance and I didn't
think you would mind. Also, people can correct me if I get anything
wrong, and people can fill in any gaps, if I miss a few, and so that
others may read an understand a bit more, if they want.
In fact, some ports (linux for example) already use the `new' scheme
by default. Thunks can be turned on and off on most targets via
-fvtable-thunks. In the compiler, you can know the default by
checking DEFAULT_VTABLE_THUNKS, if defined, and if it is true, then
the new scheme is in use. At some point, we will (may) switch this,
and have all platforms default the otherway. I suspect you'll just
have to discover this, and readjust your code.
Ok, now onto the main stuff...
The new format is exactly like the old, except the entries in the
vtables are smaller, and just consist of one function pointer. This
pointer points to a routine that will implement the virtual function,
with the this offset being done in the called code. In C++, if MI
offseted virtual calls are needed, then we generate a thunk, and put
the address of this generated function in in the vtable instead of the
address of the real virtual function, as dispatches from that vtable
assume that this pointer is to the base class.
Let's consider:
class Space {
public:
int i;
};
class Right {
public:
int j;
virtual void foo() { printf("j is at %x\n", &j); }
};
class Mid : public Space, public Right {
public:
int k;
virtual void foo() { printf("j is at %x\n", &j); }
} a, *ptr_a = &a;
int main() {
printf("a is at %x, j is at %x, k is at %x\n", &a, &a.j, &a.k);
ptr_a->foo();
}
which is almost the canonical example case of an MI offseted virtual
call. The canonical one would use a Right pointer, and dispatch that
way. For an example of a thunk function, on the sparc, we get:
__thunk_4_foo__3Mid:
add %o0,-4,%o0
mov %o7,%g1
call foo__3Mid,0
mov %g1,%o7
which just adds a -4 (that was the offset that was in the vtable
before) to the this pointer ($%o0) and calls the real foo (Mid::foo).
The other stuff is SPARC hair, and can be ignored. All you need do,
is dispatch as you did before, just removing the this offset code.
The adding of -4 to $o0 used to be handled at the call site in main
body code, via fetching the -4 out of the vtable and assing it to %o0,
now, instead the main body code just does:
sethi %hi(ptr_a),%o0
ld [%o0+%lo(ptr_a)],%o0
ld [%o0+8],%o1 ; get vtable pointer
ld [%o1+8],%o1 ; get ``first'' vtable entry
call %o1,0
add %o0,4,%o0 ; this happens before the call above
Where %o0 is the same as before (a pointer to the Right subobject).
The adding the 4 converts it from a Mid pointer to a Right pointer,
and this happens before the call. The first vtable entry is at 8
(really 2 * sizeof (void (*)())) (magic number, sorry). The function
to call is found from the vtable pointer (found in same way as
before), and it slightly too complex to elaborate on here. And the 8,
is because it is in the first slot in the vtable, (again, slightly to
complex to elaborate on why here, in general).
Hopefully this answers your immediate question. Let us know if
something was unclear, or if you need elaboration on any of the
points. For others reading this, I realize that some things like slot
management and assignment is wholly unanswered, sorry.