|
|
|
|
@ -38,36 +38,18 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 4. 当前代码框架
|
|
|
|
|
## 4. 相关文件
|
|
|
|
|
|
|
|
|
|
1. IR 与分析
|
|
|
|
|
- `include/ir/IR.h`
|
|
|
|
|
- `src/ir/analysis/DominatorTree.cpp`
|
|
|
|
|
- `src/ir/analysis/LoopInfo.cpp`
|
|
|
|
|
以下文件与本实验内容相关,建议优先阅读。
|
|
|
|
|
|
|
|
|
|
2. IR 优化
|
|
|
|
|
- `src/ir/passes/`
|
|
|
|
|
|
|
|
|
|
3. 入口与验证
|
|
|
|
|
- `src/main.cpp`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 5. 需要修改的文件
|
|
|
|
|
|
|
|
|
|
1. 核心文件
|
|
|
|
|
- `src/ir/analysis/LoopInfo.cpp`(完善循环分析)
|
|
|
|
|
- `src/ir/passes/PassManager.cpp`(接入新的循环优化 pass)
|
|
|
|
|
- 新增循环优化 pass 文件(建议放在 `src/ir/passes/` 下)
|
|
|
|
|
|
|
|
|
|
2. 视实现需要可能修改
|
|
|
|
|
- `src/ir/analysis/DominatorTree.cpp`(若分析信息不足)
|
|
|
|
|
- `include/ir/IR.h`、`src/ir/Instruction.cpp`(若需要补充指令属性)
|
|
|
|
|
- `src/ir/IRPrinter.cpp`(调试输出增强)
|
|
|
|
|
- `include/ir/IR.h`
|
|
|
|
|
- `src/ir/analysis/DominatorTree.cpp`
|
|
|
|
|
- `src/ir/analysis/LoopInfo.cpp`
|
|
|
|
|
- `src/ir/passes/PassManager.cpp`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 6. 可选优化方向
|
|
|
|
|
## 5. 可选优化方向
|
|
|
|
|
|
|
|
|
|
1. 循环不变代码外提(LICM)
|
|
|
|
|
2. 归纳变量简化与强度削弱
|
|
|
|
|
@ -78,9 +60,9 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. 简要优化原理
|
|
|
|
|
## 6. 简要优化原理
|
|
|
|
|
|
|
|
|
|
### 7.1 循环不变量外提(Loop Invariant Code Motion)
|
|
|
|
|
### 6.1 循环不变量外提(Loop Invariant Code Motion)
|
|
|
|
|
|
|
|
|
|
原理:
|
|
|
|
|
将循环中“每次迭代结果都不变”的表达式移动到循环外执行。若某表达式不依赖循环内变化的值,并且其操作数在循环内不被改写,则它是循环不变量。
|
|
|
|
|
@ -91,7 +73,7 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
实现思路:
|
|
|
|
|
先识别循环结构,再判断循环体中哪些表达式对所有迭代恒定。把这些表达式外提到循环前置块(或等价安全位置),循环体改用外提结果。
|
|
|
|
|
|
|
|
|
|
### 7.2 强度削弱(Strength Reduction)
|
|
|
|
|
### 6.2 强度削弱(Strength Reduction)
|
|
|
|
|
|
|
|
|
|
原理:
|
|
|
|
|
将高开销运算替换为等价低开销运算。典型场景是把循环中的乘法/除法改写为增量更新(加减)。
|
|
|
|
|
@ -102,7 +84,7 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
实现思路:
|
|
|
|
|
识别归纳变量与其线性相关表达式,判断是否可改写为递增/递减更新。引入辅助变量后,用加减替代高成本运算并保持语义一致。
|
|
|
|
|
|
|
|
|
|
### 7.3 循环展开(Loop Unrolling)
|
|
|
|
|
### 6.3 循环展开(Loop Unrolling)
|
|
|
|
|
|
|
|
|
|
原理:
|
|
|
|
|
一次迭代中执行多份循环体副本。
|
|
|
|
|
@ -113,7 +95,7 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
实现思路:
|
|
|
|
|
选择展开因子,复制循环体并调整步长。若总迭代次数不能整除展开因子,需要保留余数迭代处理路径以保证结果正确。
|
|
|
|
|
|
|
|
|
|
### 7.4 循环分裂(Loop Fission)
|
|
|
|
|
### 6.4 循环分裂(Loop Fission)
|
|
|
|
|
|
|
|
|
|
原理:
|
|
|
|
|
把一个包含多类语句的循环拆成多个循环,每个循环执行原循环的一部分语句。
|
|
|
|
|
@ -124,7 +106,7 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
实现思路:
|
|
|
|
|
先做数据依赖分析。若若干语句间不存在阻碍重排的依赖,可将其拆分到不同循环中,同时维持必要执行顺序保证语义不变。
|
|
|
|
|
|
|
|
|
|
### 7.5 循环并行化(Loop Parallelization)
|
|
|
|
|
### 6.5 循环并行化(Loop Parallelization)
|
|
|
|
|
|
|
|
|
|
原理:
|
|
|
|
|
把循环不同迭代同时执行,以利用多核并行能力。前提是迭代间不存在破坏语义的数据依赖。
|
|
|
|
|
@ -137,7 +119,7 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 8. 推荐实验流程
|
|
|
|
|
## 7. 推荐实验流程
|
|
|
|
|
|
|
|
|
|
1. 跑通循环分析。
|
|
|
|
|
2. 选择循环优化实现
|
|
|
|
|
@ -147,14 +129,14 @@ Lab6 的目标是在 Lab5 基本标量优化之后,面向“循环密集型代
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 9. 构建与验证
|
|
|
|
|
## 8. 构建与验证
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
|
|
|
cmake --build build -j "$(nproc)"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 9.1 功能回归
|
|
|
|
|
### 8.1 功能回归
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
./scripts/verify_ir.sh test/test_case/simple_add.sy test/test_result/ir --run
|
|
|
|
|
@ -165,7 +147,7 @@ cmake --build build -j "$(nproc)"
|
|
|
|
|
|
|
|
|
|
完成 Lab6 后,不能只检查 `simple_add` 这类单个样例,而应对 `test/test_case` 下全部测试用例逐个回归;如有需要,也可以自行编写批量测试脚本统一执行。
|
|
|
|
|
|
|
|
|
|
### 9.2 优化效果对比(示例)
|
|
|
|
|
### 8.2 优化效果对比(示例)
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# 对比优化前后 IR/汇编输出(按你实现的开关或日志方式执行)
|
|
|
|
|
|