Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8b5d9c864 | |||
| d9d36e017f | |||
| 18efd26881 | |||
| b107db66a6 | |||
| 012536acea | |||
| 3ef7bc28d6 | |||
| c4e4513a9d | |||
| cb091a4b21 | |||
| 6f943b395f | |||
| 108f3d9e4b | |||
| cd46ff6fdd | |||
| 11fd0e3e89 |
@@ -36,6 +36,7 @@ class DominatorTree {
|
||||
bool RunMem2Reg(Function* func, Context& ctx);
|
||||
bool RunConstProp(Function* func, Context& ctx);
|
||||
bool RunConstFold(Function* func, Context& ctx);
|
||||
bool RunAlgebraicSimplify(Function* func, Context& ctx);
|
||||
bool RunDCE(Function* func);
|
||||
bool RunCFGSimplify(Function* func);
|
||||
bool RunCSE(Function* func);
|
||||
|
||||
@@ -55,6 +55,7 @@ enum class Opcode {
|
||||
MovReg,
|
||||
Adrp,
|
||||
AddRegImm,
|
||||
LslImm,
|
||||
LdrRegReg,
|
||||
StrRegReg,
|
||||
SIToFP,
|
||||
|
||||
1
presentation/.~lock.presentation.pptx#
Normal file
1
presentation/.~lock.presentation.pptx#
Normal file
@@ -0,0 +1 @@
|
||||
,gh0s7,HakureiShrine,29.06.2026 18:09,/home/gh0s7/.local/share/onlyoffice;
|
||||
@@ -395,7 +395,8 @@ style: |
|
||||
|
||||
<div class="chips">
|
||||
<span class="chip">Lab1–Lab6 全部完成</span>
|
||||
<span class="chip">11 项功能测试通过</span>
|
||||
<span class="chip">21 项回归测试通过</span>
|
||||
<span class="chip">全量测试 217.293s</span>
|
||||
<span class="chip">实验验收汇报</span>
|
||||
</div>
|
||||
|
||||
@@ -403,7 +404,7 @@ style: |
|
||||
|
||||
小组成员:**程景愉** · **舒钰权** · **杨力嘉**
|
||||
|
||||
<p>仓库地址:<strong>https://bdgit.educoder.net/pw7hgk2xf/nudt-compiler-cpp</strong></p>
|
||||
<p>仓库地址:<strong>https://git.nudt.space/CompileThreeMaggot/nudt-compiler-cpp.git</strong></p>
|
||||
|
||||
---
|
||||
|
||||
@@ -444,9 +445,9 @@ style: |
|
||||
<div class="tech-item"><strong>前端</strong>ANTLR4 + Visitor 模式,词法/语法分析 → 语法树</div>
|
||||
<div class="tech-item"><strong>语义分析</strong>符号表 + 作用域栈 + 类型检查 + 名称绑定</div>
|
||||
<div class="tech-item"><strong>中端 IR</strong>LLVM 风格 SSA IR,含完整 use-def 链与 CFG</div>
|
||||
<div class="tech-item"><strong>中端优化</strong>Mem2Reg · ConstFold/Prop · CSE · DCE · CFGSimplify · LICM</div>
|
||||
<div class="tech-item"><strong>中端优化</strong>Mem2Reg · ConstFold/Prop · CSE · Load CSE · DCE · CFGSimplify · LICM</div>
|
||||
<div class="tech-item"><strong>后端 MIR</strong>机器级中间表示 → 虚拟寄存器 → 物理寄存器</div>
|
||||
<div class="tech-item"><strong>后端优化</strong>窥孔优化 · 冗余消除 · 寄存器别名感知</div>
|
||||
<div class="tech-item"><strong>后端优化</strong>窥孔优化 · 冗余消除 · 栈帧压缩 · SP 直接寻址 · 寄存器别名感知</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
@@ -654,6 +655,42 @@ style: |
|
||||
|
||||
---
|
||||
|
||||
# 性能优化专项:无硬编码的通用提速
|
||||
|
||||
<div class="grid-3">
|
||||
<div class="card">
|
||||
<h3>IR 层:基本块内 Load CSE</h3>
|
||||
<p>在 <code>CSE</code> Pass 中增加同一基本块内的重复 <code>load</code> 消除:相同指针地址若未被 <code>store/call</code> 破坏,后续读取直接复用已有结果。该优化对 <code>A[i][j] * A[i][j]</code> 等循环密集模式收益明显,同时通过内存写入失效保证语义安全。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>MIR 层:死栈槽删除 + 栈帧压缩</h3>
|
||||
<p>后端扫描所有 <code>FrameIndex</code> 使用,删除从未被读取或取地址的临时栈槽写入;随后重新紧凑布局仍然活跃的栈槽,缩小 frame size,减少无效栈空间和大偏移访存。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>汇编层:SP 直接寻址</h3>
|
||||
<p>原先大偏移栈访问只能退化为 <code>ldr x10, =offset</code> + 寄存器偏移访存。优化后优先尝试 <code>[sp, #imm]</code> 正偏移寻址,大幅减少 literal load 和临时寄存器占用。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-3" style="margin-top:14px;">
|
||||
<div class="card">
|
||||
<h3>重点样例收益</h3>
|
||||
<p><code>2025-MYO-20.sy</code> 单测运行时间由约 <strong>130.8s</strong> 降至约 <strong>90.2s</strong>;生成汇编中大偏移栈访问 literal load 由 <strong>24</strong> 降至 <strong>0</strong>。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>栈访问优化收益</h3>
|
||||
<p><code>if-combine3.sy</code> 中 <code>ldr x10, =offset</code> 由 <strong>208</strong> 降至 <strong>0</strong>,汇编行数由约 <strong>923</strong> 行降至约 <strong>715</strong> 行,单测约 <strong>25s</strong> 完成。</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>全量测试结果</h3>
|
||||
<p>取消所有 benchmark 特化和硬编码后,完整脚本 <code>./scripts/run_all_tests_verbose.sh</code> 从约 <strong>279.6s</strong> 降至 <strong>217.293s</strong>,21 项测试全部通过。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="lead">核心原则:所有提速均来自<strong>通用 IR/MIR/汇编优化</strong>,不依赖文件名、测试名或特定输出常量,可接受代码检查。</p>
|
||||
|
||||
---
|
||||
|
||||
# 关键技术难点与突破
|
||||
|
||||
<div class="grid-3">
|
||||
@@ -688,7 +725,7 @@ style: |
|
||||
|
||||
---
|
||||
|
||||
# 测试验证结果
|
||||
# 功能测试验证结果
|
||||
|
||||
<div class="table-wrapper">
|
||||
|
||||
@@ -708,7 +745,28 @@ style: |
|
||||
|
||||
</div>
|
||||
|
||||
<p class="lead" style="text-align:center;">全部 <strong>11 项功能测试用例</strong> 与 <strong>10 项性能测试用例</strong>(共 21 项)在<strong>优化开启</strong>条件下均通过 <code>verify_asm.sh --run</code> 验证,输出与退出码 <strong>100% 匹配</strong>预期。</p>
|
||||
---
|
||||
|
||||
# 功能测试验证结果
|
||||
|
||||
<div class="table-wrapper">
|
||||
|
||||
| 序号 | 测试用例 | 测试内容 | 结果 |
|
||||
|:---:|:---|:---|:---:|
|
||||
| 12 | `01_mm2.sy` | 矩阵乘法(性能) | ✅ |
|
||||
| 13 | `02_mv3.sy` | 矩阵向量乘法(性能) | ✅ |
|
||||
| 14 | `03_sort1.sy` | 排序算法(性能) | ✅ |
|
||||
| 15 | `2025-MYO-20.sy` | 综合性能测试(循环/数组) | ✅ |
|
||||
| 16 | `fft0.sy` | 快速傅里叶变换(性能) | ✅ |
|
||||
| 17 | `gameoflife-oscillator.sy` | 生命游戏振荡器(性能) | ✅ |
|
||||
| 18 | `if-combine3.sy` | 条件组合与分支密集(性能) | ✅ |
|
||||
| 19 | `large_loop_array_2.sy` | 大循环数组访问(性能) | ✅ |
|
||||
| 20 | `transpose0.sy` | 矩阵转置(性能) | ✅ |
|
||||
| 21 | `vector_mul3.sy` | 向量乘法(性能) | ✅ |
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <p class="lead" style="text-align:center;">全部 <strong>11 项功能测试用例</strong> 与 <strong>10 项性能测试用例</strong>(共 21 项)在<strong>优化开启</strong>条件下通过 <code>./scripts/run_all_tests_verbose.sh</code> 验证;当前无硬编码优化版本总耗时 <strong>217.293 秒</strong>,输出与退出码 <strong>100% 匹配</strong>预期。</p> -->
|
||||
|
||||
<p class="mini" style="text-align:center;">验证链路:SysY 源码 → IR 生成 → 标量优化 → 循环优化 → 指令选择 → 寄存器分配 → 窥孔优化 → AArch64 汇编 → QEMU 模拟运行 → 输出比对</p>
|
||||
|
||||
@@ -766,8 +824,9 @@ style: |
|
||||
<li>✅ LLVM 风格 SSA IR 生成与打印</li>
|
||||
<li>✅ AArch64 后端指令选择与汇编输出</li>
|
||||
<li>✅ Mem2Reg + 五大标量优化 + LICM</li>
|
||||
<li>✅ 寄存器别名感知窥孔优化</li>
|
||||
<li>✅ 11 项功能测试全部通过</li>
|
||||
<li>✅ Load CSE + 死栈槽删除 + 栈帧压缩</li>
|
||||
<li>✅ SP 直接寻址与寄存器别名感知窥孔优化</li>
|
||||
<li>✅ 21 项完整回归测试全部通过</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
@@ -782,7 +841,7 @@ style: |
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="lead">本项目已构建起一个<strong>结构清晰、可扩展、语义正确</strong>的 SysY 编译器框架,为后续继续深入编译器优化与并行化研究提供了坚实的基础。</p>
|
||||
<p class="lead">本项目已构建起一个<strong>结构清晰、可扩展、语义正确</strong>的 SysY 编译器框架,并在不引入测例硬编码的前提下,将完整回归测试耗时优化至 <strong>217.293 秒</strong>,为后续继续深入编译器优化与并行化研究提供了坚实基础。</p>
|
||||
|
||||
---
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -37,9 +37,9 @@
|
||||
)[
|
||||
#text(weight: "bold", fill: rgb("#0f4c81"))[8 分钟汇报时间分配] \
|
||||
#set text(size: 9pt)
|
||||
- *页面时间*:封面 ~10s / 概述 ~25s / 技术栈 ~15s / Lab1 ~20s / Lab2 ~50s / Lab3 ~35s / Lab4 ~50s / Lab5 ~25s / Lab6 ~30s / 近期攻坚 ~35s / 难点 ~30s / 测试 ~20s / 分工 ~15s / 总结 ~25s / 致谢 ~5s。总计约 390 秒 ≈ 6.5 分钟演讲 + 1.5 分钟缓冲。
|
||||
- *精讲原则*:每页只讲 1-2 个核心技术点,不展开细节。五个必讲亮点:编译期/运行期分离、支配树+Mem2Reg、浮点位精确、寄存器别名、LICM。
|
||||
- *语速*:中文约 260 字/分钟,本稿演讲正文约 2100 字。
|
||||
- *页面时间*:封面 ~10s / 概述 ~25s / 技术栈 ~15s / Lab1 ~20s / Lab2 ~45s / Lab3 ~30s / Lab4 ~45s / Lab5 ~25s / Lab6 ~25s / 近期攻坚 ~30s / 性能优化专项 ~45s / 难点 ~25s / 测试 ~20s / 分工 ~15s / 总结 ~20s / 致谢 ~5s。总计约 420 秒 ≈ 7 分钟演讲 + 1 分钟缓冲。
|
||||
- *精讲原则*:每页只讲 1-2 个核心技术点,不展开细节。六个必讲亮点:编译期/运行期分离、支配树+Mem2Reg、浮点位精确、寄存器别名、LICM、无硬编码性能优化。
|
||||
- *语速*:中文约 260 字/分钟,本稿演讲正文约 2300 字。
|
||||
]
|
||||
|
||||
#v(0.3cm)
|
||||
@@ -53,7 +53,7 @@
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 1 页:封面页(~10 秒)
|
||||
*【逐字演讲稿】* 各位老师、同学们,下午好!我是程景愉。今天代表我们小组——程景愉、舒钰权、杨力嘉,汇报 SysY 编译器课程实验成果。我们实现了从 SysY 到 AArch64 汇编的完整编译器,六个实验全部完成,11 项测试通过。 \
|
||||
*【逐字演讲稿】* 各位老师、同学们,下午好!我是舒钰权。今天代表我们小组——程景愉、舒钰权、杨力嘉,汇报 SysY 编译器课程实验成果。我们实现了从 SysY 到 AArch64 汇编的完整编译器,六个实验全部完成,21 项完整回归测试通过,并将全量测试耗时优化到 217.293 秒。 \
|
||||
*【演讲技巧】* 站姿挺拔,声音洪亮。一句话自我介绍 + 一句话项目概括。
|
||||
]
|
||||
|
||||
@@ -65,19 +65,19 @@
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 3 页:技术栈总览(~15 秒)
|
||||
*【逐字演讲稿】* 快速一览技术栈。前端 ANTLR4 + Visitor,中端自研 SSA IR 含完整 use-def 链,中端优化实现了 Mem2Reg 加五个标量 Pass 及 LICM,后端 MIR → 汇编。LLVM 工具链验证 IR,AArch64 交叉编译 + QEMU 验证汇编,全程自动化。 \
|
||||
*【逐字演讲稿】* 快速一览技术栈。前端 ANTLR4 + Visitor,中端自研 SSA IR 含完整 use-def 链,中端优化实现了 Mem2Reg、五个标量 Pass、Load CSE 及 LICM,后端 MIR 到 AArch64 汇编,并加入栈帧压缩和 SP 直接寻址等后端优化。LLVM 工具链验证 IR,AArch64 交叉编译 + QEMU 验证汇编,全程自动化。 \
|
||||
*【演讲技巧】* 快速全景扫描,15 秒带过。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 4 页:Lab1 语法树构建 — 前端基石(~20 秒)
|
||||
*【逐字演讲稿】* Lab1 由舒钰权负责。核心是扩展 ANTLR4 文法,覆盖完整 SysY——控制流、表达式、浮点、数组、函数参数。关键认知:ANTLR 文法的 rule 命名直接决定生成 C++ 类的类型名——意味着 sem 和 irgen 所有 visit\* 函数必须与文法精确匹配。文法一变,下游全部同步适配。这是前端工程"牵一发而动全身"的典型体现。 \
|
||||
*【逐字演讲稿】* Lab1 由我负责。核心是扩展 ANTLR4 文法,覆盖完整 SysY——控制流、表达式、浮点、数组、函数参数。关键认知:ANTLR 文法的 rule 命名直接决定生成 C++ 类的类型名——意味着 sem 和 irgen 所有 visit\* 函数必须与文法精确匹配。文法一变,下游全部同步适配。这是前端工程"牵一发而动全身"的典型体现。 \
|
||||
*【演讲技巧】* 强调文法与下游耦合关系。快速带过,不展开细节。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 5 页:Lab2 中间表示生成 — IR 语义全覆盖(~45 秒)
|
||||
*【逐字演讲稿】* Lab2 工作量最大,由我负责。扩展了 IR 类型系统和指令集,实现了短路求值、控制流、函数调用与多维数组的 IR 翻译。
|
||||
*【逐字演讲稿】* Lab2 工作量最大,由程景愉负责。扩展了 IR 类型系统和指令集,实现了短路求值、控制流、函数调用与多维数组的 IR 翻译。
|
||||
|
||||
讲两个最核心的难点。第一,"编译期/运行期路径混用"——原 EvalConstExpr 内部调用了需要插入点的 IRBuilder,全局初始化时直接崩溃。解决方案:彻底分离,常量路径只返回 ConstantInt/ConstantFloat,绝不碰 IRBuilder。这是编译器设计的基本原则,但初学者极易违反。
|
||||
|
||||
@@ -87,13 +87,13 @@
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 6 页:Lab3 指令选择与汇编生成 — AArch64 后端(~30 秒)
|
||||
*【逐字演讲稿】* Lab3 由舒钰权负责。采用高可靠栈槽模型——每个 IR Value 分配专属栈槽,100% 保证变量活跃期正确性。攻克四个底层难题:vector 扩容指针失效——预分配容量;栈帧超 256 字节 ldur/stur 立即数越界——自适应回退寄存器寻址;浮点精度丢失——memcpy 取 IEEE 754 位、.word 原样输出,全链路位精确;重写 sylib.c 补齐 I/O。 \
|
||||
*【逐字演讲稿】* Lab3 由我负责。采用高可靠栈槽模型——每个 IR Value 分配专属栈槽,100% 保证变量活跃期正确性。攻克四个底层难题:vector 扩容指针失效——预分配容量;栈帧超 256 字节 ldur/stur 立即数越界——自适应回退寄存器寻址;浮点精度丢失——memcpy 取 IEEE 754 位、.word 原样输出,全链路位精确;重写 sylib.c 补齐 I/O。 \
|
||||
*【演讲技巧】* "浮点位精确"和"大栈帧自适应寻址"最体现工程严谨性。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 7 页:Lab4 基本标量优化 — SSA 中端核心(~45 秒)
|
||||
*【逐字演讲稿】* Lab4 理论深度最高,由我负责。先实现迭代支配树算法、计算支配边界,然后完成 Mem2Reg——汇合点插 Phi、沿支配树 DFS 变量重命名,将内存形式 IR 提升为 SSA。
|
||||
*【逐字演讲稿】* Lab4 理论深度最高,由程景愉负责。先实现迭代支配树算法、计算支配边界,然后完成 Mem2Reg——汇合点插 Phi、沿支配树 DFS 变量重命名,将内存形式 IR 提升为 SSA。
|
||||
|
||||
在此基础上实现五个优化 Pass。ConstFold 做编译期计算与代数简化。ConstProp 传播常量并简化条件分支——这里有个极易遗漏的细节:简化后必须显式清理 Phi 的 dead incoming 边,否则后续 Pass 会基于脏数据做错误替换。CSE 做块内公共子表达式消除,DCE 用 Mark-and-Sweep,CFGSimplify 合并线性块。
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 9 页:Lab6 并行与循环优化 — LICM(~25 秒)
|
||||
*【逐字演讲稿】* Lab6 由我负责,实现循环不变式外提。三步:基于支配树识别回边——B→H 且 H 支配 B,H 为循环头,BFS 收集循环体;检查 Preheader 唯一性确保安全;worklist 迭代判定不变指令,覆盖 GEP 和类型转换,按拓扑序保序外提。
|
||||
*【逐字演讲稿】* Lab6 由程景愉负责,实现循环不变式外提。三步:基于支配树识别回边——B→H 且 H 支配 B,H 为循环头,BFS 收集循环体;检查 Preheader 唯一性确保安全;worklist 迭代判定不变指令,覆盖 GEP 和类型转换,按拓扑序保序外提。
|
||||
|
||||
修复了一个隐蔽的死循环漏洞:DCE 后可能留下 idom 为空或自环的不可达死块,ComputeDF 的 while 循环永不收敛、编译器卡死。定位两三小时,修复只需两行阻断代码。 \
|
||||
*【演讲技巧】* 死循环漏洞是精彩的调试故事。
|
||||
@@ -128,31 +128,43 @@
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 10 页:关键技术难点与突破(~30 秒)
|
||||
== 第 10 页:性能优化专项:无硬编码的通用提速(~45 秒)
|
||||
*【逐字演讲稿】* 这里重点汇报基础六个 Lab 之外,我们最后针对性能测试做的通用优化。要求是不能硬编码测试名、文件名或输出常量,所以我们只保留可以解释为编译器正常优化的方案。
|
||||
|
||||
第一是 IR 层 Load CSE:同一基本块内,如果两次 load 来自同一个指针,并且中间没有 store 或 call 破坏内存,就直接复用第一次 load 的结果。这个优化对 `A[i][j] * A[i][j]` 这类循环密集表达式非常有效。
|
||||
|
||||
第二是 MIR 层死栈槽删除和栈帧压缩。删除从未被读取的临时栈槽后,重新紧凑布局活跃 frame slot,减少大负偏移访存。
|
||||
|
||||
第三是汇编层 SP 直接寻址。原先大偏移访问会生成 `ldr x10, =offset` 再访存;优化后能用 `[sp, #imm]` 就直接编码。效果上,`2025-MYO-20.sy` 单测从约 130.8 秒降到约 90.2 秒,`if-combine3.sy` 的大偏移 literal load 从 208 次降为 0。完整脚本从约 279.6 秒降到 217.293 秒,21 项测试全部通过。 \
|
||||
*【演讲技巧】* 这一页是性能亮点,强调“无硬编码”和“可解释为通用优化”。数字要讲清楚。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 11 页:关键技术难点与突破(~25 秒)
|
||||
*【逐字演讲稿】* 六大技术挑战总结。编译期/运行期分离——常量求值绝不碰 IRBuilder。数组语义三层拆分——标量、聚合、指针退化严格区分。浮点精度保全——从常量折叠到 .word 汇编全链路位精确。SSA 一致性——每个改变 CFG 的 Pass 必须同步维护 Phi 边。后端指针安全——预分配容量、64 位强制 X 寄存器、栈槽静态扫描。支配树鲁棒性——不可达节点和自环必须优雅阻断。这六点是优化开启后仍保持语义正确的基石。 \
|
||||
*【演讲技巧】* 快速过六个要点,手指逐一指向卡片。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 11 页:测试验证结果(~20 秒)
|
||||
*【逐字演讲稿】* 全部 11 项功能测试与 10 项性能测试在优化全开条件下通过,21 个用例输出与退出码 100% 匹配。覆盖从 simple_add 到递归图着色到 95_float 浮点综合测试。特别强调:这是在 Mem2Reg + 五个 Pass + LICM 全部开启下通过的——优化管线在提升性能的同时保证了语义正确。验证链路:SysY 源码 → IR → 优化 → AArch64 汇编 → QEMU 模拟 → 输出比对。 \
|
||||
*【演讲技巧】* 强调"优化全开"和"100% 匹配"。
|
||||
== 第 12 页:测试验证结果(~20 秒)
|
||||
*【逐字演讲稿】* 全部 11 项功能测试与 10 项性能测试在优化全开条件下通过,21 个用例输出与退出码 100% 匹配。当前无硬编码优化版本完整脚本耗时 217.293 秒。覆盖从 simple_add 到递归图着色、95_float 浮点综合测试,再到 2025-MYO-20 等性能测试。特别强调:这是在 Mem2Reg、五个 Pass、LICM、Load CSE 和后端栈优化全部开启下通过的——优化管线在提升性能的同时保证了语义正确。验证链路:SysY 源码 → IR → 优化 → AArch64 汇编 → QEMU 模拟 → 输出比对。 \
|
||||
*【演讲技巧】* 强调"优化全开"、"21/21"和"217.293 秒"。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 12 页:人员分工(~15 秒)
|
||||
*【逐字演讲稿】* 三人分工。我负责中端优化——Lab2 IR 生成、Lab4 支配树与全部 Pass、Lab6 LICM。舒钰权负责 Lab1 文法扩展和 Lab3 AArch64 后端,攻克了浮点位精确等底层难题。杨力嘉负责 Lab5 窥孔优化与全量测试回归,在寄存器别名感知方面做出关键贡献。通过 Git 分支 + MR + Code Review 完成协作。 \
|
||||
== 第 13 页:人员分工(~15 秒)
|
||||
*【逐字演讲稿】* 三人分工。程景愉负责中端优化——Lab2 IR 生成、Lab4 支配树与全部 Pass、Lab6 LICM。我负责 Lab1 文法扩展和 Lab3 AArch64 后端,攻克了浮点位精确等底层难题。杨力嘉负责 Lab5 窥孔优化与全量测试回归,在寄存器别名感知方面做出关键贡献。通过 Git 分支 + MR + Code Review 完成协作。 \
|
||||
*【演讲技巧】* 真诚肯定组员贡献。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 13 页:实验总结与展望(~25 秒)
|
||||
*【逐字演讲稿】* 核心成果:构建了一个结构清晰、语义正确、可扩展的 SysY 编译器框架。六个实验覆盖前端到后端全环节,在支配树、SSA 构建、Phi 降低、浮点位精确、寄存器别名、LICM 等关键技术上做了深入实现。可继续方向:寄存器分配升级为图着色/线性扫描,循环优化扩展到强度削弱和展开,中端引入 GVN/PRE。通过这六个实验,我们对编译器三层次架构和 SSA 优化有了系统性理解,为今后程序语言和系统优化方向的研究打下了基础。 \
|
||||
== 第 14 页:实验总结与展望(~20 秒)
|
||||
*【逐字演讲稿】* 核心成果:构建了一个结构清晰、语义正确、可扩展的 SysY 编译器框架。六个实验覆盖前端到后端全环节,在支配树、SSA 构建、Phi 降低、浮点位精确、寄存器别名、LICM 等关键技术上做了深入实现;同时额外完成了 Load CSE、栈帧压缩和 SP 直接寻址等通用性能优化,把完整回归测试稳定压到 217.293 秒。可继续方向:寄存器分配升级为图着色/线性扫描,循环优化扩展到强度削弱和展开,中端引入 GVN/PRE。 \
|
||||
*【演讲技巧】* 直视评委,展示热情和清晰规划。
|
||||
]
|
||||
|
||||
#block(width: 100%, breakable: true)[
|
||||
== 第 14 页:致谢与 Q&A
|
||||
== 第 15 页:致谢与 Q&A
|
||||
*【逐字演讲稿】* 感谢各位老师和同学的聆听!从语法树到 AArch64 汇编,从 SSA 优化到循环不变式外提——我们构建了一个完整、正确、可扩展的 SysY 编译器。接下来是答辩与提问环节,敬请批评指正!谢谢!
|
||||
|
||||
#v(0.3cm)
|
||||
@@ -208,8 +220,8 @@
|
||||
*应答*:一是在 Lab1/2 之间引入独立 AST 层解耦文法与下游;二是在 Lab3 就用虚拟寄存器,避免后续从栈槽模型重构。
|
||||
|
||||
#v(0.1cm)
|
||||
*问题十:性能怎么样?* \
|
||||
*应答*:教学编译器追求语义正确优先。优化前因栈槽冗余不如 GCC -O0。全量优化开启后循环密集用例有明显提升。要达到 GCC 水平不现实,但已验证了优化管线的语义正确性。
|
||||
*问题十:性能怎么样?做了哪些非基础 Lab 优化?* \
|
||||
*应答*:教学编译器仍然以语义正确优先,但我们额外做了三类通用性能优化:IR 层基本块内 Load CSE,MIR 层死栈槽删除与栈帧压缩,汇编层 SP 直接寻址。取消所有测例硬编码后,完整脚本从约 279.6 秒降到 217.293 秒;其中 2025-MYO-20 单测从约 130.8 秒降到约 90.2 秒,if-combine3 的大偏移 literal load 从 208 次降到 0。
|
||||
|
||||
#v(0.1cm)
|
||||
*问题十一:能在真机上运行吗?* \
|
||||
|
||||
0
scripts/run_all_tests.sh → scripts/run_all_tests_lab1.sh
Executable file → Normal file
0
scripts/run_all_tests.sh → scripts/run_all_tests_lab1.sh
Executable file → Normal file
@@ -17,6 +17,13 @@ MAGENTA='\e[35m'
|
||||
BOLD='\e[1m'
|
||||
RESET='\e[0m'
|
||||
|
||||
elapsed_seconds() {
|
||||
local start_ms=$1
|
||||
local end_ms
|
||||
end_ms=$(date +%s%3N)
|
||||
awk "BEGIN { printf \"%.3f\", ($end_ms - $start_ms) / 1000 }"
|
||||
}
|
||||
|
||||
test_dir="$(pwd)/test/test_case"
|
||||
compiler="$(pwd)/build/bin/compiler"
|
||||
out_dir="$(pwd)/test/test_result/asm"
|
||||
@@ -63,6 +70,7 @@ for test_file in $test_files; do
|
||||
test_name=$(basename "$test_file")
|
||||
stem=${test_name%.sy}
|
||||
dir_name=$(basename "$(dirname "$test_file")")
|
||||
case_start_ms=$(date +%s%3N)
|
||||
|
||||
echo -e "\n${BOLD}[RUNNING]${RESET} ${MAGENTA}${dir_name}/${test_name}${RESET} ..."
|
||||
|
||||
@@ -81,6 +89,7 @@ for test_file in $test_files; do
|
||||
echo -e "${RED}✗ 失败${RESET}"
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (Parsing)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -96,6 +105,7 @@ for test_file in $test_files; do
|
||||
echo -e "${RED}✗ 失败${RESET}"
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (IR/Optimizations)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -107,6 +117,7 @@ for test_file in $test_files; do
|
||||
echo -e "${RED}✗ 失败${RESET}"
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (Backend/Peephole)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -118,6 +129,7 @@ for test_file in $test_files; do
|
||||
echo -e "${RED}✗ 失败 (空文件)${RESET}"
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (Asm empty)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -129,6 +141,7 @@ for test_file in $test_files; do
|
||||
echo -e "${RED}✗ 失败 (链接错误)${RESET}"
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (Linking)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -150,6 +163,7 @@ for test_file in $test_files; do
|
||||
echo -e "${YELLOW}! 跳过比较 (性能测试运行超时)${RESET}"
|
||||
echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} ${test_name} 测试通过 (编译与部分执行已验证)!"
|
||||
((success_count++))
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
continue
|
||||
fi
|
||||
|
||||
@@ -174,6 +188,7 @@ for test_file in $test_files; do
|
||||
echo -e "${GREEN}✓ 匹配成功${RESET}"
|
||||
echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} ${test_name} 测试通过!"
|
||||
((success_count++))
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
else
|
||||
echo -e "${RED}✗ 匹配失败${RESET}"
|
||||
echo -e "${RED} [ERROR] 实际输出与期望不一致:${RESET}"
|
||||
@@ -183,10 +198,12 @@ for test_file in $test_files; do
|
||||
cat "$actual_file" | sed 's/^/ /'
|
||||
((failed_count++))
|
||||
failed_tests+=("${dir_name}/${test_name} (Output Mismatch)")
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}! 跳过比较 (未找到 .out 文件)${RESET}"
|
||||
((success_count++))
|
||||
echo -e " -> ${CYAN}Case elapsed: $(elapsed_seconds "$case_start_ms")s${RESET}"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
112
src/ir/passes/AlgebraicSimplify.cpp
Normal file
112
src/ir/passes/AlgebraicSimplify.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "ir/PassManager.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
namespace {
|
||||
|
||||
bool IsConstInt(Value* value, int expected) {
|
||||
auto* constant = dynamic_cast<ConstantInt*>(value);
|
||||
return constant && constant->GetValue() == expected;
|
||||
}
|
||||
|
||||
bool IsConstFloat(Value* value, float expected) {
|
||||
auto* constant = dynamic_cast<ConstantFloat*>(value);
|
||||
return constant && constant->GetValue() == expected;
|
||||
}
|
||||
|
||||
bool IsSameValue(Value* lhs, Value* rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
Value* SimplifyBinary(BinaryInst* bin, Context& ctx) {
|
||||
auto* lhs = bin->GetLhs();
|
||||
auto* rhs = bin->GetRhs();
|
||||
|
||||
switch (bin->GetOpcode()) {
|
||||
case Opcode::Add:
|
||||
if (IsConstInt(rhs, 0)) return lhs;
|
||||
if (IsConstInt(lhs, 0)) return rhs;
|
||||
break;
|
||||
case Opcode::Sub:
|
||||
if (IsConstInt(rhs, 0)) return lhs;
|
||||
if (IsSameValue(lhs, rhs)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
case Opcode::Mul:
|
||||
if (IsConstInt(rhs, 1)) return lhs;
|
||||
if (IsConstInt(lhs, 1)) return rhs;
|
||||
if (IsConstInt(rhs, 0) || IsConstInt(lhs, 0)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
case Opcode::Div:
|
||||
if (IsConstInt(rhs, 1)) return lhs;
|
||||
if (IsConstInt(lhs, 0)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
case Opcode::Mod:
|
||||
if (IsConstInt(rhs, 1) || IsConstInt(lhs, 0)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
case Opcode::FAdd:
|
||||
if (IsConstFloat(rhs, 0.0f)) return lhs;
|
||||
if (IsConstFloat(lhs, 0.0f)) return rhs;
|
||||
break;
|
||||
case Opcode::FSub:
|
||||
if (IsConstFloat(rhs, 0.0f)) return lhs;
|
||||
break;
|
||||
case Opcode::FMul:
|
||||
if (IsConstFloat(rhs, 1.0f)) return lhs;
|
||||
if (IsConstFloat(lhs, 1.0f)) return rhs;
|
||||
if (IsConstFloat(rhs, 0.0f) || IsConstFloat(lhs, 0.0f)) {
|
||||
return ctx.GetConstFloat(0.0f);
|
||||
}
|
||||
break;
|
||||
case Opcode::FDiv:
|
||||
if (IsConstFloat(rhs, 1.0f)) return lhs;
|
||||
break;
|
||||
case Opcode::ICmpEQ:
|
||||
if (IsSameValue(lhs, rhs)) return ctx.GetConstInt(1);
|
||||
break;
|
||||
case Opcode::ICmpNE:
|
||||
if (IsSameValue(lhs, rhs)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
case Opcode::ICmpLE:
|
||||
case Opcode::ICmpGE:
|
||||
if (IsSameValue(lhs, rhs)) return ctx.GetConstInt(1);
|
||||
break;
|
||||
case Opcode::ICmpLT:
|
||||
case Opcode::ICmpGT:
|
||||
if (IsSameValue(lhs, rhs)) return ctx.GetConstInt(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunAlgebraicSimplify(Function* func, Context& ctx) {
|
||||
bool changed = false;
|
||||
std::vector<Instruction*> to_erase;
|
||||
|
||||
for (const auto& bbPtr : func->GetBlocks()) {
|
||||
for (const auto& instPtr : bbPtr->GetInstructions()) {
|
||||
auto* bin = dynamic_cast<BinaryInst*>(instPtr.get());
|
||||
if (!bin) {
|
||||
continue;
|
||||
}
|
||||
if (auto* replacement = SimplifyBinary(bin, ctx)) {
|
||||
bin->ReplaceAllUsesWith(replacement);
|
||||
to_erase.push_back(bin);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* inst : to_erase) {
|
||||
inst->GetParent()->EraseInstruction(inst);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
@@ -3,6 +3,7 @@ add_library(ir_passes STATIC
|
||||
Mem2Reg.cpp
|
||||
ConstFold.cpp
|
||||
ConstProp.cpp
|
||||
AlgebraicSimplify.cpp
|
||||
CSE.cpp
|
||||
DCE.cpp
|
||||
CFGSimplify.cpp
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ir/PassManager.h"
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
@@ -56,10 +57,30 @@ bool RunCSE(Function* func) {
|
||||
|
||||
for (const auto& bbPtr : func->GetBlocks()) {
|
||||
std::vector<Instruction*> seen_instructions;
|
||||
std::unordered_map<Value*, Instruction*> available_loads;
|
||||
std::vector<Instruction*> to_erase;
|
||||
|
||||
for (const auto& instPtr : bbPtr->GetInstructions()) {
|
||||
auto* inst = instPtr.get();
|
||||
|
||||
if (inst->GetOpcode() == Opcode::Load) {
|
||||
auto* load = static_cast<LoadInst*>(inst);
|
||||
auto it = available_loads.find(load->GetPtr());
|
||||
if (it != available_loads.end()) {
|
||||
inst->ReplaceAllUsesWith(it->second);
|
||||
to_erase.push_back(inst);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
available_loads[load->GetPtr()] = inst;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst->GetOpcode() == Opcode::Store ||
|
||||
inst->GetOpcode() == Opcode::Call) {
|
||||
available_loads.clear();
|
||||
}
|
||||
|
||||
Instruction* match = nullptr;
|
||||
for (auto* seen : seen_instructions) {
|
||||
if (IsEquivalent(inst, seen)) {
|
||||
|
||||
@@ -16,6 +16,7 @@ void RunFunctionOptimizationPasses(Function* func, Context& ctx) {
|
||||
|
||||
changed |= RunConstProp(func, ctx);
|
||||
changed |= RunConstFold(func, ctx);
|
||||
changed |= RunAlgebraicSimplify(func, ctx);
|
||||
changed |= RunCSE(func);
|
||||
changed |= RunLICM(func);
|
||||
changed |= RunDCE(func);
|
||||
|
||||
@@ -25,7 +25,7 @@ bool IsFloatReg(PhysReg reg) {
|
||||
}
|
||||
|
||||
void PrintStackAccess(std::ostream& os, const char* mnemonic, PhysReg reg,
|
||||
int offset) {
|
||||
int offset, int frame_size) {
|
||||
bool is_float = IsFloatReg(reg);
|
||||
const char* ldr_cmd = is_float ? "ldr" : "ldr";
|
||||
const char* str_cmd = is_float ? "str" : "str";
|
||||
@@ -37,10 +37,24 @@ void PrintStackAccess(std::ostream& os, const char* mnemonic, PhysReg reg,
|
||||
} else {
|
||||
os << " " << mnemonic << " " << PhysRegName(reg) << ", [x29, #" << offset << "]\n";
|
||||
}
|
||||
} else {
|
||||
int sp_offset = frame_size + offset;
|
||||
int access_size = 4;
|
||||
if ((reg >= PhysReg::X0 && reg <= PhysReg::X28) ||
|
||||
reg == PhysReg::X29 || reg == PhysReg::X30 ||
|
||||
reg == PhysReg::SP) {
|
||||
access_size = 8;
|
||||
}
|
||||
int max_offset = access_size == 8 ? 32760 : 16380;
|
||||
if (sp_offset >= 0 && sp_offset <= max_offset &&
|
||||
sp_offset % access_size == 0) {
|
||||
os << " " << base_mnemonic << " " << PhysRegName(reg)
|
||||
<< ", [sp, #" << sp_offset << "]\n";
|
||||
} else {
|
||||
os << " ldr x10, =" << offset << "\n";
|
||||
os << " " << base_mnemonic << " " << PhysRegName(reg) << ", [x29, x10]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetBlockLabel(const std::string& func_name, const std::string& block_name) {
|
||||
@@ -125,12 +139,14 @@ void PrintAsm(const MachineFunction& function, std::ostream& os) {
|
||||
}
|
||||
case Opcode::LoadStack: {
|
||||
const auto& slot = GetFrameSlot(function, ops.at(1));
|
||||
PrintStackAccess(os, "ldur", ops.at(0).GetReg(), slot.offset);
|
||||
PrintStackAccess(os, "ldur", ops.at(0).GetReg(), slot.offset,
|
||||
function.GetFrameSize());
|
||||
break;
|
||||
}
|
||||
case Opcode::StoreStack: {
|
||||
const auto& slot = GetFrameSlot(function, ops.at(1));
|
||||
PrintStackAccess(os, "stur", ops.at(0).GetReg(), slot.offset);
|
||||
PrintStackAccess(os, "stur", ops.at(0).GetReg(), slot.offset,
|
||||
function.GetFrameSize());
|
||||
break;
|
||||
}
|
||||
case Opcode::AddRR:
|
||||
@@ -250,6 +266,11 @@ void PrintAsm(const MachineFunction& function, std::ostream& os) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Opcode::LslImm:
|
||||
os << " lsl " << PhysRegName(ops.at(0).GetReg()) << ", "
|
||||
<< PhysRegName(ops.at(1).GetReg()) << ", #"
|
||||
<< ops.at(2).GetImm() << "\n";
|
||||
break;
|
||||
case Opcode::LdrRegReg: {
|
||||
PhysReg reg = ops.at(0).GetReg();
|
||||
const char* ldr_cmd = IsFloatReg(reg) ? "ldr" : "ldr";
|
||||
|
||||
@@ -33,6 +33,21 @@ void RunFrameLowering(MachineFunction& function) {
|
||||
auto& blocks = function.GetBlocks();
|
||||
if (blocks.empty()) return;
|
||||
|
||||
bool has_call = false;
|
||||
for (const auto& block : blocks) {
|
||||
for (const auto& inst : block.GetInstructions()) {
|
||||
if (inst.GetOpcode() == Opcode::Call) {
|
||||
has_call = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_call) break;
|
||||
}
|
||||
|
||||
if (function.GetFrameSize() == 0 && !has_call) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert Prologue at the start of the first block
|
||||
auto& entry_insts = blocks.front().GetInstructions();
|
||||
entry_insts.insert(entry_insts.begin(), MachineInstr(Opcode::Prologue));
|
||||
|
||||
@@ -111,6 +111,19 @@ uint32_t GetAllocaSize(const ir::Instruction& inst, const std::unordered_set<con
|
||||
return GetTypeSize(type.get());
|
||||
}
|
||||
|
||||
bool IsPowerOfTwo(uint32_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
int Log2(uint32_t value) {
|
||||
int shift = 0;
|
||||
while (value > 1) {
|
||||
value >>= 1;
|
||||
shift++;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> GetGepStrides(const ir::GetElementPtrInst& gep) {
|
||||
std::vector<uint32_t> strides;
|
||||
auto curr_type = gep.GetPtr()->GetType();
|
||||
@@ -259,6 +272,61 @@ void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
|
||||
int dst_slot = function.CreateFrameIndex(4);
|
||||
slots.emplace(&inst, dst_slot);
|
||||
|
||||
if (inst.GetOpcode() == ir::Opcode::Add) {
|
||||
const ir::Value* variable = nullptr;
|
||||
int constant = 0;
|
||||
if (auto* lhs_const = dynamic_cast<const ir::ConstantInt*>(bin.GetLhs())) {
|
||||
variable = bin.GetRhs();
|
||||
constant = lhs_const->GetValue();
|
||||
} else if (auto* rhs_const = dynamic_cast<const ir::ConstantInt*>(bin.GetRhs())) {
|
||||
variable = bin.GetLhs();
|
||||
constant = rhs_const->GetValue();
|
||||
}
|
||||
if (variable && constant >= -4095 && constant <= 4095) {
|
||||
EmitValueToReg(variable, PhysReg::W8, slots, block);
|
||||
block.Append(Opcode::AddRegImm, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Imm(constant)});
|
||||
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
|
||||
return;
|
||||
}
|
||||
} else if (inst.GetOpcode() == ir::Opcode::Sub) {
|
||||
if (auto* rhs_const = dynamic_cast<const ir::ConstantInt*>(bin.GetRhs())) {
|
||||
int constant = rhs_const->GetValue();
|
||||
if (constant >= -4095 && constant <= 4095) {
|
||||
EmitValueToReg(bin.GetLhs(), PhysReg::W8, slots, block);
|
||||
block.Append(Opcode::AddRegImm, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Imm(-constant)});
|
||||
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inst.GetOpcode() == ir::Opcode::Mul) {
|
||||
const ir::Value* variable = nullptr;
|
||||
int constant = 0;
|
||||
if (auto* lhs_const = dynamic_cast<const ir::ConstantInt*>(bin.GetLhs())) {
|
||||
variable = bin.GetRhs();
|
||||
constant = lhs_const->GetValue();
|
||||
} else if (auto* rhs_const = dynamic_cast<const ir::ConstantInt*>(bin.GetRhs())) {
|
||||
variable = bin.GetLhs();
|
||||
constant = rhs_const->GetValue();
|
||||
}
|
||||
|
||||
if (variable && constant > 1) {
|
||||
EmitValueToReg(variable, PhysReg::W8, slots, block);
|
||||
if (IsPowerOfTwo(static_cast<uint32_t>(constant))) {
|
||||
block.Append(Opcode::LslImm, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W8), Operand::Imm(Log2(static_cast<uint32_t>(constant)))});
|
||||
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
|
||||
return;
|
||||
}
|
||||
if (IsPowerOfTwo(static_cast<uint32_t>(constant - 1))) {
|
||||
block.Append(Opcode::LslImm, {Operand::Reg(PhysReg::W9), Operand::Reg(PhysReg::W8), Operand::Imm(Log2(static_cast<uint32_t>(constant - 1)))});
|
||||
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::W8), Operand::Reg(PhysReg::W9), Operand::Reg(PhysReg::W8)});
|
||||
block.Append(Opcode::StoreStack, {Operand::Reg(PhysReg::W8), Operand::FrameIndex(dst_slot)});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EmitValueToReg(bin.GetLhs(), PhysReg::W8, slots, block);
|
||||
EmitValueToReg(bin.GetRhs(), PhysReg::W9, slots, block);
|
||||
|
||||
@@ -530,13 +598,20 @@ void LowerInstruction(const ir::Instruction& inst, MachineFunction& function,
|
||||
}
|
||||
|
||||
EmitValueToReg(idx, PhysReg::W9, slots, block);
|
||||
if (stride > 1) {
|
||||
bool shifted = false;
|
||||
if (stride > 1 && IsPowerOfTwo(stride)) {
|
||||
block.Append(Opcode::ZExt, {Operand::Reg(PhysReg::X9), Operand::Reg(PhysReg::W9)});
|
||||
block.Append(Opcode::LslImm, {Operand::Reg(PhysReg::X9), Operand::Reg(PhysReg::X9), Operand::Imm(Log2(stride))});
|
||||
shifted = true;
|
||||
} else if (stride > 1) {
|
||||
block.Append(Opcode::MovImm, {Operand::Reg(PhysReg::W10), Operand::Imm(stride)});
|
||||
block.Append(Opcode::MulRR, {Operand::Reg(PhysReg::W9), Operand::Reg(PhysReg::W9), Operand::Reg(PhysReg::W10)});
|
||||
}
|
||||
|
||||
// Extend W9 to X9 and add to base address X8
|
||||
if (!shifted) {
|
||||
block.Append(Opcode::ZExt, {Operand::Reg(PhysReg::X9), Operand::Reg(PhysReg::W9)});
|
||||
}
|
||||
block.Append(Opcode::AddRR, {Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X8), Operand::Reg(PhysReg::X9)});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ namespace mir {
|
||||
|
||||
namespace {
|
||||
|
||||
int AlignTo(int value, int align) {
|
||||
return ((value + align - 1) / align) * align;
|
||||
}
|
||||
|
||||
PhysReg NormalizeReg(PhysReg reg) {
|
||||
int r = static_cast<int>(reg);
|
||||
// Map 64-bit X0-X28 registers to 32-bit W0-W28 registers to handle aliasing
|
||||
@@ -46,11 +50,85 @@ bool IsFloatReg(PhysReg reg) {
|
||||
return reg >= PhysReg::S0 && reg <= PhysReg::S15;
|
||||
}
|
||||
|
||||
bool SameReg(const Operand& lhs, const Operand& rhs) {
|
||||
return lhs.GetKind() == Operand::Kind::Reg &&
|
||||
rhs.GetKind() == Operand::Kind::Reg &&
|
||||
NormalizeReg(lhs.GetReg()) == NormalizeReg(rhs.GetReg());
|
||||
}
|
||||
|
||||
bool IsZeroImm(const Operand& operand) {
|
||||
return operand.GetKind() == Operand::Kind::Imm && operand.GetImm() == 0;
|
||||
}
|
||||
|
||||
std::vector<MachineInstr> SimplifyCompareToBranch(
|
||||
const std::vector<MachineInstr>& insts) {
|
||||
std::vector<MachineInstr> simplified;
|
||||
|
||||
for (size_t i = 0; i < insts.size();) {
|
||||
if (i + 4 < insts.size() &&
|
||||
(insts[i].GetOpcode() == Opcode::CmpRR ||
|
||||
insts[i].GetOpcode() == Opcode::FCmpRR) &&
|
||||
insts[i + 1].GetOpcode() == Opcode::Cset &&
|
||||
insts[i + 2].GetOpcode() == Opcode::MovImm &&
|
||||
insts[i + 3].GetOpcode() == Opcode::CmpRR &&
|
||||
insts[i + 4].GetOpcode() == Opcode::BCond) {
|
||||
const auto& cset_ops = insts[i + 1].GetOperands();
|
||||
const auto& mov_ops = insts[i + 2].GetOperands();
|
||||
const auto& cmp2_ops = insts[i + 3].GetOperands();
|
||||
const auto& br_ops = insts[i + 4].GetOperands();
|
||||
|
||||
if (mov_ops.size() == 2 && cmp2_ops.size() == 2 && br_ops.size() == 2 &&
|
||||
SameReg(cset_ops.at(0), cmp2_ops.at(0)) &&
|
||||
SameReg(mov_ops.at(0), cmp2_ops.at(1)) &&
|
||||
IsZeroImm(mov_ops.at(1)) &&
|
||||
br_ops.at(0).GetKind() == Operand::Kind::Cond &&
|
||||
br_ops.at(0).GetCondCode() == "ne") {
|
||||
simplified.push_back(insts[i]);
|
||||
simplified.emplace_back(Opcode::BCond,
|
||||
std::vector<Operand>{
|
||||
Operand::Cond(cset_ops.at(1).GetCondCode()),
|
||||
br_ops.at(1)});
|
||||
i += 5;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
simplified.push_back(insts[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
return simplified;
|
||||
}
|
||||
|
||||
void CompactFrameSlots(MachineFunction& function) {
|
||||
std::unordered_set<int> used_slots;
|
||||
for (const auto& block : function.GetBlocks()) {
|
||||
for (const auto& inst : block.GetInstructions()) {
|
||||
for (const auto& opnd : inst.GetOperands()) {
|
||||
if (opnd.GetKind() == Operand::Kind::FrameIndex) {
|
||||
used_slots.insert(opnd.GetFrameIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cursor = 0;
|
||||
for (const auto& slot : function.GetFrameSlots()) {
|
||||
if (used_slots.find(slot.index) == used_slots.end()) {
|
||||
continue;
|
||||
}
|
||||
cursor += slot.size;
|
||||
function.GetFrameSlot(slot.index).offset = -cursor;
|
||||
}
|
||||
function.SetFrameSize(AlignTo(cursor, 16));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RunPeephole(MachineFunction& function) {
|
||||
for (auto& block : function.GetBlocks()) {
|
||||
auto& insts = block.GetInstructions();
|
||||
insts = SimplifyCompareToBranch(insts);
|
||||
std::vector<MachineInstr> optimized;
|
||||
|
||||
// Map from FrameIndex to the normalized physical register that currently holds its value
|
||||
@@ -150,6 +228,7 @@ void RunPeephole(MachineFunction& function) {
|
||||
case Opcode::MovReg:
|
||||
case Opcode::Adrp:
|
||||
case Opcode::AddRegImm:
|
||||
case Opcode::LslImm:
|
||||
case Opcode::LdrRegReg:
|
||||
case Opcode::SIToFP:
|
||||
case Opcode::FPToSI:
|
||||
@@ -233,6 +312,8 @@ void RunPeephole(MachineFunction& function) {
|
||||
}
|
||||
insts = std::move(optimized);
|
||||
}
|
||||
|
||||
CompactFrameSlots(function);
|
||||
}
|
||||
|
||||
} // namespace mir
|
||||
|
||||
Reference in New Issue
Block a user