我正在编写一个玩具编译器,并且希望我的语言支持虚拟方法,但是我不知道该怎么做,这似乎不像其他语句那样简单,可以很容易地将其转换为IR代码,v-table概念在我脑海中的存在就像一些高级图示所显示的那样。这可能足以使用一种OOP语言,但似乎不足以编写一种。
我试图编写一些C ++代码并将其转换为ir代码,但遗憾的是我仍然无法理解输出。我检查了Clang的源代码,甚至不知道这部分在哪里...(嗯,我得到了代码,它似乎位于lib/CodeGen/CGClass.cpp
,但是Clang是一个复杂的项目,我仍然无法理解它如何实现v表)
那么有什么想法怎么做,还是有一些llvm api可以帮助我实现这一点?
答案 0 :(得分:1)
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);
}