# Lab6：并行与循环优化

## 1. 本实验定位

Lab6 的重点是在 Lab5 基本标量优化之后，继续围绕循环结构开展更进一步的性能优化。本实验不再以补齐语义覆盖为主，而是把重点放在循环识别、循环变换以及可并行循环分析等问题上，为进一步提升最终生成代码的性能打基础。

## 2. Lab6 要求

本实验需要完成的事情包括：在现有 IR 上识别循环结构，能够区分循环头、循环体、前置块、退出块与回边等部分；实现有效的循环优化，并保证变换前后语义一致；将这些优化接入 `PassManager`，使其能够与 Lab5 的优化流程协同工作；最后通过回归测试和性能或代码规模对比，验证优化结果的正确性与收益。若希望进一步提升性能，也可以继续尝试可并行循环识别与并行化改造。

## 3. 相关文件

以下文件与本实验内容相关，建议优先阅读。

- `include/ir/IR.h`
- `src/ir/analysis/DominatorTree.cpp`
- `src/ir/analysis/LoopInfo.cpp`
- `src/ir/passes/PassManager.cpp`

## 4. 当前基础与前置准备

循环优化通常依赖一组相对稳定的基础分析，包括 CFG 与支配关系、循环层次信息（`LoopInfo`），以及 def-use/use-def 和副作用信息。只有这些基础信息足够稳定，后续的循环变换才不容易“优化错程序”。因此，在正式实现具体优化之前，建议先把分析链路与验证手段理顺。

## 5. 可实现的优化方向与实现提示

本实验可以选择的方向包括循环不变代码外提、归纳变量简化与强度削弱、循环展开、循环分裂，以及简单的并行化识别。如果你的实现还需要围绕当前框架补充其他循环相关优化，也可以按需扩展。

### 5.1 循环不变量外提（Loop Invariant Code Motion）

循环不变代码外提的核心，是把循环中每次迭代结果都不变的表达式移动到循环外执行。若某个表达式不依赖循环内变化的值，并且其操作数在循环体内不被改写，那么它就具备外提条件。这样做的直接收益，是减少循环体内的重复计算，降低迭代开销。实现时，通常需要先识别循环结构，再判断哪些表达式对所有迭代都恒定，然后把它们外提到循环前置块或其他等价安全位置。

### 5.2 强度削弱（Strength Reduction）

强度削弱的思路，是把高开销运算替换为等价的低开销运算。循环中的典型场景，是把乘法、除法等操作改写为递增或递减更新。这样可以降低每次迭代的算术成本，提高整体执行效率。实现时，通常需要先识别归纳变量以及与其线性相关的表达式，再判断是否可以通过引入辅助变量，用加减更新替代高成本运算。

### 5.3 循环展开（Loop Unrolling）

循环展开的做法，是在一次迭代中执行多份循环体副本，以减少控制指令比例，并提升指令级并行机会。它也常常能为后续向量化或流水线优化创造条件。实现时，需要选择合适的展开因子，复制循环体并调整步长；如果总迭代次数不能整除展开因子，还需要保留余数迭代路径以保证结果正确。

### 5.4 循环分裂（Loop Fission）

循环分裂是把一个包含多类语句的循环拆成多个循环，每个循环只执行原循环中的一部分语句。这样做通常有助于降低单个循环体的复杂度，改善数据局部性，并为并行化或向量化提供更好的前提。实现时，一般需要先做数据依赖分析；只有在若干语句之间不存在阻碍重排的依赖时，才适合将其拆分到不同循环中。

### 5.5 循环并行化（Loop Parallelization）

循环并行化的目标，是让不同迭代可以并发执行，以利用多核并行能力。它成立的前提，是迭代间不存在破坏语义的数据依赖。若分析结果表明循环可以并行，就可以进一步考虑任务划分、执行与归并，从而继续提升整体性能。不过，这一部分通常也有一定难度，对依赖分析、任务划分和执行正确性的要求都更高，因此更适合作为在前面优化基础上继续深入的方向。

## 6. 推荐实验流程

比较自然的推进顺序是：先跑通循环分析，再选择一种或几种循环优化逐步实现，然后接入 `PassManager`，最后结合测试与输出对比检查优化结果是否正确、是否带来预期收益。

## 7. 构建与验证

```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j "$(nproc)"
```

### 7.1 功能回归

```bash
./scripts/verify_ir.sh test/test_case/function/simple_add.sy test/test_result/function/ir --run
./scripts/verify_asm.sh test/test_case/function/simple_add.sy test/test_result/function/asm --run
```

`--run` 模式下脚本会自动读取同名 `.in`，并将程序输出与退出码和同名 `.out` 比对。

完成 Lab6 后，不能只检查 `simple_add` 这类单个样例，而应对 `test/test_case` 下全部测试用例逐个回归；如有需要，也可以自行编写批量测试脚本统一执行。

### 7.2 优化效果对比（示例）

```bash
# 对比优化前后 IR/汇编输出（按你实现的开关或日志方式执行）
./build/bin/compiler --emit-ir test/test_case/function/simple_add.sy
./build/bin/compiler --emit-asm test/test_case/function/simple_add.sy
```

这里的 `simple_add` 只用于展示如何观察单个样例的输出差异；实际评估优化效果时，仍应结合更多测试用例，必要时覆盖全部测试集。
