LLVM-IR
结构
LLVM IR文件的基本单位称为module,一个module中可以拥有多个顶层实体(比如function和global variable)
一个function define里面至少有一个basicblock,每个basicblock中有若干instruction,并且都以terminator instruction结尾
例子
@a = global i32 5
define i32 @foo(i32 %0, i32 %1) {
%3 = alloca i32
%4 = alloca i32
store i32 %0, i32* %3
store i32 %1, i32* %4
%5 = load i32, i32* %3
%6 = load i32, i32* %4
%7 = add nsw i32 %5, %6
ret i32 %7
}
define i32 @main() {
%1 = alloca i32
%2 = alloca i32
store i32 0, i32* %1
store i32 4, i32* %2
%3 = load i32, i32* @a
%4 = load i32, i32* %2
%5 = call i32 @foo(i32 %3, i32 %4)
ret i32 %5
}
全局变量与局部变量
全局变量和局部变量用前缀作为区分,全局变量和函数名以@
为前缀,局部变量以%
为前缀
局部变量的作用域为单个函数,两个不同局部变量的%1
指代不同的内容
函数定义
define i32 @main() {
ret i32 0
}
define i32 @foo(i32 %a, i32 %b) {
return i32 0
}
函数声明
我们在一个module里,如果想要调用别的模块的函数,就需要在本模块先声明这个函数
declare i32 @getint()
declare i32 @getarray(i32*)
declare i32 @getch()
declare void @putint(i32)
declare void @putch(i32)
declare void @putarray(i32,i32*)
基本块
一个基本块是包含了若干指令和一个终结指令的代码序列
基本块只会从终结指令退出,并且基本块的执行是原子性的,这个约束是通过代码的语义实现的。基本块内部没有控制流,控制流是多个基本块直接通过跳转指令实现的
指令
指令是LLVM IR中的非分支指令,通常用来进行某种计算或者是访存,这些指令不会改变程序的控制流
call指令也是非分支指令(仅仅是跳转,没有分支)
类型
类型 | 描述 |
---|---|
void | 不代表值,不占用空间,占位用 |
i1/i32... | 表示1bit/32bit长的integer |
label | 标签类型,用作代码标签,跳转时用 |
SSA
静态单赋值(Static Single Assignment,SSA)是编译器中间表示中非常重要的一个概念,它是一种变量的命名约定。当程序中每个变量有且仅有一个赋值语句时,称一个程序是SSA形式的
在LLVM IR中,每个变量在使用前都必须被定义,且每个变量只能被赋值一次
有的时候生成SSA不太方便,就是会出现本质为变量重新赋值的情况,我们有两种解决方案
phi
TODO
memory
利用对一个局部变量进行多次load/store操作,利用内存不要求是SSA形式的特点来妥协,但是把每次变量的存取都变成了访存操作,导致严重的性能问题