Commit e555b9cc authored by 李晓奇's avatar 李晓奇

Merge branch 'master' of 202.38.79.174:compiler_staff/2022fall-compiler_cminus

parents 7cc5d18a 2f5d0fa7
...@@ -48,6 +48,6 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) ...@@ -48,6 +48,6 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_BINARY_DIR}) include_directories(${PROJECT_BINARY_DIR})
include_directories(include/lightir) include_directories(include/lightir)
include_directories(include/optimization)
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(tests) add_subdirectory(tests)
...@@ -33,6 +33,7 @@ Lab3 实验建立在 Lab2 的基础上,带领大家进一步理解源代码到 ...@@ -33,6 +33,7 @@ Lab3 实验建立在 Lab2 的基础上,带领大家进一步理解源代码到
2. 助教们在 `cminusf_builder.cpp` 提供的部分函数是不完整的,无法通过所有测试用例。需要你补充代码的地方已经用 `TODO` 标出。我们提供的`cminusf_builder.cpp` 只可以满足测试用例 `lv0_1` 中的 `return.cminus`。虽然`eval.py`会显示其他一些测试用例也是`success`,但它对应的`.ll`文件是不正确的,不要因其显示 `success` 忽略了对应的代码补充,那会影响你后面较复杂样例的正确性。 2. 助教们在 `cminusf_builder.cpp` 提供的部分函数是不完整的,无法通过所有测试用例。需要你补充代码的地方已经用 `TODO` 标出。我们提供的`cminusf_builder.cpp` 只可以满足测试用例 `lv0_1` 中的 `return.cminus`。虽然`eval.py`会显示其他一些测试用例也是`success`,但它对应的`.ll`文件是不正确的,不要因其显示 `success` 忽略了对应的代码补充,那会影响你后面较复杂样例的正确性。
3. 简要阅读 `cminusf_builder.hpp` 和其他头文件中定义的函数和变量,理解项目框架也会为你的实验提供很大的帮助。 3. 简要阅读 `cminusf_builder.hpp` 和其他头文件中定义的函数和变量,理解项目框架也会为你的实验提供很大的帮助。
4. 请独立完成实验,不要抄袭他人代码。 4. 请独立完成实验,不要抄袭他人代码。
5. 为方便同学们更好完成实验,补充提示文档,参见[Lab3 More Tips](./more-tips.md)
## 1. 实验框架 ## 1. 实验框架
...@@ -64,6 +65,7 @@ bool in_global(); ...@@ -64,6 +65,7 @@ bool in_global();
```sh ```sh
mkdir build && cd build mkdir build && cd build
cmake ..
make clean make clean
make -j make -j
# 安装库 libcminus_io.a 至系统目录 # 安装库 libcminus_io.a 至系统目录
...@@ -179,8 +181,8 @@ cminusfc test.cminus ...@@ -179,8 +181,8 @@ cminusfc test.cminus
* 禁止执行恶意代码,违者本次实验0分处理 * 禁止执行恶意代码,违者本次实验0分处理
* 迟交规定 * 迟交规定
* `Soft Deadline` : 2022/11/07 23:59:59 (北京标准时间,UTC+8) * `Soft Deadline` : 2022/11/13 23:59:59 (北京标准时间,UTC+8)
* `Hard Deadline` : 2022/11/14 23:59:59 (北京标准时间,UTC+8) * `Hard Deadline` : 2022/11/20 23:59:59 (北京标准时间,UTC+8)
* 迟交需要邮件通知TA: * 迟交需要邮件通知TA:
* 邮箱: `lq_2019@mail.ustc.edu.cn` 抄送 `edwardzcn@mail.ustc.edu.cn` * 邮箱: `lq_2019@mail.ustc.edu.cn` 抄送 `edwardzcn@mail.ustc.edu.cn`
* 邮件主题: lab3迟交-学号-姓名 * 邮件主题: lab3迟交-学号-姓名
......
# Lab3 补充说明
## 重要提醒
希望同学们仔细阅读实验文档`cminusf.md``LightIR.md`,在lab3编写的过程中也结合自己在lab2所做的工作。
如果在编写过程中不知道具体用哪些函数,可以去查看给出代码中的函数所在的头文件,里面包含部分其他需要用的函数。
## 部分函数的注释
```c++
//存储当前value
Value *tmp_val = nullptr;
// 当前函数
Function *cur_fun = nullptr;
// 表示是否在之前已经进入scope,用于CompoundStmt
// 进入CompoundStmt不仅包括通过Fundeclaration进入,也包括selection-stmt等。
// pre_enter_scope用于控制是否在CompoundStmt中添加scope.enter,scope.exit
bool pre_enter_scope = false;
// types
Type *VOID_T;
Type *INT1_T;
Type *INT32_T;
Type *INT32PTR_T;
Type *FLOAT_T;
Type *FLOATPTR_T;
/*
* use CMinusfBuilder::Scope to construct scopes
* scope.enter: enter a new scope
* scope.exit: exit current scope
* scope.push: add a new binding to current scope
* scope.find: find and return the value bound to the name
*/
void CminusfBuilder::visit(ASTProgram &node) {
VOID_T = Type::get_void_type(module.get());
INT1_T = Type::get_int1_type(module.get());
INT32_T = Type::get_int32_type(module.get());
INT32PTR_T = Type::get_int32_ptr_type(module.get());
FLOAT_T = Type::get_float_type(module.get());
FLOATPTR_T = Type::get_float_ptr_type(module.get());
for (auto decl: node.declarations) { // program -> declaration-list
decl->accept(*this);//进入下一层函数
}
}
void CminusfBuilder::visit(ASTFunDeclaration &node) {
FunctionType *fun_type;
Type *ret_type;
std::vector<Type *> param_types;
if (node.type == TYPE_INT)//函数返回值类型
ret_type = INT32_T;
else if (node.type == TYPE_FLOAT)
ret_type = FLOAT_T;
else
ret_type = VOID_T;
for (auto& param: node.params) { //补全param_types
//TODO:
//根据param的类型分类
//需要考虑int型数组,int型,float型数组,float型
//对于不同的类型,向param_types中添加不同的Type
//param_types.push_back
}
fun_type = FunctionType::get(ret_type, param_types);
auto fun =
Function::create(
fun_type,
node.id,
module.get());//定义函数变量
scope.push(node.id, fun);
cur_fun = fun;
auto funBB = BasicBlock::create(module.get(), "entry", fun);//创建基本块
builder->set_insert_point(funBB);
scope.enter();
pre_enter_scope = true;
std::vector<Value*> args;
for (auto arg = fun->arg_begin();arg != fun->arg_end();arg++) {
args.push_back(*arg);
}
for (int i = 0;i < node.params.size();++i) {
//TODO:
//需要考虑int型数组,int型,float型数组,float型
//builder->create_alloca创建alloca语句
//builder->create_store创建store语句
//scope.push
}
node.compound_stmt->accept(*this);//fun-declaration -> type-specifier ID ( params ) compound-stmt
if (builder->get_insert_block()->get_terminator() == nullptr){//创建ret语句
if (cur_fun->get_return_type()->is_void_type())
builder->create_void_ret();
else if (cur_fun->get_return_type()->is_float_type())
builder->create_ret(CONST_FP(0.));
else
builder->create_ret(CONST_INT(0));
}
scope.exit();
}
void CminusfBuilder::visit(ASTParam &node) { }
void CminusfBuilder::visit(ASTCompoundStmt &node) {
//TODO:此函数为完整实现
bool need_exit_scope = !pre_enter_scope;//添加need_exit_scope变量
if (pre_enter_scope) {
pre_enter_scope = false;
} else {
scope.enter();
}
for (auto& decl: node.local_declarations) {//compound-stmt -> { local-declarations statement-list }
decl->accept(*this);
}
for (auto& stmt: node.statement_list) {
stmt->accept(*this);
if (builder->get_insert_block()->get_terminator() != nullptr)
break;
}
if (need_exit_scope) {
scope.exit();
}
}
void CminusfBuilder::visit(ASTReturnStmt &node) {//return-stmt -> return ; | return expression ;
if (node.expression == nullptr) {
builder->create_void_ret();
} else {
//TODO:
//需要考虑类型转换
//函数返回值和表达式值类型不同时,转换成函数返回值的类型
//用cur_fun获取当前函数返回值类型
//类型转换:builder->create_fptosi
//ret语句
}
}
```
# Lab4.1 实验文档
- [Lab4.1 实验文档](#lab41-实验文档)
- [0. 前言](#0-前言)
- [1. 实验内容](#1-实验内容)
- [1.1 阅读相关材料与代码](#11-阅读相关材料与代码)
- [1.1.1 SSA IR 材料阅读](#111-ssa-ir-材料阅读)
- [1.1.2 Mem2Reg Pass 代码阅读](#112-mem2reg-pass-代码阅读)
- [Pass 概念](#pass-概念)
- [UD 链(Use-Define Chain)与 DU 链(Definition-Use Chain)](#ud-链use-define-chain与-du-链definition-use-chain)
- [Mem2Reg](#mem2reg)
- [1.2 注册及运行 Mem2Reg Pass](#12-注册及运行-mem2reg-pass)
- [注册 Pass](#注册-pass)
- [运行 Pass](#运行-pass)
- [1.3 实验任务](#13-实验任务)
- [思考题](#思考题)
- [2. 提交要求](#2-提交要求)
- [2.1 目录结构](#21-目录结构)
- [2.2 提交要求和评分标准](#22-提交要求和评分标准)
## 0. 前言
在 lab3 中,我们通过访问者模式遍历抽象语法树,实现了一个中间代码自动生成工具。在 lab4,我们将对 lab3 生成的 IR 进行优化。本次实验是一个预热实验,为下一阶段编写优化方案提供必要的基础知识:SSA 格式的 IR ,以及优化 Pass 的概念。
## 1. 实验内容
### 1.1 阅读相关材料与代码
#### 1.1.1 SSA IR 材料阅读
Static single-assignment form(静态单赋值形式,通常缩写为SSA形式)是 IR 的一个属性,它要求每个变量在使用前被精确赋值一次并被定义。对原有变量的多次赋值会被分割成不同*版本*,这样每个定义都有自己的版本。SSA 形式下,[UD 链](#ud-链use-define-chain与-du-链definition-use-chain)是显式的,并且只含有一个元素。
观察下面的 `cminus` 程序,局部变量 `a` 经历了多次赋值。
```c
int main(void) {
int a;
a = 1 + 2;
a = a * 4;
return 0;
}
```
如下 IR 由 lab3 的中间代码生成器得出。虽然该段 IR 中的虚拟寄存器仅被赋值一次,属于 SSA 格式,但该 `cminus` 程序对局部变量`a`的多次赋值,在 IR 中仍以多次冗余的访存体现,存在不必要的 `alloca\load\store` 指令。这些指令会严重影响程序性能,所以我们需要移除这些冗余指令。
```asm
define i32 @main() {
label_entry:
%op0 = alloca i32
%op1 = add i32 1, 2
store i32 %op1, i32* %op0
%op2 = load i32, i32* %op0
%op3 = mul i32 %op2, 4
store i32 %op3, i32* %op0
ret i32 0
}
```
我们将冗余的 load,store 删除,可以得到如下 SSA IR 格式:
```
define i32 @main() {
label_entry:
%op1 = add i32 1, 2
%op3 = mul i32 %op1, 4
ret i32 0
}
```
我们将利用 Mem2Reg Pass 将对变量的冗余`load/store`指令删除。其中 Mem2Reg Pass 参考的算法详见[附件](./ssa.pdf)
**Note:**
1. 更详细的 SSA 格式的细节请仔细阅读[附件](./ssa.pdf)
2. 我们的实验设计从编译器前端分离了构造 SSA 过程,使用 `alloca` 来分配局部变量,保留了局部变量的访存操作,用 Mem2Reg Pass 来实现构造 SSA 的算法,这是保持了与 LLVM 一致的策略。
#### 1.1.2 Mem2Reg Pass 代码阅读
[1.1.1](#111-ssa-ir-材料阅读) 我们已经简单介绍了 Mem2Reg Pass 的相关功能,接下来我们将介绍关于 Pass 优化的基础概念。
###### Pass 概念
Pass 是编译器中优化方案的基本单元,它对 IR 进行一定的变换,从而提高运行时程序性能,比如上文的 Mem2Reg Pass。在我们的框架中, 可以将`Pass`理解为一个函数,`Pass` 会接受一个 `module` 作为参数,在运行时`Pass`将遍历整个`module`,对`module`内的指令和 bb 上做一些变换来实现优化。`module`内指令与 bb 的结构层次可以参考[LightIR核心类介绍.md](../common/LightIR.md)
###### UD 链(Use-Define Chain)与 DU 链(Definition-Use Chain)
Pass 在对指令进行替换与修改时,需要查找相关变量的定义和使用。在这里,我们引入UD 链(Use-Define Chain)与 DU 链(Definition-Use Chain)的概念。UD链描述了一个变量的使用(use)和其所有定义(def),DU链描述了一个变量的所有定义(def)和该定义能到达的所有使用(use)。在我们的框架中,UD 链和 DU 链的实现可以阅读`LightIR`中的[LightIR核心类介绍.md](../common/LightIR.md)中的User类的`operands_`成员,Value类的`use_list_`成员。
###### Mem2Reg
我们依据[附件](./ssa.pdf)中的 SSA 构造算法实现了Mem2Reg Pass,可以将 Lab3 中自动化生成的伪 SSA IR 转换成 SSA IR,代码相关的四个文件分别是
- `src/optimization/Dominators.cpp`
- `src/optimization/Mem2Reg.cpp`
- `include/optimization/Dominators.hpp`
- `include/optimization/Mem2Reg.hpp`
请对照[附件](./ssa.pdf)算法伪代码,仔细阅读上述代码
### 1.2 注册及运行 Mem2Reg Pass
#### 注册 Pass
本次实验使用了由 C++ 编写的 `LightIR` 来在 IR 层面完成优化化简,在`include/optimization/PassManager.hpp`中,定义了一个用于管理 Pass 的类`PassManager`。它的作用是注册与运行 Pass 。它提供了以下接口:
```cpp
PassManager pm(module.get())
pm.add_Pass<Mem2Reg>(emit) // 注册Pass,emit为true时打印优化后的IR
pm.run() // 按照注册的顺序运行Pass的run()函数
```
#### 运行 Pass
```sh
mkdir build && cd build
cmake ..
make -j
make install
```
为了便于大家进行实验,助教对之前的`cminusfc`增加了选项,用来选择是否开启某种优化,通过`[-mem2reg]`开关来控制优化 Pass 的使用,当需要对 `.cminus` 文件测试时,可以这样使用:
```bash
./cminusfc [-mem2reg] <input-file>
```
### 1.3 实验任务
阅读相关材料与代码,结合自己运行 Mem2Reg Pass 的体验,在实验报告中回答以下思考题。
#### 思考题
1. 请简述概念:支配性、严格支配性、直接支配性、支配边界。
2. `phi`节点是SSA的关键特征,请简述`phi`节点的概念,以及引入`phi`节点的理由。
3. 观察下面给出的`cminus`程序对应的 LLVM IR,与**开启**`Mem2Reg`生成的LLVM IR对比,每条`load`, `store`指令发生了变化吗?变化或者没变化的原因是什么?请分类解释。
```c
int globVar;
int func(int x){
if(x > 0){
x = 0;
}
return x;
}
int main(void){
int arr[10];
int b;
globVar = 1;
arr[5] = 999;
b = 2333;
func(b);
func(globVar);
return 0;
}
```
before `Mem2Reg`
```asm
@globVar = global i32 zeroinitializer
declare void @neg_idx_except()
define i32 @func(i32 %arg0) {
label_entry:
%op1 = alloca i32
store i32 %arg0, i32* %op1
%op2 = load i32, i32* %op1
%op3 = icmp sgt i32 %op2, 0
%op4 = zext i1 %op3 to i32
%op5 = icmp ne i32 %op4, 0
br i1 %op5, label %label6, label %label7
label6: ; preds = %label_entry
store i32 0, i32* %op1
br label %label7
label7: ; preds = %label_entry, %label6
%op8 = load i32, i32* %op1
ret i32 %op8
}
define i32 @main() {
label_entry:
%op0 = alloca [10 x i32]
%op1 = alloca i32
store i32 1, i32* @globVar
%op2 = icmp slt i32 5, 0
br i1 %op2, label %label3, label %label4
label3: ; preds = %label_entry
call void @neg_idx_except()
ret i32 0
label4: ; preds = %label_entry
%op5 = getelementptr [10 x i32], [10 x i32]* %op0, i32 0, i32 5
store i32 999, i32* %op5
store i32 2333, i32* %op1
%op6 = load i32, i32* %op1
%op7 = call i32 @func(i32 %op6)
%op8 = load i32, i32* @globVar
%op9 = call i32 @func(i32 %op8)
ret i32 0
}
```
After `Mem2Reg`
```asm
@globVar = global i32 zeroinitializer
declare void @neg_idx_except()
define i32 @func(i32 %arg0) {
label_entry:
%op3 = icmp sgt i32 %arg0, 0
%op4 = zext i1 %op3 to i32
%op5 = icmp ne i32 %op4, 0
br i1 %op5, label %label6, label %label7
label6: ; preds = %label_entry
br label %label7
label7: ; preds = %label_entry, %label6
%op9 = phi i32 [ %arg0, %label_entry ], [ 0, %label6 ]
ret i32 %op9
}
define i32 @main() {
label_entry:
%op0 = alloca [10 x i32]
store i32 1, i32* @globVar
%op2 = icmp slt i32 5, 0
br i1 %op2, label %label3, label %label4
label3: ; preds = %label_entry
call void @neg_idx_except()
ret i32 0
label4: ; preds = %label_entry
%op5 = getelementptr [10 x i32], [10 x i32]* %op0, i32 0, i32 5
store i32 999, i32* %op5
%op7 = call i32 @func(i32 2333)
%op8 = load i32, i32* @globVar
%op9 = call i32 @func(i32 %op8)
ret i32 0
}
```
4. 指出放置phi节点的代码,并解释是如何使用支配树的信息的。(需要给出代码中的成员变量或成员函数名称)
5. 算法是如何选择`value`(变量最新的值)来替换`load`指令的?(描述清楚对应变量与维护该变量的位置)
## 2. 提交要求
### 2.1 目录结构
与实验相关的文件如下
```
.
├── CMakeLists.txt
├── Documentations
│   ├── ...
│   ├── common
│   | ├── LightIR.md LightIR 相关文档
│   | ├── logging.md logging 工具相关文档
│   |   └── cminusf.md cminus-f 的语法和语义文档
│   └── 4.1-IR-opt
│   ├── ssa.pdf SSA 格式简介及构造算法材料
│      └── README.md Lab4.1 实验说明(你在这里)
├── include
│   ├── ...
│   └── optimization/* Mem2Reg.h & Dominators.h
├── Reports
│   ├── ...
│   └── 4.1-IR-opt
│   └── report.md lab4.1 实验阅读部分报告 <--- 修改并提交
└── src
   ├── ...
   └── optimization
   ├── Mem2Reg.cpp Mem2Reg.cpp
└── Dominators.cpp Dominators.cpp
```
### 2.2 提交要求和评分标准
* 提交要求
本次实验,只需要提交实验报告。
*`Reports/4.1-IR-opt/report.md` 中撰写实验报告,并在希冀平台对应提交 pdf 格式实验报告
* 评分标准:
* 由助教阅读实验报告并根据思考题回答正误给分。本次实验在 Lab4 实验中占40分。
* 迟交规定
* `Soft Deadline` :2022/11/27 23:59:59 (北京标准时间,UTC+8)
* `Hard Deadline`:2022/12/04 23:59:59 (北京标准时间,UTC+8)
* 迟交需要邮件通知 TA :
* 邮箱:
chen16614@mail.ustc.edu.cn 抄送 farmerzhang1@mail.ustc.edu.cn
* 邮件主题: lab4-1迟交-学号
* 内容: 包括迟交原因、最后版本 `commitID`、迟交时间等
* 迟交分数
* x为迟交天数(对于`Soft Deadline`而言),grade为满分
``` bash
final_grade = grade, x = 0
final_grade = grade * (0.9)^x, 0 < x <= 7
final_grade = 0, x > 7 # 这一条严格执行,请对自己负责
```
* 关于抄袭和雷同
经过助教和老师判定属于实验抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳(严禁对开源代码或者其他同学代码的直接搬运)。
如有任何问题,欢迎提issue进行批判指正。
...@@ -13,9 +13,16 @@ ...@@ -13,9 +13,16 @@
- [report](./Reports/2-ir-gen-warmup/report.md) - [report](./Reports/2-ir-gen-warmup/report.md)
* [lab3](./Documentations/3-ir-gen/) * [lab3](./Documentations/3-ir-gen/)
<<<<<<< HEAD
+ DDL:2022-11-07 23:59:59 (UTC+8) + DDL:2022-11-07 23:59:59 (UTC+8)
- [report](./Reports/3-ir-gen/report.md) - [report](./Reports/3-ir-gen/report.md)
=======
+ DDL:2022-11-13 23:59:59 (UTC+8)
* [lab4.1](./Documentations/4.1-ssa/)
+ DDL:2022-11-27 23:59:59 (UTC+8)
>>>>>>> 2f5d0fa70297542b1032ca4614f0f426fa7a4e33
## FAQ: How to merge upstream remote branches ## FAQ: How to merge upstream remote branches
......
# Lab4.1 实验报告
## 实验要求
请按照自己的理解,写明本次实验需要干什么
## 思考题
### Mem2reg
1. ...
2. ...
3. ...
### 代码阅读总结
此次实验有什么收获
### 实验反馈 (可选 不会评分)
对本次实验的建议
...@@ -49,7 +49,7 @@ class BasicBlock : public Value, public llvm::ilist_node<BasicBlock> { ...@@ -49,7 +49,7 @@ class BasicBlock : public Value, public llvm::ilist_node<BasicBlock> {
void add_instr_begin(Instruction *instr); void add_instr_begin(Instruction *instr);
void delete_instr(Instruction *instr); void delete_instr(Instruction *instr);
void erase_instr(Instruction *instr);
bool empty() { return instr_list_.empty(); } bool empty() { return instr_list_.empty(); }
int get_num_of_instr() { return instr_list_.size(); } int get_num_of_instr() { return instr_list_.size(); }
...@@ -60,6 +60,7 @@ class BasicBlock : public Value, public llvm::ilist_node<BasicBlock> { ...@@ -60,6 +60,7 @@ class BasicBlock : public Value, public llvm::ilist_node<BasicBlock> {
virtual std::string print() override; virtual std::string print() override;
private: private:
BasicBlock(const BasicBlock &) = delete;
explicit BasicBlock(Module *m, const std::string &name, Function *parent); explicit BasicBlock(Module *m, const std::string &name, Function *parent);
std::list<BasicBlock *> pre_bbs_; std::list<BasicBlock *> pre_bbs_;
std::list<BasicBlock *> succ_bbs_; std::list<BasicBlock *> succ_bbs_;
......
...@@ -21,6 +21,7 @@ class FunctionType; ...@@ -21,6 +21,7 @@ class FunctionType;
class Function : public Value, public llvm::ilist_node<Function> class Function : public Value, public llvm::ilist_node<Function>
{ {
public: public:
Function(const Function &) = delete;
Function(FunctionType *ty, const std::string &name, Module *parent); Function(FunctionType *ty, const std::string &name, Module *parent);
virtual ~Function(); virtual ~Function();
static Function *create(FunctionType *ty, const std::string &name, static Function *create(FunctionType *ty, const std::string &name,
......
...@@ -17,6 +17,7 @@ class GlobalVariable : public User, public llvm::ilist_node<GlobalVariable> { ...@@ -17,6 +17,7 @@ class GlobalVariable : public User, public llvm::ilist_node<GlobalVariable> {
GlobalVariable(std::string name, Module *m, Type *ty, bool is_const, Constant *init = nullptr); GlobalVariable(std::string name, Module *m, Type *ty, bool is_const, Constant *init = nullptr);
public: public:
GlobalVariable(const GlobalVariable &) = delete;
static GlobalVariable *create(std::string name, Module *m, Type *ty, bool is_const, Constant *init); static GlobalVariable *create(std::string name, Module *m, Type *ty, bool is_const, Constant *init);
virtual ~GlobalVariable() = default; virtual ~GlobalVariable() = default;
Constant *get_init() { return init_val_; } Constant *get_init() { return init_val_; }
......
...@@ -46,6 +46,7 @@ class Instruction : public User, public llvm::ilist_node<Instruction> ...@@ -46,6 +46,7 @@ class Instruction : public User, public llvm::ilist_node<Instruction>
// ty here is result type // ty here is result type
Instruction(Type *ty, OpID id, unsigned num_ops, BasicBlock *parent); Instruction(Type *ty, OpID id, unsigned num_ops, BasicBlock *parent);
Instruction(Type *ty, OpID id, unsigned num_ops); Instruction(Type *ty, OpID id, unsigned num_ops);
Instruction(const Instruction &) = delete;
virtual ~Instruction() = default; virtual ~Instruction() = default;
inline const BasicBlock *get_parent() const { return parent_; } inline const BasicBlock *get_parent() const { return parent_; }
inline BasicBlock *get_parent() { return parent_; } inline BasicBlock *get_parent() { return parent_; }
......
#ifndef SYSYC_DOMINATORS_HPP
#define SYSYC_DOMINATORS_HPP
#include "BasicBlock.h"
#include "PassManager.hpp"
#include <list>
#include <map>
#include <set>
class Dominators : public Pass {
public:
explicit Dominators(Module *m) : Pass(m) {}
~Dominators() = default;
void run() override;
void create_doms(Function *f);
void create_reverse_post_order(Function *f);
void create_idom(Function *f);
void create_dominance_frontier(Function *f);
void create_dom_tree_succ(Function *f);
// for debug
void print_idom(Function *f);
void print_dominance_frontier(Function *f);
/****************api about Dominator****************/
void add_dom(BasicBlock *bb, BasicBlock *dom_bb) { doms_[bb].insert(dom_bb); }
std::set<BasicBlock *> &get_doms(BasicBlock *bb) { return doms_[bb]; }
void set_doms(BasicBlock *bb, std::set<BasicBlock *> &doms) {
doms_[bb].clear();
doms_[bb].insert(doms.begin(), doms.end());
}
BasicBlock *get_idom(BasicBlock *bb) { return idom_[bb]; }
void set_idom(BasicBlock *bb, BasicBlock *idom) { idom_[bb] = idom; }
void add_dominance_frontier(BasicBlock *bb, BasicBlock *dom_frontier_bb) {
dom_frontier_[bb].insert(dom_frontier_bb);
}
std::set<BasicBlock *> &get_dominance_frontier(BasicBlock *bb) { return dom_frontier_[bb]; }
void set_dominance_frontier(BasicBlock *bb, std::set<BasicBlock *> &df) {
dom_frontier_[bb].clear();
dom_frontier_[bb].insert(df.begin(), df.end());
}
// successor blocks of this node in dominance tree
std::set<BasicBlock *> get_dom_tree_succ_blocks(BasicBlock *bb) { return dom_tree_succ_blocks_[bb]; }
void add_dom_tree_succ_block(BasicBlock *bb, BasicBlock *dom_tree_succ_bb) {
dom_tree_succ_blocks_[bb].insert(dom_tree_succ_bb);
}
/****************api about Dominator****************/
private:
void post_order_visit(BasicBlock *bb, std::set<BasicBlock *> &visited);
BasicBlock *intersect(BasicBlock *b1, BasicBlock *b2);
std::list<BasicBlock *> reverse_post_order_{};
std::map<BasicBlock *, int> post_order_id_{}; // the root has highest ID
std::map<BasicBlock *, std::set<BasicBlock *>> doms_{}; // dominance set
std::map<BasicBlock *, BasicBlock *> idom_{}; // immediate dominance
std::map<BasicBlock *, std::set<BasicBlock *>> dom_frontier_{}; // dominance frontier set
std::map<BasicBlock *, std::set<BasicBlock *>> dom_tree_succ_blocks_{};
};
#endif
#ifndef SYSYC_MEM2REG_HPP
#define SYSYC_MEM2REG_HPP
#include "BasicBlock.h"
#include "Dominators.h"
#include "Function.h"
#include "IRBuilder.h"
#include "Instruction.h"
#include "Module.h"
#include "PassManager.hpp"
#include <memory>
class Mem2Reg : public Pass {
private:
Function *func_;
std::unique_ptr<Dominators> dominators_;
public:
Mem2Reg(Module *m) : Pass(m) {}
~Mem2Reg(){};
void run() override;
void generate_phi();
void re_name(BasicBlock *bb);
void remove_alloca();
};
#endif
\ No newline at end of file
#ifndef PASSMANAGER_HPP
#define PASSMANAGER_HPP
#include "Module.h"
#include <memory>
#include <vector>
class Pass {
public:
Pass(Module *m) : m_(m) {}
virtual ~Pass() = default;
virtual void run() = 0;
protected:
Module *m_;
};
class PassManager {
public:
PassManager(Module *m) : m_(m) {}
template <typename PassType, typename... Args>
void add_pass(bool print_ir, Args &&...args) {
passes_.push_back(
std::pair<std::unique_ptr<Pass>, bool>(new PassType(m_, std::forward<Args>(args)...), print_ir));
}
void run() {
for (auto &pass : passes_) {
pass.first->run();
if (pass.second) {
m_->set_print_name();
std::cout << m_->print();
}
}
}
private:
std::vector<std::pair<std::unique_ptr<Pass>, bool>> passes_;
Module *m_;
};
#endif
\ No newline at end of file
...@@ -3,3 +3,4 @@ add_subdirectory(common) ...@@ -3,3 +3,4 @@ add_subdirectory(common)
add_subdirectory(io) add_subdirectory(io)
add_subdirectory(lightir) add_subdirectory(lightir)
add_subdirectory(cminusfc) add_subdirectory(cminusfc)
add_subdirectory(optimization)
\ No newline at end of file
...@@ -9,6 +9,7 @@ target_link_libraries( ...@@ -9,6 +9,7 @@ target_link_libraries(
IR_lib IR_lib
common common
syntax syntax
OP_lib
) )
install( install(
......
#include "Dominators.h"
#include "Mem2Reg.hpp"
#include "PassManager.hpp"
#include "cminusf_builder.hpp" #include "cminusf_builder.hpp"
#include "logging.hpp" #include "logging.hpp"
...@@ -8,7 +11,11 @@ ...@@ -8,7 +11,11 @@
using namespace std::literals::string_literals; using namespace std::literals::string_literals;
void print_help(std::string exe_name) { void print_help(std::string exe_name) {
<<<<<<< HEAD
std::cout << "Usage: " << exe_name << " [ -h | --help ] [ -o <target-file> ] [ -emit-llvm ] <input-file>" std::cout << "Usage: " << exe_name << " [ -h | --help ] [ -o <target-file> ] [ -emit-llvm ] <input-file>"
=======
std::cout << "Usage: " << exe_name << " [ -h | --help ] [ -o <target-file> ] [ -emit-llvm ] [-mem2reg] <input-file>"
>>>>>>> 2f5d0fa70297542b1032ca4614f0f426fa7a4e33
<< std::endl; << std::endl;
} }
...@@ -17,10 +24,6 @@ int main(int argc, char **argv) { ...@@ -17,10 +24,6 @@ int main(int argc, char **argv) {
std::string input_path; std::string input_path;
bool mem2reg = false; bool mem2reg = false;
bool const_propagation = false;
bool activevars = false;
bool loop_inv_hoist = false;
bool loop_search = false;
bool emit = false; bool emit = false;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
...@@ -39,14 +42,6 @@ int main(int argc, char **argv) { ...@@ -39,14 +42,6 @@ int main(int argc, char **argv) {
emit = true; emit = true;
} else if (argv[i] == "-mem2reg"s) { } else if (argv[i] == "-mem2reg"s) {
mem2reg = true; mem2reg = true;
} else if (argv[i] == "-loop-search"s) {
loop_search = true;
} else if (argv[i] == "-loop-inv-hoist"s) {
loop_inv_hoist = true;
} else if (argv[i] == "-const-propagation"s) {
const_propagation = true;
} else if (argv[i] == "-active-vars"s) {
activevars = true;
} else { } else {
if (input_path.empty()) { if (input_path.empty()) {
input_path = argv[i]; input_path = argv[i];
...@@ -93,6 +88,14 @@ int main(int argc, char **argv) { ...@@ -93,6 +88,14 @@ int main(int argc, char **argv) {
auto m = builder.getModule(); auto m = builder.getModule();
m->set_print_name();
PassManager PM(m.get());
if (mem2reg) {
PM.add_pass<Mem2Reg>(emit);
}
PM.run();
auto IR = m->print(); auto IR = m->print();
std::ofstream output_stream; std::ofstream output_stream;
......
...@@ -22,6 +22,10 @@ void BasicBlock::delete_instr(Instruction *instr) { ...@@ -22,6 +22,10 @@ void BasicBlock::delete_instr(Instruction *instr) {
instr_list_.remove(instr); instr_list_.remove(instr);
instr->remove_use_of_ops(); instr->remove_use_of_ops();
} }
void BasicBlock::erase_instr(Instruction *instr) {
instr->remove_use_of_ops();
instr_list_.erase(instr);
}
const Instruction *BasicBlock::get_terminator() const { const Instruction *BasicBlock::get_terminator() const {
if (instr_list_.empty()) { if (instr_list_.empty()) {
......
...@@ -114,7 +114,8 @@ IntegerType *IntegerType::get(unsigned num_bits, Module *m) { ...@@ -114,7 +114,8 @@ IntegerType *IntegerType::get(unsigned num_bits, Module *m) {
} else if (num_bits == 32) { } else if (num_bits == 32) {
return m->get_int32_type(); return m->get_int32_type();
} else { } else {
assert("IntegerType::get has error num_bits"); assert(false and "IntegerType::get has error num_bits");
return nullptr;
} }
} }
......
...@@ -17,6 +17,7 @@ void Value::replace_all_use_with(Value *new_val) { ...@@ -17,6 +17,7 @@ void Value::replace_all_use_with(Value *new_val) {
assert(val && "new_val is not a user"); assert(val && "new_val is not a user");
val->set_operand(use.arg_no_, new_val); val->set_operand(use.arg_no_, new_val);
} }
use_list_.clear();
} }
void Value::remove_use(Value *val) { void Value::remove_use(Value *val) {
......
add_library(
OP_lib STATIC
Dominators.cpp
Mem2Reg.cpp
)
#include "Dominators.h"
#include <algorithm>
#include <string>
void Dominators::run() {
for (auto &f1 : m_->get_functions()) {
auto f = &f1;
if (f->get_basic_blocks().size() == 0)
continue;
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
doms_.insert({bb, {}});
idom_.insert({bb, {}});
dom_frontier_.insert({bb, {}});
dom_tree_succ_blocks_.insert({bb, {}});
}
create_reverse_post_order(f);
create_idom(f);
create_dominance_frontier(f);
create_dom_tree_succ(f);
// for debug
// print_idom(f);
// print_dominance_frontier(f);
}
}
void Dominators::create_doms(Function *f) {
// init
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
add_dom(bb, bb);
}
// iterate
bool changed = true;
std::vector<BasicBlock *> ret(f->get_num_basic_blocks());
std::vector<BasicBlock *> pre(f->get_num_basic_blocks());
while (changed) {
changed = false;
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
auto &bbs = bb->get_pre_basic_blocks();
auto &first = get_doms((*bbs.begin()));
pre.insert(pre.begin(), first.begin(), first.end());
pre.resize(first.size());
ret.resize(f->get_num_basic_blocks());
for (auto iter = ++bbs.begin(); iter != bbs.end(); ++iter) {
auto &now = get_doms((*iter));
auto it = std::set_intersection(pre.begin(), pre.end(), now.begin(), now.end(), ret.begin());
ret.resize(it - ret.begin());
pre.resize(ret.size());
pre.insert(pre.begin(), ret.begin(), ret.end());
}
std::set<BasicBlock *> doms;
doms.insert(bb);
doms.insert(pre.begin(), pre.end());
if (get_doms(bb) != doms) {
set_doms(bb, doms);
changed = true;
}
}
}
}
void Dominators::create_reverse_post_order(Function *f) {
reverse_post_order_.clear();
post_order_id_.clear();
std::set<BasicBlock *> visited;
post_order_visit(f->get_entry_block(), visited);
reverse_post_order_.reverse();
}
void Dominators::post_order_visit(BasicBlock *bb, std::set<BasicBlock *> &visited) {
visited.insert(bb);
for (auto b : bb->get_succ_basic_blocks()) {
if (visited.find(b) == visited.end())
post_order_visit(b, visited);
}
post_order_id_[bb] = reverse_post_order_.size();
reverse_post_order_.push_back(bb);
}
void Dominators::create_idom(Function *f) {
// init
for (auto &bb : f->get_basic_blocks())
set_idom(&bb, nullptr);
auto root = f->get_entry_block();
set_idom(root, root);
// iterate
bool changed = true;
while (changed) {
changed = false;
for (auto bb : this->reverse_post_order_) {
if (bb == root) {
continue;
}
// find one pred which has idom
BasicBlock *pred = nullptr;
for (auto p : bb->get_pre_basic_blocks()) {
if (get_idom(p)) {
pred = p;
break;
}
}
assert(pred);
BasicBlock *new_idom = pred;
for (auto p : bb->get_pre_basic_blocks()) {
if (p == pred)
continue;
if (get_idom(p)) {
new_idom = intersect(p, new_idom);
}
}
if (get_idom(bb) != new_idom) {
set_idom(bb, new_idom);
changed = true;
}
}
}
}
// find closest parent of b1 and b2
BasicBlock *Dominators::intersect(BasicBlock *b1, BasicBlock *b2) {
while (b1 != b2) {
while (post_order_id_[b1] < post_order_id_[b2]) {
assert(get_idom(b1));
b1 = get_idom(b1);
}
while (post_order_id_[b2] < post_order_id_[b1]) {
assert(get_idom(b2));
b2 = get_idom(b2);
}
}
return b1;
}
void Dominators::create_dominance_frontier(Function *f) {
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
if (bb->get_pre_basic_blocks().size() >= 2) {
for (auto p : bb->get_pre_basic_blocks()) {
auto runner = p;
while (runner != get_idom(bb)) {
add_dominance_frontier(runner, bb);
runner = get_idom(runner);
}
}
}
}
}
void Dominators::create_dom_tree_succ(Function *f) {
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
auto idom = get_idom(bb);
// e.g, entry bb
if (idom != bb) {
add_dom_tree_succ_block(idom, bb);
}
}
}
void Dominators::print_idom(Function *f) {
int counter = 0;
std::map<BasicBlock *, std::string> bb_id;
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
if (bb->get_name().empty())
bb_id[bb] = "bb" + std::to_string(counter);
else
bb_id[bb] = bb->get_name();
counter++;
}
printf("Immediate dominance of function %s:\n", f->get_name().c_str());
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
std::string output;
output = bb_id[bb] + ": ";
if (get_idom(bb)) {
output += bb_id[get_idom(bb)];
} else {
output += "null";
}
printf("%s\n", output.c_str());
}
}
void Dominators::print_dominance_frontier(Function *f) {
int counter = 0;
std::map<BasicBlock *, std::string> bb_id;
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
if (bb->get_name().empty())
bb_id[bb] = "bb" + std::to_string(counter);
else
bb_id[bb] = bb->get_name();
counter++;
}
printf("Dominance Frontier of function %s:\n", f->get_name().c_str());
for (auto &bb1 : f->get_basic_blocks()) {
auto bb = &bb1;
std::string output;
output = bb_id[bb] + ": ";
if (get_dominance_frontier(bb).empty()) {
output += "null";
} else {
bool first = true;
for (auto df : get_dominance_frontier(bb)) {
if (first) {
first = false;
} else {
output += ", ";
}
output += bb_id[df];
}
}
printf("%s\n", output.c_str());
}
}
#include "Mem2Reg.hpp"
#include "IRBuilder.h"
#include <memory>
// 判断是否是全局变量地址
#define IS_GLOBAL_VARIABLE(l_val) dynamic_cast<GlobalVariable *>(l_val)
// 判断是否是 getelementptr 指令
#define IS_GEP_INSTR(l_val) dynamic_cast<GetElementPtrInst *>(l_val)
std::map<Value *, std::vector<Value *>> var_val_stack; // 全局变量初值提前存入栈中
void Mem2Reg::run() {
// 创建支配树分析 Pass 的实例
dominators_ = std::make_unique<Dominators>(m_);
// 建立支配树
dominators_->run();
// 以函数为单元遍历实现 Mem2Reg 算法
for (auto &f : m_->get_functions()) {
func_ = &f;
if (func_->get_basic_blocks().size() >= 1) {
// 对应伪代码中 phi 指令插入的阶段
generate_phi();
// 对应伪代码中重命名阶段
re_name(func_->get_entry_block());
}
// 移除冗余的局部变量的分配空间
remove_alloca();
}
}
void Mem2Reg::generate_phi() {
// global_live_var_name 是全局名字集合,以 alloca 出的局部变量来统计。
// 步骤一:找到活跃在多个 block 的全局名字集合,以及它们所属的 bb 块
std::set<Value *> global_live_var_name;
std::map<Value *, std::set<BasicBlock *>> live_var_2blocks;
for (auto &bb1 : func_->get_basic_blocks()) {
auto bb = &bb1;
std::set<Value *> var_is_killed;
for (auto &instr1 : bb->get_instructions()) {
auto instr = &instr1;
if (instr->is_store()) {
// store i32 a, i32 *b
// a is r_val, b is l_val
auto r_val = static_cast<StoreInst *>(instr)->get_rval();
auto l_val = static_cast<StoreInst *>(instr)->get_lval();
if (!IS_GLOBAL_VARIABLE(l_val) && !IS_GEP_INSTR(l_val)) {
global_live_var_name.insert(l_val);
live_var_2blocks[l_val].insert(bb);
}
}
}
}
// 步骤二:从支配树获取支配边界信息,并在对应位置插入 phi 指令
std::map<std::pair<BasicBlock *, Value *>, bool> bb_has_var_phi; // bb has phi for var
for (auto var : global_live_var_name) {
std::vector<BasicBlock *> work_list;
work_list.assign(live_var_2blocks[var].begin(), live_var_2blocks[var].end());
for (int i = 0; i < work_list.size(); i++) {
auto bb = work_list[i];
for (auto bb_dominance_frontier_bb : dominators_->get_dominance_frontier(bb)) {
if (bb_has_var_phi.find({bb_dominance_frontier_bb, var}) == bb_has_var_phi.end()) {
// generate phi for bb_dominance_frontier_bb & add bb_dominance_frontier_bb to work list
auto phi =
PhiInst::create_phi(var->get_type()->get_pointer_element_type(), bb_dominance_frontier_bb);
phi->set_lval(var);
bb_dominance_frontier_bb->add_instr_begin(phi);
work_list.push_back(bb_dominance_frontier_bb);
bb_has_var_phi[{bb_dominance_frontier_bb, var}] = true;
}
}
}
}
}
void Mem2Reg::re_name(BasicBlock *bb) {
std::vector<Instruction *> wait_delete;
// 步骤三:将 phi 指令作为 lval 的最新定值,lval 即是为局部变量 alloca 出的地址空间
for (auto &instr1 : bb->get_instructions()) {
auto instr = &instr1;
if (instr->is_phi()) {
auto l_val = static_cast<PhiInst *>(instr)->get_lval();
var_val_stack[l_val].push_back(instr);
}
}
for (auto &instr1 : bb->get_instructions()) {
auto instr = &instr1;
// 步骤四:用 lval 最新的定值替代对应的load指令
if (instr->is_load()) {
auto l_val = static_cast<LoadInst *>(instr)->get_lval();
if (!IS_GLOBAL_VARIABLE(l_val) && !IS_GEP_INSTR(l_val)) {
if (var_val_stack.find(l_val) != var_val_stack.end()) {
// 此处指令替换会维护 UD 链与 DU 链
instr->replace_all_use_with(var_val_stack[l_val].back());
wait_delete.push_back(instr);
}
}
}
// 步骤五:将 store 指令的 rval,也即被存入内存的值,作为 lval 的最新定值
if (instr->is_store()) {
auto l_val = static_cast<StoreInst *>(instr)->get_lval();
auto r_val = static_cast<StoreInst *>(instr)->get_rval();
if (!IS_GLOBAL_VARIABLE(l_val) && !IS_GEP_INSTR(l_val)) {
var_val_stack[l_val].push_back(r_val);
wait_delete.push_back(instr);
}
}
}
// 步骤六:为 lval 对应的 phi 指令参数补充完整
for (auto succ_bb : bb->get_succ_basic_blocks()) {
for (auto &instr1 : succ_bb->get_instructions()) {
auto instr = &instr1;
if (instr->is_phi()) {
auto l_val = static_cast<PhiInst *>(instr)->get_lval();
if (var_val_stack.find(l_val) != var_val_stack.end()) {
assert(var_val_stack[l_val].size() != 0);
static_cast<PhiInst *>(instr)->add_phi_pair_operand(var_val_stack[l_val].back(), bb);
}
// 对于 phi 参数只有一个前驱定值的情况,将会输出 [ undef, bb ] 的参数格式
}
}
}
// 步骤七:对 bb 在支配树上的所有后继节点,递归执行 re_name 操作
for (auto dom_succ_bb : dominators_->get_dom_tree_succ_blocks(bb)) {
re_name(dom_succ_bb);
}
// 步骤八:pop出 lval 的最新定值
for (auto &instr1 : bb->get_instructions()) {
auto instr = &instr1;
if (instr->is_store()) {
auto l_val = static_cast<StoreInst *>(instr)->get_lval();
if (!IS_GLOBAL_VARIABLE(l_val) && !IS_GEP_INSTR(l_val)) {
var_val_stack[l_val].pop_back();
}
} else if (instr->is_phi()) {
auto l_val = static_cast<PhiInst *>(instr)->get_lval();
if (var_val_stack.find(l_val) != var_val_stack.end()) {
var_val_stack[l_val].pop_back();
}
}
}
// 清除冗余的指令
for (auto instr : wait_delete) {
bb->erase_instr(instr);
}
}
void Mem2Reg::remove_alloca() {
for (auto &bb1 : func_->get_basic_blocks()) {
auto bb = &bb1;
std::vector<Instruction *> wait_delete;
for (auto &instr1 : bb->get_instructions()) {
auto instr = &instr1;
auto is_alloca = dynamic_cast<AllocaInst *>(instr);
if (is_alloca) {
bool is_int = is_alloca->get_type()->get_pointer_element_type()->is_integer_type();
bool is_float = is_alloca->get_type()->get_pointer_element_type()->is_float_type();
if (is_int || is_float) {
wait_delete.push_back(instr);
}
}
}
for (auto instr : wait_delete) {
bb->erase_instr(instr);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment