LLVM_IR
LLVM IR 中间代码生成
类的设计
Value
- 主要作用:llvm中的基类
Type
- 主要作用:llvm所有类型的基类
- 子类:
IntType
: 整数类型,包括i32和i1VoidType
: 主要用于占位ArrayType
: 数组类型PointerType
: 指针类型
Module
- 主要作用:一个Module代表一个文件,本次实验中只需要一个Module即可。
BasicBlock
- 主要作用:基本块,部分指令的合集,一个基本块中不应该有跳转和返回指令,一个基本块中的所有指令要么全都执行要么全都不执行。
Instruction
- 主要作用:llvm指令的基类。
- 子类:
BinaryInst
UnaryInst
AllocInst
BranchInst
GetElementPtrInst
RetInst
LoadInst
StoreInst
- …
更多详细的可以参考llvm的官方文档。
递归下降分析
该过程就是根据语法分析生成的语法树,递归下降分析,生成llvm中间代码并生成一个新的llvm的数据结构,用于之后的中间代码优化。
个人递归下降分析的时候是用visit
函数,递归分析的时候基本是按照如下模式:
1 |
|
值得注意的是,对于expression
和statement
的visit
函数可能略有不同:
1 |
|
原因在于对statement
的处理只需要生成一条或数条指令即可,但是对于expression
的处理应当返回一个结果,用于外层语句的调用。
比如return a + 2;
,对于a + 2
的访问应当返回一个value
结果,用于生成一条RetInst
语句。
难点
变量声明和存储
局部变量声明是需要生成AllocInst
,结果是一个该变量类型的对应指针类型。
变量的取值是需要通过LoadInst
,从之前AllocInst
的结果存储的指针里取出数值。
变量的存储是将一个新的值存到之前的指针中。
对一个变量的操作都离不开它的指针,因此我们需要一个新的符号表,将一个Identifier
对应到一个指针类型的Value
。
变常量的初始化
全局变量和常量的初始化一定是编译时可求的值,一般是常数值或者是常量,对于常数值可以单独处理,在编译过程中直接求值;对于常量在访问符号表时获得其常数值然后求值。
局部变量的初始化通过StoreInst
即可解决。
数组的处理
- 数组的处理涉及到
ArrayType
和PointerType
的转换,建议多看标准的llvm工具链如何处理数组然后学习。 - 数组的取址需要用到
GetElementPtr
指令,使用方法非常灵活。在处理二维数组时,建议调用两次该指令,每次降一个维度。可以参考这里