# Lab6：并行与循环优化

## 1. 本实验定位

Lab6 的目标是在 Lab5 基本标量优化之后，面向“循环密集型代码”继续优化。  
本实验重点不是再补语义覆盖，而是提升中后端对循环结构的处理能力，为性能优化打基础。

核心方向：

1. 循环识别与规范化
2. 循环相关优化（如不变代码外提、强度削弱等）
3. 可并行循环分析与并行化改造（可选，按课程要求推进）

---

## 2. 先看这一点：循环优化依赖分析结果

循环优化通常依赖一组基础分析：

1. CFG 与支配关系
2. 循环层次信息（LoopInfo）
3. def-use/use-def 与副作用信息（保证变换合法）

如果这些基础信息不稳定，循环优化很容易“优化错程序”。  
因此 Lab6 建议先把分析链路和验证手段搭好，再做具体变换。

---

## 3. Lab6 要求

需要同学完成：

1. 在现有 IR 上识别循环结构（至少能区分循环头、循环体、回边）。
2. 实现有效的循环优化，并保证语义不变。
3. 将循环优化并入 `PassManager`，与 Lab5 的优化流程协同工作。
4. 对优化前后结果做回归验证，并给出性能或代码规模对比（至少一种指标）。
5. 可选：尝试实现可并行循环识别与并行化改造（按课程要求）。

---

## 4. 当前代码框架（与 Lab6 相关）

1. IR 与分析
   - `src/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`（若分析信息不足）
   - `src/ir/IR.h`、`src/ir/Instruction.cpp`（若需要补充指令属性）
   - `src/ir/IRPrinter.cpp`（调试输出增强）

---

## 6. 可选优化方向

1. 循环不变代码外提（LICM）
2. 归纳变量简化与强度削弱
3. 循环展开（小规模、受控展开）
4. 循环分裂
5. 简单并行化识别（无跨迭代写后读/写后写冲突时标记可并行）
...

---

## 7. 简要优化原理

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

原理：  
将循环中“每次迭代结果都不变”的表达式移动到循环外执行。若某表达式不依赖循环内变化的值，并且其操作数在循环内不被改写，则它是循环不变量。

作用：  
减少循环体内重复计算，降低迭代开销。

实现思路：  
先识别循环结构，再判断循环体中哪些表达式对所有迭代恒定。把这些表达式外提到循环前置块（或等价安全位置），循环体改用外提结果。

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

原理：  
将高开销运算替换为等价低开销运算。典型场景是把循环中的乘法/除法改写为增量更新（加减）。

作用：  
降低每次迭代的算术成本，提高整体执行效率。

实现思路：  
识别归纳变量与其线性相关表达式，判断是否可改写为递增/递减更新。引入辅助变量后，用加减替代高成本运算并保持语义一致。

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

原理：  
一次迭代中执行多份循环体副本。

作用：  
减少控制指令比例，提高指令级并行机会，也有利于后续向量化或流水线优化。

实现思路：  
选择展开因子，复制循环体并调整步长。若总迭代次数不能整除展开因子，需要保留余数迭代处理路径以保证结果正确。

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

原理：  
把一个包含多类语句的循环拆成多个循环，每个循环执行原循环的一部分语句。

作用：  
降低单个循环体复杂度，改善数据局部性，并为并行化/向量化创造条件。

实现思路：  
先做数据依赖分析。若若干语句间不存在阻碍重排的依赖，可将其拆分到不同循环中，同时维持必要执行顺序保证语义不变。

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

原理：  
把循环不同迭代同时执行，以利用多核并行能力。前提是迭代间不存在破坏语义的数据依赖。

作用：  
在数据规模较大时可显著缩短运行时间，尤其适用于数值计算与批量数据处理。

实现思路：  
先判定迭代间读写依赖。若可并行，则将迭代划分为任务并分发到线程/处理单元；结束后进行同步与归并，确保结果正确。

---

## 8. 推荐实验流程

1. 跑通循环分析。
2. 选择循环优化实现
3. 接入 `PassManager` 。
4. 用样例验证正确性。
5. 统计优化前后差异。

---

## 9. 构建与验证

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

### 9.1 功能回归

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

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

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

---
