Skip to content

Commit b1f3409

Browse files
committed
update docs
1 parent e3167ea commit b1f3409

19 files changed

Lines changed: 104 additions & 225 deletions

backend/CodeGenerator.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
///
22
/// @file CodeGenerator.cpp
33
/// @brief 代码生成器共同类的实现
4-
/// @author zenglj (zenglj@live.com)
5-
/// @version 1.0
6-
/// @date 2024-11-21
7-
///
8-
/// @copyright Copyright (c) 2024
9-
///
10-
/// @par 修改日志:
11-
/// <table>
12-
/// <tr><th>Date <th>Version <th>Author <th>Description
13-
/// <tr><td>2024-11-21 <td>1.0 <td>zenglj <td>新做
14-
/// </table>
154
///
165
#include <cstdio>
176
#include <string>

backend/CodeGenerator.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
///
22
/// @file CodeGenerator.h
33
/// @brief 代码生成器共同类的头文件
4-
/// @author zenglj (zenglj@live.com)
5-
/// @version 1.0
6-
/// @date 2024-11-21
7-
///
8-
/// @copyright Copyright (c) 2024
9-
///
10-
/// @par 修改日志:
11-
/// <table>
12-
/// <tr><th>Date <th>Version <th>Author <th>Description
13-
/// <tr><td>2024-11-21 <td>1.0 <td>zenglj <td>新做
14-
/// </table>
154
///
165
#pragma once
176

backend/CodeGeneratorAsm.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
///
22
/// @file CodeGeneratorAsm.cpp
33
/// @brief 后端汇编代码生成器接口的实现
4-
/// @author zenglj (zenglj@live.com)
5-
/// @version 1.0
6-
/// @date 2024-11-21
7-
///
8-
/// @copyright Copyright (c) 2024
9-
///
10-
/// @par 修改日志:
11-
/// <table>
12-
/// <tr><th>Date <th>Version <th>Author <th>Description
13-
/// <tr><td>2024-11-21 <td>1.0 <td>zenglj <td>新做
14-
/// </table>
154
///
165
#include "CodeGenerator.h"
176
#include "CodeGeneratorAsm.h"

backend/CodeGeneratorAsm.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
///
22
/// @file CodeGeneratorAsm.h
33
/// @brief 后端汇编代码生成器接口的头文件
4-
/// @author zenglj (zenglj@live.com)
5-
/// @version 1.0
6-
/// @date 2024-11-21
7-
///
8-
/// @copyright Copyright (c) 2024
9-
///
10-
/// @par 修改日志:
11-
/// <table>
12-
/// <tr><th>Date <th>Version <th>Author <th>Description
13-
/// <tr><td>2024-11-21 <td>1.0 <td>zenglj <td>新做
14-
/// </table>
154
///
165
#include <cstdio>
176
#include <cstring>

docs/backend-abi-regalloc.md

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ a0-a7 是"可分配的 caller-saved 寄存器"。平时参与全局寄存器分
2121
2222
`GreedyRegAllocator::buildRegisterPool` 将 a0-a7 (x10-x17) 纳入可用寄存器池,与 t0-t6、s1-s11 一起参与全局分配。
2323
24-
**源码位置**: `backend/riscv64/GreedyRegAllocator.cpp:518-551`
24+
**源码位置**: `backend/riscv64/GreedyRegAllocator.cpp:524-556`
2525
2626
```cpp
2727
std::vector<int> GreedyRegAllocator::buildRegisterPool(Function * func) const
@@ -57,7 +57,7 @@ std::vector<int> GreedyRegAllocator::buildRegisterPool(Function * func) const
5757

5858
a0-a7 是 caller-saved,函数调用会破坏它们。分配器通过 `canAssignReg` 约束保证正确性。
5959

60-
**源码位置**: `backend/riscv64/GreedyRegAllocator.cpp:782-800`
60+
**源码位置**: `backend/riscv64/GreedyRegAllocator.cpp:785-803`
6161

6262
```cpp
6363
bool GreedyRegAllocator::canAssignReg(LiveInterval * interval, int reg) const
@@ -74,7 +74,7 @@ bool GreedyRegAllocator::canAssignReg(LiveInterval * interval, int reg) const
7474
}
7575
```
7676
77-
**caller-saved 判定**: `backend/riscv64/GreedyRegAllocator.cpp:709-712`
77+
**caller-saved 判定**: `backend/riscv64/GreedyRegAllocator.cpp:714-717`
7878
7979
```cpp
8080
bool GreedyRegAllocator::isCallerSavedReg(int reg)
@@ -102,7 +102,7 @@ for (auto & [inst, num] : instNumbering) {
102102

103103
形参在 ABI 上进入函数时确实在 a0-a7 / fa0-fa7,但 allocator 可能把形参分到任意寄存器(比如 s2、t1、甚至还是 a0),也可能 spill 到栈。所以指令选择一开始会调用 `emitFormalParamMoves()`
104104

105-
**调用位置**: `backend/riscv64/InstSelectorRiscV64.cpp:547-571`
105+
**调用位置**: `backend/riscv64/InstSelectorRiscV64.cpp:677-690`
106106

107107
```cpp
108108
void InstSelectorRiscV64::run()
@@ -115,9 +115,9 @@ void InstSelectorRiscV64::run()
115115

116116
### 3.1 emitFormalParamMoves 逻辑
117117

118-
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:2524-2663`
118+
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:2969-3108`
119119

120-
#### 整数形参处理 (line 2484-2506)
120+
#### 整数形参处理 (line 3015-3029)
121121

122122
```cpp
123123
if (loc.kind == AbiArgLocKind::IntReg) {
@@ -141,7 +141,7 @@ if (loc.kind == AbiArgLocKind::IntReg) {
141141
| 分配到其他寄存器 | 加入 regMoves | param0 在 a0 传入,分配到 s1 → `mv s1, a0` |
142142
| 分配到栈 | 直接 store | param0 在 a0 传入,分配到栈 → `sw a0, offset(s0)` |
143143

144-
#### 循环依赖打破 (line 2508-2541)
144+
#### 循环依赖打破 (`emitGprRegMoves`, line 2925-2962)
145145

146146
当出现 `a0→a1, a1→a0` 这种循环搬运时,用 scratch 寄存器打破:
147147

@@ -151,9 +151,9 @@ mv a0, a1 # 搬原 a1 → a0
151151
mv a1, scratch # 搬原 a0 → a1
152152
```
153153

154-
scratch 寄存器通过 `tempMgr.borrowExcluding` 借用,排除所有入参寄存器和目标寄存器 (line 2459-2482)。
154+
scratch 寄存器通过 `tempMgr.borrowExcluding` 借用,排除所有入参寄存器和目标寄存器 (line 2982-3005)。
155155

156-
#### 栈传参数 (line 2589-2607)
156+
#### 栈传参数 (line 3033-3052)
157157

158158
超过 8 个的参数由 caller 通过栈传递;当前无帧指针,按 `frameSize + 槽偏移(sp)` 读取(基址/偏移由 `incomingStackBaseReg()`/`incomingStackOffset()` 决定):
159159

@@ -169,7 +169,7 @@ if (info.hasReg()) {
169169
}
170170
```
171171

172-
#### 浮点形参 (line 2562-2614)
172+
#### 浮点形参 (line 3054-3086)
173173

174174
浮点形参从 fa0-fa7 传入,处理方式与整数类似,但额外支持:
175175
- 分配到 FPR: 浮点寄存器间移动 (含循环依赖打破,用 `fmv.x.w`/`fmv.w.x` 经 GPR 中转)
@@ -182,7 +182,7 @@ if (info.hasReg()) {
182182

183183
`translate_call` 仍然遵守 ABI:前 8 个整数实参加载到 a0-a7,浮点实参加载到 fa0-fa7,超出的存到栈,然后 call,返回值从 a0/fa0 取。
184184

185-
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:1949-2149`
185+
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:2336-2559`
186186

187187
```cpp
188188
// 整数实参放入 a0-a7
@@ -248,7 +248,7 @@ for (auto * param : params) {
248248

249249
## 6. 栈分配阶段对形参的处理
250250

251-
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:974-1007`
251+
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:657-690`
252252

253253
```cpp
254254
// 为所有形参创建分配信息
@@ -286,7 +286,7 @@ for (auto * param : func->getParams()) {
286286

287287
## 7. 栈帧布局
288288

289-
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:946-954` (注释), `line 1050` (计算)
289+
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:632-639` (注释), `line 739` (计算)
290290

291291
```
292292
高地址
@@ -308,7 +308,7 @@ for (auto * param : func->getParams()) {
308308
- 保存区下方较小的 `sp` 正偏移是本函数局部对象、`alloca` 和 spill 栈槽。
309309
- `0(sp)`, `8(sp)`, ... 是本函数调用别人时使用的 outgoing 参数区。
310310

311-
栈帧大小计算 (`line 1072`):
311+
栈帧大小计算 (`line 737-739`):
312312

313313
```cpp
314314
const int maxArgs = maxCallArgCount(func);
@@ -322,15 +322,16 @@ const int frameSize = alignTo(savedFrameBytes + localBytes + outgoingBytes, 16);
322322

323323
### Prologue
324324

325-
**源码位置**: `backend/riscv64/ILocRiscV64.cpp:727-786`
325+
**源码位置**: `backend/riscv64/ILocRiscV64.cpp:767-834`
326326

327327
```
328328
addi sp, sp, -framesize # 分配栈帧
329-
sd ra, offset(sp) # 保存 ra (若函数有调用)
329+
sd ra, offset(sp) # 保存 ra (若函数有调用且未做 ra shrink-wrapping)
330330
sd s1, offset(sp) # 保存被分配器使用的 callee-saved GPR
331331
... # (注意: s0/fp 当前不保存)
332332
fsd fs0, offset(sp) # 保存被使用的 callee-saved FPR (若启用)
333333
# addi s0, sp, framesize 仅在 usesFramePointer() 时生成 —— 当前不会
334+
# 若 shrinkWrapRA 为真,上面的 sd ra 跳过,改在调用点保存 (见 §8 callee-saved 保存策略)
334335
```
335336

336337
因此函数体内的栈寻址都基于 sp:
@@ -344,21 +345,23 @@ sd t1, 0(sp) # 第 9 个实参的值写入 outgoing 参数区
344345

345346
### Epilogue
346347

347-
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:2678-2717`
348+
**源码位置**: `backend/riscv64/InstSelectorRiscV64.cpp:3123-3170` (`emitEpilogue`)
348349

349350
逆序恢复 callee-saved 寄存器,恢复 SP,执行 `ret`
350351

351352
### Callee-saved 保存策略
352353

353-
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:149-187`
354+
**源码位置**: `backend/riscv64/CodeGeneratorRiscV64.cpp:155-217`
354355

355356
| 寄存器 | 保存条件 |
356357
|--------|---------|
357-
| ra (x1) | 函数内有调用指令时 |
358+
| ra (x1) | 函数内有调用指令时(条件性叶子默认走 ra shrink-wrapping,见下) |
358359
| s0/fp (x8) | 当前不保存(`requiresFramePointer()` 恒为 false) |
359360
| s1-s11 | 仅当被寄存器分配器实际使用时 |
360361
| fs0-fs11 | 被使用且启用 callee-saved FPR(默认开启)时 |
361362

363+
**ra shrink-wrapping(默认 `enableShrinkWrap = true`**:当函数有调用但并非所有路径都调用(条件性叶子,由 `ConditionalLeafAnalysis.cpp``analyzeCallPaths` 判定 `canOptimize`)时,ra 仍占栈槽但不在 prologue 保存,改由首个调用点附近的 `emitCallSiteSaveRA`/`emitCallSiteRestoreRA` 保存恢复,使纯叶子路径省去 `sd ra`/`ld ra``ILocRiscV64::allocStack``:795`)与 `emitEpilogue``:3141`)据 `shrinkWrapRA` 标志跳过 ra 的保存/恢复。
364+
362365
叶子函数(无调用、无栈传参数、无栈分配值)的 savedRegs 为空、frameSize 为 0,自然省略整个栈帧(代码中没有 `canOmitLeafFrame` 函数)。
363366

364367
---
@@ -435,8 +438,8 @@ a0-a7 同时承担 ABI 参数寄存器和全局分配寄存器两个角色,通
435438

436439
| 层次 | 机制 | 源码位置 |
437440
|------|------|---------|
438-
| 分配层 | a0-a7 纳入全局 GPR 池,任何值都可能分到 a0-a7 | `GreedyRegAllocator.cpp:530` |
439-
| 约束层 | 跨调用的值不能分到 a0-a7 (caller-saved),只能去 s1-s11 或栈 | `GreedyRegAllocator.cpp:796-799` |
440-
| 搬运层 | 函数入口 `emitFormalParamMoves` 将形参从 ABI 位置搬到分配位置;调用点 `translate_call` 将实参放回 ABI 位置 | `InstSelectorRiscV64.cpp:555, 1949` |
441+
| 分配层 | a0-a7 纳入全局 GPR 池,任何值都可能分到 a0-a7 | `GreedyRegAllocator.cpp:533` |
442+
| 约束层 | 跨调用的值不能分到 a0-a7 (caller-saved),只能去 s1-s11 或栈 | `GreedyRegAllocator.cpp:799-803` |
443+
| 搬运层 | 函数入口 `emitFormalParamMoves` 将形参从 ABI 位置搬到分配位置;调用点 `translate_call` 将实参放回 ABI 位置 | `InstSelectorRiscV64.cpp:685, 2336` |
441444

442445
**一句话**: a0-a7 在"两个 call 之间"的普通代码里当 caller-saved 临时寄存器用;在 ABI 边界(函数入口/调用/返回)由指令选择阶段插入搬运,保证 ABI 约定不被破坏。

docs/backend-liveness-analysis.md

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,29 @@
66

77
## 整体流程
88

9-
**源码位置**: `backend/riscv64/LiveIntervalAnalysis.cpp:217-531` (`computeLiveIntervals`)
9+
**源码位置**: `backend/riscv64/LiveIntervalAnalysis.cpp:217-552` (`computeLiveIntervals`)
1010

1111
```
1212
┌─────────────────────────────────────────────────────────────┐
13-
│ Phase 1: 指令编号 & 局部 def/use 收集 (line 257-280) │
13+
│ Phase 1: 指令编号 & 局部 def/use 收集 (line 257-283) │
1414
│ 按 func->getBlocks() 正序遍历,为每条指令编号 │
1515
├─────────────────────────────────────────────────────────────┤
1616
│ Phase 2: CFG 级活跃性传播 (line 285-311) │
1717
│ 反向数据流固定点迭代,计算 liveIn/liveOut │
1818
├─────────────────────────────────────────────────────────────┤
19-
│ Phase 3: 逐块反向扫描生成区间 (line 317-374) │
19+
│ Phase 3: 逐块反向扫描生成区间 (line 313-374) │
2020
│ 按基本块正序,块内指令逆序,构造 LiveInterval │
2121
├─────────────────────────────────────────────────────────────┤
22-
│ Phase 4: 循环多定义值保守扩展 (line 382-426) │
22+
│ 精确段快照 (line 376-390) │
23+
│ 保守扩展前快照每个值的精确 def-use 段,供 RegCoalescer 判干涉│
24+
├─────────────────────────────────────────────────────────────┤
25+
│ Phase 4: 循环多定义值保守扩展 (line 392-442) │
2326
│ PhiLowering 后同一 Value 多次定义的保守处理 │
2427
├─────────────────────────────────────────────────────────────┤
25-
│ Phase 5: 跨循环活跃性补足 (line 432-502) │
28+
│ Phase 5: 跨循环活跃性补足 (line 444-518) │
2629
│ 对跨越自然循环的值补足整个循环体区间 │
2730
├─────────────────────────────────────────────────────────────┤
28-
│ Phase 6: 溢出权重计算 (line 504-530) │
31+
│ Phase 6: 溢出权重计算 (line 520-545) │
2932
│ 基于循环深度加权 │
3033
└─────────────────────────────────────────────────────────────┘
3134
```
@@ -34,7 +37,7 @@
3437

3538
## Phase 1: 指令编号 & 局部 def/use 收集
3639

37-
**源码位置**: `LiveIntervalAnalysis.cpp:257-280`
40+
**源码位置**: `LiveIntervalAnalysis.cpp:257-283`
3841

3942
`func->getBlocks()` 的存储顺序(即基本块在函数中的排列顺序)正序遍历,为每条指令分配递增编号:
4043

@@ -165,7 +168,7 @@ while (changed) {
165168

166169
## Phase 3: 逐块反向扫描生成区间
167170

168-
**源码位置**: `LiveIntervalAnalysis.cpp:317-374`
171+
**源码位置**: `LiveIntervalAnalysis.cpp:313-374`
169172

170173
在 Phase 2 计算出 liveIn/liveOut 后,逐块构造 LiveInterval。
171174

@@ -231,7 +234,9 @@ for (auto * bb : blocks) {
231234

232235
## Phase 4: 循环多定义值保守扩展
233236

234-
**源码位置**: `LiveIntervalAnalysis.cpp:382-426`
237+
**源码位置**: `LiveIntervalAnalysis.cpp:392-442`
238+
239+
> 注:在本阶段之前(`line 376-390`)会先把每个值的精确 def-use 段快照到 `preciseSegments`,供 `RegCoalescer` 用精确干涉判断合并循环累加器一类 copy;保守扩展只是在此之上为 split/call-transfer 补的冗余覆盖。
235240
236241
PhiLowering 会把循环携带值改写成"同一个逻辑 Value 的多次定义"。当前 split lane 仍按 Value 的整体区间决定 call-site transfer,因此对这类值做保守扩展。
237242

@@ -249,7 +254,7 @@ if (firstPos != INT_MAX && lastPos != INT_MIN) {
249254

250255
## Phase 5: 跨循环活跃性补足
251256

252-
**源码位置**: `LiveIntervalAnalysis.cpp:432-502`
257+
**源码位置**: `LiveIntervalAnalysis.cpp:444-518`
253258

254259
对跨越自然循环的值,补足整个循环体区间,避免回边值在 split lane 被误判为可中途改位。
255260

@@ -270,7 +275,7 @@ if ((hasDefBeforeLoop && (usedInLoop || usedAtOrAfterLoop)) ||
270275

271276
## Phase 6: 溢出权重计算
272277

273-
**源码位置**: `LiveIntervalAnalysis.cpp:504-530`
278+
**源码位置**: `LiveIntervalAnalysis.cpp:520-545`
274279

275280
基于循环深度加权,循环越深溢出代价越高:
276281

@@ -292,7 +297,7 @@ for (auto * interval : intervals) {
292297

293298
## 干涉图构建
294299

295-
**源码位置**: `LiveIntervalAnalysis.cpp:538-565` (`buildInterferenceGraph`)
300+
**源码位置**: `LiveIntervalAnalysis.cpp:554-581` (`buildInterferenceGraph`)
296301

297302
在活跃区间计算完成后,根据区间重叠关系构建干涉图:
298303

docs/backend-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ flowchart TD
114114
115115
Greedy --> AdjustCall["adjustFuncCallInsts(func)<br>调整函数调用指令"]
116116
AdjustCall --> AdjustParam["adjustFormalParamInsts(func)<br>调整形参指令"]
117-
AdjustParam --> SavedRegs["computeSavedRegs(...)<br>计算callee-saved寄存器列表<br>(ra若有调用 / s1-s11按需; s0/fp 不保存)"]
117+
AdjustParam --> SavedRegs["computeSavedRegs(...)<br>计算callee-saved寄存器列表<br>(ra若有调用 / s1-s11按需; s0/fp 不保存)<br>条件性叶子默认对ra做shrink-wrapping(调用点保存)"]
118118
SavedRegs --> StackAlloc["stackAlloc(func)<br>栈空间分配"]
119119
120120
StackAlloc --> ParamLoop["遍历形参<br>按RISC-V ABI分配寄存器(a0-a7/fa0-fa7)<br>超出部分分配栈槽"]

0 commit comments

Comments
 (0)