CPU只有16个整数寄存器(x86-64),但现代处理器内部有上百个物理寄存器。寄存器重命名技术把程序看到的"逻辑寄存器"动态映射到"物理寄存器",消除WAW和WAR假依赖,让指令乱序执行成为可能。
1. 重命名的核心思想
程序看到的寄存器是逻辑寄存器(Architectural Register),x86-64有16个。处理器内部有物理寄存器(Physical Register),现代CPU有200+个。
映射关系:
- 每条写逻辑寄存器的指令,分配一个新的物理寄存器
- 后续读该逻辑寄存器的指令,读取最新的物理寄存器
- 旧物理寄存器在不再被使用时释放
示例:
原始代码:
ADD R1, R2, R3 ; 写R1
MUL R4, R1, R5 ; 读R1
SUB R1, R6, R7 ; 写R1(WAW!)
DIV R8, R1, R9 ; 读R1
重命名后:
ADD P10, P2, P3 ; R1→P10
MUL P11, P10, P5 ; 读P10
SUB P12, P6, P7 ; R1→P12(新物理寄存器,消除WAW)
DIV P13, P12, P9 ; 读P12
现在ADD和SUB可以并行执行,因为它们写不同的物理寄存器。
2. 三种实现方式
2.1 ROB-based重命名(Intel P6)
Intel P6架构(Pentium Pro/II/III)使用重排序缓冲区(ROB)作为物理寄存器[1]:
- 推测结果存在ROB中
- 退休时复制到架构寄存器文件(ARF)
- 重命名映射表记录逻辑寄存器→ROB entry
问题:
- 每条指令都占ROB entry,即使没有目的寄存器(如分支)
- 源操作数可能来自ROB或ARF,需要双端口支持
- 退休时需要数据搬运(ROB→ARF),增加功耗
2.2 ARF扩展(Intel Netburst早期)
使用独立的物理寄存器文件(PRF)存储推测结果[2]:
- 只有有目的寄存器的指令才占PRF entry
- 退休时复制到ARF
- 比ROB方案节省空间
问题:逻辑寄存器的值仍可能存在于PRF和ARF两个地方,取数时需要判断。
2.3 统一PRF(现代主流)
Intel Sandy Bridge及以后、ARM Cortex-A73及以后采用统一物理寄存器文件[1][3]:
- 合并ARF和PRF,所有寄存器值都存在PRF
- 没有数据搬运,退休时只需更新映射关系
- 需要空闲列表(Free List)管理物理寄存器
- 需要两个重命名映射表(RAT):
- Front-end RAT:记录推测状态
- Retirement RAT:记录已提交状态
优势:
- 指令结果只写一次,降低功耗
- 源数据只在一个地方,简化取数逻辑
- 减少电路连线和逻辑门级数
ARM Cortex-A73的具体实现[3]:
- 整数寄存器文件:41个物理寄存器(64位宽)
- FP/向量寄存器文件:38个物理寄存器(128位宽)
- 分离设计降低端口数,节省面积和功耗
3. Tomasulo算法:重命名的经典实现
Tomasulo算法(IBM 360/91,1967年)首次实现了寄存器重命名[4][5]。
3.1 核心组件
保留站(Reservation Station):
- 每个功能单元有独立的保留站
- 缓存指令和操作数
- 操作数未就绪时,记录产生它的保留站标签(Tag)
寄存器重命名:
- 指令中的逻辑寄存器被替换为值或指向保留站的指针
- 消除WAR和WAW依赖
公共数据总线(CDB):
- 广播已完成指令的<值, 标签>
- 等待该标签的保留站捕获值
3.2 执行流程
-
发射(Issue):
- 从指令队列取指令
- 分配空闲保留站
- 重命名源寄存器:如果值已就绪,直接读入;否则记录标签
- 重命名目的寄存器:分配新标签
-
执行(Execute):
- 监视CDB,等待操作数就绪
- 所有操作数就绪后,送入功能单元执行
-
写回(Writeback):
- 结果通过CDB广播<值, 标签>
- 更新寄存器文件和等待的保留站
- 释放保留站
3.3 处理WAW和WAR
WAW示例:
DIV F0, F2, F4 ; F0 = F2/F4
SUB F0, F6, F8 ; F0 = F6-F8(WAW!)
Tomasulo处理:
- DIV发射到保留站Mult1,目的F0重命名为Mult1的标签
- SUB发射到保留站Add1,目的F0重命名为Add1的标签
- 后续读F0的指令会读到Add1的标签(最新映射)
- DIV的结果写入F0时,如果映射已更新到Add1,则不更新(避免覆盖)
WAR示例:
SUB F4, F0, F8 ; 读F0
DIV F0, F2, F4 ; 写F0(WAR!)
Tomasulo处理:
- SUB发射到Add1,源F0的值直接读入(或记录标签)
- DIV发射到Mult1,目的F0重命名为Mult1的标签
- SUB从Add1的Vj字段读F0,不受DIV写F0的影响
4. 物理寄存器的释放
关键问题:什么时候释放物理寄存器?
原理:当后续指令都使用新的映射时,旧物理寄存器可以释放。
实现[4]:
- 重命名时,记录目的寄存器的旧映射(Previous Mapping)
- 指令退休时,检查该旧映射是否还被后续指令使用
- 如果没有使用,放入Free List
示例:
指令a: ADD R1, R2, R3 ; R1→P1(旧映射:P0)
指令b: SUB R1, R4, R5 ; R1→P6(旧映射:P1)
当指令b退休时:
- 后续指令使用R1都读到P6
- P1不再被使用,可以释放
ROB在这里的作用是记录指令顺序,确保按程序顺序退休和释放寄存器。
5. 总结
| 依赖类型 | 是否可消除 | 消除方法 | 实现复杂度 |
|---|---|---|---|
| RAW | 否 | 转发/等待 | - |
| WAW | 是 | 重命名目的寄存器 | 中 |
| WAR | 是 | 重命名目的寄存器 | 中 |
寄存器重命名的核心价值:
- 消除假依赖,暴露更多指令级并行
- 支持乱序执行,提升IPC
- 动态映射,比静态编译优化更灵活
三种实现方式对比:
- ROB-based:设计简单,但数据搬运多,功耗高
- ARF扩展:节省PRF空间,但取数逻辑复杂
- 统一PRF:无数据搬运,能效最优,现代主流
理解寄存器重命名,就能理解为什么现代CPU能在只有16个架构寄存器的情况下,实现深度乱序执行和高性能。
参考
-
Chips and Cheese. Sandy Bridge: Setting Intel's Modern Foundation. P6 vs Sandy Bridge PRF. ↩ ↩
-
Chips and Cheese. Tracing Intel's Atom Journey: Goldmont Plus. PRF vs ROB. ↩
-
Chips and Cheese. Cortex A73's Not-So-Infinite Reordering Capacity. A73 PRF design. ↩ ↩
-
中国科学技术大学. 5-3 动态指令流调度II. Tomasulo算法详解. ↩