什么是 IR
IR是中间表达式,它在高级语言和汇编语言的中间。这意味着,它的特征也是处于两者之间的。
AST 也是一种 IR,IR 有很多种类(AST 也是一种 IR),每种 IR 都有不同的特点和用途,有的编译器,甚至要用到几种不同的 IR。
LLVM 汇编码(LLVM Assembly),是 LLVM 的 IR。LLVM 汇编码是采用静态单赋值代码形式(Static Single Assignment, SSA)的。SSA 形式,体现了精确的 “使用 - 定义” 关系:只会被定义一次,可以多次使用,方便于数据流的分析。
内存对齐
我们在内存里存放数据的时候,有时会从2、4、8个字节的整数倍地址开始存。有些汇编指令要求必须从这样对齐的地址来取数据。
基本块
函数中的代码会分成一个个的基本块,可以用标签(Label)来标记一个基本块。
; <label>:9: ; preds = %1
%10 = load i32, i32* %3, align 4
%11 = add nsw i32 %10, 3
store i32 %11, i32* %2, align 4
br label %12
第一行是整个基本块的唯一入口,从其他基本块跳转过来的时候,只能跳转到这个入口行,不能跳转到基本块中的其他行。
第二行(%11 = add nsw i32 %10, 3)的含义是:把 10 号变量(32 位整型)加上 3,保存到 11 号变量,其中 nsw 是加法计算时没有符号环绕(No Signed Wrap)的意思。它的细节你可以查阅“LLVM 语言参考手册”。
第三行(store i32 %11, i32* %2, align 4)的含义是:把 11 号变量(32 位整型)存入内存中的 2 号变量,内存对齐 4 字节。
第四行(br label %12)的含义是:跳转到标签为 12 的代码块。其中,br 指令是一条终结指令。终结指令要么是跳转到另一个基本块,要么是从函数中返回(ret 指令),基本块的最后一行必须是一条终结指令。
从其他基本块不可以跳转到入口基本块,也就是函数中的第一个基本块。这个规定也是有利于做数据优化。