Search code examples
c++compiler-constructionvtable

how to implement jump call to appropriate vtable entry


I am working on an assignment on compiler design. In the code generation part I am stuck with how to create instructions which will ensure that appropriate method is called at run time. The language is a very small subset of C++.

let's say:

void main()
{
  Animal* a;
  a = new Cow;
  //what code should be generated to ensure that object 'a' calls Cow::Init here
  a->Init(5);
}

class Cow : public Animal{
 void Init(int h)
 {
   height = h;
 }
}

class Animal {
 int height; 
 virtual void Init(int h){
   height = h;
  }
}

Solution

  • a very simple way of doing this(note: this excludes optimizing for know calls at compile time): if your class has any virtual members (including inherited), then its very first member becomes a pointer to a vftable. the vftable is constant per class definition which is why you need only a pointer.

    from there, each unique function is assigned an index in that vftable, so each unique name (note: by name I mean the symbol name including types, but no class-namespace qualification) has a unique index, then the table is filled in from the class at the very top of the inheritance tree down to your current working class definition.

    In doing so, newer redifinitions of virtual function will overwrite the older entries that share their index. calling the functions then becomes trivial as you just generate a call to the index for that function's name-index.

    So in your example, Animal has a vftable with 1 entry, Init(int), which is assigned the unique index of 0. so you have a vftable looking like so:

    ;Animal - vftable
    &Animal::Init //note: this isn't a class member pointer in the C++ sense, its a namespaced function pointer if you will
    

    then when you build the vftable for Cow, you use Animals as a base and add in the virtual functions, in this case Init(int), but it already has a unique index of 0, so we overwrite the function at index 0:

    ;Cow - vftable
    &Cow::Init
    

    then if we have the call:

    a->Init(5);
    

    we simply transform that to:

    a->vftable[0](5);
    

    where 0 was the unique index allocated to Init(int).

    an assembly example just in case that helps:

    ;ecx contains our class pointer
    mov eax,[ecx] ;get the vftable ptr
    mov eax,[eax] ; get the ptr at (vftable + (unique_index * sizeof(func_ptr)))
    push 5 ;push our arg 5, ecx is already setup for __thiscall
    call eax ; let it rip!
    

    note: this all assumes your symbol table is setup to be able to detect virtual functions passed through inheritance or those that become virtual from inheritance.


    If this where to be optimized you could analyze a and find that its only assigned a value once, thus you can morph its class to the class of the value it was assigned, Cow. then seeing as you have a class at the end of a derivation chain, you can fold away the vftable call and use a call directly to Cow::Init, how this is a lot more tricky, and there are many ways of optimizing out vftable calls, for a project it shouldn't matter.