寄存器重命名(Register Renaming)—— 消除假依赖,为保留站挖出 ILP
承 cpu-out-of-order 的乱序执行五大部件(见 ooo-renaming-retirement §一 的寄存器重命名初步分析)。寄存器重命名是乱序执行真正能挖出并行度的前提——没有它,保留站里大量 µop 明明操作数都齐了,却被"名字冲突"卡住发不出。它是"小寄存器集→大物理寄存器池"的映射引擎。
一、重命名解决什么问题
x86 只有 16 个通用寄存器(32 位模式 8 个,64 位模式 16 个)。但程序写了三行都用同一个 rax,并不意味着这三条指令真的互相依赖——很多时候只是"寄存器不够用,复用一下名字"。
① add rax, rbx ; rax ← rbx + 旧rax 真依赖(读旧rax的值)
② mov [mem], rax ; 读rax 真依赖(读①的结果)
③ add rax, rcx ; rax ← rcx + 旧rax?? 假依赖!③ 的 rax 跟① 的 rax 是两码事——③ 没必要等① 算完,它只需要 rcx 的值,rax 只是碰巧复用了同一个名字。但因为都叫 rax,在寄存器层面看起来像② 写完 rax 之后③ 才能写(WAW 假依赖),③ 读取 rax 要等② 写完(WAR 假依赖)。
没有重命名的情况下,③ 被这两层假依赖卡住,不能发射。而③ 身边可能有几十条跟它无关的指令——保留站里堆满"就绪但不能发"的 µop。
三种依赖中:
- RAW(真依赖,读后写):② 必须等① 的结果 → 不可消除,硬边界。保留站里的 µop 必须等 tag 匹配才发射。
- WAW(假依赖,写后写):③ 和① 都写
rax,但③ 的结果与① 无关 → 重命名消除:各分配不同物理寄存器。 - WAR(假依赖,读后写):③ 写
rax前,② 必须先读完rax→ 重命名消除:③ 写的是 PR52,不影响② 读 PR37。
现代 x86 CPU 有 ~180(Zen 4)到 ~280(Golden Cove)个整数物理寄存器,以及类似数量的浮点/SIMD 物理寄存器。16 个架构寄存器 × 6 个 µop/拍的发包速率,理论上不够一拍的发射量——重命名就是把"名字瓶颈"彻底打掉。
二、功能拆解
| 部件 | 做什么 | 物理形态 |
|---|---|---|
| RAT(寄存器别名表) | 记录"架构寄存器 → 最新物理寄存器"的当前映射 | SRAM 查找表,每条指令都得查 |
| 物理寄存器文件 | 存储所有物理寄存器的实际值 | 几百个物理寄存器条目,每个存 64 位值 |
| 空闲列表(Free List) | 记录当前未被分配的物理寄存器号 | 简单的 FIFO/计数器 |
三、运作机制:一条指令走完重命名的全流程
阶段 操作 RAT 状态变化
─────────────────────────────────────────────────────────────────
① 进入重命名 指令从译码级进入重命名级 查 RAT:rax→PR37, rcx→PR42
─────────────────────────────────────────────────────────────────
② 读源操作数 读取"最新的物理寄存器号" 源 PR = RAT[rax]=PR37, RAT[rcx]=PR42
src1 = PR37 (rax的值)
src2 = PR42 (rcx的值)
─────────────────────────────────────────────────────────────────
③ 分配目的寄存器 从空闲列表取一个新的物理寄存器 空闲列表弹出 PR58
目的操作数的值将写入 PR58
─────────────────────────────────────────────────────────────────
④ 更新 RAT 目的架构寄存器映射到新物理寄存器 RAT[rax] = PR58(覆盖旧映射)
旧映射 PR37 不立即释放
─────────────────────────────────────────────────────────────────
⑤ 保留旧映射 旧映射 PR37 记在 ROB 槽位上 ROB 记录:退休时释放 PR37
退休后归还空闲列表
─────────────────────────────────────────────────────────────────
⑥ 进入保留站 µop 携带源 PR tag + 目的 PR tag 保留站里监控 PR37 的就绪状态
进入保留站等待操作数就绪
─────────────────────────────────────────────────────────────────3.1 RAT 查找:每次重命名都要查
RAT 是一个快速 SRAM 表,16 个架构寄存器号 → 各自的"最新物理寄存器号"。每条指令译码后,它的源架构寄存器号 → 查 RAT → 得到物理寄存器号(源 tag)。每拍 4~6 条 µop 可能需要查 8~10 个源寄存器,RAT 有多读端口支持并行查找。
3.2 旧映射的延迟回收:为什么必须等"退休"
③ 更新 RAT 后,旧映射 PR37 不能立即归还空闲列表——因为可能还有以前的指令在引用 PR37。比如:
① add rax, rbx → rax 映射到 PR37
② mov rcx, rax → 源操作数引用 PR37,此刻 RAT[rax]=PR37
③ add rax, rdx → rax 映射更新为 PR52(覆盖 RAT[rax])③ 更新 RAT 时,② 还在流水线里、还没退休,② 必须读 PR37 的值。如果 PR37 被立即回收并被④ 分配出去、写入了新值,② 就会读到错误的数据。
所以旧映射的回收必须延迟到② 退休之后——这是 ROB 的职责:退休时,ROB 把该指令的旧物理寄存器号归还空闲列表。
3.3 分支误预测时的 RAT 恢复
分支指令后的所有指令都可能基于错误预测发射。当分支在 ROB 退休时发现预测错了,不仅 ROB 后面的投机指令要清空,RAT 也要恢复到分支指令之前的状态。
现代 CPU 用 checkpoint(检查点) 机制:每次遇到分支时,拍一个 RAT 的快照。误预测后,恢复到该快照——不需要逐条回滚,O(1) 恢复。
四、限制与瓶颈
| 限制 | 说明 | 表现 |
|---|---|---|
| 物理寄存器耗尽 | 空闲列表空了,新指令分配不到目的物理寄存器,前端停摆 | 程序里大量使用不同寄存器(编译器过度展开循环、大量变量生命期重叠) |
| RAT 查表端口 | 每拍 4~6 µop 需要同时查 8~10 个源寄存器的映射 | 很少作为瓶颈,RAT 多端口设计能匹配前端带宽 |
| 重命名宽度 | 每拍最多重命名 4~6 条 µop(与译码宽度一致),因为分配物理寄存器、更新 RAT 都需要 1 拍内完成 | 前端带宽就是上限,硬件已匹配 |
| 分支误预测后的 RAT 恢复 | checkpoint 数量有限,嵌套分支过深时无法全部保存快照 | 回退到更早的 checkpoint 或重新从零填充 |
五、和周边部件的关系
译码 ──→ 寄存器重命名 ──→ 保留站 ──→ 执行单元 ──→ ROB ──→ 退休
│ ↑ │
└── 分配 PR ───┘(监控这PR的就绪状态) └── 释放旧 PR- 保留站:重命名后 µop 携带源/目的物理寄存器 tag,进入保留站监控 tag 就绪状态。没有重命名,保留站里一组 µop 被假依赖卡死。详见 reservation-station。
- ROB:退休时释放该指令"覆盖了的旧物理寄存器",归还空闲列表。分支机构还需要 ROB 触发 RAT checkpoint 恢复。详见 rob。
- 分支预测:每次分支生成一个 RAT checkpoint,误预测后用于快照恢复。详见 branch-prediction。
- 数据冒险:重命名消除的是 WAR/WAW 假依赖;RAW 真依赖不可消除,需要转发网络和乱序执行来掩盖。详见 data-hazards。
- 纵切面走读:uop-pipeline-walkthrough——重命名在完整流水线中的位置与 RAT 检查点恢复的时序。
六、观测:重命名相关的 perf 事件
perf stat -e uops_issued.any,uops_retired.any,resource_stalls.any ./app
# uops_issued.any — 发射的 µop 总数
# uops_retired.any — 退休的 µop 总数重命名本身没有直接的 perf 计数器(它嵌在译码→发射的中间路径上),但可以通过间接指标判断:
- 大量
nop(mov rax, rax)指令 → 可能因为架构寄存器不够,编译器插入了无用的"改名"指令。重命名让这些 nop 变成零延迟的 register move elimination。 uops_retired/uops_issued比值低 → 大量 µop 发射了但没退休(投机路径错误被清空),RAT checkpoint 恢复频率高。
七、一句话总结
寄存器重命名是保留站的"前置放大器"——它把 16 个架构寄存器的名字瓶颈打掉,映射到几百个物理寄存器,让 WAR/WAW 假依赖消失,为保留站准备更多互不依赖的 µop。没有重命名,保留站里一半 µop 会因为名字冲突卡住,乱序执行基本废掉。RAT 的旧映射回收必须等 ROB 退休、分支误预测靠 checkpoint 机制快速恢复——这三个机制构成一个完整的"物理寄存器生命周期管理"闭环。