diff --git a/.clang-format b/.clang-format index db13e1da7a7b72b4e811bcb0c3d11799ce3d5684..12a004820ab633b6e7f6dec81d1b5ae4d720f900 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 26930c11b52340ce05da819caa5c7fb3606bf78c..c7863fc0cbe5f22cb73cc12261c609273f656694 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 Binary files a/Documentations/4.2-gvn/gvn.pdf and b/Documentations/4.2-gvn/gvn.pdf differ diff --git a/include/lightir/Instruction.h b/include/lightir/Instruction.h index d029ceec46281d201ae740a94013e03473cbdb16..b8571e1548142f82b7423c88dfbcfa79b7665e1b 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 6b794acc8ddb7991b1fd42f0f9d1c1c0d6902f85..df408a550fe636e595c9b195aad4f3756426fd1b 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 0000000000000000000000000000000000000000..258cd5725da9a125878490703e64117560b11872 --- /dev/null +++ b/src/optimization/.gitignore @@ -0,0 +1 @@ +todo diff --git a/src/optimization/GVN.cpp b/src/optimization/GVN.cpp index 8c4ce9b12e1bd538ce64306d84d8011bf72d2797..5f76ab405010d1a57629ed21d7bb60a2f386110b 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_; }