From efbf4233c12da072cebb01f2bb709c6156d5d182 Mon Sep 17 00:00:00 2001 From: LiXiaoQi Date: Fri, 2 Dec 2022 10:15:44 +0800 Subject: [PATCH] follow update --- .clang-format | 3 +- Documentations/4.2-gvn/README.md | 225 ++++++++++---------- Documentations/4.2-gvn/gvn.pdf | Bin 514740 -> 569401 bytes include/lightir/Instruction.h | 7 +- include/optimization/GVN.h | 8 + src/optimization/.gitignore | 1 + src/optimization/GVN.cpp | 342 ++++++++++++++++++++++++------- 7 files changed, 399 insertions(+), 187 deletions(-) create mode 100644 src/optimization/.gitignore diff --git a/.clang-format b/.clang-format index db13e1d..12a0048 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BreakConstructorInitializers: BeforeComma -ColumnLimit: 120 +ColumnLimit: 80 CommentPragmas: '^(!|NOLINT)' ConstructorInitializerAllOnOneLineOrOnePerLine: true IncludeBlocks: Regroup @@ -23,4 +23,5 @@ PenaltyReturnTypeOnItsOwnLine: 200 SpacesBeforeTrailingComments: 1 TabWidth: 4 UseTab: Never +AlwaysBreakAfterReturnType: TopLevelDefinitions ... diff --git a/Documentations/4.2-gvn/README.md b/Documentations/4.2-gvn/README.md index 26930c1..c7863fc 100644 --- a/Documentations/4.2-gvn/README.md +++ b/Documentations/4.2-gvn/README.md @@ -1,33 +1,36 @@ # 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 # 这一条严格执行,请对自己负责 + ``` * 关于抄袭和雷同 经过助教和老师判定属于实验抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳(严禁对开源代码或者其他同学代码的直接搬运)。 diff --git a/Documentations/4.2-gvn/gvn.pdf b/Documentations/4.2-gvn/gvn.pdf index 87b2c577f0674df1b6ebbcaf9669b949ddea7030..f31c2a66ffcce8983816770ba81561ec29d7267e 100644 GIT binary patch delta 89524 zcmce92|QG9_qegll(MA=VI)zq%xVkCRv}vv5@IZ6E81m9Dp^{Frj0D^sjR7_L{XAx zAuW`W7D}m9qW^Q3nYlC3Ti?&;_kZ6{-)$9uXboMI?i3E<_>)`Jf^nG~|Phd>9}fhRBBz z@!T=#=>cUkB=0*BKYuS;l?`<^=wEaB87$^0Dh2(6gq+h_(26fP`EZE8j(ar z@Y1sZqe#dK^lWHE3K@BVjk6J@IvAo)ELa!G&~x$&Vwjmph%>?iU?M>a;!?{AkZeAH ziV(=mOmB9?$^gbn2zT&*usAOwizurNaGm0hGcn=grbj-=ubc@_<%r@{tFPc zg##W!#Vr75%tHm@g8&o~72(>jsIV=9adPa|0Gu=c^(FwjDu`^qf%?O$5Kv(Z7}Q1v z7AuG~f}kh}zedJtPaq6QIuMH#B%gwisQ(iwDNmdjdtV@K3|QphK%5beUXYyq2Je6K z-y{fUV$9_~tR@0b{wM$(-aQDy8DhZjsV+wt|HtB{f+Yn7}T`gBjr%09qX1 zqK4D9KY1e#m}RBZnG%sx)HU0fG(^ z6~PifGbEB|h!NnANB}HYcc!`k1bE?zo{b^E1(6ax8zb-?(NH*SVVkFqtcfsz1Xx2t zHGg=sEMqGw$3B=2o8|m;J{1^7A4z|7&d*uG6WID0FsxY_SXeNl{vEID2&@MH7SBS% zEQVpRcLm^8%FOoQLggK}fN1yvkv!<2nY!e!iIbl zu9m?9DRBmO9*3Eht32$T$@t0a{ps2}`8vBQglSAWR2eP-&+Esf43#DksdQ0$Yw=;S5Hd5LP7+r5XFT5w|2!&(@U; zBo$Y1INpL@YdK0u8`L+~744Ft8o00iua+D!bDO31N~6yxNjhbH%X80HgrW=Rq7HEwAzpN#+$;GD&V`6le?Eea8 z0y`;P7}}@pOBcp$8>oDC21SX6Sc5+}1EmZYmUf0PM!I39!+5b_>E4|o96OsQ-LQ*+ zZ$r|}2GXrk=DR~=4vwAID$K-;<7xpLl5-tSP|6i_@^6ApXLaJAs-=BFZTun)1MCASdtf_ZkRf<1ftk$kEJ5$~cn#@nRMrXih65{9PH*UV2V! zNgGj}k#-UAx;a$iXb>H-|HF$bHbs$ZBZ}F0!?gPi7NXn3!vb*-v|~1~H zdRUhZ(~L_5Cn_zW_YmZ zhPn2!m?2MEVBW!CU~!!BM2tF)XF&`g8Rt%dS`Y)oUSKSU0pf-Jb~6CD{uhpeZ%;fT zqeqP5K#)&DF&%-m8i40K6EY%iL0X@-r&mmDB>uvaaB0Lb4jjKcOje(bG9s*33}eb+ z8p2?Z9q5Bzv2(JnTw87c*)HxZ98Mr{G|>Rvs2Ly@6U!MyX-DLY5+1Q)!&FE950rAG zy&At#QUK!s`KYi`g1jaPUi+UklouJWW$^eBHPL`;8F`-fXj|5k>_W8wgq+FVv3 z4F4k-G&AW;z+;l#Ft7dv3Mc*c27tlnFE^S%#Kvh9{wc;87;*z$c1{^Vl^uHsufdL= zgdgHTFodi(B6yXX@M`P=W&AXtp{>ezHSqC689yI0mTM0Xa2v!38tj8A_(9ANu75*P z8j_*}EdZxa6+an#Od#SH@XU)L*V|z>i-w4o#t1Q|riSq^d;!XELOcfq>wmE$#F`Im zu8bE=K>0BmJ6jDuq5*&r7T_f5k7c4~RQ!1M4>dTT6Q7J_C%jY`7Hp0qC*yBp@|T8? zo^xkHjesGd^h7i%K}2_y82TCg?w|iB{Q$2Z8*L9IF-OkPU|sHClF|W9)(jhH!E50z z5VfVS+CrPJ;cO`&Cq7kAKL?hJT8=(f@2<5#I8k>>yk>HF-(noj> zz90r3e8IYH1*B{aj4YIn`!DW+@<+)SUKkKv(z6>`m*9m9ko<3KYqlNMLc?VH7bt8g zdpwkQHL~Xe!{>AV#?a4~nE{~Ox5pZKm}dgW{IL?rG4!DhSeb@dE&%3_lwm(|fY&5C zpKnc�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•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 diff --git a/include/lightir/Instruction.h b/include/lightir/Instruction.h index d029cee..b8571e1 100644 --- a/include/lightir/Instruction.h +++ b/include/lightir/Instruction.h @@ -56,6 +56,7 @@ class Instruction : public User, public llvm::ilist_node 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 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() diff --git a/include/optimization/GVN.h b/include/optimization/GVN.h index 6b794ac..df408a5 100644 --- a/include/optimization/GVN.h +++ b/include/optimization/GVN.h @@ -159,6 +159,7 @@ class GVN : public Pass { partitions join(const partitions &P1, const partitions &P2); std::shared_ptr intersect(std::shared_ptr, std::shared_ptr); partitions transferFunction(Instruction *x, Value *e, partitions pin); + partitions transferFunction(BasicBlock *bb); std::shared_ptr valuePhiFunc(std::shared_ptr, const partitions &); std::shared_ptr valueExpr(Instruction *instr); @@ -176,6 +177,10 @@ class GVN : public Pass { return std::make_shared(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 func_info_; std::unique_ptr folder_; std::unique_ptr dce_; + + // self add + std::map _TOP; }; bool operator==(const GVN::partitions &p1, const GVN::partitions &p2); diff --git a/src/optimization/.gitignore b/src/optimization/.gitignore new file mode 100644 index 0000000..258cd57 --- /dev/null +++ b/src/optimization/.gitignore @@ -0,0 +1 @@ +todo diff --git a/src/optimization/GVN.cpp b/src/optimization/GVN.cpp index 8c4ce9b..5f76ab4 100644 --- a/src/optimization/GVN.cpp +++ b/src/optimization/GVN.cpp @@ -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(v)->get_value(); }; -static auto get_const_fp_value = [](Value *v) { return dynamic_cast(v)->get_value(); }; +static auto get_const_int_value = [](Value *v) { + return dynamic_cast(v)->get_value(); +}; +static auto get_const_fp_value = [](Value *v) { + return dynamic_cast(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(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(instr)->get_cmp_op()) { case FCmpInst::EQ: - return ConstantInt::get(get_const_fp_value(value1) == get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) == + get_const_fp_value(value2), + module_); case FCmpInst::NE: - return ConstantInt::get(get_const_fp_value(value1) != get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) != + get_const_fp_value(value2), + module_); case FCmpInst::GT: - return ConstantInt::get(get_const_fp_value(value1) > get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) > + get_const_fp_value(value2), + module_); case FCmpInst::GE: - return ConstantInt::get(get_const_fp_value(value1) >= get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) >= + get_const_fp_value(value2), + module_); case FCmpInst::LT: - return ConstantInt::get(get_const_fp_value(value1) < get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) < + get_const_fp_value(value2), + module_); case FCmpInst::LE: - return ConstantInt::get(get_const_fp_value(value1) <= get_const_fp_value(value2), module_); + return ConstantInt::get(get_const_fp_value(value1) <= + get_const_fp_value(value2), + module_); } default: return nullptr; } } -Constant *ConstFolder::compute(Instruction *instr, Constant *value1) { +Constant * +ConstFolder::compute(Instruction *instr, Constant *value1) { auto op = instr->get_instr_type(); switch (op) { case Instruction::sitofp: @@ -95,22 +142,27 @@ Constant *ConstFolder::compute(Instruction *instr, Constant *value1) { } namespace utils { -static std::string print_congruence_class(const CongruenceClass &cc) { +static std::string +print_congruence_class(const CongruenceClass &cc) { std::stringstream ss; if (cc.index_ == 0) { ss << "top class\n"; return ss.str(); } ss << "\nindex: " << cc.index_ << "\nleader: " << cc.leader_->print() - << "\nvalue phi: " << (cc.value_phi_ ? cc.value_phi_->print() : "nullptr"s) - << "\nvalue expr: " << (cc.value_expr_ ? cc.value_expr_->print() : "nullptr"s) << "\nmembers: {"; + << "\nvalue phi: " + << (cc.value_phi_ ? cc.value_phi_->print() : "nullptr"s) + << "\nvalue expr: " + << (cc.value_expr_ ? cc.value_expr_->print() : "nullptr"s) + << "\nmembers: {"; for (auto &member : cc.members_) ss << member->print() << "; "; ss << "}\n"; return ss.str(); } -static std::string dump_cc_json(const CongruenceClass &cc) { +static std::string +dump_cc_json(const CongruenceClass &cc) { std::string json; json += "["; for (auto member : cc.members_) { @@ -123,7 +175,8 @@ static std::string dump_cc_json(const CongruenceClass &cc) { return json; } -static std::string dump_partition_json(const GVN::partitions &p) { +static std::string +dump_partition_json(const GVN::partitions &p) { std::string json; json += "["; for (auto cc : p) @@ -132,7 +185,8 @@ static std::string dump_partition_json(const GVN::partitions &p) { return json; } -static std::string dump_bb2partition(const std::map &map) { +static std::string +dump_bb2partition(const std::map &map) { std::string json; json += "{"; for (auto [bb, p] : map) @@ -142,7 +196,8 @@ static std::string dump_bb2partition(const std::mapmembers_.empty()) + continue; + P.insert(c); + } + + return P; } -std::shared_ptr GVN::intersect(std::shared_ptr Ci, - std::shared_ptr Cj) { +std::shared_ptr +GVN::intersect(std::shared_ptr ci, + std::shared_ptr cj) { // TODO - return {}; + // If no common members, return null + auto c = createCongruenceClass(); + std::set 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 GVN::valueExpr(Instruction *instr) { +shared_ptr +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::iterator iter; + for (auto c : pout) { + if ((iter = std::find(c->members_.begin(), c->members_.end(), x)) != + c->members_.end()) { + // static_cast(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 GVN::valuePhiFunc(shared_ptr ve, const partitions &P) { +shared_ptr +GVN::valuePhiFunc(shared_ptr ve, const partitions &P) { // TODO return {}; } -shared_ptr GVN::getVN(const partitions &pout, shared_ptr ve) { +shared_ptr +GVN::getVN(const partitions &pout, shared_ptr 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 GVN::getVN(const partitions &pout, shared_ptr 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(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(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(user)) { + auto parent = instr->get_parent(); + auto &bb_pre = parent->get_pre_basic_blocks(); + if (instr->is_phi()) // as copy stmt, the phi + // belongs to this block + return std::find(bb_pre.begin(), + bb_pre.end(), + bb) != bb_pre.end(); + else + return parent == bb; + } + return false; + }); } } } @@ -245,7 +423,8 @@ void GVN::replace_cc_members() { } // top-level function, done for you -void GVN::run() { +void +GVN::run() { std::ofstream gvn_json; if (dump_json_) { gvn_json.open("gvn.json", std::ios::out); @@ -267,13 +446,15 @@ void GVN::run() { detectEquivalences(); LOG_INFO << "===============pin=========================\n"; for (auto &[bb, part] : pin_) { - LOG_INFO << "\n===============bb: " << bb->get_name() << "=========================\npartitionIn: "; + LOG_INFO << "\n===============bb: " << bb->get_name() + << "=========================\npartitionIn: "; for (auto &cc : part) LOG_INFO << utils::print_congruence_class(*cc); } LOG_INFO << "\n===============pout=========================\n"; for (auto &[bb, part] : pout_) { - LOG_INFO << "\n=====bb: " << bb->get_name() << "=====\npartitionOut: "; + LOG_INFO << "\n=====bb: " << bb->get_name() + << "=====\npartitionOut: "; for (auto &cc : part) LOG_INFO << utils::print_congruence_class(*cc); } @@ -291,12 +472,15 @@ void GVN::run() { } template -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(&lhs)->equiv(static_cast(&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 &lhs, const shared_ptr &rhs) { - if (lhs == nullptr and rhs == nullptr) // is the nullptr check necessary here? +bool +GVNExpression::operator==(const shared_ptr &lhs, + const shared_ptr &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(*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_; } -- GitLab