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

follow update

parent f0f0bb81
......@@ -6,7 +6,7 @@ AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: BeforeComma
ColumnLimit: 120
ColumnLimit: 80
CommentPragmas: '^(!|NOLINT)'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IncludeBlocks: Regroup
......@@ -23,4 +23,5 @@ PenaltyReturnTypeOnItsOwnLine: 200
SpacesBeforeTrailingComments: 1
TabWidth: 4
UseTab: Never
AlwaysBreakAfterReturnType: TopLevelDefinitions
...
# Lab4 实验文档
- [Lab4 实验文档](#lab4-实验文档)
- [0. 前言](#0-前言)
- [1. GVN 基础知识](#1-gvn-基础知识)
- [1.1 GVN 简介](#11-gvn-简介)
- [1.2 GVN 相关概念](#12-gvn-相关概念)
- [1. IR 假设](#1-ir-假设)
- [2. Expression 概念](#2-expression-概念)
- [3. 等价概念](#3-等价概念)
- [4. Value Expression 概念](#4-value-expression-概念)
- [5. Value phi-function 概念](#5-value-phi-function-概念)
- [6. Partition](#6-partition)
- [7. Join 操作](#7-join-操作)
- [8. 变量等价与表达式等价](#8-变量等价与表达式等价)
- [9. Transfer Function](#9-transfer-function)
- [2. GVN 算法(论文中提供的伪代码)](#2-gvn-算法论文中提供的伪代码)
- [3. 实验内容](#3-实验内容)
- [3.1 GVN pass 实现内容要求](#31-gvn-pass-实现内容要求)
- [3.2 GVN 辅助类](#32-gvn-辅助类)
- [3.3 注册及运行 GVN Pass](#33-注册及运行-gvn-pass)
- [注册 Pass](#注册-pass)
- [运行 Pass](#运行-pass)
- [3.3 自动测试](#33-自动测试)
- [3.4 Tips](#34-tips)
- [4. 提交要求](#4-提交要求)
- [目录结构](#目录结构)
- [提交要求和评分标准](#提交要求和评分标准)
## 0. 前言
- [0. 前言](#0-前言)
- [1. GVN 基础知识](#1-gvn-基础知识)
- [1.1 GVN 简介](#11-gvn-简介)
- [1.2 GVN 相关概念](#12-gvn-相关概念)
- [1. IR 假设](#1-ir-假设)
- [2. Expression 概念](#2-expression-概念)
- [3. 等价概念](#3-等价概念)
- [4. Value Expression 概念](#4-value-expression-概念)
- [5. Value phi-function 概念](#5-value-phi-function-概念)
- [6. Partition](#6-partition)
- [7. Join 操作](#7-join-操作)
- [8. 变量等价与表达式等价](#8-变量等价与表达式等价)
- [9. Transfer Function](#9-transfer-function)
- [2. GVN 算法(论文中提供的伪代码)](#2-gvn-算法论文中提供的伪代码)
- [3. 实验内容](#3-实验内容)
- [3.1 GVN pass 实现内容要求](#31-gvn-pass-实现内容要求)
- [3.2 GVN 辅助类](#32-gvn-辅助类)
- [3.3 注册及运行 GVN Pass](#33-注册及运行-gvn-pass)
- [注册 Pass](#注册-pass)
- [运行 Pass](#运行-pass)
- [3.3 自动测试](#33-自动测试)
- [3.4 Tips](#34-tips)
- [4. 提交要求](#4-提交要求)
- [目录结构](#目录结构)
- [提交要求和评分标准](#提交要求和评分标准)
## 0. 前言
在 Lab4.1 中,我们介绍了 SSA IR,并阐明了其优势。本次实验中我们需要在 SSA IR 基础上,实现一个基于数据流分析的冗余消除的优化 Pass : Global Value Numbering(全局值编号)。
## 1. GVN 基础知识
### 1.1 GVN 简介
......@@ -74,7 +77,7 @@ bb2:
br bb3
bb3:
...
...
```
#### 3. 等价概念
......@@ -110,6 +113,7 @@ bb3:
一个 Partition (分区)由一系列等价类组成,一个等价类是由一个值编号,和一系列成员组成。每个成员可以是:变量,常量,值表达式。同时,一个等价类可能会关联一个 value-phi-function。
#### 7. Join 操作
Join 操作检测到达此处的所有路径共有的等价项。在 SSA 格式的 IR 中,变量只会被赋值一次,当程序点 p 支配 join block 时,在 p 点成立的等价关系,在 join block 处仍然成立。通过对 join block 的前驱 Partition 取交集,可以保留所有支配 join block 的程序点的等价关系。对于在每个路径的等价的探测,我们将在[2.GVN算法](#2-gvn-算法论文中提供的伪代码)中通过伪代码进行阐述。对于表达式的等价关系与变量等价关系的检测与判定,我们会分别阐述。
#### 8. 变量等价与表达式等价
......@@ -263,6 +267,7 @@ valuePhiFunc(ve,P)
## 3. 实验内容
在本次实验中,请仔细阅读[3.1 GVN pass 实现内容要求](#31-gvn-pass-实现内容要求),根据要求补全`src/optimization/GVN.cpp``include/optimization/GVN.h`中关于 GVN pass 数据流分析部分,同时需要在 `Reports/4-ir-opt/` 目录下撰写实验报告。**为了在评测中统一分析结果,请大家采用 lab3 的 TA-impl 分支提供的[答案](http://202.38.79.174/compiler_staff/2022fall-compiler_cminus/-/blob/TA-impl/src/cminusfc/cminusf_builder.cpp)来完成后续实验。**
### 3.1 GVN pass 实现内容要求
GVN 通过数据流分析来检测冗余的变量和计算,通过替换和死代码删除结合,实现优化效果。前述的例子中主要以二元运算语句来解释原理,且助教为大家提供了代码替换和删除的逻辑,除此之外,需要完成的方向有:
......@@ -270,11 +275,11 @@ GVN 通过数据流分析来检测冗余的变量和计算,通过替换和死
1. 对冗余指令的检测与消除包括(二元运算指令,cmp,gep,类型转换指令)
2. 对纯函数的冗余调用消除(助教提供了纯函数分析,见[FuncInfo.h](../../include/optimization/FuncInfo.h)
该 Pass 的接口`is_pure_function`接受一个lightIR Function,判断该函数是否为纯函数;对于纯函数,如果其参数等价,对这个纯函数的不同调用也等价。
3. 常量传播
在数据流分析的过程中,可以使用常量折叠来实现常量传播,从而将可在编译时计算的结果计算好,减少运行时开销。(助教提供了常量折叠类,在辅助类的介绍中)
我们会在测试样例中对这三点进行考察。
......@@ -403,27 +408,27 @@ root@3fd22a9ed627:/labs/2022fall-compiler_cminus-taversion/tests/4-ir-opt#
### 3.4 Tips
1. 为了大家能够循序渐进地实现 GVN pass,助教给出一个实现上的规划:
1. 使用 GVN pass 分析单一基本块的程序 case
补充 `detectEquivalences` 函数(无需处理 Join ),在转移方程`transferFunction`中,为每个变量创建等价类。
2. 使用 GVN pass 分析带有冗余计算的单一基本块的程序 case,并在分析过程中进行常量传播
依照伪代码补全转移方程`transferFunction`,得到正确的等价类,将输出结构 dump 至 json 文件,与手动推算结果进行比对。
3. 使用 GVN pass 分析带有选择语句的程序 case,处理phi语句之间的冗余
完善 `detectEquivalences` ,实现合并两个前驱分区的合并算法 `Join`,和 `valuePhiFunc`,注意本次实验只考察`phi(a+b, c+d)`与`phi(a,c)+phi(b,d)`之间的冗余。
4. 正确分析带有循环的程序
完成了前三点后,你的 GVN 应该比较完整了,可以根据带循环的简单程序来调试。
1. 使用 GVN pass 分析单一基本块的程序 case
补充 `detectEquivalences` 函数(无需处理 Join ),在转移方程`transferFunction`中,为每个变量创建等价类。
2. 使用 GVN pass 分析带有冗余计算的单一基本块的程序 case,并在分析过程中进行常量传播
依照伪代码补全转移方程`transferFunction`,得到正确的等价类,将输出结构 dump 至 json 文件,与手动推算结果进行比对。
3. 使用 GVN pass 分析带有选择语句的程序 case,处理phi语句之间的冗余
完善 `detectEquivalences` ,实现合并两个前驱分区的合并算法 `Join`,和 `valuePhiFunc`,注意本次实验只考察`phi(a+b, c+d)``phi(a,c)+phi(b,d)`之间的冗余。
4. 正确分析带有循环的程序
完成了前三点后,你的 GVN 应该比较完整了,可以根据带循环的简单程序来调试。
2. **Lab3** 的测试样例仍然可以用来测试优化正确性
3. 使用 logging 工具来打印调试信息,以及 GDB 等软件进行单步调试来检查错误的原因
[logging](../common/logging.md) 是帮助大家打印调试信息的工具,如有需求可以阅读文档后进行使用
4. 你有可能会遇到智能指针相关的问题,详见[C++文档](https://zh.cppreference.com/w/cpp/memory/shared_ptr),以及[段错误调试指北](../common/simple_cpp.md#debugging-seg-fault)
......@@ -449,97 +454,101 @@ root@3fd22a9ed627:/labs/2022fall-compiler_cminus-taversion/tests/4-ir-opt#
│   ├── optimization
│   │ ├── Mem2Reg.hpp Mem2Reg
│ │ ├── Dominators.hpp 支配树
│ │ └── GVN.h GVN <--- 修改并提交
│ │ └── GVN.h GVN <--- 修改并提交
│   ├── cminusf_builder.hpp
| └── ast.hpp
├── Reports
│   ├── ...
│   └── 4.2-gvn
│   └── report.md lab4 本次实验报告模板 <--- 修改并提交
│   └── report.md lab4 本次实验报告模板 <--- 修改并提交
│  
├── src
│   ├── ...
│   └── optimization
│   ├── Mem2Reg.cpp Mem2Reg
│ ├── Dominators.cpp 支配树
│ └── GVN.cpp GVN <--- 修改并提交
│ └── GVN.cpp GVN <--- 修改并提交
└── tests
├── ...
└── 4-ir-opt
   ├── testcases 助教提供的测试样例
   └── lab4_test.py 助教提供的测试脚本
```
### 提交要求和评分标准
* 提交要求
本实验是个人实验,需要将代码上传至gitlab实验仓库,并在希冀评测平台上提交仓库链接。
* 需要填补
`include/optimization/GVN.h`, `src/optimization/GVN.cpp`,在 `Reports/4.2-gvn/report.md` 中撰写实验报告。
- 实验报告内容包括
实验要求、实验难点、实验设计、实验结果验证、思考题、实验反馈等,具体见 `Reports/4.2-gvn/report.md`
* 本次实验不提供希冀平台在线测试通道,将在 soft ddl 时收取同学仓库 `master` 分支下`include/optimization/GVN.h``src/optimization/GVN.cpp` 文件,请同学们在ddl前将自己的最新分支push到 git 仓库上
* 本次实验报告以 pdf 格式提交到希冀平台对应提交通道
* 需要填补
`include/optimization/GVN.h`, `src/optimization/GVN.cpp`,在 `Reports/4.2-gvn/report.md` 中撰写实验报告。
- 实验报告内容包括
实验要求、实验难点、实验设计、实验结果验证、思考题、实验反馈等,具体见 `Reports/4.2-gvn/report.md`
* 本次实验不提供希冀平台在线测试通道,将在 soft ddl 时收取同学仓库 `master` 分支下`include/optimization/GVN.h``src/optimization/GVN.cpp` 文件,请同学们在ddl前将自己的最新分支push到 git 仓库上
* 本次实验报告以 pdf 格式提交到希冀平台对应提交通道
* 评分标准: 实验完成分(总分 60 分)组成如下:
* 实验报告 (5 分)
需要回答 Reports 目录下实验报告模板的思考题。
* easy case(40分)
助教提供了 4 个公开测试用例,并保留 4 个隐藏用例,根据每个用例 dump 出来的分析结果正误给分。json 文件判断的逻辑如下,仅对每个bb的 pout 进行判断。
例如:
```json
"pout": {
"label_entry": [["%op0", ], ["%op1", ], ["%op2", ], ],
"label3": [["%op0", ], ["%op1", ], ["%op2", ], ["%op4", "%op10", ], ["%op5", "%op9", ], ["%op6", "%op8", ], ],
"label7": [["%op0", ], ["%op1", ], ["%op2", ], ["%op10", ], ["%op9", ], ["%op11", "%op8", ], ],
"label12": [["%op0", ], ["%op1", ], ["%op2", ], ["%op13", "%op10", ], ["%op14", "%op9", ], ["%op15", "%op8", ], ],}
```
对于分值为 x,n 个基本块的程序,每个 bb 分析结果为$`x/n`$分,某个bb的分析结果多或者少一个等价类,或有分析错误的等价类,该 bb 分析结果没有分值。
* performance case(15分)
助教提供了 2 个公开case,并保留 2 个隐藏用例。以及助教实现优化后的 baseline.ll ,优化效果按照如下方式给分(执行结果不正确则此项分数为0)
* 实验报告 (5 分)
需要回答 Reports 目录下实验报告模板的思考题。
* easy case(40分)
助教提供了 4 个公开测试用例,并保留 4 个隐藏用例,根据每个用例 dump 出来的分析结果正误给分。json 文件判断的逻辑如下,仅对每个bb的 pout 进行判断。
例如:
```json
"pout": {
"label_entry": [["%op0", ], ["%op1", ], ["%op2", ], ],
"label3": [["%op0", ], ["%op1", ], ["%op2", ], ["%op4", "%op10", ], ["%op5", "%op9", ], ["%op6", "%op8", ], ],
"label7": [["%op0", ], ["%op1", ], ["%op2", ], ["%op10", ], ["%op9", ], ["%op11", "%op8", ], ],
"label12": [["%op0", ], ["%op1", ], ["%op2", ], ["%op13", "%op10", ], ["%op14", "%op9", ], ["%op15", "%op8", ], ],}
```
对于分值为 x,n 个基本块的程序,每个 bb 分析结果为$`x/n`$分,某个bb的分析结果多或者少一个等价类,或有分析错误的等价类,该 bb 分析结果没有分值。
```
对于每一个testcase:
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.8 得满分
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.5 得85%分数
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.2 得60%分数
```
* performance case(15分)
助教提供了 2 个公开case,并保留 2 个隐藏用例。以及助教实现优化后的 baseline.ll ,优化效果按照如下方式给分(执行结果不正确则此项分数为0)
```
对于每一个testcase:
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.8 得满分
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.5 得85%分数
(before_optimization-after_optimization)/(before_optimization-baseline) > 0.2 得60%分数
```
* 禁止执行恶意代码,违者本次实验0分处理
* 禁止执行恶意代码,违者本次实验0分处理
* 迟交规定
* `Soft Deadline`: 2022/12/12 23:59:59 (北京标准时间,UTC+8)
* `Hard Deadline`: 2022/12/19 23:59:59 (北京标准时间,UTC+8)
* 迟交需要邮件通知 TA :
* 邮箱:
chen16614@mail.ustc.edu.cn 抄送 farmerzhang1@mail.ustc.edu.cn
* 邮件主题: lab4.2迟交-学号
* 内容: 包括迟交原因、最后版本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 # 这一条严格执行,请对自己负责
```
* `Soft Deadline`: 2022/12/12 23:59:59 (北京标准时间,UTC+8)
* `Hard Deadline`: 2022/12/19 23:59:59 (北京标准时间,UTC+8)
* 迟交需要邮件通知 TA :
* 邮箱:
chen16614@mail.ustc.edu.cn 抄送 farmerzhang1@mail.ustc.edu.cn
* 邮件主题: lab4.2迟交-学号
* 内容: 包括迟交原因、最后版本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 # 这一条严格执行,请对自己负责
```
* 关于抄袭和雷同
经过助教和老师判定属于实验抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳(严禁对开源代码或者其他同学代码的直接搬运)。
......
......@@ -56,6 +56,7 @@ class Instruction : public User, public llvm::ilist_node<Instruction>
Module *get_module();
OpID get_instr_type() const { return op_id_; }
// clang-format off
static std::string get_instr_op_name(OpID id) {
switch (id) {
case ret: return "ret"; break;
......@@ -79,12 +80,10 @@ class Instruction : public User, public llvm::ilist_node<Instruction>
case zext: return "zext"; break;
case fptosi: return "fptosi"; break;
case sitofp: return "sitofp"; break;
default:
return "";
break;
default: return ""; break;
}
}
// clang-format on
std::string get_instr_op_name() { return get_instr_op_name(op_id_); }
bool is_void()
......
......@@ -159,6 +159,7 @@ class GVN : public Pass {
partitions join(const partitions &P1, const partitions &P2);
std::shared_ptr<CongruenceClass> intersect(std::shared_ptr<CongruenceClass>, std::shared_ptr<CongruenceClass>);
partitions transferFunction(Instruction *x, Value *e, partitions pin);
partitions transferFunction(BasicBlock *bb);
std::shared_ptr<GVNExpression::PhiExpression> valuePhiFunc(std::shared_ptr<GVNExpression::Expression>,
const partitions &);
std::shared_ptr<GVNExpression::Expression> valueExpr(Instruction *instr);
......@@ -176,6 +177,10 @@ class GVN : public Pass {
return std::make_shared<CongruenceClass>(index);
}
// self add
//
std::uint64_t new_number() { return next_value_number_++; }
private:
bool dump_json_;
std::uint64_t next_value_number_ = 1;
......@@ -184,6 +189,9 @@ class GVN : public Pass {
std::unique_ptr<FuncInfo> func_info_;
std::unique_ptr<GVNExpression::ConstFolder> folder_;
std::unique_ptr<DeadCode> dce_;
// self add
std::map<Instruction*, bool> _TOP;
};
bool operator==(const GVN::partitions &p1, const GVN::partitions &p2);
......@@ -22,65 +22,112 @@ using namespace GVNExpression;
using std::string_literals::operator""s;
using std::shared_ptr;
static auto get_const_int_value = [](Value *v) { return dynamic_cast<ConstantInt *>(v)->get_value(); };
static auto get_const_fp_value = [](Value *v) { return dynamic_cast<ConstantFP *>(v)->get_value(); };
static auto get_const_int_value = [](Value *v) {
return dynamic_cast<ConstantInt *>(v)->get_value();
};
static auto get_const_fp_value = [](Value *v) {
return dynamic_cast<ConstantFP *>(v)->get_value();
};
// Constant Propagation helper, folders are done for you
Constant *ConstFolder::compute(Instruction *instr, Constant *value1, Constant *value2) {
Constant *
ConstFolder::compute(Instruction *instr, Constant *value1, Constant *value2) {
auto op = instr->get_instr_type();
switch (op) {
case Instruction::add:
return ConstantInt::get(get_const_int_value(value1) + get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) +
get_const_int_value(value2),
module_);
case Instruction::sub:
return ConstantInt::get(get_const_int_value(value1) - get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) -
get_const_int_value(value2),
module_);
case Instruction::mul:
return ConstantInt::get(get_const_int_value(value1) * get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) *
get_const_int_value(value2),
module_);
case Instruction::sdiv:
return ConstantInt::get(get_const_int_value(value1) / get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) /
get_const_int_value(value2),
module_);
case Instruction::fadd:
return ConstantFP::get(get_const_fp_value(value1) + get_const_fp_value(value2), module_);
return ConstantFP::get(get_const_fp_value(value1) +
get_const_fp_value(value2),
module_);
case Instruction::fsub:
return ConstantFP::get(get_const_fp_value(value1) - get_const_fp_value(value2), module_);
return ConstantFP::get(get_const_fp_value(value1) -
get_const_fp_value(value2),
module_);
case Instruction::fmul:
return ConstantFP::get(get_const_fp_value(value1) * get_const_fp_value(value2), module_);
return ConstantFP::get(get_const_fp_value(value1) *
get_const_fp_value(value2),
module_);
case Instruction::fdiv:
return ConstantFP::get(get_const_fp_value(value1) / get_const_fp_value(value2), module_);
return ConstantFP::get(get_const_fp_value(value1) /
get_const_fp_value(value2),
module_);
case Instruction::cmp:
switch (dynamic_cast<CmpInst *>(instr)->get_cmp_op()) {
case CmpInst::EQ:
return ConstantInt::get(get_const_int_value(value1) == get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) ==
get_const_int_value(value2),
module_);
case CmpInst::NE:
return ConstantInt::get(get_const_int_value(value1) != get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) !=
get_const_int_value(value2),
module_);
case CmpInst::GT:
return ConstantInt::get(get_const_int_value(value1) > get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) >
get_const_int_value(value2),
module_);
case CmpInst::GE:
return ConstantInt::get(get_const_int_value(value1) >= get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) >=
get_const_int_value(value2),
module_);
case CmpInst::LT:
return ConstantInt::get(get_const_int_value(value1) < get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) <
get_const_int_value(value2),
module_);
case CmpInst::LE:
return ConstantInt::get(get_const_int_value(value1) <= get_const_int_value(value2), module_);
return ConstantInt::get(get_const_int_value(value1) <=
get_const_int_value(value2),
module_);
}
case Instruction::fcmp:
switch (dynamic_cast<FCmpInst *>(instr)->get_cmp_op()) {
case FCmpInst::EQ:
return ConstantInt::get(get_const_fp_value(value1) == get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) ==
get_const_fp_value(value2),
module_);
case FCmpInst::NE:
return ConstantInt::get(get_const_fp_value(value1) != get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) !=
get_const_fp_value(value2),
module_);
case FCmpInst::GT:
return ConstantInt::get(get_const_fp_value(value1) > get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) >
get_const_fp_value(value2),
module_);
case FCmpInst::GE:
return ConstantInt::get(get_const_fp_value(value1) >= get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) >=
get_const_fp_value(value2),
module_);
case FCmpInst::LT:
return ConstantInt::get(get_const_fp_value(value1) < get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) <
get_const_fp_value(value2),
module_);
case FCmpInst::LE:
return ConstantInt::get(get_const_fp_value(value1) <= get_const_fp_value(value2), module_);
return ConstantInt::get(get_const_fp_value(value1) <=
get_const_fp_value(value2),
module_);
}
default:
return nullptr;
}
}
Constant *ConstFolder::compute(Instruction *instr, Constant *value1) {
Constant *
ConstFolder::compute(Instruction *instr, Constant *value1) {
auto op = instr->get_instr_type();
switch (op) {
case Instruction::sitofp:
......@@ -95,22 +142,27 @@ Constant *ConstFolder::compute(Instruction *instr, Constant *value1) {
}
namespace utils {
static std::string print_congruence_class(const CongruenceClass &cc) {
static std::string
print_congruence_class(const CongruenceClass &cc) {
std::stringstream ss;
if (cc.index_ == 0) {
ss << "top class\n";
return ss.str();
}
ss << "\nindex: " << cc.index_ << "\nleader: " << cc.leader_->print()
<< "\nvalue phi: " << (cc.value_phi_ ? cc.value_phi_->print() : "nullptr"s)
<< "\nvalue expr: " << (cc.value_expr_ ? cc.value_expr_->print() : "nullptr"s) << "\nmembers: {";
<< "\nvalue phi: "
<< (cc.value_phi_ ? cc.value_phi_->print() : "nullptr"s)
<< "\nvalue expr: "
<< (cc.value_expr_ ? cc.value_expr_->print() : "nullptr"s)
<< "\nmembers: {";
for (auto &member : cc.members_)
ss << member->print() << "; ";
ss << "}\n";
return ss.str();
}
static std::string dump_cc_json(const CongruenceClass &cc) {
static std::string
dump_cc_json(const CongruenceClass &cc) {
std::string json;
json += "[";
for (auto member : cc.members_) {
......@@ -123,7 +175,8 @@ static std::string dump_cc_json(const CongruenceClass &cc) {
return json;
}
static std::string dump_partition_json(const GVN::partitions &p) {
static std::string
dump_partition_json(const GVN::partitions &p) {
std::string json;
json += "[";
for (auto cc : p)
......@@ -132,7 +185,8 @@ static std::string dump_partition_json(const GVN::partitions &p) {
return json;
}
static std::string dump_bb2partition(const std::map<BasicBlock *, GVN::partitions> &map) {
static std::string
dump_bb2partition(const std::map<BasicBlock *, GVN::partitions> &map) {
std::string json;
json += "{";
for (auto [bb, p] : map)
......@@ -142,7 +196,8 @@ static std::string dump_bb2partition(const std::map<BasicBlock *, GVN::partition
}
// logging utility for you
static void print_partitions(const GVN::partitions &p) {
static void
print_partitions(const GVN::partitions &p) {
if (p.empty()) {
LOG_DEBUG << "empty partitions\n";
return;
......@@ -154,53 +209,166 @@ static void print_partitions(const GVN::partitions &p) {
}
} // namespace utils
GVN::partitions GVN::join(const partitions &P1, const partitions &P2) {
GVN::partitions
GVN::join(const partitions &P1, const partitions &P2) {
// TODO: do intersection pair-wise
return {};
partitions P{};
for (auto c1 : P1)
for (auto c2 : P2) {
auto c = intersect(c1, c2);
if (c->members_.empty())
continue;
P.insert(c);
}
return P;
}
std::shared_ptr<CongruenceClass> GVN::intersect(std::shared_ptr<CongruenceClass> Ci,
std::shared_ptr<CongruenceClass> Cj) {
std::shared_ptr<CongruenceClass>
GVN::intersect(std::shared_ptr<CongruenceClass> ci,
std::shared_ptr<CongruenceClass> cj) {
// TODO
return {};
// If no common members, return null
auto c = createCongruenceClass();
std::set<Value *> intersection;
std::set_intersection(ci->members_.begin(),
ci->members_.end(),
cj->members_.begin(),
cj->members_.end(),
std::inserter(intersection, intersection.begin()));
c->members_ = intersection;
if (ci->index_ == cj->index_)
c->index_ = ci->index_;
if (ci->leader_ == cj->leader_)
c->leader_ = cj->leader_;
/* if (*ci == *cj)
* return ci; */
return c;
}
void GVN::detectEquivalences() {
bool changed = false;
void
GVN::detectEquivalences() {
bool changed;
// initialize pout with top
for (auto &bb : func_->get_basic_blocks()) {
// pin_[&bb].clear();
// pout_[&bb].clear();
for (auto &instr : bb.get_instructions())
_TOP[&instr] = true;
}
// modify entry block
auto Entry = func_->get_entry_block();
_TOP[&*Entry->get_instructions().begin()] = false;
pin_[Entry].clear();
pout_[Entry].clear(); // pout_[Entry] = transferFunction(Entry);
// iterate until converge
do {
changed = false;
// see the pseudo code in documentation
for (auto &bb : func_->get_basic_blocks()) { // you might need to visit the blocks in depth-first order
for (auto &_bb :
func_->get_basic_blocks()) { // you might need to visit the
// blocks in depth-first order
auto bb = &_bb;
// get PIN of bb by predecessor(s)
auto pre_bbs_ = bb->get_pre_basic_blocks();
if (bb != Entry) {
// only update PIN for blocks that are not Entry
// that is: the PIN for entry is always empty
switch (pre_bbs_.size()) {
case 2: {
auto pre_1 = *pre_bbs_.begin();
auto pre_2 = *(++pre_bbs_.begin());
pin_[bb] = join(pin_[pre_1], pin_[pre_2]);
break;
}
case 1: {
auto pre = *(pre_bbs_.begin());
pin_[bb] = clone(pin_[pre]);
break;
}
default:
LOG_ERROR << "block has count of predecessors: "
<< pre_bbs_.size();
abort();
}
}
auto part = pin_[bb];
// iterate through all instructions in the block
for (auto &instr : bb->get_instructions()) {
// ??
if (not instr.is_phi())
part = transferFunction(&instr, instr.get_operand(1), part);
}
// and the phi instruction in all the successors
for (auto succ : bb->get_succ_basic_blocks()) {
for (auto &instr : succ->get_instructions())
if (instr.is_phi()) {
Instruction *pretend;
// ??
part = transferFunction(
pretend, instr.get_operand(1), part);
}
}
// check changes in pout
changed |= not(part == pout_[bb]);
pout_[bb] = part;
}
} while (changed);
}
shared_ptr<Expression> GVN::valueExpr(Instruction *instr) {
shared_ptr<Expression>
GVN::valueExpr(Instruction *instr) {
// TODO
return {};
}
// instruction of the form `x = e`, mostly x is just e (SSA), but for copy stmt x is a phi instruction in the
// successor. Phi values (not copy stmt) should be handled in detectEquiv
// instruction of the form `x = e`, mostly x is just e (SSA),
// but for copy stmt x is a phi instruction in the successor.
// Phi values (not copy stmt) should be handled in detectEquiv
/// \param bb basic block in which the transfer function is called
GVN::partitions GVN::transferFunction(Instruction *x, Value *e, partitions pin) {
GVN::partitions
GVN::transferFunction(Instruction *x, Value *e, partitions pin) {
partitions pout = clone(pin);
// TODO: get different ValueExpr by Instruction::OpID, modify pout
std::set<Value *>::iterator iter;
for (auto c : pout) {
if ((iter = std::find(c->members_.begin(), c->members_.end(), x)) !=
c->members_.end()) {
// static_cast<Value *>(x))) != c->members_.end()) {
c->members_.erase(iter);
}
}
auto ve = valueExpr(x);
auto vpf = valuePhiFunc(ve, pin);
/* pout.insert({});
* auto c = CongruenceClass(new_number());
* c.leader_ = e; */
return pout;
}
GVN::partitions
GVN::transferFunction(BasicBlock *bb) {
partitions pout = clone(pin_[bb]);
// ??
return pout;
}
shared_ptr<PhiExpression> GVN::valuePhiFunc(shared_ptr<Expression> ve, const partitions &P) {
shared_ptr<PhiExpression>
GVN::valuePhiFunc(shared_ptr<Expression> ve, const partitions &P) {
// TODO
return {};
}
shared_ptr<Expression> GVN::getVN(const partitions &pout, shared_ptr<Expression> ve) {
shared_ptr<Expression>
GVN::getVN(const partitions &pout, shared_ptr<Expression> ve) {
// TODO: return what?
for (auto it = pout.begin(); it != pout.end(); it++)
if ((*it)->value_expr_ and *(*it)->value_expr_ == *ve)
......@@ -208,35 +376,45 @@ shared_ptr<Expression> GVN::getVN(const partitions &pout, shared_ptr<Expression>
return nullptr;
}
void GVN::initPerFunction() {
void
GVN::initPerFunction() {
next_value_number_ = 1;
pin_.clear();
pout_.clear();
}
void GVN::replace_cc_members() {
void
GVN::replace_cc_members() {
for (auto &[_bb, part] : pout_) {
auto bb = _bb; // workaround: structured bindings can't be captured in C++17
auto bb =
_bb; // workaround: structured bindings can't be captured in C++17
for (auto &cc : part) {
if (cc->index_ == 0)
continue;
// if you are planning to do constant propagation, leaders should be set to constant at some point
// if you are planning to do constant propagation, leaders should be
// set to constant at some point
for (auto &member : cc->members_) {
bool member_is_phi = dynamic_cast<PhiInst *>(member);
bool value_phi = cc->value_phi_ != nullptr;
if (member != cc->leader_ and (value_phi or !member_is_phi)) {
// only replace the members if users are in the same block as bb
member->replace_use_with_when(cc->leader_, [bb](User *user) {
if (auto instr = dynamic_cast<Instruction *>(user)) {
auto parent = instr->get_parent();
auto &bb_pre = parent->get_pre_basic_blocks();
if (instr->is_phi()) // as copy stmt, the phi belongs to this block
return std::find(bb_pre.begin(), bb_pre.end(), bb) != bb_pre.end();
else
return parent == bb;
}
return false;
});
// only replace the members if users are in the same block
// as bb
member->replace_use_with_when(
cc->leader_, [bb](User *user) {
if (auto instr =
dynamic_cast<Instruction *>(user)) {
auto parent = instr->get_parent();
auto &bb_pre = parent->get_pre_basic_blocks();
if (instr->is_phi()) // as copy stmt, the phi
// belongs to this block
return std::find(bb_pre.begin(),
bb_pre.end(),
bb) != bb_pre.end();
else
return parent == bb;
}
return false;
});
}
}
}
......@@ -245,7 +423,8 @@ void GVN::replace_cc_members() {
}
// top-level function, done for you
void GVN::run() {
void
GVN::run() {
std::ofstream gvn_json;
if (dump_json_) {
gvn_json.open("gvn.json", std::ios::out);
......@@ -267,13 +446,15 @@ void GVN::run() {
detectEquivalences();
LOG_INFO << "===============pin=========================\n";
for (auto &[bb, part] : pin_) {
LOG_INFO << "\n===============bb: " << bb->get_name() << "=========================\npartitionIn: ";
LOG_INFO << "\n===============bb: " << bb->get_name()
<< "=========================\npartitionIn: ";
for (auto &cc : part)
LOG_INFO << utils::print_congruence_class(*cc);
}
LOG_INFO << "\n===============pout=========================\n";
for (auto &[bb, part] : pout_) {
LOG_INFO << "\n=====bb: " << bb->get_name() << "=====\npartitionOut: ";
LOG_INFO << "\n=====bb: " << bb->get_name()
<< "=====\npartitionOut: ";
for (auto &cc : part)
LOG_INFO << utils::print_congruence_class(*cc);
}
......@@ -291,12 +472,15 @@ void GVN::run() {
}
template <typename T>
static bool equiv_as(const Expression &lhs, const Expression &rhs) {
// we use static_cast because we are very sure that both operands are actually T, not other types.
static bool
equiv_as(const Expression &lhs, const Expression &rhs) {
// we use static_cast because we are very sure that both operands are
// actually T, not other types.
return static_cast<const T *>(&lhs)->equiv(static_cast<const T *>(&rhs));
}
bool GVNExpression::operator==(const Expression &lhs, const Expression &rhs) {
bool
GVNExpression::operator==(const Expression &lhs, const Expression &rhs) {
if (lhs.get_expr_type() != rhs.get_expr_type())
return false;
switch (lhs.get_expr_type()) {
......@@ -309,13 +493,17 @@ bool GVNExpression::operator==(const Expression &lhs, const Expression &rhs) {
}
}
bool GVNExpression::operator==(const shared_ptr<Expression> &lhs, const shared_ptr<Expression> &rhs) {
if (lhs == nullptr and rhs == nullptr) // is the nullptr check necessary here?
bool
GVNExpression::operator==(const shared_ptr<Expression> &lhs,
const shared_ptr<Expression> &rhs) {
if (lhs == nullptr and
rhs == nullptr) // is the nullptr check necessary here?
return true;
return lhs and rhs and *lhs == *rhs;
}
GVN::partitions GVN::clone(const partitions &p) {
GVN::partitions
GVN::clone(const partitions &p) {
partitions data;
for (auto &cc : p) {
data.insert(std::make_shared<CongruenceClass>(*cc));
......@@ -323,12 +511,18 @@ GVN::partitions GVN::clone(const partitions &p) {
return data;
}
bool operator==(const GVN::partitions &p1, const GVN::partitions &p2) {
bool
operator==(const GVN::partitions &p1, const GVN::partitions &p2) {
// TODO: how to compare partitions?
return false;
// cannot direct compare???
if (p1.size() != p2.size())
return false;
return std::equal(p1.begin(), p1.end(), p2.begin(), p2.end());
}
bool CongruenceClass::operator==(const CongruenceClass &other) const {
// only compare index
bool
CongruenceClass::operator==(const CongruenceClass &other) const {
// TODO: which fields need to be compared?
return false;
return index_ == other.index_;
}
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