A vtable is an array of function pointers. In a single-inheritance context, you'd have one such array per class where the elements of the array are the class's virtual methods. Each object would then contain a pointer to its class's vtable and each virtual method call would simply invoke the corresponding pointer in the vtable (after casting it to the needed type).
So let's say you're compiling a program that looks like this:
class A {
int x,y;
virtual int foo() { return x+y; }
virtual int bar() { return x*y; }
}
class B inherits A {
int z;
override int bar() { return x*y+z; }
}
int f(A a) {
return a.foo() + a.bar();
}
Then you could define functions named A_foo
, A_bar
and B_bar
taking an A
or B
pointer and containing the code for A.foo
, A.bar
and B.bar
respectively (the exact naming would depend on your name mangling scheme of course). Then you'd generate two globals A_vtable
and B_vtable
that'd look like this:
@A_vtable = global [2 x void (...)*] [
void (...)* bitcast (i32 (%struct.A*)* @A_foo to void (...)*),
void (...)* bitcast (i32 (%struct.A*)* @A_bar to void (...)*)
]
@B_vtable = global [2 x void (...)*] [
void (...)* bitcast (i32 (%struct.A*)* @A_foo to void (...)*),
void (...)* bitcast (i32 (%struct.B*)* @B_bar to void (...)*)
]
Which would correspond to this C code (which is hopefully more readable):
typedef void (*fpointer_t)();
fpointer_t A_vtable[] = {(fpointer_t) A_foo, (fpointer_t) A_bar};
fpointer_t B_vtable[] = {(fpointer_t) A_foo, (fpointer_t) B_bar};
f
could then be translated like this:
define i32 @f(%struct.A*) {
%2 = getelementptr inbounds %struct.A, %struct.A* %0, i64 0, i32 0
%3 = bitcast %struct.A* %0 to i32 (%struct.A*)***
%4 = load i32 (%struct.A*)**, i32 (%struct.A*)*** %3
%5 = load i32 (%struct.A*)*, i32 (%struct.A*)** %4
%6 = call i32 %5(%struct.A* %0)
%7 = load void (...)**, void (...)*** %2
%8 = getelementptr inbounds void (...)*, void (...)** %7, i64 1
%9 = bitcast void (...)** %8 to i32 (%struct.A*)**
%10 = load i32 (%struct.A*)*, i32 (%struct.A*)** %9
%11 = call i32 %10(%struct.A* %0)
%12 = add nsw i32 %11, %6
ret i32 %12
}
Or in C:
typedef int (*A_int_method_t)(struct A*);
int f(struct A* a) {
return ((A_int_method_t) a->vtable[0])(a) + ((A_int_method_t) a->vtable[1])(a);
}