Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
2
2022fall-Compiler_CMinus
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
李晓奇
2022fall-Compiler_CMinus
Commits
efbf4233
Commit
efbf4233
authored
Dec 02, 2022
by
李晓奇
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
follow update
parent
f0f0bb81
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
399 additions
and
187 deletions
+399
-187
.clang-format
.clang-format
+2
-1
Documentations/4.2-gvn/README.md
Documentations/4.2-gvn/README.md
+117
-108
Documentations/4.2-gvn/gvn.pdf
Documentations/4.2-gvn/gvn.pdf
+0
-0
include/lightir/Instruction.h
include/lightir/Instruction.h
+3
-4
include/optimization/GVN.h
include/optimization/GVN.h
+8
-0
src/optimization/.gitignore
src/optimization/.gitignore
+1
-0
src/optimization/GVN.cpp
src/optimization/GVN.cpp
+268
-74
No files found.
.clang-format
View file @
efbf4233
...
...
@@ -6,7 +6,7 @@ AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: BeforeComma
ColumnLimit:
12
0
ColumnLimit:
8
0
CommentPragmas: '^(!|NOLINT)'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
IncludeBlocks: Regroup
...
...
@@ -23,4 +23,5 @@ PenaltyReturnTypeOnItsOwnLine: 200
SpacesBeforeTrailingComments: 1
TabWidth: 4
UseTab: Never
AlwaysBreakAfterReturnType: TopLevelDefinitions
...
Documentations/4.2-gvn/README.md
View file @
efbf4233
# 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
# 这一条严格执行,请对自己负责
```
*
关于抄袭和雷同
经过助教和老师判定属于实验抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳(严禁对开源代码或者其他同学代码的直接搬运)。
...
...
Documentations/4.2-gvn/gvn.pdf
View file @
efbf4233
No preview for this file type
include/lightir/Instruction.h
View file @
efbf4233
...
...
@@ -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
()
...
...
include/optimization/GVN.h
View file @
efbf4233
...
...
@@ -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
);
src/optimization/.gitignore
0 → 100644
View file @
efbf4233
todo
src/optimization/GVN.cpp
View file @
efbf4233
...
...
@@ -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
<<
"
\n
index: "
<<
cc
.
index_
<<
"
\n
leader: "
<<
cc
.
leader_
->
print
()
<<
"
\n
value phi: "
<<
(
cc
.
value_phi_
?
cc
.
value_phi_
->
print
()
:
"nullptr"
s
)
<<
"
\n
value expr: "
<<
(
cc
.
value_expr_
?
cc
.
value_expr_
->
print
()
:
"nullptr"
s
)
<<
"
\n
members: {"
;
<<
"
\n
value phi: "
<<
(
cc
.
value_phi_
?
cc
.
value_phi_
->
print
()
:
"nullptr"
s
)
<<
"
\n
value expr: "
<<
(
cc
.
value_expr_
?
cc
.
value_expr_
->
print
()
:
"nullptr"
s
)
<<
"
\n
members: {"
;
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
()
<<
"=========================
\n
partitionIn: "
;
LOG_INFO
<<
"
\n
===============bb: "
<<
bb
->
get_name
()
<<
"=========================
\n
partitionIn: "
;
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
()
<<
"=====
\n
partitionOut: "
;
LOG_INFO
<<
"
\n
=====bb: "
<<
bb
->
get_name
()
<<
"=====
\n
partitionOut: "
;
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_
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment