“若异常到达退休队首,先取消全部未退休指令及其尚未发布的完成记录,再令 $S=C$,把C像集之外的物理位置全部回收。顺序提交状态仍在原位置,无需把年轻值逐项反向算回去。取消结果记录必须先于编号…”
一条年轻加法算好了,不代表程序已经走到了它。前面还有慢乘法,或者一条尚未报告的故障指令。重排序缓冲记录“算到哪了”和“允许承诺到哪了”这两个进度,只有后者改变软件可见的寄存器与内存。
形式陈述
缓冲器按年龄排,结果可以乱序回来
精确异常要求故障点前的较老指令都生效,故障指令及较年轻指令都不留下架构数据效果。重排序缓冲(ROB)提供一种实现方法,并不是这种恢复接口唯一可能的硬件结构。[1]
设ROB容量
本页组合物理版本与标签就绪执行:计算结果先写物理目的,DONE仅表示结果或错误记录已经到齐。程序可观察的寄存器值始终是 (地址,值),尚未改变架构内存。
队首退休的三种情形
每周期退休阶段只查看队首,至多处理一项:
| 队首状态 | 行为 |
|---|---|
| 不是DONE | 不退休;后端仍可继续计算 |
| DONE且无错误 | 提交本条效果,取出队首 |
| DONE且有错误 | 报告该PC/原因,保留旧提交前缀,取消本条及全部年轻项 |
无错误的寄存器写令
异常恢复顺序为:取消执行单元内的全部未发布记录,清空ROB,把推测映射设为C,回收C像集之外的位置。该异常指令的目标版本也被取消,不能把fault标记误当“可以退休但跳过报错”。[2]
本页采用有限、无分支、精确整数的共同教学ISA,详细运算定义见重命名页。它的DIV除零有异常;不声称这就是RISC-V除法规则。STORE的立即槽地址已经合法,不会在退休后再发生访问错误;若设备写、地址转换或持久化还能失败,必须扩充接口。
直觉
两条进度线
后端可以先算I2,再算I0;ROB却不能先把I2从队列中拿走。这像先完成了后面的草稿,但尚不能把它算作已确认的记录。等I0正常结束,退休位置才能向前移。
“顺序退休”也不等于“所有较年轻工作都要等较老工作结束才开始”。年轻结果可以在物理版本中暂存,还能继续供其他年轻指令使用。被限制的是不可撤销的架构效果,尤其是STORE。如果内存已经被写坏,仅把寄存器映射恢复并不能撤销那次写。
例子与边界
周期5发现故障,周期10才交给软件
初始寄存器 [0,2,3,4,0,9]、内存槽0为31,11个物理位置、ROB容量8,执行:
I0: MUL r1,r2,r3
I1: DIV r4,r2,r0
I2: CONST r1,99
I3: STORE [0],r3
I4: ADD r5,r2,r3
I0应得到12;I1除数为0,因此正确异常前缀只包括I0。后面的99、STORE值4、r5值7都不应对架构可见。
按共同周期规则,I1在周期5发布除零错误,I0仍在乘法单元运行。此时不立即清空机器,否则会丢掉应完成的I0。I2在周期6把99写进p8,I3在周期7准备好STORE数据4;这两项都只在推测状态里。
| 观察点 | ROB中关键状态 | 架构r1 | 架构r5 | 内存槽0 |
|---|---|---|---|---|
| 周期5后 | I0 RUN;I1 DONE+fault | 2 | 9 | 31 |
| 周期7后 | I2 DONE=99;I3 DONE=4 | 2 | 9 | 31 |
| 周期8后 | I0 DONE=12 | 2 | 9 | 31 |
| 周期9后 | I0已退休;I1到队首;I4 DONE=7 | 12 | 9 | 31 |
| 周期10后 | 报告PC=1,取消I1–I4 | 12 | 9 | 31 |
注意周期8发布12之后,架构r1仍是2,因为当周期退休阶段已经过去。周期9才把C的r1项由p1改成p6。周期10正式报告异常时,寄存器为 [0,12,3,4,0,9],内存仍31。
恢复后两映射都为 [p0,p6,p2,p3,p4,p5],空闲集合为 {p1,p7,p8,p9,p10}。C不曾指向保存99的p8,也不曾指向保存7的p9,因此这些值算出来过仍不影响软件看到的前缀。
三种看似省事的错误
谁先DONE就退休谁。 正常片段的I2在周期5已DONE,若马上令r1=7,而较老I0稍后又提交12,最终r1会停在旧版本。即使重新安排寄存器覆盖顺序,也仍需处理更老异常,不能用“最后一次写回获胜”代替程序年龄。
首次收到错误就清空。 异常片段周期5若立即报告,已提交r1还是2,正确故障前缀却要求I0的12。故障的发现时刻与允许报告时刻不同。多条指令都出错时,同理只允许最老应到达的故障控制前缀,不能按错误消息先后来选择。
STORE一算好就写内存。 周期7若把4写入槽0,周期10清空ROB也不能把它自动变回31。参考器实际修改这一处后跑到异常,得到错误内存4;寄存器恢复成功并不足以挽救精确性。
清空队列不等于取消将来的结果
另取 DIV r0,r1,r2; MUL r1,r1,r1,初值 [8,2,0],乘法延迟30。DIV在周期4发布除零,周期5便到队首报告;年轻MUL已于周期3发射,本来要到周期33才允许发布。
正确恢复必须删掉这条未发布完成记录。参考器恢复后执行单元表与ROB都为空,架构值仍 [8,2,0]。只把ROB清空、还让周期33的旧结果继续写原标签,可能在编号重新分配后破坏另一条指令。若真实外设或执行单元不能取消,需用代次标识丢弃迟到结果等额外机制;本页不把“清空列表”推广成外部取消保证。
推论与应用
精确前缀证明
归纳不变量是:已提交寄存器和内存等于顺序解释器执行前k条正常指令后的状态,且这k条恰是已经退休的连续前缀。
初始k=0成立。重命名、发射和结果发布只改变推测位置、执行记录或DONE/fault,既不改C,也不改内存,所以保持架构状态不变量。
无错误队首退休时,它就是第k条未提交指令。源标签与就绪证明保证其结果等于顺序机器在当前前缀后的执行结果。若写寄存器,把C中该名字改指正确新版本;若是STORE,用已经捕获的正确值更新正确槽。其他架构位置不变,所以得到k+1前缀。
若队首有错,较老前缀已全部退休;本条没有执行架构写,年轻项也从未越过它退休。取消推测状态不改变C与内存,因此保留的恰是故障前的正确前缀。这里同时使用了三个接口:重命名保证输入来自对的版本,就绪规则保证拿到真的结果,队首退休保证只有正确前缀被承诺。
容量回收与实现费用
ROB满时只停止接收新指令,不停止发布和退休。正常项离开队首释放ROB位置;寄存器写还释放一个旧物理版本。以一个额外物理位置、一个ROB项运行时,机器可以退化为逐条完成,不需要为了腾空间先破坏架构顺序。
参考器用deque存ROB,队首出队为常数;动态执行选择另扫描至多Q项。正常单项退休只改常数个映射/集合或一个内存槽,以整数和散列表操作为单位计费;异常清理与空闲重建为
机器本体需要
完成、退休、可见与持久并非一回事
本教学STORE在退休一步原子更新抽象内存,所以内存可见性与本页退休对应。真实处理器可先把store标为已提交,再由存储缓冲排出;处理器间何时观察到它由内存模型规定,写入何时持久又取决于存储系统。ROB的精确异常证明不单独提供多核顺序或断电持久化保证。
同样,本文没有分支预测恢复、推测load、地址别名检查、缓存副作用或瞬态执行安全保证。软件看见正确前缀不代表内部没有产生其他可观测的微架构痕迹。把范围限定清楚,才能将本页的退休证明接到更完整机器,而不是把“有ROB”当作所有推测问题的答案。
参考资料
- James E. Smith、Andrew R. Pleszkun,Implementing Precise Interrupts in Pipelined Processors,IEEE Transactions on Computers 37(5),1988,pp.562–573;§III-B及§IV-A–D,印刷565–567页:延迟store、按年龄重排、错误与PC归属和结果提前旁路。本文没有借用其中CRAY工作负载的性能数字。
- Berkeley Out-of-Order Machine,The Reorder Buffer and the Dispatch Stage,官方实现文档,访问于2026-10-10;Commit Stage、Exceptions and Flushes、Rollback versus Single-cycle Reset。用于队首提交和恢复已提交映射的实现依据,本文只实现单条退休、完整本地取消和整数槽内存。