Light IR¶
Light IR 简介¶
本课程以 Cminusf 语言为源语言,从 LLVM IR 中裁剪出了适用于教学的精简的 IR 子集,并将其命名为 Light IR。同时依据 LLVM 的设计,为 Light IR 提供了配套简化的 C++ 库,用于生成 IR。
如下是一段 C 语言代码 easy.c
与 其对应的 IR 文件 easy.ll
示例。
easy.c
:
int main(){
int a;
int b;
a = 1;
b = 2;
return a + b;
}
easy.ll
:
; 整个 .ll 文件称为 module
; ModuleID = 'easy.c'
; ...
; module 中至少有一个 main function
define dso_local i32 @main() #0 {
; 此处 main function 仅有 1 个 basicblock
; basicblock 由一系列 instruction 组成
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 1, i32* %2, align 4
store i32 2, i32* %3, align 4
%4 = load i32, i32* %2, align 4
%5 = load i32, i32* %3, align 4
%6 = add nsw i32 %4, %5
ret i32 %6
}
; ...
Light IR 指令¶
Light IR 指令假设¶
Light IR 指令从 LLVM IR 中裁剪得到,因此保留了 LLVM IR 如下的指令规范
- 采用 3 地址的方式
%2 = add i32 %0, %1
- 无限寄存器 + 静态单赋值形式
- IR 中的变量均代表了一个虚拟寄存器,并且数量无上限
- 每个虚拟寄存器只被赋值一次
-
强类型系统
-
每个操作数都具备自身的类型,分为基本类型,以及组合类型
基本类型:
i1
:1 位宽的整数类型i32
:32 位宽的整数类型float
:单精度浮点数类型label
: 基本块的标识符类型
组合类型:
- 指针类型:
<type> *
,例如i32*, [10 x i32*]
- 数组类型:
[n x <type>]
,例如[10 x i32], [10 x [10 x i32]]
- 函数类型:
<ret-type>@(<arg-type>)
,由函数返回值类型与参数类型列表组合成的类型
组合类型可以嵌套,例如前面的
[10 x [10 x i32]]
就是嵌套后的类型。
Light IR 指令详解¶
Terminator Instructions¶
注:ret
与 br
都是 Terminator Instructions 也就是终止指令,在 llvm 基本块的定义里,基本块是单进单出的,因此只能有一条终止指令(ret
或 br
)。
Ret¶
- 概念:返回指令。用于将控制流(以及可选的值)从函数返回给调用者。
ret
指令有两种形式:一种返回值,然后终结函数,另一种仅终结函数。 - 格式
ret <type> <value>
ret void
- 例子:
ret i32 %0
ret void
Br¶
- 概念:跳转指令。用于使控制流转移到当前功能中的另一个基本块。该指令有两种形式,分别对应于条件分支和无条件分支。
- 格式:
br i1 <cond>, label <iftrue>, label <iffalse>
br label <dest>
- 例子:
br i1 %cond, label %truebb, label %falsebb
br label %bb
Standard Binary Instructions¶
Add FAdd¶
- 概念:
add
指令返回其两个i32
类型的操作数之和,返回值为i32
类型,fadd
指令返回其两个float
类型的操作数之和,返回值为float
类型。 - 格式:
<result> = add <type> <op1>, <op2>
<result> = fadd <type> <op1>, <op2>
- 例子:
%2 = add i32 %1, %0
%2 = fadd float %1, %0
Sub FSub¶
- 概念:
sub
指令返回其两个i32
类型的操作数之差,返回值为i32
类型,fsub
指令返回其两个float
类型的操作数之差,返回值为float
类型。 - 格式与例子与
add
,fadd
类似
Mul FMul¶
- 概念:
mul
指令返回其两个i32
类型的操作数之积,返回值为i32
类型,fmul
指令返回其两个float
类型的操作数之积,返回值为float
类型。 - 格式与例子与
add
,fadd
类似
SDiv FDiv¶
- 概念:
sdiv
指令返回其两个i32
类型的操作数之商,返回值为i32
类型,fdiv
指令返回其两个float
类型的操作数之商,返回值为float
类型。 - 格式与例子与
add
,fadd
类似
Memory Instructions¶
Alloca¶
- 概念:
alloca
指令在当前执行函数的栈帧(Stack Frame)上分配内存。 - 格式:
<result> = alloca <type>
- 例子:
%ptr = alloca i32
%ptr = alloca [10 x i32]
Load¶
- 概念:
load
指令用于从内存中读取。 - 格式:
<result> = load <type>, <type>* <pointer>
- 例子:
%val = load i32, i32* %ptr
Store¶
- 概念:
store
指令用于写入内存。 - 格式:
store <type> <value>, <type>* <pointer>
- 例子:
store i32 3, i32* %ptr
CastInst¶
ZExt¶
- 概念:
zext
指令将其操作数 零 扩展为type2
类型。 - 格式:
<result> = zext <type> <value> to <type2>
- 例子:
%1 = zext i1 %0 to i32
FpToSi¶
- 概念:
fptosi
指令将浮点值转换为type2
(整数)类型。 - 格式:
<result> = fptosi <type> <value> to <type2>
- 例子:
%Y = fptosi float 1.0E-247 to i32
SiToFp¶
- 概念:
sitofp
指令将有符号整数转换为type2
(浮点数)类型。 - 格式:
<result> = sitofp <type> <value> to <type2>
- 例子:
%X = sitofp i32 257 to float
Other Instructions¶
ICmp FCmp¶
- 概念:
icmp
指令根据两个整数的比较返回布尔值,fcmp
指令根据两个浮点数的比较返回布尔值。 - 格式:
<result> = icmp <cond> <type> <op1>, <op2>
<cond> = eq | ne | sgt | sge | slt | sle
<result> = fcmp <cond> <type> <op1>, <op2>
<cond> = eq | ne | ugt | uge | ult | ule
- 例子:
i1 %2 = icmp sge i32 %0, %1
Call¶
- 概念:
call
指令用于使控制流转移到指定的函数,其传入参数绑定到指定的值。在被调用函数中执行ret
指令后,如果被调用函数返回值不为void
类型,控制流程将在函数调用后继续执行该指令,并且该函数的返回值绑定到result
参数。 - 格式:
<result> = call <return ty> <func name>(<function args>)
call <func name>(<function args>)
- 例子:
%0 = call i32 @func( i32 %1, i32* %0)
call @func( i32 %arg)
GetElementPtr¶
- 概念:
getelementptr
指令用于获取数组结构的元素的地址。它 仅执行地址计算 ,并且不访问内存。 - 格式
- 数组:
<result> = getelementptr <type>, <type>* <ptrval>, i32 0, i32 <idx>
- 指针(仅在数组作为函数参数时存在):
<result> = getelementptr <type>, <type>* <ptrval>, i32 <idx>
- 参数解释:
getelementptr
后面的两个<type>
一定是相同的。<ptrval>
表示指针,<idx>
表示索引(也即数组下标) - 例子:
%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0
%2 = getelementptr i32, i32* %1 i32 %0
- 额外阅读:The Often Misunderstood GEP Instruction