我正在编写一个玩具编译器,并希望我的语言支持虚拟方法,但我不知道如何做到这一点,它似乎不像其他语句一样直接,可以很容易地转换成IR代码而不需要再考虑,v我脑子里的表格概念就像一些图形和线条一样存在,就像一些高级别的说明。这可能足以使用OOP语言,但似乎还不足以写一个。
我试着编写一些C ++代码并将其转换为代码,但遗憾的是我仍无法理解输出。我检查了Clang的源代码,甚至无法弄清楚这部分的位置......(好吧,我得到了代码,它似乎位于lib/CodeGen/CGClass.cpp
,但是Clang是一个复杂的项目,我仍然无法理解它是怎么回事实现v表)
所以任何想法如何做到这一点,还是有一些llvm api来帮我实现这个?
vtable是一个函数指针数组。在单继承上下文中,每个类都有一个这样的数组,其中数组的元素是类的虚方法。然后,每个对象都包含一个指向其类的vtable的指针,每个虚方法调用只会调用vtable中的相应指针(在将其转换为所需类型之后)。
所以,假设您正在编译一个如下所示的程序:
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();
}
然后你可以定义一个名为A_foo
,A_bar
和B_bar
的函数,它们分别使用A
或B
指针并包含A.foo
,A.bar
和B.bar
的代码(确切的命名当然取决于你的名字修改方案)。然后你会生成两个全局的A_vtable
和B_vtable
,看起来像这样:
@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 (...)*)
]
这对应于这个C代码(希望更具可读性):
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
然后可以这样翻译:
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
}
或者在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);
}