提交与写回

什么是提交、什么是写回?在五级流水线的顺序单发射CPU中,似乎“提交”就是在“写回”级进行的。但实际上,这并不是一回事。

说文解字

  • 写回(writeback, WB):结果已经算出来了,准备写回某个存储体,最常见是写回寄存器堆
  • 提交(commit / retire):这条指令被体系结构正式承认“已经执行完成”,它的效果从这一刻起对软件可见,且不会再因为乱序、异常预测错误等原因被撤销

说白了,写回是“结果产生并回填”;提交是“结果正式生效”。

遵守顺序

对于顺序执行的CPU,写回就是ALU / Load / CSR 指令的结果,它们会写到通用寄存器堆xN里面。此时因为流水线是顺序执行、顺序完成,写回和提交几乎可以看成同时发生

但对于乱序CPU,或者是多发射的呢?写回通常不是直接写“架构寄存器”,而是写到ROB[1]或PRF[2]。在这种情况下,写回表示“计算结果已经准备好”,但这条指令还不一定已经提交

对于提交,重点不在“提”而在“交”,也就是说,结果交上去之后,一切就尘埃落定。

提交表示这条指令已经满足以下条件:

  1. 它之前的所有更老指令都已经处理完毕
  2. 它自己已经执行完成
  3. 没有更老的异常、错误预测、冲刷需要撤销它
  4. 它的架构效果现在正式生效

“架构效果”包括:

  • 整数寄存器更新
  • 浮点寄存器更新
  • CSR 修改
  • Store 对内存真正可见
  • PC 按正确程序顺序前进
  • 指令计数器、异常精确性得到保证

因此,提交本质上是保证 CPU 对外表现得像一条一条按程序顺序执行。

指令也有后悔药

如果“写回”就等于“最终生效”,那会出大问题,比如遇到了指令异常等,无法处理。

举个例子:

1
2
3
add x5, x1, x2
div x6, x3, x0 # 可能异常
add x7, x8, x9

乱序 CPU 里,第 3 条可能比第 2 条更早算完,甚至先写回到物理寄存器。但是如果第 2 条触发异常,那么第 3 条虽然写回过,却不能提交;最终它必须被撤销。这说明,写回不代表指令一定完成,提交才代表不可撤销。

严重的问题:乱序提交

ISA规范要求:指令必须按照顺序提交

我们回过去看一下 之前 的仿真波形:

连续执行的乘法运算

黄色区域对应的代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ========================================
# 30. Test MUL Data Hazards - RAW (Read After Write)
# ========================================
addi x5, x0, 7 # x5 = 7
addi x6, x0, 8 # x6 = 8
mul x7, x5, x6 # x7 = 7 * 8 = 56
mul x8, x7, x5 # RAW hazard: x8 = 56 * 7 = 392 (0x188)
mul x9, x8, x6 # RAW hazard: x9 = 392 * 8 = 3136 (0xC40)

# ========================================
# 31. Test MUL with ALU Instructions - Mixed Hazards
# ========================================
addi x10, x0, 3 # x10 = 3
addi x11, x0, 4 # x11 = 4
mul x12, x10, x11 # x12 = 3 * 4 = 12
addi x13, x12, 5 # RAW hazard: x13 = 12 + 5 = 17
mul x14, x13, x10 # RAW hazard: x14 = 17 * 3 = 51 (0x33)

发生了什么?

mul x9, x8, x6计算结果被提交之前,下一行addi x10, x0, 3便已经提交了,改变了ISA的寄存器对外可见状态!这样是错误的。

因此,我们只能给乘法器加上阻塞,即处理乘法时,后面的指令全部阻塞。


  1. ROB,Reorder Buffer,重排序缓冲区,里面每一项对应一条已经进入乱序执行流水线、但还没有正式提交的指令。

  2. PRF,Physical Register File,物理寄存器文件,在乱序核中真正存放寄存器值。