|
|
|
|
# SysYF IR
|
|
|
|
|
|
|
|
|
|
- [SysYF IR](#sysyf-ir)
|
|
|
|
|
- [IR](#ir)
|
|
|
|
|
- [IR Features](#ir-features)
|
|
|
|
|
- [IR Format](#ir-format)
|
|
|
|
|
- [Instruction](#instruction)
|
|
|
|
|
- [Terminator Instructions](#terminator-instructions)
|
|
|
|
|
- [Ret](#ret)
|
|
|
|
|
- [Br](#br)
|
|
|
|
|
- [Standard binary operators](#standard-binary-operators)
|
|
|
|
|
- [Add FAdd](#add-fadd)
|
|
|
|
|
- [Sub FSub](#sub-fsub)
|
|
|
|
|
- [Mul FMul](#mul-fmul)
|
|
|
|
|
- [SDiv FDiv](#sdiv-fdiv)
|
|
|
|
|
- [SRem](#srem)
|
|
|
|
|
- [Memory operators](#memory-operators)
|
|
|
|
|
- [Alloca](#alloca)
|
|
|
|
|
- [Load](#load)
|
|
|
|
|
- [Store](#store)
|
|
|
|
|
- [CastInst](#castinst)
|
|
|
|
|
- [ZExt](#zext)
|
|
|
|
|
- [FpToSi](#fptosi)
|
|
|
|
|
- [SiToFp](#sitofp)
|
|
|
|
|
- [Other operators](#other-operators)
|
|
|
|
|
- [ICmp FCmp](#icmp-fcmp)
|
|
|
|
|
- [Call](#call)
|
|
|
|
|
- [GetElementPtr](#getelementptr)
|
|
|
|
|
- [C++ APIs](#c-apis)
|
|
|
|
|
- [核心类概念图](#核心类概念图)
|
|
|
|
|
- [BasicBlock](#basicblock)
|
|
|
|
|
- [Constant](#constant)
|
|
|
|
|
- [Function](#function)
|
|
|
|
|
- [GlobalVariable](#globalvariable)
|
|
|
|
|
- [IRStmtBuilder](#irstmtbuilder)
|
|
|
|
|
- [Instruction](#instruction-1)
|
|
|
|
|
- [Module](#module)
|
|
|
|
|
- [Type](#type)
|
|
|
|
|
- [User](#user)
|
|
|
|
|
- [Value](#value)
|
|
|
|
|
- [总结](#总结)
|
|
|
|
|
|
|
|
|
|
## IR
|
|
|
|
|
|
|
|
|
|
### IR Features
|
|
|
|
|
- 采用类型化三地址代码的方式
|
|
|
|
|
- 区别于 X86 汇编的目标和源寄存器共用的模式: ADD EAX, EBX
|
|
|
|
|
- %2 = add i32 %0, %1
|
|
|
|
|
- 静态单赋值 (SSA) 形式 + 无限寄存器
|
|
|
|
|
- 每个变量都只被赋值一次
|
|
|
|
|
- 容易确定操作间的依赖关系,便于优化分析
|
|
|
|
|
- 强类型系统
|
|
|
|
|
- 每个 Value 都具备自身的类型,
|
|
|
|
|
- IR类型系统:
|
|
|
|
|
- `i1`:1位宽的整数类型
|
|
|
|
|
- `i32`:32位宽的整数类型
|
|
|
|
|
- `float`:单精度浮点数类型
|
|
|
|
|
- `pointer`:指针类型
|
|
|
|
|
- 例如:`i32*, [10 x i32*]`
|
|
|
|
|
- `label` bb的标识符类型
|
|
|
|
|
- `functiontype`函数类型,包括函数返回值类型与参数类型(下述文档未提及)
|
|
|
|
|
|
|
|
|
|
### IR Format
|
|
|
|
|
下面以`easy.c`与`easy.ll`为例进行说明。
|
|
|
|
|
通过命令`clang -S -emit-llvm easy.c`可以得到对应的`easy.ll`如下(其中增加了额外的注释)。`.ll`文件中注释以`;`开头。
|
|
|
|
|
|
|
|
|
|
- `easy.c`:
|
|
|
|
|
``` c
|
|
|
|
|
int main(){
|
|
|
|
|
int a;
|
|
|
|
|
int b;
|
|
|
|
|
a = 1;
|
|
|
|
|
b = 2;
|
|
|
|
|
if(a < b)
|
|
|
|
|
b = 3;
|
|
|
|
|
return a + b;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
- `easy.ll`:
|
|
|
|
|
``` c
|
|
|
|
|
; 注释: .ll文件中注释以';'开头
|
|
|
|
|
; ModuleID = 'easy.c'
|
|
|
|
|
source_filename = "easy.c"
|
|
|
|
|
; 注释: target的开始
|
|
|
|
|
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
|
|
|
|
target triple = "x86_64-unknown-linux-gnu"
|
|
|
|
|
; 注释: target的结束
|
|
|
|
|
|
|
|
|
|
; 注释: 全局main函数的定义
|
|
|
|
|
; Function Attrs: noinline nounwind optnone uwtable
|
|
|
|
|
define dso_local i32 @main() #0 {
|
|
|
|
|
; 注释: 第一个基本块的开始
|
|
|
|
|
%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 = icmp slt i32 %4, %5
|
|
|
|
|
br i1 %6, label %7, label %8
|
|
|
|
|
; 注释: 第一个基本块的结束
|
|
|
|
|
|
|
|
|
|
; 注释: 第二个基本块的开始
|
|
|
|
|
7: ; preds = %0
|
|
|
|
|
store i32 3, i32* %3, align 4
|
|
|
|
|
br label %8
|
|
|
|
|
; 注释: 第二个基本块的结束
|
|
|
|
|
|
|
|
|
|
; 注释: 第三个基本块的开始
|
|
|
|
|
8: ; preds = %7, %0
|
|
|
|
|
%9 = load i32, i32* %2, align 4
|
|
|
|
|
%10 = load i32, i32* %3, align 4
|
|
|
|
|
%11 = add nsw i32 %9, %10
|
|
|
|
|
ret i32 %11 ; 注释: 返回语句
|
|
|
|
|
; 注释: 第三个基本块的结束
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
|
|
|
|
|
|
|
|
|
!llvm.module.flags = !{!0}
|
|
|
|
|
!llvm.ident = !{!1}
|
|
|
|
|
|
|
|
|
|
!0 = !{i32 1, !"wchar_size", i32 4}
|
|
|
|
|
!1 = !{!"clang version 10.0.1 "}
|
|
|
|
|
```
|
|
|
|
|
每个program由1个或多个module组成,每个module对应1个程序文件,module之间由LLVM Linker进行链接形成1个可执行文件或者库。
|
|
|
|
|
每个module组成如下:
|
|
|
|
|
- Target Information:
|
|
|
|
|
``` c
|
|
|
|
|
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
|
|
|
|
target triple = "x86_64-unknown-linux-gnu"
|
|
|
|
|
```
|
|
|
|
|
- Global Symbols: main函数的定义
|
|
|
|
|
- Others:尾部其他信息
|
|
|
|
|
|
|
|
|
|
每个函数的组成如下:
|
|
|
|
|
- 头部:函数返回值类型、函数名、函数参数
|
|
|
|
|
- 一个或多个基本块:
|
|
|
|
|
- 每个基本块由Label和Instruction组成。
|
|
|
|
|
``` c
|
|
|
|
|
8: ; preds = %7, %0
|
|
|
|
|
%9 = load i32, i32* %2, align 4
|
|
|
|
|
%10 = load i32, i32* %3, align 4
|
|
|
|
|
%11 = add nsw i32 %9, %10
|
|
|
|
|
ret i32 %11
|
|
|
|
|
```
|
|
|
|
|
这个例子中,`8`就是Label。
|
|
|
|
|
`%9 = load i32, i32* %2, align 4`中的`%9`是目的操作数,`load`是指令助记符,`i32`是`int32`的类型,`i32*`是指向`int32`的地址类型,`%2`是源操作数,`align 4`表示对齐。
|
|
|
|
|
### Instruction
|
|
|
|
|
#### Terminator Instructions
|
|
|
|
|
**注**:ret与br都是Terminator Instructions也就是终止指令,在llvm基本块的定义里,基本块是单进单出的,因此只能有一条终止指令(ret或br)。当一个基本块有两条终止指令,clang 在做解析时会认为第一个终结指令是此基本块的结束,并会开启一个新的匿名的基本块(并占用了下一个编号)。
|
|
|
|
|
##### Ret
|
|
|
|
|
- 格式
|
|
|
|
|
- `ret <type> <value>`
|
|
|
|
|
- `ret void`
|
|
|
|
|
- 例子:
|
|
|
|
|
- `ret i32 %0`
|
|
|
|
|
- `ret void`
|
|
|
|
|
- 概念:` ret`指令用于将控制流(以及可选的值)从函数返回给调用者。`ret`指令有两种形式:一种返回值,然后终结函数,另一种仅终结函数。
|
|
|
|
|
|
|
|
|
|
##### Br
|
|
|
|
|
- 格式:
|
|
|
|
|
- `br i1 <cond>, label <iftrue>, label <iffalse>`
|
|
|
|
|
- `br label <dest>`
|
|
|
|
|
- 例子:
|
|
|
|
|
- `br i1 %cond label %truebb label %falsebb`
|
|
|
|
|
- `br label %bb`
|
|
|
|
|
- 概念:`br`指令用于使控制流转移到当前功能中的另一个基本块。 该指令有两种形式,分别对应于条件分支和无条件分支。
|
|
|
|
|
|
|
|
|
|
#### Standard binary operators
|
|
|
|
|
##### Add FAdd
|
|
|
|
|
- 格式:
|
|
|
|
|
- `<result> = add <type> <op1>, <op2>`
|
|
|
|
|
- `<result> = fadd <type> <op1>, <op2>`
|
|
|
|
|
- 例子:
|
|
|
|
|
- `%2 = add i32 %1, %0`
|
|
|
|
|
- `%2 = fadd float %1, %0`
|
|
|
|
|
- 概念:`add`指令返回其两个`i32`类型的操作数之和,返回值为`i32`类型,`fadd`指令返回其两个`float`类型的操作数之和,返回值为`float`类型
|
|
|
|
|
|
|
|
|
|
##### Sub FSub
|
|
|
|
|
- 格式与例子与`add`,`fadd`类似
|
|
|
|
|
- 概念:`sub`指令返回其两个`i32`类型的操作数之差,返回值为`i32`类型,`fsub`指令返回其两个`float`类型的操作数之差,返回值为`float`类型
|
|
|
|
|
|
|
|
|
|
##### Mul FMul
|
|
|
|
|
- 格式与例子与`add`,`fadd`类似
|
|
|
|
|
- 概念:`mul`指令返回其两个`i32`类型的操作数之积,返回值为`i32`类型,`fmul`指令返回其两个`float`类型的操作数之积,返回值为`float`类型
|
|
|
|
|
|
|
|
|
|
##### SDiv FDiv
|
|
|
|
|
- 格式与例子与`add`,`fadd`类似
|
|
|
|
|
- 概念:`sdiv`指令返回其两个`i32`类型的操作数之商,返回值为`i32`类型,`fdiv`指令返回其两个`float`类型的操作数之商,返回值为`float`类型
|
|
|
|
|
|
|
|
|
|
##### SRem
|
|
|
|
|
- 格式与例子与`add`类似
|
|
|
|
|
- 概念:`srem`指令返回其两个`i32`类型的操作数之模,返回值为`i32`类型
|
|
|
|
|
|
|
|
|
|
#### Memory operators
|
|
|
|
|
##### Alloca
|
|
|
|
|
- 格式:`<result> = alloca <type>`
|
|
|
|
|
- 例子:
|
|
|
|
|
- `%ptr = alloca i32`
|
|
|
|
|
- `%ptr = alloca [10 x i32]`
|
|
|
|
|
- 概念: `alloca`指令在当前执行函数的栈帧上分配内存,当该函数返回其调用者时将自动释放该内存。 始终在地址空间中为数据布局中指示的分配资源分配对象
|
|
|
|
|
|
|
|
|
|
##### Load
|
|
|
|
|
- 格式:`<result> = load <type>, <type>* <pointer>`
|
|
|
|
|
- 例子:`%val = load i32, i32* %ptr`
|
|
|
|
|
- 概念:`load`指令用于从内存中读取。
|
|
|
|
|
|
|
|
|
|
##### Store
|
|
|
|
|
- 格式:`store <type> <value>, <type>* <pointer>`
|
|
|
|
|
- 例子:`store i32 3, i32* %ptr`
|
|
|
|
|
- 概念:`store`指令用于写入内存
|
|
|
|
|
|
|
|
|
|
#### CastInst
|
|
|
|
|
##### ZExt
|
|
|
|
|
- 格式:`<result> = zext <type> <value> to <type2>`
|
|
|
|
|
- 例子:`%1 = zext i1 %0 to i32`
|
|
|
|
|
- 概念:`zext`指令将其操作数**零**扩展为`type2`类型。
|
|
|
|
|
|
|
|
|
|
##### FpToSi
|
|
|
|
|
- 概念:`fptosi`指令将浮点值转换为`type2`(整数)类型。
|
|
|
|
|
- 格式:`<result> = fptosi <type> <value> to <type2>`
|
|
|
|
|
- 例子:`%Y = fptosi float 1.0E-247 to i32`
|
|
|
|
|
|
|
|
|
|
##### SiToFp
|
|
|
|
|
- 格式:`<result> = sitofp <type> <value> to <type2>`
|
|
|
|
|
- 例子:`%X = sitofp i32 257 to float`
|
|
|
|
|
- 概念:`sitofp`指令将有符号整数转换为`type2`(浮点数)类型。
|
|
|
|
|
|
|
|
|
|
#### Other operators
|
|
|
|
|
##### 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`
|
|
|
|
|
- 概念:`icmp`指令根据两个整数的比较返回布尔值,`fcmp`指令根据两个浮点数的比较返回布尔值。
|
|
|
|
|
|
|
|
|
|
##### Call
|
|
|
|
|
- 格式:
|
|
|
|
|
- `<result> = call <return ty> <func name>(<function args>) `
|
|
|
|
|
- 例子:
|
|
|
|
|
- `%0 = call i32 @func( i32 %1, i32* %0)`
|
|
|
|
|
- `call @func( i32 %arg)`
|
|
|
|
|
- 概念:`call`指令用于使控制流转移到指定的函数,其传入参数绑定到指定的值。 在被调用函数中执行`ret`指令后,控制流程将在函数调用后继续执行该指令,并且该函数的返回值绑定到`result`参数。
|
|
|
|
|
|
|
|
|
|
##### GetElementPtr
|
|
|
|
|
- 格式:`<result> = getelementptr <type>, <type>* <ptrval> [, <type> <idx>]`
|
|
|
|
|
- 例子:
|
|
|
|
|
- `%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0`
|
|
|
|
|
- `%2 = getelementptr i32, i32* %1 i32 %0`
|
|
|
|
|
- 参数解释:第一个参数是计算基础类型,第二第三个参数表示索引开始的指针类型及指针,`[]`表示可重复参数,里面表示的数组索引的偏移类型及偏移值。(思考指针类型为`[10 x i32]`指针和`i32`指针`getelementptr`用法的不同)
|
|
|
|
|
- 概念:`getelementptr`指令用于获取数组结构的元素的地址。 它仅执行地址计算,并且不访问内存。
|
|
|
|
|
|
|
|
|
|
## C++ APIs
|
|
|
|
|
|
|
|
|
|
### 核心类概念图
|
|
|
|
|
|
|
|
|
|
![](figs/核心类概念图.png)
|
|
|
|
|
|
|
|
|
|
### BasicBlock
|
|
|
|
|
- 继承:从[value](#value)继承
|
|
|
|
|
|
|
|
|
|
- 含义:基本块,是一个是单入单出的代码块,该类维护了一个指令链表,基本块本身属于 Value, 类型是 \<label\>,会被分支指令调用
|
|
|
|
|
|
|
|
|
|
- 成员:
|
|
|
|
|
|
|
|
|
|
- instr_list_:指令链表
|
|
|
|
|
- pre_bbs_: bb前驱集合
|
|
|
|
|
- succ_bbs_:bb后继集合
|
|
|
|
|
|
|
|
|
|
- API:
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
// 创建并返回BB块,参数分别是BB块所属的Module,name是其名字默认为空,BB块所属的Function
|
|
|
|
|
static Ptr<BasicBlock> create(Ptr<Module> m, const std::string &name ,Ptr<Function> parent )
|
|
|
|
|
// 返回BB块所属的函数
|
|
|
|
|
Ptr<Function> get_parent();
|
|
|
|
|
// 返回BB块所属的Module
|
|
|
|
|
Ptr<Module> get_module();
|
|
|
|
|
// 返回BB块的终止指令(ret|br),若BB块最后一条指令不是终止指令返回null
|
|
|
|
|
Ptr<Instruction> get_terminator();
|
|
|
|
|
// 将instr指令添加到此BB块指令链表结尾,调用IRBuilder里来创建函数会自动调用此方法
|
|
|
|
|
void add_instruction(Ptr<Instruction> instr);
|
|
|
|
|
// 将instr指令添加到此BB块指令链表开头
|
|
|
|
|
void add_instr_begin(Ptr<Instruction> instr);
|
|
|
|
|
// 将instr指令从BB块指令链表中移除,同时调用api维护好instr的操作数的use链表
|
|
|
|
|
void delete_instr(Ptr<Instruction> instr);
|
|
|
|
|
// BB块中指令数为空返回true
|
|
|
|
|
bool empty();
|
|
|
|
|
// 返回BB块中指令的数目
|
|
|
|
|
int get_num_of_instr();
|
|
|
|
|
//返回BB块的指令链表
|
|
|
|
|
PtrList<Instruction> &get_instructions();
|
|
|
|
|
// 将此BB块从所属函数的bb链表中移除
|
|
|
|
|
void erase_from_parent();
|
|
|
|
|
|
|
|
|
|
/****************api about cfg****************/
|
|
|
|
|
PtrList<BasicBlock> &get_pre_basic_blocks() // 返回前驱块集合
|
|
|
|
|
PtrList<BasicBlock> &get_succ_basic_blocks() // 返回后继块集合
|
|
|
|
|
void add_pre_basic_block(Ptr<BasicBlock> bb) // 添加前驱块
|
|
|
|
|
void add_succ_basic_block(Ptr<BasicBlock> bb) // 添加后继块
|
|
|
|
|
void remove_pre_basic_block(Ptr<BasicBlock> bb) // 移除前驱块
|
|
|
|
|
void remove_succ_basic_block(Ptr<BasicBlock> bb) // 移除后继块
|
|
|
|
|
/****************api about cfg****************/
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Constant
|
|
|
|
|
- 继承:从[User](#user)继承
|
|
|
|
|
- 含义:常数,各种类型常量的基类
|
|
|
|
|
- 子类:
|
|
|
|
|
- ConstantInt
|
|
|
|
|
- 含义:int类型的常数
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
|
|
|
|
|
- value_:常数值
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
int get_value() // 返回该常数类型中存的常数值
|
|
|
|
|
static int get_value(Ptr<ConstantInt> const_val)// 返回该常数类型const_val中存的常数值
|
|
|
|
|
static Ptr<ConstantInt> get(int val, Ptr<Module> m) // 以val值来创建常数类
|
|
|
|
|
static Ptr<ConstantInt> get(bool val, Ptr<Module> m) // 以val值来创建bool常数类
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- ConstantFloat
|
|
|
|
|
- 含义:float类型的常数
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
|
|
|
|
|
- value_:常数值
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static Ptr<ConstantFloat> get(float val, Ptr<Module> m) // 以val值创建并返回浮点数常量类
|
|
|
|
|
float get_value() // 返回该常数类型中存的常数值
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- ConstantZero
|
|
|
|
|
|
|
|
|
|
- 含义:用于全局变量初始化的常量0值。
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static Ptr<ConstantZero> get(Ptr<Type> ty, Ptr<Module> m);// 创建并返回ConstantZero常量类
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- ConstantArray
|
|
|
|
|
|
|
|
|
|
- 含义:数组类型的常数
|
|
|
|
|
- 成员
|
|
|
|
|
- const_array:数组常量值
|
|
|
|
|
|
|
|
|
|
- API:cminus语法不需要数组常量的支持(本次实验不需要用到),在此不过多解释。感兴趣可以自行查看源代码。
|
|
|
|
|
### Function
|
|
|
|
|
- 继承:从[Value](#value)继承
|
|
|
|
|
|
|
|
|
|
- 含义:函数,该类描述 LLVM 的一个简单过程,维护基本块表,格式化参数表
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- basic_blocks_:基本块列表
|
|
|
|
|
- arguments_:形参列表
|
|
|
|
|
- parent_:函数属于的module
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static Ptr<Function> create(Ptr<FunctionType> ty, const std::string &name, Ptr<Module> parent);
|
|
|
|
|
// 创建并返回Function,参数依次是待创建函数类型ty、函数名字name(不可为空)、函数所属的Module
|
|
|
|
|
Ptr<FunctionType> get_function_type() const;
|
|
|
|
|
// 返回此函数类的函数类型
|
|
|
|
|
Ptr<Type> get_return_type() const;
|
|
|
|
|
// 返回此函数类型的返回值类型
|
|
|
|
|
void add_basic_block(Ptr<BasicBlock> bb);
|
|
|
|
|
// 将bb添加至Function的bb链表上(调用bb里的创建函数时会自动调用此函数挂在function的bb链表上)
|
|
|
|
|
unsigned get_num_of_args() const;
|
|
|
|
|
// 得到函数形参数数量
|
|
|
|
|
unsigned get_num_basic_blocks() const;
|
|
|
|
|
// 得到函数基本块数量
|
|
|
|
|
Ptr<Module> get_parent() const;
|
|
|
|
|
// 得到函数所属的Module
|
|
|
|
|
PtrList<Argument>::iterator arg_begin()
|
|
|
|
|
// 得到函数形参的list的起始迭代器
|
|
|
|
|
PtrList<Argument>::iterator arg_end()
|
|
|
|
|
// 得到函数形参的list的终止迭代器
|
|
|
|
|
void remove(Ptr<BasicBlock> bb)
|
|
|
|
|
// 从函数的bb链表中删除一个bb
|
|
|
|
|
PtrList<BasicBlock> &get_basic_blocks()
|
|
|
|
|
// 返回函数bb链表
|
|
|
|
|
PtrList<Argument> &get_args()
|
|
|
|
|
// 返回函数的形参链表
|
|
|
|
|
void set_instr_name();
|
|
|
|
|
// 给函数中未命名的基本块和指令命名
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 相关类
|
|
|
|
|
- Argument
|
|
|
|
|
- 含义:参数
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- arg_no_:参数序号
|
|
|
|
|
- parent_:参数属于哪个函数
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<Function> get_parent() // 返回参数的所属函数
|
|
|
|
|
unsigned get_arg_no() const // 返回参数在所在函数的第几个参数
|
|
|
|
|
```
|
|
|
|
|
### GlobalVariable
|
|
|
|
|
- 继承:从[User](#user)继承
|
|
|
|
|
- 含义:全局变量,该类用于表示全局变量,是 GlobalValue 的子类,根据地址来访问
|
|
|
|
|
- 成员:
|
|
|
|
|
- is_const:是否为常量
|
|
|
|
|
- init_val_:初始值
|
|
|
|
|
- API:由于cminusf语义要求所有的全局变量都默认初始化为0,故`GlobalVariable`中成员和API再构造CminusFBuilder用不到
|
|
|
|
|
### IRStmtBuilder
|
|
|
|
|
- 含义:生成IR的辅助类,该类提供了独立的接口创建各种 IR 指令,并将它们插入基本块中, 该辅助类不做任何类型检查。
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<BasicBlock> get_insert_block()// 返回正在插入指令的BB
|
|
|
|
|
void set_insert_point(Ptr<BasicBlock> bb)// 设置当前需要插入指令的bb
|
|
|
|
|
ptr<XXXInst> create_[instr_type]()// 创建instr_type(具体名字参考IRStmtBuilder.h代码)的指令并对应插入到正在插入的BB块,这种类型的指令看函数名字和参数名字和IR文档是一一对应的。
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Instruction
|
|
|
|
|
- 继承:从[User](#user)继承
|
|
|
|
|
- 含义:指令,该类是所有 LLVM 指令的基类,主要维护指令的操作码(指令类别),指令所属的基本块,指令的操作数个数信息
|
|
|
|
|
- 成员
|
|
|
|
|
- parent_:指令所属的BasicBlock
|
|
|
|
|
- op_id_:指令的类型id
|
|
|
|
|
- num_ops_指令的操作数个数
|
|
|
|
|
- 子类
|
|
|
|
|
- BinaryInst:双目运算指令包括add、sub、mul、div
|
|
|
|
|
- 其他子类和前述文档中提到的指令一一对应,不在此赘述。
|
|
|
|
|
- API:所有指令的创建都要通过 IRStmtBuilder 进行,不需要关注Instruction类的实现细节,(**注**:不通过 IRStmtBuilder 来创建指令,而直接调用指令子类的创建方法未经助教完善的测试)
|
|
|
|
|
### Module
|
|
|
|
|
- 含义:一个编译单元,在此源语言的意义下是一个文件
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- function_list_:函数链表,记录了这个编译单元的所有函数
|
|
|
|
|
- global_list_:全局变量链表
|
|
|
|
|
- instr_id2string_:通过指令类型id得到其打印的string
|
|
|
|
|
- module_name_, source_file_name:未使用
|
|
|
|
|
- 从module中能取到的基本类型
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<Type> get_void_type();
|
|
|
|
|
// 得到IR中的void类型其他类型可以用类似的API得到(推荐取得类型采用lab3助教提供的方法Type::get())
|
|
|
|
|
void add_function(Ptr<Function> f);
|
|
|
|
|
// 将f挂在module的function链表上,在function被创建的时候会自动调用此方法来添加function
|
|
|
|
|
void add_global_variable(Ptr<GlobalVariable> g);
|
|
|
|
|
// 将g挂在module的GlobalVariable链表上,在GlobalVariable被创建的时候会自动调用此方法来添加GlobalVariable
|
|
|
|
|
PtrList<GlobalVariable> &get_global_variable();
|
|
|
|
|
// 获取全局变量列表
|
|
|
|
|
std::string get_instr_op_name( Instruction::OpID instr );
|
|
|
|
|
// 获取instr对应的指令名(打印ir时调用)
|
|
|
|
|
void set_print_name();
|
|
|
|
|
// 设置打印ir的指令与bb名字;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Type
|
|
|
|
|
- 含义:IR的类型,该类是所有类型的超类
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
|
|
|
|
|
- tid_:枚举类型,表示type的类型(包含VoidType、LabelType、FloatType、Int1、Int32、ArrayType、PointerType)
|
|
|
|
|
|
|
|
|
|
- 子类
|
|
|
|
|
- IntegerType
|
|
|
|
|
- 含义:int 类型
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
|
|
|
|
|
- num_bits:长度(i1或者i32)
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
unsigned get_num_bits();// 返回int的位数
|
|
|
|
|
```
|
|
|
|
|
- FloatType
|
|
|
|
|
|
|
|
|
|
- 含义:float 类型
|
|
|
|
|
- FunctionType
|
|
|
|
|
- 含义:函数类型
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- result_:返回值类型
|
|
|
|
|
- args_:参数类型列表
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static Ptr<FunctionType> get(Ptr<Type> result, PtrVec<Type> params);
|
|
|
|
|
// 返回函数类型,参数依次是返回值类型result,形参类型列表params
|
|
|
|
|
unsigned get_num_of_args() const;
|
|
|
|
|
// 返回形参个数
|
|
|
|
|
Ptr<Type> get_param_type(unsigned i) const;
|
|
|
|
|
// 返回第i个形参的类型
|
|
|
|
|
PtrVec<Type>::iterator param_begin()
|
|
|
|
|
// 返回形参类型列表的起始迭代器
|
|
|
|
|
PtrVec<Type>::iterator param_end()
|
|
|
|
|
// 返回形参类型列表的终止迭代器
|
|
|
|
|
Ptr<Type> get_return_type() const;
|
|
|
|
|
// 返回函数类型中的返回值类型
|
|
|
|
|
```
|
|
|
|
|
- ArrayType
|
|
|
|
|
- 含义:数组类型
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- contained_:数组成员的类型
|
|
|
|
|
- num_elements_:数组维数
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
static Ptr<ArrayType> get(Ptr<Type> contained, unsigned num_elements);
|
|
|
|
|
// 返回数组类型,参数依次是 数组元素的类型contained,数组元素个数num_elements
|
|
|
|
|
Ptr<Type> get_element_type() const
|
|
|
|
|
// 返回数组元素类型
|
|
|
|
|
unsigned get_num_of_elements() const
|
|
|
|
|
// 返回数组元素个数
|
|
|
|
|
```
|
|
|
|
|
- PointerType
|
|
|
|
|
- 含义:指针类型
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
|
|
|
|
|
- contained_:指针指向的类型
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<Type> get_element_type() const { return contained_; }
|
|
|
|
|
// 返回指针指向的类型
|
|
|
|
|
static Ptr<PointerType> get(Ptr<Type> contained);
|
|
|
|
|
// 返回contained类型的指针类型
|
|
|
|
|
Ptr<Type> get_pointer_element_type();// 若是PointerType则返回指向的类型,若不是则返回nullptr。
|
|
|
|
|
static Ptr<PointerType> create(Ptr<Type> contained);
|
|
|
|
|
// 创建指向contained类型的指针类型
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
bool is_void_type()// 判断是否是void类型其他类型有类似API请查看Type.h
|
|
|
|
|
static Ptr<Type> get_void_type(Ptr<Module> m);// 得到void类型
|
|
|
|
|
Ptr<Type> get_pointer_element_type();// 若是PointerType则返回指向的类型,若不是则返回nullptr。
|
|
|
|
|
Ptr<Type> get_array_element_type();// 若是ArrayType则返回指向的类型,若不是则返回nullptr。
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### User
|
|
|
|
|
- 继承:从[value](#value)继承
|
|
|
|
|
|
|
|
|
|
- 含义:使用者,提供一个操作数表,表中每个操作数都直接指向一个 Value, 提供了 use-def 信息,它本身是 Value 的子类, Value 类会维护一个该数据使用者的列表,提供def-use信息。简单来说操作数表表示我用了谁,该数据使用者列表表示谁用了我。这两个表在后续的**优化实验**会比较重要请务必理解。
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- operands_:参数列表,表示这个使用者所用到的参数
|
|
|
|
|
- num_ops_:表示该使用者使用的参数的个数
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<Value> get_operand(unsigned i) const;
|
|
|
|
|
// 从user的操作数链表中取出第i个操作数
|
|
|
|
|
void set_operand(unsigned i, Ptr<Value> v);
|
|
|
|
|
// 将user的第i个操作数设为v
|
|
|
|
|
void add_operand(Ptr<Value> v);
|
|
|
|
|
// 将v挂到User的操作数链表上
|
|
|
|
|
unsigned get_num_operand() const;
|
|
|
|
|
// 得到操作数链表的大小
|
|
|
|
|
void remove_use_of_ops();
|
|
|
|
|
// 从User的操作数链表中的所有操作数处的use_list_ 移除该User;
|
|
|
|
|
void remove_operands(int index1, int index2);
|
|
|
|
|
// 移除操作数链表中索引为index1-index2的操作数,例如想删除第0个操作数:remove_operands(0,0)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Value
|
|
|
|
|
- 含义:最基础的类,代表一个操作数,代表一个可能用于指令操作数的带类型数据
|
|
|
|
|
|
|
|
|
|
- 成员
|
|
|
|
|
- use_list_:记录了所有使用该操作数的指令的列表
|
|
|
|
|
- name_:名字
|
|
|
|
|
- type_:类型,一个type类,表示操作数的类型
|
|
|
|
|
|
|
|
|
|
- API
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
Ptr<Type> get_type() const //返回这个操作数的类型
|
|
|
|
|
std::list<Use> &get_use_list() // 返回value的使用者链表
|
|
|
|
|
void add_use(Ptr<Value> val, unsigned arg_no = 0);
|
|
|
|
|
// 添加val至this的使用者链表上
|
|
|
|
|
void replace_all_use_with(Ptr<Value> new_val);
|
|
|
|
|
// 将this在所有的地方用new_val替代,并且维护好use_def与def_use链表
|
|
|
|
|
void remove_use(Ptr<Value> val);
|
|
|
|
|
// 将val从this的use_list_中移除
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 总结
|
|
|
|
|
|
|
|
|
|
在本文档里提供了为SysYF语言程序生成LLVM IR可能需要用到的SysYF IR应用编程接口,如果对这些API有问题的请移步issue讨论,本次`SysYF IR`应用编程接口由助教自行设计实现,并做了大量测试,如有对助教的实现方法有异议或者建议的也请移步issue讨论,**除了选做内容无需修改助教代码**。
|