Kaleidoscope LLVM-IR codegen

学习内容

完成对 https://llvm.org/docs/tutorial/MyFirstLanguageFrontend/LangImpl03.html 的学习。

练习

看懂原网页后,实现同样的功能,补充测试。
实现时需要做如下改进:
将codegen过程从AST结构中剥离。编写一个codegenerator类,llvm-ir codegen作为该类的子类实现原示例的功能。为后续提供codegen到其他IR的扩展留下空间。

扩展

1 clang 是如何实现AST到LLVM-IR的codegen
2 玩具实现和clang的实现之间有没有实质性的区别
3 是否能以较低的代价实现向方舟编译器的Maple-IR输出

进展

实现了原示例的全部功能。
考虑到单元测试效率较低,暂时尚未实现。后续可以考虑接入LLVM的测试框架。

实现

使用了code_generator这个模板虚基类来表达了处理Kaleidoscope玩具语言AST所需的功能接口。全局的入口只有function和prototype,理论上其实提供两个对应的gen函数就可以了。但是从完整性上考虑,其余的AST类型处理也最好做出约束,所以也一并写到了父类中。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <typename VAL_PTR>
class code_generator
{
public:
virtual ~code_generator(){};
virtual bool gen_function(const function_ast* func) = 0;
virtual bool gen_prototype(const prototype_ast* proto) = 0;
virtual VAL_PTR build_expr(const expr_ast* expr) = 0;
virtual VAL_PTR build_call(const call_ast* callee) = 0;
virtual VAL_PTR build_number(const number_ast* num) = 0;
virtual VAL_PTR build_variable(const variable_ast* var) = 0;
virtual VAL_PTR build_binary_op(const binary_operator_ast* var) = 0;
virtual bool codegen(const ast_vector_t& global_vec) ; //唯一的数据输入,使用AST的vector
virtual void print_IR() = 0; //唯一的数据输出,直接把生成的IR打印到stdin
};

针对LLVM-IR,参考原示例实现了基本相同的IR生成步骤。
主要的差异点包括:
1 去掉了JIT特性所需的全局expr处理;考虑学习方向是传统编译器架构,这个JIT特性会干扰到后续支持全局变量等特性,所以去掉。
2 将原实现基于AST的codegen转换为了独立的codegen框架,后续实现新的codegen会更容易(但是考虑到工作量和复杂度,没有实现vistor模式,两种codegen中一定会出现重复的AST访问模式。这样的模式确实是由AST来固化更好,参考clang对比中的相关内容)。

clang对比

1 clang 是如何实现AST到LLVM-IR的codegen
clang在lib/CodeGen实现了相关的功能。
在实现codegen功能上,代码流程和玩具实现没有本质区别。
都是遍历自己的AST,在这个过程中调用LLVM提供的核心类Module、Function、Builder来生成LLVM的IR。

2 玩具实现和clang的实现之间有没有实质性的区别
从调用LLVM的接口这个角度看没有实质性区别。
但是clang大量实现了Vistor模式,如AST/StmtVisitor.h、AST/RecursiveASTVisitor.h等。这些Visitor很好地将AST的固有访问模式固化下来,这样不同的任务(如print、codegen和evaluate等)就可以只提供针对具体AST结构的回调实现,而复用访问AST的骨架代码。
例如当前这个codegen的任务,它对AST的访问模式就是针对每一个函数递归的访问了所有AST节点。只要对每一种AST提供对应的visit代码就可以了。
例如function的visit就可以写成(如下模板),LLVM-IR生成器(作为下面的subclass)和MAPLE-IR生成器各自提供visit_prototype等visit接口就可以了。

1
2
3
4
check_muldef();
subclass::visit_prototype(func->getproto());
prepare_arg_map();
subclass::visit_func_body(func->get_body());

方舟IR输出

方舟的实现也提供了类似的build函数,可参考mir_builder.h。
应该能以相似的方式(以及成本)从AST直接build出MAPLE-IR。
具体还待确认和核实。