158 lines
4.0 KiB
Markdown
158 lines
4.0 KiB
Markdown
# Lab5:基本标量优化
|
||
|
||
## 1. 本实验定位
|
||
|
||
Lab5 的目标是让 IR 从“能跑”变成“跑的更好”。
|
||
在当前编译器基础上,做基础标量优化,框架中给出了三种,可以按需补充:
|
||
|
||
1. 常量相关优化(常量折叠/传播)
|
||
2. 无用代码删除(DCE)
|
||
3. CFG 简化与不可达代码删除
|
||
|
||
---
|
||
|
||
## 2. IR 的 use-def 关系
|
||
|
||
|
||
LLVM 中通常维护完整 `Use-User` 双向关系;当前仓库是最小 IR,实现较轻量。
|
||
|
||
### 什么是 use-def
|
||
|
||
use-def(或 def-use)描述的是“值在哪里被定义、又在哪里被使用”的关系:
|
||
|
||
1. `def`:某条指令产生了一个值(定义点)。
|
||
2. `use`:其他指令把这个值当作操作数使用(使用点)。
|
||
|
||
在 IR 中维护好这层关系后,优化遍就能快速回答:
|
||
“这个值还有人用吗?”、“我要把旧值替换成新值,需要改哪些地方?”
|
||
|
||
|
||
### use-def 的作用
|
||
|
||
在优化阶段,use-def 关系的价值主要体现在:
|
||
|
||
1. 判断“是否还被使用”更直接
|
||
DCE 可以直接依据某个值是否还有用户来决定是否可删,而不必每次全函数扫描。
|
||
|
||
2. 支持局部重写与传播
|
||
常量折叠、常量传播、复制传播时,需要把“旧值的所有使用点”替换为“新值”;有 use-def 后可以精准定位使用点。
|
||
|
||
3. 降低优化遍实现复杂度
|
||
没有 use-def 时,很多优化都要反复做全局查找;有 use-def 后可把复杂度和代码量都压下来。
|
||
|
||
4. 便于后续扩展更多优化
|
||
例如代数化简、CSE、部分冗余消除等,都依赖稳定的 def-use/use-def 信息。
|
||
|
||
这会明显降低 DCE、常量传播等优化的实现复杂度,也更利于后续扩展。
|
||
|
||
---
|
||
|
||
## 3. Lab5 要求
|
||
|
||
需要同学完成:
|
||
|
||
1. 理解当前 IR/CFG 结构,明确“有用代码、无用代码、不可达代码”的定义。
|
||
2. 完成可运行标量优化代码。
|
||
3. 将优化串联到 `PassManager`,形成可重复执行的优化流程。
|
||
4. 保证优化前后语义一致(功能不回归)。
|
||
|
||
---
|
||
|
||
## 4. 当前代码框架(与 Lab5 相关)
|
||
|
||
1. IR 核心
|
||
- `src/ir/IR.h`
|
||
- `src/ir/Instruction.cpp`
|
||
- `src/ir/BasicBlock.cpp`
|
||
- `src/ir/Function.cpp`
|
||
- `src/ir/Module.cpp`
|
||
- `src/ir/IRPrinter.cpp`
|
||
|
||
2. 分析与优化
|
||
- `src/ir/analysis/DominatorTree.cpp`
|
||
- `src/ir/analysis/LoopInfo.cpp`
|
||
- `src/ir/passes/ConstFold.cpp`
|
||
- `src/ir/passes/DCE.cpp`
|
||
- `src/ir/passes/CFGSimplify.cpp`
|
||
- `src/ir/passes/PassManager.cpp`
|
||
|
||
3. 入口
|
||
- `src/main.cpp`
|
||
|
||
---
|
||
|
||
## 5. 需要修改的文件
|
||
|
||
1. 核心优化实现
|
||
- `src/ir/passes/ConstFold.cpp`
|
||
- `src/ir/passes/DCE.cpp`
|
||
- `src/ir/passes/CFGSimplify.cpp`
|
||
- `src/ir/passes/PassManager.cpp`
|
||
|
||
2. 视实现需要可能修改
|
||
- `src/ir/IR.h`、`src/ir/Instruction.cpp`(补充副作用/可删除性信息)
|
||
- `src/ir/IRPrinter.cpp`(调试输出增强)
|
||
- `src/ir/analysis/DominatorTree.cpp`、`src/ir/analysis/LoopInfo.cpp`(辅助分析)
|
||
- `src/ir/Value.cpp`(若补充 use-def 关系)
|
||
|
||
---
|
||
|
||
## 6. 算法说明
|
||
|
||
### 6.1 Dead(无用代码删除)
|
||
|
||
可以采用“标记 + 清扫”思路:
|
||
|
||
1. 从关键操作出发标记“有用”指令
|
||
2. 沿数据依赖和必要控制依赖扩展标记
|
||
3. 删除未标记指令
|
||
|
||
> 本实验不限定具体思路,实现可自由设计。
|
||
|
||
### 6.2 Clean
|
||
|
||
在 DCE 后对 CFG 做结构化清理,常见包括:
|
||
|
||
1. 冗余分支改写
|
||
2. 空块删除/绕过
|
||
3. 线性可合并块合并
|
||
4. 不可达块删除
|
||
|
||
### 6.3 优化顺序建议
|
||
|
||
可采用迭代顺序:
|
||
|
||
1. `ConstFold`
|
||
2. `DCE`
|
||
3. `CFGSimplify`
|
||
...
|
||
必要时重复多轮,直到 IR 不再变化。
|
||
|
||
---
|
||
|
||
## 7. 构建与验证
|
||
|
||
```bash
|
||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||
cmake --build build -j "$(nproc)"
|
||
```
|
||
|
||
### 7.1 观察 IR
|
||
|
||
```bash
|
||
./build/bin/compiler --emit-ir test/test_case/simple_add.sy
|
||
```
|
||
|
||
### 7.2 语义回归
|
||
|
||
```bash
|
||
./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run
|
||
./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run
|
||
```
|
||
|
||
目标:优化后程序行为与优化前保持一致。
|
||
|
||
---
|
||
|
||
|