Commit 078a8fb5 authored by 张栋澈's avatar 张栋澈 🤡

[Lab4.2] Publish

parent 28a084f5
# 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. 前言
在 Lab4.1 中,我们介绍了 SSA IR,并阐明了其优势。本次实验中我们需要在 SSA IR 基础上,实现一个基于数据流分析的冗余消除的优化 Pass : Global Value Numbering(全局值编号)。
## 1. GVN 基础知识
### 1.1 GVN 简介
GVN(Global Value Numbering) 全局值编号,是一个基于 SSA 格式的优化,通过建立变量,表达式到值编号的映射关系,从而检测到冗余计算代码并删除。本次实验采用的算法参考论文为:**[Detection of Redundant Expressions: A Complete and Polynomial-Time Algorithm in SSA](./gvn.pdf)** 在该论文中,提出了一种适合 SSA IR ,多项式算法复杂度的数据流分析方式的算法,能够实现对冗余代码完备的检测。本次实验中,我们将在`Light IR` 上实现该数据流分析算法,并根据数据流分析结果删掉冗余代码,达到优化目的。
### 1.2 GVN 相关概念
#### 1. IR 假设
将IR抽象为具有空的`entry block``exit block`的控制流图(CFG)。块内包含形式为`x = e`的赋值语句,其中`e`为表达式,`x`为变量。每个bb块最多可以有两个前驱,有两个前驱的bb块被称为`join block`
#### 2. Expression 概念
一个`Expression`(表达式)可以是一个常量,一个变量,或者是$`x⊕y`$的形式,其中 x 与 y 是常量或者变量, ⊕ 代表一个通用的二元运算符。一个`Expression`也可以是 $`\phi_k(x,y)`$ 的形式,其中 x, y 是变量,并且 k 表示 join block。这种形式的 `Expression`被称作 `phi` 函数。基于 Lab4.1 对`phi`指令概念的理解,我们知道 phi 函数是出现在 join block 的起始处,而在实际的数据流分析过程中,我们会将 `phi` 指令作为 join block 前驱的赋值指令进行处理。
**注**:如下例子所示:形如$`x_3=\phi(x_{1},x_{2})`$的 phi 指令。
```asm
bb1:
x1 = 1 + 1
br bb3
bb2:
x2 = 2 + 2
br bb3
bb3:
x3=phi(x1,x2)
...
```
为了实现 SSA 上 GVN 分析的完备性,我们在数据流分析中,将 phi 指令在对应前驱中转化为 $`x_3 = x_1`$, $`x_3 = x_2`$ 两条赋值语句处理。如下例子所示:
```asm
bb1:
x1 = 1 + 1
x3 = x1
br bb3
bb2:
x2 = 2 + 2
x3 = x2
br bb3
bb3:
...
```
#### 3. 等价概念
两个表达式 `e1`,`e2` 如果被称作等价的,那么他们有着相同的运算符,并且操作数也是对应等价的。
#### 4. Value Expression 概念
一个 Value Expression(值表达式),$`vi⊕vj`$ 代表了值编号为vi,vj的两个等价类之间的运算。$`vi⊕vj=\{x⊕y;x\in C_i,C_i是一系列变量等价的等价类集合,值编号为v_i;y\in C_j,C_j是一系列变量等价的等价类的集合, 值编号是v_j\}`$
#### 5. Value phi-function 概念
与值表达式类似,value phi-function 视作为一系列等价的 phi-function 的抽象。在本次 GVN 实验中,需要扩展 phi-function 的理解,例如如下ir片段的例子:
```asm
bb1:
y=x1+1
br bb3
bb2:
z=x2+1
br bb3
bb3:
x3=phi(x1,x2)
w3=x3+1
```
对于 变量 w3 对应的 Expression x3+1,由于x3是phi function,因此可以通过[expression 概念](#2-expression-概念)中对phi function的处理探测出:w3,y在bb1中等价,w3,z在bb2中等价,因此可以将 w3 视作是变量 y 与变量 z 在 bb3 处的合并,可以用 $`\phi_{bb_3}(y,z)`$ 来表示w3对应的 value phi-function。
#### 6. Partition
一个 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. 变量等价与表达式等价
例如如下代码段:
```asm
bb1:
x1=1+1
y1=2+2
z1=x1+y1
br bb3
bb2:
x2=2+2
y2=3+3
z2=x2+y2
br bb3
bb3:
x3=phi(x1,x2)
y3=phi(y1,y2)
z3=phi(z1,z2)
z4=x3+y3
```
在左分支bb1中,通过将 x3=phi(x1, x2)语句转换为前驱的复制语句 x3=x1 ,可以检测出,x3与x1在左分支路径上的等价性。同理也有x3与x2在右分支上的等价性。此为**变量等价**
同时,通过对z4=x3+y3的分析,可以看出,x3+y3 这个表达式,在bb1中与x1+y1等价,在bb2中与x2+y2等价,此为**表达式等价**,通过这个表达式等价的关系,可以分析出,z4与z1在bb1中变量等价,z4与z2在bb2中变量等价,因此可以建立z4的phi函数为phi(z1,z2),进而可以导出z3与z4的**变量等价**,从而消除z3与z4的冗余。
#### 9. Transfer Function
TransferFunction 作用于一条语句s:x=e 上,接受 partition 记为 $`PIN_s`$ 并探测 e 与到达此处程序点所有 path 上是否有等价的 expression。TransferFunction 通过更新原有的等价类,或者创建新的等价类来在 partition $`PIN_s`$ 基础上修改并输出 partition 记为 $`POUT_s`$。在后面给出的伪代码可以看出,TransferFunction先在$`PIN_s`$检测是否存在 expression e 的 value expression,然后会继续检查 e 是否可以表达为不同表达式的合并。考虑如下例子(沿用了在变量等价中的例子):
```asm
bb1:
x1=1+1
y1=2+2
z1=x1+y1
br bb3
bb2:
x2=2+2
y2=3+3
z2=x2+y2
br bb3
bb3:
x3=phi(x1,x2)
y3=phi(y1,y2)
z3=phi(z1,z2)
z4=x3+y3
```
在对`z4=x3+y3`这个表达式执行$`\text{TransferFunction}(x=e, PIN_s)`$:
其中,x 为 z4, e 为 `x3+y3`
$`PIN_{bb3}=\{\{v_1,x_1,1+1:\text{non-phi}\},\{v_2,y_1,2+2:\text{non-phi}\},\{v_3,z_1,x_1+y_1:\text{non-phi}\},\{v_4,x_2,2+2:\text{non-phi}\},\{v_5,y_2,3+3:\text{non-phi}\},\{v_6,z_2,x_2+y_2:\text{non-phi}\},\{v_7,x_3:\phi(x_1,x_2)\},\{v_8,y_3:\phi(y_1,y_2)\},\{v_9,z_3:\phi(z_1,z_2)\}\}`$
在 $`PIN_{bb3}`$ 中找不到 `x3+y3` 对应的 expression 表达式。
但是,对 `x3+y3` 进一步分析可以得到:因为`x3``y3`都是`phi``x3+y3`可以表示为 `bb1``x1+y1``bb2``x2+y2` 的两个表达式的合并,因此可以记为 phi(`x1+y1`, `x2+y2`)
接下来,可以检测到变量 z1 与 `x1+y1` expression 在`bb3`前驱`bb1`中的等价关系,变量 z2 与 `x2+y2` expression 在`bb3`前驱`bb2`中的等价关系。因此可以检测到 phi(`x1+y1`, `x2+y2`)与 phi(z1,z2)的等价关系。
进而得到 变量 z3 与 z4 是等价的,`x3 + y3` expression 的计算是冗余的。
注:这里的 $`PIN_{bb3}`$ 的例子仅仅是为了说明 TransferFunction 的职能和 partition 的结构式样,不代表最终迭代稳定后的结果。
## 2. GVN 算法(论文中提供的伪代码)
本小节附上了原论文中附的伪代码,伪代码中给出了五个主函数的逻辑,这里自顶向下梳理一下:
`detectEquivalences(G)` 包含了最主要的数据流迭代过程,传入参数 G 为抽象的数据流分析的 CFG 图结构,实现中可以以函数为分析的基本单位来执行此函数,迭代初始时,将各个 Partition 初始化为顶元`Top`, 定义为`Join(P, Top) = P = Join(Top, P)`
注:此函数中的 statement 的概念对应前述的一条语句
```clike
detectEquivalences(G)
PIN1 = {} // “1” is the first statement in the program
POUT1 = transferFunction(PIN1)
for each statement s other than the first statement in the program
POUTs = Top
while changes to any POUT occur // i.e. changes in equivalences
for each statement s other than the first statement in the program
if s appears in block b that has two predecessors
then
PINs = Join(POUTs1, POUTs2) // s1 and s2 are last statements in respective predecessors
else
PINs = POUTs3 // s3 the statement just before s
POUTs = transferFunction(PINs) // apply transferFunction on each statement in the block
```
`Join` 操作仅仅在某个语句有两个前驱时被触发,注意:在join操作执行之前,join block 中的 phi 语句会作为 copy statement 加入 join block 对应的两个前驱末尾处。细节处理方式见[expression 概念](#2-expression-概念)的注栏。
```clike
Join(P1, P2)
P = {}
for each pair of classes Ci ∈ P1 and Cj ∈ P2
Ck = Intersect(Ci, Cj)
P = P ∪ Ck // Ignore when Ck is empty
return P
Intersect(Ci, Cj)
Ck = Ci ∩ Cj // set intersection
if Ck != {} and Ck does not have value number
then
Ck = Ck ∪ {vk} // vk is new value number
Ck = (Ck − {vpf}) ∪ {φb(vi, vj)}
// vpf is value φ-function in Ck, vi ∈ Ci, vj ∈ Cj, b is join block
return Ck
```
`TransferFunction` 接受一个赋值语句 x=e(x是变量,e为表达式),与一个 partition $`PIN_s`$ 。其中 getVN 从一个 partition 中根据 e 来找到对应的编号。
注: 在 lightir 的设计中,普通语句的 x 与 e 存在同一个类中,而 phi 语句中需要经转换为 copy 语句,x 与 e 与普通语句会有一些不同,需要仔细思考区分一下。
```clike
TransferFunction(x = e, PINs)
POUTs = PINs
if x is in a class Ci in POUTs
then Ci = Ci − {x}
ve = valueExpr(e)
vpf = valuePhiFunc(ve,PINs) // can be NULL
if ve or vpf is in a class Ci in POUTs // ignore vpf when NULL
then
Ci = Ci ∪ {x, ve} // set union
else
POUTs = POUTs ∪ {vn, x, ve : vpf} // vn is new value number
return POUTs
valuePhiFunc(ve,P)
if ve is of the form φk(vi1, vj1) ⊕ φk(vi2, vj2)
then
// process left edge
vi = getVN(POUTkl, vi1 ⊕ vi2)
if vi is NULL
then vi = valuePhiFunc(vi1 ⊕ vi2, POUTkl)
// process right edge
vj = getVN(POUTkr, vj1 ⊕ vj2)
if vj is NULL
then vj = valuePhiFunc(vj1 ⊕ vj2, POUTkr)
if vi is not NULL and vj is not NULL
then return φk(vi, vj)
else return NULL
```
## 3. 实验内容
在本次实验中,请仔细阅读[3.2 GVN pass 实现内容要求](#32-gvn-pass-实现内容要求),根据要求补全`src/optimization/GVN.cpp``include/optimization/GVN.cpp`中关于 GVN pass 数据流分析部分,同时需要在 `Reports/4-ir-opt/` 目录下撰写实验报告。**为了在评测中统一分析结果,请大家采用 lab3 的 TA-impl 分支提供的答案来继续后续实验。**
### 3.1 GVN pass 实现内容要求
GVN 通过数据流分析来检测冗余的变量和计算,通过替换和死代码删除结合,实现优化效果。前述的例子中主要以二元运算语句来解释原理,且助教为大家提供了代码替换和删除的逻辑,除此之外,需要完成的方向有:
1. 对冗余指令的检测与消除包括(二元运算指令,cmp,gep,类型转换指令)
2. 对纯函数的冗余调用消除(助教提供了纯函数分析,见[FuncInfo.h](../../include/optimization/FuncInfo.h)
该 Pass 的接口`is_pure_function`接受一个lightIR Function,判断该函数是否为纯函数;对于纯函数,如果其参数等价,对这个纯函数的不同调用也等价。
3. 常量传播
在数据流分析的过程中,可以使用常量折叠来实现常量传播,从而将可在编译时计算的结果计算好,减少运行时开销。(助教提供了常量折叠类,在辅助类的介绍中)
我们会在测试样例中对这三点进行考察。
**注**:我们为大家提供了冗余删除的函数 `GVN::replace_cc_members` ,只需要正确填充在 `GVN` 类中的 `pout` 变量,我们的替换逻辑将会根据每个 bb 的 pout 自行使用`CongruenceClass``leader_` 成员来替换在此 bb 内与其等价其他指令。
### 3.2 GVN 辅助类
在上述对 GVN 概念的介绍中,为了能让大家专注于核心数据流分析逻辑的实现,我们为大家提供了一些相应实现的辅助类,并在注释里解释了其相应用途,请注意与前述的 GVN 的抽象概念结合,理解其设计,并补充必要的类成员的实现。
`GVN.h`:
```c++
class ConstFolder; // 常量折叠类,用于折叠操作数都是常量的指令
// 该 namespace 下,包含了用于判断 expression 等价的结构,我们提供了 binary expression,phi expression,constant expression 的结构,请根据测试用例添加你需要的其他 expression 的结构,具体细节请据 GVN.h 结合代码与注释理解
namespace GVNExpression {
class Expression; // 所有 expression 类型的基类
class ConstantExpression; // 常量 expression 类型
class BinaryExpression; // 二元运算 expression 包括 + - * /
class PhiExpression; // phi expression 类型,表示不同路径的 expression 在此的合并
}
struct CongruenceClass; // 对应伪代码中等价类的概念,分析结果会根据此类 dump 至 json 文件中,代码替换与消除逻辑也根据此结构实现
class GVN; // GVN pass核心实现逻辑,除一些用的上的辅助函数外,重点补齐此处与伪代码对应的核心函数,其中run()函数是 pass 启动的入口,已经为大家补充好。
```
`GVN.cpp`:
```c++
namespace utils // 一些用于输出的函数,可方便调试,以及将结果 dump 到 json 文件中的方法
```
### 3.3 注册及运行 GVN Pass
#### 注册 Pass
本次实验使用了由 C++ 编写的 `LightIR` 来在 IR 层面完成优化化简,在`include/optimization/PassManager.hpp`中,定义了一个用于管理 Pass 的类`PassManager`。它的作用是注册与运行 Pass 。它提供了以下接口:
```cpp
PassManager pm(module.get())
pm.add_Pass<GVN>(emit, dump_json) // 注册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 ] [ -gvn [ -dump-json ] ] <input-file>
```
其中,gvn pass 需要在 mem2reg pass 运行后运行。
### 3.3 自动测试
助教贴心地为大家准备了自动测试脚本,它在 `tests/4-ir-opt` 目录下,使用方法如下:
```bash
python3 lab4_evals.py [ -gvn-analysis ] [ -gvn ]
```
该脚本可以在任意目录下运行
```bash
python3 tests/4-ir-opt/lab4_evals.py [ -gvn-analysis ] [ -gvn ]
```
其中 `-gvn-analysis` 对 GVN 分析结果的正确性进行判断,执行结果如下所示:
```bash
========== GlobalValueNumberAnalysis ==========
Compiling -mem2reg -emit-llvm -gvn -dump-json
0%| | 0/4 [00:00<?, ?it/s]
Compile bin.cminus success
generate json bin.cminus success
25%|███▊ | 1/4 [00:00<00:00, 8.38it/s]
Compile loop3.cminus success
generate json loop3.cminus success
50%|███████▌ | 2/4 [00:00<00:00, 7.21it/s]
Compile pure_func.cminus success
generate json pure_func.cminus success
75%|███████████▎ | 3/4 [00:00<00:00, 4.08it/s]
Compile single_bb1.cminus success
generate json single_bb1.cminus success
100%|███████████████| 4/4 [00:00<00:00, 4.64it/s]
bin.cminus: 1.0
loop3.cminus: 1.0
pure_func.cminus: 1.0
single_bb1.cminus: 1.0
```
`-gvn` 会执行测试文件,比对你实现的 GVN 算法优化后的程序,与助教的基准程序的运行时间。
```bash
========== GlobalValueNumber ==========
Compiling -mem2reg
100%|███████████████| 2/2 [00:00<00:00, 8.22it/s]
Evalution
100%|███████████████| 2/2 [00:17<00:00, 6.44s/it]
Compiling -mem2reg -gvn
100%|███████████████| 2/2 [00:00<00:00, 5.67it/s]
Evalution
100%|███████████████| 2/2 [00:13<00:00, 4.36s/it]
Compiling baseline files
100%|███████████████| 2/2 [00:00<00:00, 13.70it/s]
Evalution
100%|███████████████| 2/2 [00:09<00:00, 3.24s/it]
testcase before optimization after optimization baseline
constant.cminus 0.70 0.24 0.24
transpose.cminus 3.72 3.04 3.03
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 应该比较完整了,可以根据带循环的简单程序来调试。
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)
## 4. 提交要求
### 目录结构
```
.
├── CMakeLists.txt
├── Documentations
│   ├── ...
│   ├── common
│   | ├── LightIR.md LightIR 相关文档
│   | ├── logging.md logging 工具相关文档
│   |   └── cminusf.md cminus-f 的语法和语义文档
│   └── 4.2-gvn
│   ├── gvn.pdf 本实验参考文献原文
│      └── README.md Lab4 实验文档说明(你在这里)
├── include 实验所需的头文件
│   ├── ...
│   ├── optimization
│   │ ├── Mem2Reg.hpp Mem2Reg
│ │ ├── Dominators.hpp 支配树
│ │ └── GVN.h GVN <--- 修改并提交
│   ├── cminusf_builder.hpp
| └── ast.hpp
├── Reports
│   ├── ...
│   └── 4.2-gvn
│   └── report.md lab4 本次实验报告模板 <--- 修改并提交
│  
├── src
│   ├── ...
│   └── optimization
│   ├── Mem2Reg.cpp Mem2Reg
│ ├── Dominators.cpp 支配树
│ └── 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 格式提交到希冀平台对应提交通道
* 评分标准: 实验完成分(总分 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)
```
对于每一个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分处理
* 迟交规定
* `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 # 这一条严格执行,请对自己负责
```
* 关于抄袭和雷同
经过助教和老师判定属于实验抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳(严禁对开源代码或者其他同学代码的直接搬运)。
如有任何问题,欢迎提issue进行批判指正。
......@@ -106,3 +106,13 @@ C中,只能使用标准库中的`malloc`与`free`来进行内存分配,并
1. `std::shared_ptr`: 引用计数智能指针,使用一个共享变量来记录指针管理的对象被引用了几次。当对象引用计数为0时,说明当前该对象不再有引用,并且进程也无法再通过其它方式来引用它,也就意味着可以回收内存,这相当于低级的垃圾回收策略。
2. `std::unique_ptr`: 表示所有权的智能指针,该指针要求它所管理的对象智能有一次引用,主要用于语义上不允许共享的对象(比如`llvm::Module`)。当引用计数为0时,它也会回收内存。
## Debugging Seg Fault
Lab3/4 中,你可能会各种各样的段错误,不要害怕!clang 提供了一个工具来帮助我们解决内存泄漏。
```bash
cd build
cmake .. -DCMAKE_BUILD_TYPE=Asan
make
```
然后再次运行你的错误代码, Asan 会提供更详细的报错信息。
注:要更换到别的 build type (如Debug或Release)时需要显式指定,否则 cmake 会使用 cached 的版本。
# Lab4.2 实验报告
姓名 学号
## 实验要求
请按照自己的理解,写明本次实验需要做什么
## 实验难点
实验中遇到哪些挑战
## 实验设计
实现思路,相应代码,优化前后的IR对比(举一个例子)并辅以简单说明
### 思考题
1. 请简要分析你的算法复杂度
2. `std::shared_ptr`如果存在环形引用,则无法正确释放内存,你的 Expression 类是否存在 circular reference?
3. 尽管本次实验已经写了很多代码,但是在算法上和工程上仍然可以对 GVN 进行改进,请简述你的 GVN 实现可以改进的地方
## 实验总结
此次实验有什么收获
## 实验反馈(可选 不会评分)
对本次实验的建议
......@@ -55,8 +55,8 @@ class Instruction : public User, public llvm::ilist_node<Instruction> {
Module *get_module();
OpID get_instr_type() const { return op_id_; }
std::string get_instr_op_name() {
switch (op_id_) {
static std::string get_instr_op_name(OpID id) {
switch (id) {
case ret: return "ret"; break;
case br: return "br"; break;
case add: return "add"; break;
......@@ -82,6 +82,7 @@ class Instruction : public User, public llvm::ilist_node<Instruction> {
default: return ""; break;
}
}
std::string get_instr_op_name() { return get_instr_op_name(op_id_); }
bool is_void() {
return ((op_id_ == ret) || (op_id_ == br) || (op_id_ == store) ||
......
#ifndef SYSYC_VALUE_H
#define SYSYC_VALUE_H
#include <functional>
#include <iostream>
#include <list>
#include <string>
class Type;
class Value;
class User;
struct Use {
Value *val_;
......@@ -35,6 +37,8 @@ class Value {
std::string get_name() const;
void replace_all_use_with(Value *new_val);
/// replace `value` with `new_val` when the user of value satisfies predicate `pred`
void replace_use_with_when(Value *new_val, std::function<bool(User *)> pred);
void remove_use(Value *val);
virtual std::string print() = 0;
......
#pragma once
#include "FuncInfo.h"
#include "Function.h"
#include "Instruction.h"
#include "PassManager.hpp"
#include "logging.hpp"
/**
* 死代码消除:参见 https://www.clear.rice.edu/comp512/Lectures/10Dead-Clean-SCCP.pdf
**/
class DeadCode : public Pass {
public:
DeadCode(Module *m) : Pass(m), func_info(std::make_shared<FuncInfo>(m)) {}
// 处理流程:两趟处理,mark 标记有用变量,sweep 删除无用指令
void run() {
bool changed{};
func_info->run();
do {
changed = false;
for (auto &F : m_->get_functions()) {
auto func = &F;
mark(func);
changed |= sweep(func);
}
} while (changed);
LOG_INFO << "dead code pass erased " << ins_count << " instructions";
}
private:
std::shared_ptr<FuncInfo> func_info;
void mark(Function *func) {
work_list.clear();
marked.clear();
for (auto &bb : func->get_basic_blocks()) {
for (auto &ins : bb.get_instructions()) {
if (is_critical(&ins)) {
marked[&ins] = true;
work_list.push_back(&ins);
}
}
}
while (work_list.empty() == false) {
auto now = work_list.front();
work_list.pop_front();
mark(now);
}
}
void mark(Instruction *ins) {
for (auto op : ins->get_operands()) {
auto def = dynamic_cast<Instruction *>(op);
if (def == nullptr)
continue;
if (marked[def])
continue;
if (def->get_function() != ins->get_function())
continue;
marked[def] = true;
work_list.push_back(def);
}
}
bool sweep(Function *func) {
std::unordered_set<Instruction *> wait_del{};
for (auto &bb : func->get_basic_blocks()) {
for (auto it = bb.get_instructions().begin(); it != bb.get_instructions().end();) {
if (marked[&*it]) {
++it;
continue;
} else {
auto tmp = &*it;
wait_del.insert(tmp);
it++;
}
}
}
for (auto inst : wait_del)
inst->remove_use_of_ops();
for (auto inst : wait_del)
inst->get_parent()->get_instructions().erase(inst);
ins_count += wait_del.size();
return not wait_del.empty(); // changed
}
bool is_critical(Instruction *ins) {
// 对纯函数的无用调用也可以在删除之列
if (ins->is_call()) {
auto call_inst = dynamic_cast<CallInst *>(ins);
auto callee = dynamic_cast<Function *>(call_inst->get_operand(0));
if (func_info->is_pure_function(callee))
return false;
return true;
}
if (ins->is_br() || ins->is_ret())
return true;
if (ins->is_store())
return true;
return false;
}
// 用以衡量死代码消除的性能
int ins_count{0};
std::deque<Instruction *> work_list{};
std::unordered_map<Instruction *, bool> marked{};
};
#pragma once
#include "Function.h"
#include "PassManager.hpp"
#include "logging.hpp"
#include <deque>
#include <unordered_map>
#include <unordered_set>
/**
* 计算哪些函数是纯函数
* WARN: 假定所有函数都是纯函数,除非他写入了全局变量、修改了传入的数组、或者直接间接调用了非纯函数
*/
class FuncInfo : public Pass {
public:
FuncInfo(Module *m) : Pass(m) {}
void run() {
for (auto &f : m_->get_functions()) {
auto func = &f;
trivial_mark(func);
if (not is_pure[func])
worklist.push_back(func);
}
while (worklist.empty() == false) {
auto now = worklist.front();
worklist.pop_front();
process(now);
}
log();
}
bool is_pure_function(Function *func) const {
// exit_if(is_pure.find(func) == is_pure.end(), ERROR_IN_PURE_FUNCTION_ANALYSIS);
return is_pure.at(func);
}
void log() {
for (auto it : is_pure) {
LOG_INFO << it.first->get_name() << " is pure? " << it.second;
}
}
private:
// 有 store 操作的函数非纯函数来处理
void trivial_mark(Function *func) {
if (func->is_declaration() or func->get_name() == "main") {
is_pure[func] = false;
return;
}
// 只要传入数组,都作为非纯函数处理
for (auto it = func->get_function_type()->param_begin(); it != func->get_function_type()->param_end(); ++it) {
auto arg_type = *it;
if (arg_type->is_integer_type() == false and arg_type->is_float_type() == false) {
is_pure[func] = false;
return;
}
}
for (auto &bb : func->get_basic_blocks())
for (auto &inst : bb.get_instructions()) {
if (is_side_effect_inst(&inst)) {
is_pure[func] = false;
return;
}
}
is_pure[func] = true;
}
void process(Function *func) {
for (auto &use : func->get_use_list()) {
LOG_INFO << use.val_->print() << " uses func: " << func->get_name();
if (auto inst = dynamic_cast<Instruction *>(use.val_)) {
auto func = (inst->get_parent()->get_parent());
if (is_pure[func]) {
is_pure[func] = false;
worklist.push_back(func);
}
} else
LOG_WARNING << "Value besides instruction uses a function";
}
}
// 对局部变量进行 store 没有副作用
bool is_side_effect_inst(Instruction *inst) {
if (inst->is_store()) {
if (is_local_store(dynamic_cast<StoreInst *>(inst)))
return false;
return true;
}
if (inst->is_load()) {
if (is_local_load(dynamic_cast<LoadInst *>(inst)))
return false;
return true;
}
// call 指令的副作用会在后续 bfs 中计算
return false;
}
bool is_local_load(LoadInst *inst) {
auto addr = dynamic_cast<Instruction *>(get_first_addr(inst->get_operand(0)));
if (addr and addr->is_alloca())
return true;
return false;
}
bool is_local_store(StoreInst *inst) {
auto addr = dynamic_cast<Instruction *>(get_first_addr(inst->get_lval()));
if (addr and addr->is_alloca())
return true;
return false;
}
Value *get_first_addr(Value *val) {
if (auto inst = dynamic_cast<Instruction *>(val)) {
if (inst->is_alloca())
return inst;
if (inst->is_gep())
return get_first_addr(inst->get_operand(0));
if (inst->is_load())
return val;
LOG_WARNING << "FuncInfo: try to determine addr in operands";
for (auto op : inst->get_operands()) {
if (op->get_type()->is_pointer_type())
return get_first_addr(op);
}
}
return val;
}
std::deque<Function *> worklist;
std::unordered_map<Function *, bool> is_pure;
};
#pragma once
#include "BasicBlock.h"
#include "Constant.h"
#include "DeadCode.h"
#include "FuncInfo.h"
#include "Function.h"
#include "Instruction.h"
#include "Module.h"
#include "PassManager.hpp"
#include "Value.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
namespace GVNExpression {
// fold the constant value
class ConstFolder {
public:
ConstFolder(Module *m) : module_(m) {}
Constant *compute(Instruction *instr, Constant *value1, Constant *value2);
Constant *compute(Instruction *instr, Constant *value1);
private:
Module *module_;
};
/**
* for constructor of class derived from `Expression`, we make it public
* because `std::make_shared` needs the constructor to be publicly available,
* but you should call the static factory method `create` instead the constructor itself to get the desired data
*/
class Expression {
public:
// TODO: you need to extend expression types according to testcases
enum gvn_expr_t { e_constant, e_bin, e_phi };
Expression(gvn_expr_t t) : expr_type(t) {}
virtual ~Expression() = default;
virtual std::string print() = 0;
gvn_expr_t get_expr_type() const { return expr_type; }
private:
gvn_expr_t expr_type;
};
bool operator==(const std::shared_ptr<Expression> &lhs, const std::shared_ptr<Expression> &rhs);
bool operator==(const GVNExpression::Expression &lhs, const GVNExpression::Expression &rhs);
class ConstantExpression : public Expression {
public:
static std::shared_ptr<ConstantExpression> create(Constant *c) { return std::make_shared<ConstantExpression>(c); }
virtual std::string print() { return c_->print(); }
// we leverage the fact that constants in lightIR have unique addresses
bool equiv(const ConstantExpression *other) const { return c_ == other->c_; }
ConstantExpression(Constant *c) : Expression(e_constant), c_(c) {}
private:
Constant *c_;
};
// arithmetic expression
class BinaryExpression : public Expression {
public:
static std::shared_ptr<BinaryExpression> create(Instruction::OpID op,
std::shared_ptr<Expression> lhs,
std::shared_ptr<Expression> rhs) {
return std::make_shared<BinaryExpression>(op, lhs, rhs);
}
virtual std::string print() {
return "(" + Instruction::get_instr_op_name(op_) + " " + lhs_->print() + " " + rhs_->print() + ")";
}
bool equiv(const BinaryExpression *other) const {
if (op_ == other->op_ and *lhs_ == *other->lhs_ and *rhs_ == *other->rhs_)
return true;
else
return false;
}
BinaryExpression(Instruction::OpID op, std::shared_ptr<Expression> lhs, std::shared_ptr<Expression> rhs)
: Expression(e_bin), op_(op), lhs_(lhs), rhs_(rhs) {}
private:
Instruction::OpID op_;
std::shared_ptr<Expression> lhs_, rhs_;
};
class PhiExpression : public Expression {
public:
static std::shared_ptr<PhiExpression> create(std::shared_ptr<Expression> lhs, std::shared_ptr<Expression> rhs) {
return std::make_shared<PhiExpression>(lhs, rhs);
}
virtual std::string print() { return "(phi " + lhs_->print() + " " + rhs_->print() + ")"; }
bool equiv(const PhiExpression *other) const {
if (*lhs_ == *other->lhs_ and *rhs_ == *other->rhs_)
return true;
else
return false;
}
PhiExpression(std::shared_ptr<Expression> lhs, std::shared_ptr<Expression> rhs)
: Expression(e_phi), lhs_(lhs), rhs_(rhs) {}
private:
std::shared_ptr<Expression> lhs_, rhs_;
};
} // namespace GVNExpression
/**
* Congruence class in each partitions
* note: for constant propagation, you might need to add other fields
* and for load/store redundancy detection, you most certainly need to modify the class
*/
struct CongruenceClass {
size_t index_;
// representative of the congruence class, used to replace all the members (except itself) when analysis is done
Value *leader_;
// value expression in congruence class
std::shared_ptr<GVNExpression::Expression> value_expr_;
// value φ-function is an annotation of the congruence class
std::shared_ptr<GVNExpression::PhiExpression> value_phi_;
// equivalent variables in one congruence class
std::set<Value *> members_;
CongruenceClass(size_t index) : index_(index), leader_{}, value_expr_{}, value_phi_{}, members_{} {}
bool operator<(const CongruenceClass &other) const { return this->index_ < other.index_; }
bool operator==(const CongruenceClass &other) const;
};
namespace std {
template <>
// overload std::less for std::shared_ptr<CongruenceClass>, i.e. how to sort the congruence classes
struct less<std::shared_ptr<CongruenceClass>> {
bool operator()(const std::shared_ptr<CongruenceClass> &a, const std::shared_ptr<CongruenceClass> &b) const {
// nullptrs should never appear in partitions, so we just dereference it
return *a < *b;
}
};
} // namespace std
class GVN : public Pass {
public:
using partitions = std::set<std::shared_ptr<CongruenceClass>>;
GVN(Module *m, bool dump_json) : Pass(m), dump_json_(dump_json) {}
// pass start
void run() override;
// init for pass metadata;
void initPerFunction();
// fill the following functions according to Pseudocode, **you might need to add more arguments**
void detectEquivalences();
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);
std::shared_ptr<GVNExpression::PhiExpression> valuePhiFunc(std::shared_ptr<GVNExpression::Expression>,
const partitions &);
std::shared_ptr<GVNExpression::Expression> valueExpr(Instruction *instr);
std::shared_ptr<GVNExpression::Expression> getVN(const partitions &pout,
std::shared_ptr<GVNExpression::Expression> ve);
// replace cc members with leader
void replace_cc_members();
// note: be careful when to use copy constructor or clone
partitions clone(const partitions &p);
// create congruence class helper
std::shared_ptr<CongruenceClass> createCongruenceClass(size_t index = 0) {
return std::make_shared<CongruenceClass>(index);
}
private:
bool dump_json_;
std::uint64_t next_value_number_ = 1;
Function *func_;
std::map<BasicBlock *, partitions> pin_, pout_;
std::unique_ptr<FuncInfo> func_info_;
std::unique_ptr<GVNExpression::ConstFolder> folder_;
std::unique_ptr<DeadCode> dce_;
};
bool operator==(const GVN::partitions &p1, const GVN::partitions &p2);
#include "Dominators.h"
#include "GVN.h"
#include "Mem2Reg.hpp"
#include "PassManager.hpp"
#include "cminusf_builder.hpp"
......@@ -11,7 +12,8 @@
using namespace std::literals::string_literals;
void print_help(std::string exe_name) {
std::cout << "Usage: " << exe_name << " [ -h | --help ] [ -o <target-file> ] [ -emit-llvm ] [-mem2reg] <input-file>"
std::cout << "Usage: " << exe_name
<< " [ -h | --help ] [ -o <target-file> ] [ -emit-llvm ] [-mem2reg] [-gvn] [-dump-json] <input-file>"
<< std::endl;
}
......@@ -20,6 +22,8 @@ int main(int argc, char **argv) {
std::string input_path;
bool mem2reg = false;
bool gvn = false;
bool dump_json = false;
bool emit = false;
for (int i = 1; i < argc; ++i) {
......@@ -38,6 +42,10 @@ int main(int argc, char **argv) {
emit = true;
} else if (argv[i] == "-mem2reg"s) {
mem2reg = true;
} else if (argv[i] == "-gvn"s) {
gvn = true;
} else if (argv[i] == "-dump-json"s) {
dump_json = true;
} else {
if (input_path.empty()) {
input_path = argv[i];
......@@ -47,6 +55,8 @@ int main(int argc, char **argv) {
}
}
}
if (gvn and not mem2reg)
LOG_WARNING << "Enabling GVN without mem2reg";
if (input_path.empty()) {
print_help(argv[0]);
return 0;
......@@ -77,12 +87,15 @@ int main(int argc, char **argv) {
auto m = builder.getModule();
m->set_print_name();
// m->set_print_name();
PassManager PM(m.get());
if (mem2reg) {
PM.add_pass<Mem2Reg>(emit);
}
if (gvn)
PM.add_pass<GVN>(emit, dump_json);
PM.run();
auto IR = m->print();
......@@ -95,11 +108,13 @@ int main(int argc, char **argv) {
output_stream << IR;
output_stream.close();
if (!emit) {
std::string lib_path = argv[0];
std::string lib_path = " -L"s + argv[0];
auto idx = lib_path.rfind('/');
if (idx != std::string::npos)
lib_path.erase(lib_path.rfind('/'));
auto cmd_str = "clang -O0 -w "s + target_path + ".ll -o " + target_path + " -L" + lib_path + " -lcminus_io";
lib_path.erase(idx);
else
lib_path.clear();
auto cmd_str = "clang -O0 -w -no-pie "s + target_path + ".ll -o " + target_path + lib_path + " -lcminus_io";
int re_code0 = std::system(cmd_str.c_str());
cmd_str = "rm "s + target_path + ".ll";
int re_code1 = std::system(cmd_str.c_str());
......@@ -108,5 +123,6 @@ int main(int argc, char **argv) {
else
return 1;
}
return 0;
}
......@@ -24,3 +24,17 @@ void Value::remove_use(Value *val) {
auto is_val = [val](const Use &use) { return use.val_ == val; };
use_list_.remove_if(is_val);
}
void Value::replace_use_with_when(Value *new_val, std::function<bool(User *)> pred) {
for (auto it = use_list_.begin(); it != use_list_.end();) {
auto use = *it;
auto val = dynamic_cast<User *>(use.val_);
assert(val && "new_val is not a user");
if (not pred(val)) {
++it;
continue;
}
val->set_operand(use.arg_no_, new_val);
it = use_list_.erase(it);
}
}
......@@ -2,4 +2,5 @@ add_library(
OP_lib STATIC
Dominators.cpp
Mem2Reg.cpp
GVN.cpp
)
#include "GVN.h"
#include "BasicBlock.h"
#include "Constant.h"
#include "DeadCode.h"
#include "FuncInfo.h"
#include "Function.h"
#include "Instruction.h"
#include "logging.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <fstream>
#include <memory>
#include <sstream>
#include <tuple>
#include <utility>
#include <vector>
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(); };
// Constant Propagation helper, folders are done for you
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_);
case Instruction::sub: 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_);
case Instruction::sdiv: 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_);
case Instruction::fsub: 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_);
case Instruction::fdiv: 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_);
case CmpInst::NE: 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_);
case CmpInst::GE: 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_);
case CmpInst::LE: 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_);
case FCmpInst::NE: 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_);
case FCmpInst::GE: 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_);
case FCmpInst::LE: return ConstantInt::get(get_const_fp_value(value1) <= get_const_fp_value(value2), module_);
}
default: return nullptr;
}
}
Constant *ConstFolder::compute(Instruction *instr, Constant *value1) {
auto op = instr->get_instr_type();
switch (op) {
case Instruction::sitofp: return ConstantFP::get((float)get_const_int_value(value1), module_);
case Instruction::fptosi: return ConstantInt::get((int)get_const_fp_value(value1), module_);
case Instruction::zext: return ConstantInt::get((int)get_const_int_value(value1), module_);
default: return nullptr;
}
}
namespace utils {
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: {";
for (auto &member : cc.members_)
ss << member->print() << "; ";
ss << "}\n";
return ss.str();
}
static std::string dump_cc_json(const CongruenceClass &cc) {
std::string json;
json += "[";
for (auto member : cc.members_) {
if (auto c = dynamic_cast<Constant *>(member))
json += member->print() + ", ";
else
json += "\"%" + member->get_name() + "\", ";
}
json += "]";
return json;
}
static std::string dump_partition_json(const GVN::partitions &p) {
std::string json;
json += "[";
for (auto cc : p)
json += dump_cc_json(*cc) + ", ";
json += "]";
return json;
}
static std::string dump_bb2partition(const std::map<BasicBlock *, GVN::partitions> &map) {
std::string json;
json += "{";
for (auto [bb, p] : map)
json += "\"" + bb->get_name() + "\": " + dump_partition_json(p) + ",";
json += "}";
return json;
}
// logging utility for you
static void print_partitions(const GVN::partitions &p) {
if (p.empty()) {
LOG_DEBUG << "empty partitions\n";
return;
}
std::string log;
for (auto &cc : p)
log += print_congruence_class(*cc);
LOG_DEBUG << log; // please don't use std::cout
}
} // namespace utils
GVN::partitions GVN::join(const partitions &P1, const partitions &P2) {
// TODO: do intersection pair-wise
return {};
}
std::shared_ptr<CongruenceClass> GVN::intersect(std::shared_ptr<CongruenceClass> Ci,
std::shared_ptr<CongruenceClass> Cj) {
// TODO
return {};
}
void GVN::detectEquivalences() {
bool changed = false;
// initialize pout with top
// iterate until converge
do {
// see the pseudo code in documentation
for (auto &bb : func_->get_basic_blocks()) { // you might need to visit the blocks in depth-first order
// get PIN of bb by predecessor(s)
// iterate through all instructions in the block
// and the phi instruction in all the successors
// check changes in pout
}
} while (changed);
}
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
/// \param bb basic block in which the transfer function is called
GVN::partitions GVN::transferFunction(Instruction *x, Value *e, partitions pin) {
partitions pout = clone(pin);
// TODO: get different ValueExpr by Instruction::OpID, modify pout
return pout;
}
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) {
// TODO: return what?
for (auto it = pout.begin(); it != pout.end(); it++)
if ((*it)->value_expr_ and *(*it)->value_expr_ == *ve)
return {};
return nullptr;
}
void GVN::initPerFunction() {
next_value_number_ = 1;
pin_.clear();
pout_.clear();
}
void GVN::replace_cc_members() {
for (auto &[_bb, part] : pout_) {
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
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;
});
}
}
}
}
return;
}
// top-level function, done for you
void GVN::run() {
std::ofstream gvn_json;
if (dump_json_) {
gvn_json.open("gvn.json", std::ios::out);
gvn_json << "[";
}
folder_ = std::make_unique<ConstFolder>(m_);
func_info_ = std::make_unique<FuncInfo>(m_);
func_info_->run();
dce_ = std::make_unique<DeadCode>(m_);
dce_->run(); // let dce take care of some dead phis with undef
for (auto &f : m_->get_functions()) {
if (f.get_basic_blocks().empty())
continue;
func_ = &f;
initPerFunction();
LOG_INFO << "Processing " << f.get_name();
detectEquivalences();
LOG_INFO << "===============pin=========================\n";
for (auto &[bb, part] : pin_) {
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: ";
for (auto &cc : part)
LOG_INFO << utils::print_congruence_class(*cc);
}
if (dump_json_) {
gvn_json << "{\n\"function\": ";
gvn_json << "\"" << f.get_name() << "\", ";
gvn_json << "\n\"pout\": " << utils::dump_bb2partition(pout_);
gvn_json << "},";
}
replace_cc_members(); // don't delete instructions, just replace them
}
dce_->run(); // let dce do that for us
if (dump_json_)
gvn_json << "]";
}
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.
return static_cast<const T *>(&lhs)->equiv(static_cast<const T *>(&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()) {
case Expression::e_constant: return equiv_as<ConstantExpression>(lhs, rhs);
case Expression::e_bin: return equiv_as<BinaryExpression>(lhs, rhs);
case Expression::e_phi: return equiv_as<PhiExpression>(lhs, 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?
return true;
return lhs and rhs and *lhs == *rhs;
}
GVN::partitions GVN::clone(const partitions &p) {
partitions data;
for (auto &cc : p) {
data.insert(std::make_shared<CongruenceClass>(*cc));
}
return data;
}
bool operator==(const GVN::partitions &p1, const GVN::partitions &p2) {
// TODO: how to compare partitions?
return false;
}
bool CongruenceClass::operator==(const CongruenceClass &other) const {
// TODO: which fields need to be compared?
return false;
}
#!/usr/bin/env python3
import subprocess
import os
import argparse
import re
import time
import glob
import json5
from pathlib import Path
# you can run the script from anywhere!
cminusfc_path = Path(__file__).absolute().parents[2] / "build/cminusfc"
cminusfc = str(cminusfc_path)
try:
from tqdm import tqdm
except Exception as _:
os.system("apt install -y python3-tqdm || python3 -m pip install tqdm")
from tqdm import tqdm
repeated_time = 3
def init_args():
parser = argparse.ArgumentParser()
parser.add_argument("--GlobalValueNumber", "-gvn", action="store_true")
parser.add_argument(
"--GlobalValueNumberAnalysis", "-gvn-analysis", action="store_true"
)
args = parser.parse_args()
return args
def get_raw_testcases(root_path):
file_names = glob.glob(root_path + "/*.cminus")
# pattern=r'[0-9]+'
# file_names.sort(key= lambda item:int(re.findall(pattern, os.path.basename(item))[0]))
return file_names
def get_baseline_files(root_path):
file_names = glob.glob(root_path + "/*.ll")
# pattern=r'[0-9]+'
# file_names.sort(key= lambda item:int(re.findall(pattern, os.path.basename(item))[0]))
return file_names
def compile_baseline_files(file_lists):
print("Compiling baseline files")
progess_bar = tqdm(total=len(file_lists), ncols=50)
exec_files = list()
for each in file_lists:
exec_file, _ = os.path.splitext(each)
COMMAND = "clang -O0 -w " + each + " -o " + exec_file + " -L. -lcminus_io"
try:
result = subprocess.run(
COMMAND,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
timeout=1,
)
if result.returncode == 0:
exec_files.append(exec_file)
else:
exec_files.append(None)
print(
f"\nCompile {each.split('/')[-1]} \033[31;1m failed\033[0m")
except Exception as _:
exec_files.append(None)
print(f"Compile {each.split('/')[-1]} \033[31;1m failed\033[0m")
progess_bar.update(1)
progess_bar.close()
return exec_files
def compile_testcases(file_lists, option):
COMMAND = cminusfc + " " + option + " "
exec_files = list()
print("Compiling ", option)
progess_bar = tqdm(total=len(file_lists), ncols=50)
for each in file_lists:
try:
result = subprocess.run(
COMMAND + each,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
timeout=1,
)
if result.returncode == 0:
exec_file, _ = os.path.splitext(each)
exec_files.append(exec_file)
else:
exec_files.append(None)
print(
f"\nCompile {each.split('/')[-1]} \033[31;1m failed\033[0m")
except Exception as _:
exec_files.append(None)
print(f"Compile {each.split('/')[-1]} \033[31;1m failed\033[0m")
progess_bar.update(1)
progess_bar.close()
return exec_files
def gvn_evaluate(file_lists, metric_func, check_mode=True):
result = list()
print("Evalution ")
progess_bar = tqdm(total=len(file_lists), ncols=50)
for each in file_lists:
if each == None:
result.append(None)
continue
if check_if_correct(each, check_mode):
base = 0
for _ in range(repeated_time):
re_value = metric_func(each)
if re_value != None:
base += re_value / repeated_time
else:
base = None
break
result.append(base)
else:
result.append(None)
subprocess.call(["rm", "-rf", each])
progess_bar.update(1)
progess_bar.close()
return result
def check_if_correct(exec_file, check_mode=True):
if check_mode:
input_option = None
if os.path.exists(exec_file + ".in"):
with open(exec_file + ".in", "rb") as fin:
input_option = fin.read()
try:
result = subprocess.run(
[exec_file],
input=input_option,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=10,
)
with open(exec_file + ".out", "rb") as fout:
answer = fout.read()
if result.stdout == answer:
return True
else:
print(
f"Execute {exec_file.split('/')[-1]} result is not correct! your output:{result.stdout}, but the answer is:{answer}"
)
return False
except Exception as e:
print(
f"Execute {exec_file.split('/')[-1]} \033[31;1m failed\033[0m")
return False
else:
return True
def get_execute_time(exec_file):
try:
cmdline = "taskset -c 0 " + exec_file + " < " + exec_file + ".in" + " 2>&1"
input_option = None
start = time.time()
result = subprocess.run(
[cmdline],
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
timeout=10,
)
elapsed = time.time() - start
return elapsed
except Exception as e:
print(f"Execute {exec_file.split('/')[-1]} \033[31;1m failed\033[0m")
return None
def table_print(testcase, before_optimization, after_optimization, baseline):
if len(before_optimization) == len(baseline) and len(before_optimization) == len(
after_optimization
):
pass
else:
max_len = max(
[len(before_optimization), len(after_optimization), len(baseline)]
)
if len(before_optimization) < max_len:
before_optimization += [None] * \
(max_len - len(before_optimization))
if len(after_optimization) < max_len:
after_optimization += [None] * (max_len - len(after_optimization))
if len(baseline) < max_len:
baseline += [None] * (max_len - len(baseline))
print(
"\033[33;1mtestcase",
"\t",
"\033[31;1mbefore optimization\033[0m",
"\t",
"\033[32;1mafter optimization\033[0m",
"\t",
"\033[35;1mbaseline\033[0m",
)
for index, (result1, result2, result3) in enumerate(
zip(before_optimization, after_optimization, baseline)
):
print(
testcase[index].split("/")[-1],
"\t\t%.2f" % result1 if result1 != None else "\t\tNone",
"\t\t\t%.2f" % result2 if result2 != None else "\t\t\tNone",
"\t\t %.2f" % result3 if result3 != None else "\t\t None",
)
def calculate_gvn_bb_score(input_partition, answer_partition):
# score of every bb is either 0 or 1
if len(input_partition) != len(answer_partition):
return 0
score_cnt = 0
for in_cc in input_partition:
for ans_cc in answer_partition:
if set(in_cc) == set(ans_cc):
score_cnt += 1
break
if score_cnt == len(answer_partition):
return 1
return 0
def calculate_gvn_score(input_functions, answer_functions):
# input & answer is dict from json
# calculate score use sum(score of every bb)/total_bb
# score of every bb is either 1 or 0
# total_bb is count of pout
total_bb = 0
for ans_func in answer_functions:
total_bb += len(ans_func["pout"])
cal_score = 0
for ans_func in answer_functions:
for in_func in input_functions:
if ans_func["function"] == in_func["function"]:
for ans_bb, ans_partition in ans_func["pout"].items():
for in_bb, in_partition in in_func["pout"].items():
if ans_bb == in_bb:
cal_score += calculate_gvn_bb_score(
in_partition, ans_partition
)
else:
continue
else:
continue
return cal_score / total_bb
if __name__ == "__main__":
script_path = os.path.join(os.getcwd(), __file__)
usr_args = init_args()
if usr_args.GlobalValueNumber:
print("=" * 10, "GlobalValueNumber", "=" * 10)
root_path = os.path.join(
os.path.dirname(script_path), "testcases/GVN/performance"
)
testcases = get_raw_testcases(root_path=root_path)
exec_files1 = compile_testcases(
file_lists=testcases, option="-mem2reg")
results1 = gvn_evaluate(file_lists=exec_files1,
metric_func=get_execute_time)
exec_files2 = compile_testcases(
file_lists=testcases, option="-mem2reg -gvn")
results2 = gvn_evaluate(file_lists=exec_files2,
metric_func=get_execute_time)
baseline_files = get_baseline_files(
os.path.join(root_path, "baseline"))
exec_files3 = compile_baseline_files(baseline_files)
results3 = gvn_evaluate(
file_lists=exec_files3, metric_func=get_execute_time, check_mode=False
)
table_print(
testcase=testcases,
before_optimization=results1,
after_optimization=results2,
baseline=results3,
)
if usr_args.GlobalValueNumberAnalysis:
print("=" * 10, "GlobalValueNumberAnalysis", "=" * 10)
root_path = os.path.join(
os.path.dirname(script_path), "testcases/GVN/functional"
)
testcases = get_raw_testcases(root_path=root_path)
option = "-mem2reg -emit-llvm -gvn -dump-json"
COMMAND = cminusfc + " " + option + " "
print("Compiling ", option)
progess_bar = tqdm(total=len(testcases), ncols=50)
score_list = []
i = 0
for each in testcases:
i += 1
try:
result = subprocess.run(
COMMAND + each,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True,
timeout=1,
)
if result.returncode == 0:
exec_file, _ = os.path.splitext(each)
each_base = each.split("/")[-1]
print(f"\nCompile {each_base} \033[32;1m success\033[0m")
with open("gvn.json", "r") as load_input:
with open(exec_file + ".json", "r") as load_answer:
print(
f"generate json {each_base} \033[32;1m success\033[0m"
)
# here, input is a list of dict
input_functions = json5.load(load_input)
answer_functions = json5.load(load_answer)
score = calculate_gvn_score(
input_functions, answer_functions
)
score_list.append((each_base, score))
subprocess.call(["rm", "-rf", exec_file + ".ll"])
else:
print(
f"\nnCompile {each.split('/')[-1]} \033[31;1m failed\033[0m")
except Exception as _:
print(
f"Analyze {each.split('/')[-1]} \033[31;1m failed\033[0m")
progess_bar.update(1)
progess_bar.close()
i = 0
for file, score in score_list:
i += 1
print(file + ":", score)
/* c and d are redundant, and also check for constant propagation */
int main(void) {
int a;
int b;
int c;
int d;
if (input() > input()) {
a = 33 + 33;
b = 44 + 44;
c = a + b;
} else {
a = 55 + 55;
b = 66 + 66;
c = a + b;
}
output(c);
d = a + b;
output(d);
}
[
{
"function": "main",
"pout": {
"label_entry": [
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
],
"label5": [
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
[
"%op6",
"%op12",
],
[
"%op7",
"%op11",
],
[
"%op8",
"%op10",
],
],
"label9": [
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
[
"%op12",
],
[
"%op11",
],
[
"%op13",
"%op10",
],
],
"label14": [
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
[
"%op15",
"%op12",
],
[
"%op16",
"%op11",
],
[
"%op17",
"%op10",
],
],
}
},
]
\ No newline at end of file
/* c and d are redundant, e and f are not redundant */
int main(void) {
int i;
int j;
int a;
int b;
int c;
int d;
int e;
int f;
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
f = 0;
i = 1;
while (i < 10) {
j = 1;
a = input();
b = input();
c = a + b;
while (j < 10) {
i = i + j;
j = i + j;
c = a + b;
}
d = a + b;
i = i + 1;
e = c + d;
output(c);
output(d);
}
f = c + d;
output(e);
output(f);
return 0;
}
[
{
"function": "main",
"pout": {
"label_entry": [
[
0,
"%op3",
"%op2",
"%op1",
],
[
1,
"%op7",
],
],
"label0": [
[
"%op3",
"%op2",
],
[
"%op1",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
],
"label11": [
[
"%op3",
"%op2",
],
[
"%op1",
],
[
"%op20",
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
[
"%op12",
],
[
"%op13",
],
[
"%op14",
"%op18",
],
[
1,
"%op19",
],
],
"label15": [
[
"%op3",
"%op2",
],
[
"%op1",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
[
"%op16",
],
],
"label17": [
[
"%op12",
],
[
"%op13",
],
[
"%op3",
"%op2",
],
[
"%op1",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
[
"%op7",
],
[
"%op20",
],
[
"%op14",
"%op18",
],
[
"%op19",
],
[
"%op21",
],
[
"%op22",
],
[
"%op23",
],
],
"label24": [
[
"%op12",
],
[
"%op13",
],
[
"%op3",
"%op2",
],
[
"%op1",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
[
"%op7",
],
[
"%op14",
"%op27",
"%op18",
],
[
"%op21",
],
[
"%op22",
],
[
"%op23",
],
[
"%op25",
],
[
"%op26",
"%op19",
],
[
"%op20",
],
],
"label28": [
[
"%op12",
],
[
"%op13",
],
[
"%op8",
],
[
"%op9",
],
[
"%op10",
],
[
"%op20",
],
[
"%op14",
"%op29",
"%op3",
"%op18",
"%op2",
],
[
"%op19",
],
[
"%op21",
],
[
"%op22",
],
[
"%op23",
],
[
"%op30",
"%op7",
],
[
"%op31",
"%op1",
],
],
}
},
]
\ No newline at end of file
/* `max` is pure, can be detected using FuncInfo */
int max(int a, int b) {
if (a > b)
return a;
return b;
}
int a[10000];
int b[10000];
void inputarray(int a[], int n) {
int i;
i = 0;
while (i < n) {
a[i] = input();
i = i + 1;
}
}
int main(void) {
int i;
int n;
n = input();
inputarray(a, n);
inputarray(b, n);
i = 0;
while (i < n) {
int ai;
int bi;
/* `gep a i` and `max(ai,bi)` are redundant */
ai = a[i];
bi = b[i];
a[i] = max(ai, bi) * max(ai, bi);
i = i + 1;
}
i = 0;
while (i < n) {
output(a[i]);
i = i + 1;
}
}
\ No newline at end of file
[
{
"function": "max",
"pout": {
"label_entry": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
],
"label5": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
],
"label6": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op2",
],
[
"%op3",
],
[
"%op4",
],
],
}
},
{
"function": "inputarray",
"pout": {
"label_entry": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op4",
0,
],
],
"label3": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op4",
],
[
"%op5",
],
[
"%op6",
],
[
"%op7",
],
],
"label8": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op4",
],
[
"%op5",
],
[
"%op6",
],
[
"%op7",
],
[
"%op9",
],
[
"%op10",
],
],
"label11": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op4",
],
[
"%op5",
],
[
"%op6",
],
[
"%op7",
],
],
"label12": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op4",
],
[
"%op5",
],
[
"%op6",
],
[
"%op7",
],
[
"%op9",
],
[
"%op10",
],
],
"label13": [
[
"%arg0",
],
[
"%arg1",
],
[
"%a",
],
[
"%b",
],
[
"%op5",
],
[
"%op6",
],
[
"%op7",
],
[
"%op9",
],
[
"%op10",
],
[
"%op14",
],
[
"%op4",
"%op15",
],
],
}
},
{
"function": "main",
"pout": {
"label_entry": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
0,
"%op6",
],
[
"%op1",
],
[
"%op2",
],
],
"label3": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
],
"label10": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
],
],
"label12": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
0,
"%op31",
],
],
"label13": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
],
],
"label14": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
"%op17",
],
[
"%op15",
],
[
"%op16",
],
],
"label18": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
"%op17",
],
[
"%op15",
],
[
"%op16",
],
],
"label19": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
"%op17",
"%op25",
],
[
"%op15",
],
[
"%op16",
],
[
"%op20",
],
[
"%op21",
],
[
"%op22",
"%op23",
],
[
"%op24",
],
],
"label26": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
"%op17",
"%op25",
],
[
"%op15",
],
[
"%op16",
],
[
"%op20",
],
[
"%op21",
],
[
"%op22",
"%op23",
],
[
"%op24",
],
],
"label27": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op11",
"%op17",
"%op25",
],
[
"%op15",
"%op28",
],
[
"%op16",
],
[
"%op20",
],
[
"%op21",
],
[
"%op22",
"%op23",
],
[
"%op24",
],
[
"%op6",
"%op29",
],
],
"label30": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op31",
],
[
"%op32",
],
[
"%op33",
],
[
"%op34",
],
],
"label35": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op31",
],
[
"%op32",
],
[
"%op33",
],
[
"%op34",
],
[
"%op36",
],
],
"label37": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op31",
],
[
"%op32",
],
[
"%op33",
],
[
"%op34",
],
],
"label38": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op31",
],
[
"%op32",
],
[
"%op33",
],
[
"%op34",
],
[
"%op36",
],
],
"label39": [
[
"%a",
],
[
"%b",
],
[
"%op0",
],
[
"%op1",
],
[
"%op2",
],
[
"%op6",
],
[
"%op7",
],
[
"%op8",
],
[
"%op9",
],
[
"%op32",
],
[
"%op33",
],
[
"%op34",
],
[
"%op36",
],
[
"%op40",
],
[
"%op41",
],
[
"%op31",
"%op42",
],
],
}
},
]
\ No newline at end of file
/* check GVN on one-block input */
int main(void) {
int a;
int b;
/* we provide a pure function analysis,
input is not side-effect free, so multiple calls to input are considered different */
a = input();
b = input();
output(a + b);
output(a + b);
return 0;
}
[{
"function": "main",
"pout": {"label_entry": [["%op0", ], ["%op1", ], ["%op2", "%op3", ], ],
}},]
\ No newline at end of file
declare i32 @input()
declare void @output(i32)
declare void @outputFloat(float)
declare void @neg_idx_except()
define void @main() {
label_entry:
%op0 = call i32 @input()
br label %label1
label1: ; preds = %label_entry, %label11
%op2 = phi i32 [ 0, %label_entry ], [ 711082625, %label11 ]
%op7 = phi i32 [ 0, %label_entry ], [ %op37, %label11 ]
%op8 = icmp slt i32 %op7, %op0
%op9 = zext i1 %op8 to i32
%op10 = icmp ne i32 %op9, 0
br i1 %op10, label %label11, label %label38
label11: ; preds = %label1
%op37 = add i32 %op7, 1
br label %label1
label38: ; preds = %label1
call void @output(i32 %op2)
ret void
}
10000000
30
2
5
4
25
8
125
16
625
32
3125
2
5
4
25
8
125
16
625
32
3125
2
5
4
25
8
125
16
625
32
3125
; ModuleID = 'cminus'
source_filename = "./test.cminus"
@matrix = global [20000000 x i32] zeroinitializer
@ad = global [100000 x i32] zeroinitializer
@len = global i32 zeroinitializer
declare i32 @input()
declare void @output(i32)
declare void @outputFloat(float)
declare void @neg_idx_except()
define void @readarray() {
label_entry:
br label %label0
label0: ; preds = %label_entry, %label11
%op1 = phi i32 [ 0, %label_entry ], [ %op13, %label11 ]
%op2 = load i32, i32* @len
%op3 = icmp slt i32 %op1, %op2
%op4 = zext i1 %op3 to i32
%op5 = icmp ne i32 %op4, 0
br i1 %op5, label %label6, label %label9
label6: ; preds = %label0
%op7 = call i32 @input()
%op8 = icmp slt i32 %op1, 0
br i1 %op8, label %label10, label %label11
label9: ; preds = %label0
ret void
label10: ; preds = %label6
call void @neg_idx_except()
ret void
label11: ; preds = %label6
%op12 = getelementptr [100000 x i32], [100000 x i32]* @ad, i32 0, i32 %op1
store i32 %op7, i32* %op12
%op13 = add i32 %op1, 1
br label %label0
}
define i32 @transpose(i32 %arg0, i32* %arg1, i32 %arg2) {
label_entry:
%op4 = sdiv i32 %arg0, %arg2
br label %label5
label5: ; preds = %label_entry, %label25
%op8 = phi i32 [ 0, %label_entry ], [ %op26, %label25 ]
%op9 = icmp slt i32 %op8, %op4
%op10 = zext i1 %op9 to i32
%op11 = icmp ne i32 %op10, 0
br i1 %op11, label %label12, label %label13
label12: ; preds = %label5
br label %label15
label13: ; preds = %label5
ret i32 -1
label15: ; preds = %label12, %label29
%op17 = phi i32 [ 0, %label12 ], [ %op31, %label29 ]
%op18 = icmp slt i32 %op17, %arg2
%op19 = zext i1 %op18 to i32
%op20 = icmp ne i32 %op19, 0
br i1 %op20, label %label21, label %label25
label21: ; preds = %label15
%op22 = icmp slt i32 %op8, %op17
%op23 = zext i1 %op22 to i32
%op24 = icmp ne i32 %op23, 0
br i1 %op24, label %label27, label %label32
label25: ; preds = %label15
%op26 = add i32 %op8, 1
br label %label5
label27: ; preds = %label21
%op28 = add i32 %op17, 1
br label %label29
label29: ; preds = %label27, %label57
%op31 = phi i32 [ %op28, %label27 ], [ %op59, %label57 ]
br label %label15
label32: ; preds = %label21
%op33 = mul i32 %op8, %arg2
%op34 = add i32 %op33, %op17
%op35 = icmp slt i32 %op34, 0
br i1 %op35, label %label36, label %label37
label36: ; preds = %label32
call void @neg_idx_except()
ret i32 0
label37: ; preds = %label32
%op38 = getelementptr i32, i32* %arg1, i32 %op34
%op39 = load i32, i32* %op38
br i1 %op35, label %label43, label %label44
label43: ; preds = %label37
call void @neg_idx_except()
ret i32 0
label44: ; preds = %label37
%op46 = load i32, i32* %op38
%op47 = mul i32 %op17, %op4
%op48 = add i32 %op47, %op8
%op49 = icmp slt i32 %op48, 0
br i1 %op49, label %label50, label %label51
label50: ; preds = %label44
call void @neg_idx_except()
ret i32 0
label51: ; preds = %label44
%op52 = getelementptr i32, i32* %arg1, i32 %op48
store i32 %op46, i32* %op52
br i1 %op35, label %label56, label %label57
label56: ; preds = %label51
call void @neg_idx_except()
ret i32 0
label57: ; preds = %label51
store i32 %op39, i32* %op38
%op59 = add i32 %op17, 1
br label %label29
}
define i32 @main() {
label_entry:
%op0 = call i32 @input()
%op1 = call i32 @input()
store i32 %op1, i32* @len
call void @readarray()
br label %label2
label2: ; preds = %label_entry, %label11
%op3 = phi i32 [ 0, %label_entry ], [ %op13, %label11 ]
%op4 = icmp slt i32 %op3, %op0
%op5 = zext i1 %op4 to i32
%op6 = icmp ne i32 %op5, 0
br i1 %op6, label %label7, label %label9
label7: ; preds = %label2
%op8 = icmp slt i32 %op3, 0
br i1 %op8, label %label10, label %label11
label9: ; preds = %label2
br label %label14
label10: ; preds = %label7
call void @neg_idx_except()
ret i32 0
label11: ; preds = %label7
%op12 = getelementptr [20000000 x i32], [20000000 x i32]* @matrix, i32 0, i32 %op3
store i32 %op3, i32* %op12
%op13 = add i32 %op3, 1
br label %label2
label14: ; preds = %label9, %label25
%op15 = phi i32 [ 0, %label9 ], [ %op29, %label25 ]
%op16 = load i32, i32* @len
%op17 = icmp slt i32 %op15, %op16
%op18 = zext i1 %op17 to i32
%op19 = icmp ne i32 %op18, 0
br i1 %op19, label %label20, label %label23
label20: ; preds = %label14
%op21 = getelementptr [20000000 x i32], [20000000 x i32]* @matrix, i32 0, i32 0
%op22 = icmp slt i32 %op15, 0
br i1 %op22, label %label24, label %label25
label23: ; preds = %label14
br label %label30
label24: ; preds = %label20
call void @neg_idx_except()
ret i32 0
label25: ; preds = %label20
%op26 = getelementptr [100000 x i32], [100000 x i32]* @ad, i32 0, i32 %op15
%op27 = load i32, i32* %op26
%op28 = call i32 @transpose(i32 %op0, i32* %op21, i32 %op27)
%op29 = add i32 %op15, 1
br label %label14
label30: ; preds = %label23, %label45
%op31 = phi i32 [ 0, %label23 ], [ %op49, %label45 ]
%op32 = phi i32 [ 0, %label23 ], [ %op50, %label45 ]
%op33 = load i32, i32* @len
%op34 = icmp slt i32 %op32, %op33
%op35 = zext i1 %op34 to i32
%op36 = icmp ne i32 %op35, 0
br i1 %op36, label %label37, label %label40
label37: ; preds = %label30
%op38 = mul i32 %op32, %op32
%op39 = icmp slt i32 %op32, 0
br i1 %op39, label %label44, label %label45
label40: ; preds = %label30
%op41 = icmp slt i32 %op31, 0
%op42 = zext i1 %op41 to i32
%op43 = icmp ne i32 %op42, 0
br i1 %op43, label %label51, label %label53
label44: ; preds = %label37
call void @neg_idx_except()
ret i32 0
label45: ; preds = %label37
%op46 = getelementptr [20000000 x i32], [20000000 x i32]* @matrix, i32 0, i32 %op32
%op47 = load i32, i32* %op46
%op48 = mul i32 %op38, %op47
%op49 = add i32 %op31, %op48
%op50 = add i32 %op32, 1
br label %label30
label51: ; preds = %label40
%op52 = sub i32 0, %op31
br label %label53
label53: ; preds = %label40, %label51
%op54 = phi i32 [ %op31, %label40 ], [ %op52, %label51 ]
call void @output(i32 %op54)
ret i32 0
}
void main(void) {
int c;
int a;
int b;
int d;
int f;
int g;
int loopCnt;
loopCnt = input();
c = 0;
a = 0;
g = 0;
while (c < loopCnt) {
a = 1.23456 * 5.73478 * 2.3333 * 4.3673 * 6.34636;
b = a * a * a * a * a * a;
d = b * b * b * b * b * b;
f = d * d * d * d * d * d;
g = f * f * f * f * f * f;
c = c + 1;
}
output(g);
return;
}
\ No newline at end of file
int matrix[20000000];
int ad[100000];
int len;
void readarray(void) {
int cnt;
cnt = 0;
while (cnt < len) {
ad[cnt] = input();
cnt = cnt + 1;
}
}
int transpose(int n, int matrix[], int rowsize) {
int colsize;
int i;
int j;
int curr;
colsize = n / rowsize;
i = 0;
j = 0;
while (i < colsize) {
j = 0;
while (j < rowsize) {
if (i < j) {
j = j + 1;
} else {
curr = matrix[i * rowsize + j];
matrix[j * colsize + i] = matrix[i * rowsize + j];
matrix[i * rowsize + j] = curr;
j = j + 1;
}
}
i = i + 1;
}
return 0 - 1;
}
int main(void) {
int n;
int i;
int ans;
n = input();
len = input();
readarray();
i = 0;
while (i < n) {
matrix[i] = i;
i = i + 1;
}
i = 0;
while (i < len) {
transpose(n, matrix, ad[i]);
i = i + 1;
}
ans = 0;
i = 0;
while (i < len) {
ans = ans + i * i * matrix[i];
i = i + 1;
}
if (ans < 0) {
ans = 0 - ans;
}
output(ans);
return 0;
}
10000000
30
2
5
4
25
8
125
16
625
32
3125
2
5
4
25
8
125
16
625
32
3125
2
5
4
25
8
125
16
625
32
3125
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