本文分析对应的 xs-gem5 版本只考虑 commit 103c503233

xs-gem5 前端总体流程是一个“预测生产”和“取指消费”解耦的前端控制流系统:

  1. predictor 侧从 s0PC 与 speculative histories 出发,启动一轮 block 级预测
  2. 多个 predictor component 并行工作,并按各自的 latency 把结果写入不同 stage
  3. 晚到但更准确的 stage 可以覆盖早到的 stage
  4. 最终结果被封装成一个 FetchTarget
  5. FetchTarget 进入 FTQ
  6. fetch 再从 FTQ 头部取出该 target,并在其覆盖的 stream 内顺序取指
  7. 当真实执行结果返回后,前端通过 squash / resolve / commit 完成恢复与训练

BTBTAGE 不是一个 per-instruction、同步返回结果的 direction predictor,而是 decoupled pipeline 中的一个带 stage-delay 的 direction component。当前默认配置里,它的 numDelay = 2,因此预测结果从 stage 2 开始可见


顶层架构

顶层对象是 DecoupledBPUWithBTB,定义在:

  • src/cpu/pred/btb/decoupled_bpred.hh
  • src/cpu/pred/btb/decoupled_bpred.cc

职责包括:

  • 持有并管理各个 predictor component
  • 维护 predictor 侧的 speculative frontend state
  • 生成分 stage 的预测结果
  • 从多个 stage 中挑选 final prediction
  • 构造 FetchTarget
  • 维护 FTQ
  • squash 时恢复 histories
  • resolve / commit 时协调各 component 的训练

主要 component

启用的 component 会按如下顺序加入 components

  1. UBTB
  2. AheadBTB
  3. MicroTAGE
  4. MBTB
  5. BTBTAGE
  6. RAS
  7. BTBITTAGE
  8. MGSC

这一顺序在 src/cpu/pred/btb/decoupled_bpred.cc 中可见

这个顺序会影响:

  • componentIdx 的分配
  • FetchTarget.predMetas 中 metadata 的布局
  • tracing 与 profiling 的 source 编号

每线程 predictor state

每个 thread 维护一组 predictor 侧的 speculative state,核心字段包括:

  • s0PC
  • predsOfEachStage
  • s0History
  • s0PHistory
  • s0BwHistory
  • s0LHistory
  • finalPred
  • numOverrideBubbles
  • validprediction
  • squashing
  • blockPredictionPending

这些状态属于 predictor 侧,而不是 fetch queue 侧。


核心数据结构

1. FullBTBPrediction

定义在 src/cpu/pred/btb/common.hh

它表示“一次 block 级预测的聚合结果”,包含:

  • bbStart:预测 block 的起始 PC
  • btbEntries:按程序顺序排列的控制流候选
  • condTakens:条件分支的 direction 结果
  • indirectTargets:间接跳转的目标地址结果
  • returnTarget:由 RAS 提供的返回目标
  • tageInfoForMgscs:供 MGSC 使用的 TAGE provider/confidence 信息
  • predSource:最终结果来自哪个 stage
  • overrideReason:为什么晚到 stage 覆盖了早到 stage
  • s1Sources3Source:profiling 使用的 source 标记

其中最关键的 helper method 有:

  • getTakenEntry()
  • isTaken()
  • getTarget(predictWidth)
  • getFallThrough(predictWidth)
  • getEnd(predictWidth)
  • match(other, predictWidth)
  • getHistInfo()
  • getBwHistInfo()
  • getPHistInfo()

它们分别用于:

  • 计算最终 next PC
  • 比较不同 stage 是否一致
  • 从预测结果中提取 speculative history update 所需的信息

2. FetchTarget

定义在 src/cpu/pred/btb/common.hh

FetchTargetFTQ 中真正流动的单元。它既是:

  • fetch 阶段使用的 stream descriptor
  • 也是 predictor 更新时使用的 checkpoint

它包含几类信息:

预测态:

  • startPC
  • predTaken
  • predEndPC
  • predBranchInfo
  • predBTBEntries

执行/解析态:

  • exeTaken
  • exeBranchInfo
  • resolved
  • squashType
  • squashPC

更新训练态:

  • updateNewBTBEntry
  • updateIsOldEntry
  • updateEndInstPC
  • updateBTBEntries

预测时快照:

  • predMetas
  • history
  • phistory
  • bwhistory
  • lhistory
  • previousPCs
  • predTick
  • predSource
  • overrideReason

所以 FetchTarget 是连接以下三件事的桥梁:

  • predictor-time speculation
  • fetch-time consumption
  • resolve/commit-time training

3. FetchTargetQueue

定义在 src/cpu/pred/btb/ftq.hh

关键语义如下:

  • front():队头,最老的 target
  • fetching():当前正被 fetch 消费的 target
  • finishTarget():fetch 完成对一个 target 的消费
  • commitTarget():commit 退休一个 target
  • squashAfter():在 squash 后删掉错误路径上的后续 target

FTQ 是 predictor 与 fetch 之间最关键的边界。


流水线顶层流程

1. Fetch 侧处理

Fetch::tick() 中,顺序是:

  1. 先处理 signal、redirect、squash
  2. 再调用 dbpbtb->tick() 推进 predictor 侧
  3. 最后 fetch 从 FTQ 消费 target 并真正取指

所以 predictor 生产 FetchTarget 的动作,总是在 fetch 当拍消费前完成。

2. DecoupledBPUWithBTB::tick()

从行为上看,它每拍做四件事:

  1. 处理 squash 留下的 predictor 内部清理逻辑
  2. 如果当前 thread 没有 validpredictionFTQ 未满,则发起新的 prediction
  3. 尝试把已经形成的 prediction 封装成 FetchTarget
  4. 如果有 override bubble,则消耗一个 bubble

这说明 predictor 侧本身就是一个小流水,而不是 fetch 的一个即时子函数。


预测流程

1. 发起预测:requestNewPrediction

入口函数: DecoupledBPUWithBTB::requestNewPrediction

核心行为是:

  1. 初始化 predsOfEachStage[i]tidbbStart
  2. 对每个 component 调用:
    putPCHistory(s0PC, s0History, predsOfEachStage)
  3. 让各 component 根据自己的 latency 把结果写入不同 stage
  4. 调用 generateFinalPredAndCreateBubbles()
  5. 设置 validprediction = true

因此输入是: 一份 predictor-side checkpoint:s0PC + histories
输出是:一组按 stage 划分的候选预测结果

2. component 写入 stage

所有 BTB frontend predictor component 都继承自 TimedBaseBTBPredictor

这个基类提供的关键接口是:

  • putPCHistory(...)
  • getPredictionMeta()
  • specUpdateHist(...)
  • recoverHist(...)
  • update(...)
  • getDelay()

最重要的时序语义是:

  • 一个 predictor 的 latency 通过 getDelay() 建模
  • predictor 只会从 stagePreds[getDelay()] 起写入结果

3. 选出 final prediction 并计算 override bubble

入口函数:DecoupledBPUWithBTB::generateFinalPredAndCreateBubbles

流程是:

  1. 遍历所有 stage 的 FullBTBPrediction
  2. 从最后一级往前找,选出最晚且有效的预测作为 finalPred
  3. FullBTBPrediction::match() 比较前面 stage 是否与最终结果一致
  4. 找到第一个与最终结果一致的 stage
  5. 根据这个 stage 位置计算 numOverrideBubbles

含义很直接:

  • stage 0 是最快但通常最粗糙的预测
  • 晚到的 stage 更准确
  • 如果后面的 stage 覆盖了前面的结果,就要插入 override bubble

4. 等待 override bubble

processNewPrediction() 中,如果 numOverrideBubbles > 0,则不允许立即把 finalPredFTQ

而是每拍把 bubble 计数减一,直到 bubble 消耗完,才允许入队

硬件语义:

  • 晚到的精确预测可以纠正早到的粗糙预测
  • 纠正需要前端为 pipeline consistency 付出 bubble

5. 构造 FetchTarget

入口函数:DecoupledBPUWithBTB::createFetchTargetEntry

它会:

  1. finalPred 中提取 block 级信息
  2. 决定是否 hit、是否 predict taken
  3. finalPred.btbEntries 和选中的 predBranchInfo 写入 FetchTarget
  4. 保存当前所有 histories
  5. 保存所有 component 的 predMeta
  6. 记录 predSourceoverrideReason
  7. 初始化默认 execute 结果,使其与 prediction 一致

6. 进行 speculative history update

入口函数:DecoupledBPUWithBTB::updateHistoryForPrediction

  1. 对每个 component 调用 specUpdateHist(...)
  2. 如果该 component 声明 needMoreHistories,则继续调用:
    • specUpdatePHist(...)
    • specUpdateBwHist(...)
    • specUpdateIHist(...)
    • specUpdateLHist(...)
  3. finalPred 中提取:
    • global history update 信息
    • backward history update 信息
    • path history update 信息
  4. 更新:
    • s0History
    • s0BwHistory
    • s0PHistory
    • s0LHistory
  5. historyManager 记录一条 speculative history

作用是:

  • 下一次 stream prediction 发生时,可以看见当前这次 prediction 的结果
  • 即便真实执行尚未返回,predictor 也已经开始沿着 speculative path 前进

7. 入 FTQ

processNewPrediction() 中,如果:

  • validprediction == true
  • 没有待消耗的 override bubble
  • FTQ 没满

那么 predictor 会:

  1. FetchTarget 插入 FTQ
  2. s0PC 更新为 finalPred.getTarget(predictWidth)
  3. 清掉当前这份 validprediction

Fetch 侧消费 FTQ

Fetch 的前提:必须有有效 FTQ Head Entry

入口函数:Fetch::checkDecoupledFrontend

如果 dbpbtb->ftqHasFetching(tid) 为假,fetch 不会去查询 predictor,而是直接停住,并记一个 FTQBubble

这体现了 decoupled frontend 与传统 coupled predictor 的根本差异:

  • 传统路径是 fetch 直接调用 predictor
  • 当前路径是 fetch 等待 FTQ 中已经准备好的 stream

lookupAndUpdateNextPC() 的 decoupled 语义

入口函数:Fetch::lookupAndUpdateNextPC

  1. 取出当前 ftqFetchingTarget(tid) 作为 stream
  2. 若当前 instruction PC 恰好等于 stream.predBranchInfo.pcpredTaken 为真,则跳到预测目标
  3. 否则就在 stream.predEndPC 以内顺序取指
  4. 如果整个 stream 被消费完,则调用 consumeFetchTarget()

所以 fetch 看到的不是“原始 predictor 输出”,而是已经聚合完成的 stream-level prediction


各 predictor component 的职责与 stage 分工

UBTB

UBTB 是前端中最快的 control-flow predictor。核心任务是:

  • 尽快给出一个早期 block 命中结果
  • 尽快给出一个粗粒度的 taken/not-taken stream 轮廓
  • 让 fetch 不至于因为等待深层 predictor 而长期空转

UBTB 对应的是最早一级 stage 0

它提供:

  • 最早的 btbEntries
  • 最早的 stream 边界感知
  • direct control-flow 的粗略目标

优点:

  • latency 最低
  • 对前端吞吐最有帮助

局限:

  • 精度较低
  • history 使用能力有限
  • 很容易被后续 stage 覆盖

设计定位: “最早给答案”的 component,不是“最终最准确”的 component


MBTB

MBTB 是主力 block target structure。核心任务是:

  • 查出当前 block 中可能出现的控制流 entry
  • 提供排序后的 BTBEntry 列表
  • 对 direct branch / jump 给出目标地址
  • 为后续 direction predictor 提供 branch candidate 集合

它通常处于 stage 1

它提供:

  • btbEntries
  • direct target
  • block 内控制流骨架

MBTB 负责“有哪些 control-flow entry”,而 BTBTAGE 负责“这些 conditional entry 里谁 taken”

设计定位: 决定 block 的控制流骨架,TAGE 在这个骨架上做方向精化


BTBTAGE

BTBTAGE 是 BTB frontend 中的主条件分支 direction predictor。任务是:

  • 对 block 中每个 conditional BTBEntry 预测 taken / not taken

默认配置中:numDelay = 2
即它从 stagePreds[2] 开始写入结果

BTBTAGE::putPCHistory()

  1. 保存 folded history snapshot 到 meta
  2. getDelay() 开始遍历 stagePreds
  3. 读取对应 stage 中已有的 btbEntries
  4. 对 conditional entry 调用 lookupHelper(...)
  5. 把方向结果写入 stage_pred.condTakens

它提供:

  • 高精度 conditional direction
  • provider / alternate-provider 信息
  • MGSC 使用的 confidence 信息

由于 BTBTAGE 是晚到的 predictor,它很容易推翻 UBTB 或更早 stage 的结果。
因此,系统必须实现:

  • generateFinalPredAndCreateBubbles()
  • override bubble

设计定位: 晚到但高精度


BTBITTAGE

BTBITTAGE 负责 indirect branch 的 target prediction。它解决的问题是:

  • BTB 可以知道这里是一个 indirect branch
  • 但真正的动态 target 需要借助 history 才能预测

它也是一个高延迟、晚到的 predictor,通常与 BTBTAGE 同属后级 refinement 层

它提供: indirectTargets

当最终 taken entry 是 indirect branch 且不是 return 时,FullBTBPrediction::getTarget() 会优先使用它提供的结果

设计定位: BTBITTAGE 是 late-stage 的 indirect target specialist


BTBMGSC

MGSC 是 statistical corrector 层。它建立在 TAGE 输出之上的校正层,用 richer history features 去修正基础 decision。

逻辑上属于最后一级 refinement,只有在足够多的 history 与 TAGE 信息已经准备好之后,它才能发挥作用。

它消费:

  • TAGE prediction
  • TAGE confidence
  • global / path / local / backward histories

它产出:

  • 校正后的 conditional direction
  • 详细 profiling / trace 信息

设计定位: 最后一层 correction policy


BTBRAS

RAS 负责 return target prediction。

当最终选中的 taken entry 是 return 时,系统使用:returnTarget, 而不是 BTB target

它是一个专门服务于 return path 的独立 component

它提供:

  • return address prediction
  • squash 恢复时使用的栈顶 metadata

return 的行为与:

  • direct target lookup
  • conditional direction prediction
  • indirect target prediction
    都不相同,因此必须由单独的 structure 处理。

设计定位: 专用 return-target 子路径


History 系统

predictor 维护了多种 history:

  • global history
  • path history
  • backward history
  • local history

在当前实现中,它们对应:

  • s0History
  • s0PHistory
  • s0BwHistory
  • s0LHistory

为什么要有多类 history?

不同 predictor 对上下文的需求不同:

  • TAGE 依赖 branch outcome history 与 folded path information
  • ITTAGE 需要 target-sensitive context
  • MGSC 会融合多类 history
  • path history 可以区分 outcome 类似但执行路径不同的情形

speculative update 的原则

一旦一个 stream 被预测并进入 FTQ,这些 history 就会立即被推测更新,而不是等到执行之后再统一更新

目的是:后续 stream prediction 可以看到前面 prediction 的影响

recovery 的原则

如果后续证明 speculative path 错了,那么系统通过:

  • 保存下来的 predMeta
  • 保存下来的 histories
  • 真正执行得到的 outcome

把 predictor-side state 恢复到正确路径上


squash 与恢复流程

系统主要处理三类 squash

  • control squash
  • non-control squash
  • trap squash

公共逻辑收敛在 handleSquash(...)

squash

流程是:

  1. 标记 thread 进入 squashing 状态
  2. FTQ 中找到被 squash 的对应 FetchTarget
  3. 把真实执行结果写入这个 target
  4. 调用 ftq.squashAfter(target_id) 删掉错误路径上的后续 target
  5. 调用 recoverHistoryForSquash(...) 恢复各类 history
  6. 清空临时 stage prediction
  7. s0PC 改成正确 redirect PC

predMeta

每个 FetchTarget 都带着:predMetas[i]

也就是各 predictor component 在 prediction 时刻留下的 metadata snapshot。

这些 snapshot 的作用是:

  • 恢复 folded history
  • 恢复 predictor 内部 checkpoint
  • 保证错误路径被回滚后,新的 prediction 从正确上下文重新开始

Decode squash 与 Commit squash

decode squash

  • 更早发现 branch misprediction
  • 更快把 fetch 拉回正确路径

commit squash

  • 更晚,但更接近 architectural truth

二者最终都会收敛到同一套 predictor 恢复逻辑


resolvecommit 阶段的训练

训练分两类:

  • resolve-time update
  • commit-time update

resolve-time update

某些 component 支持在 branch 解析时提前更新。

相关路径包括:

  • prepareResolveUpdateEntries
  • resolveUpdate
  • canResolveUpdate
  • doResolveUpdate

这里使用了 two-phase check:

  1. 先检查所有支持 resolved update 的 component 是否都 ready
  2. 全部 ready 后,再统一执行更新

这样可以避免某些 component 先更新、另一些 component 因冲突没更新,导致状态不一致

commit-time update

对普通 component 而言,训练发生在 commit(target_id, tid) 中。

对每个退休的 FetchTarget,系统会:

  1. 更新统计信息
  2. 计算 updateEndInstPC
  3. 生成 updateBTBEntries
  4. 如有需要,让 MBTB 构造新 entry
  5. 对所有 !getResolvedUpdate() 的 component 调用 update(target)

因此可以把 FetchTarget 看成整条 frontend 的统一训练包。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
Time ------>

Predictor side
==============

cycle N
s0PC + speculative histories
|
v
requestNewPrediction()
|
+--> UBTB / zero-delay helpers --------------------------> stage 0
|
+--> MBTB / mid-latency structures ----------------------> stage 1
|
+--> BTBTAGE / BTBITTAGE / MGSC / late predictors -------> stage 2
|
v
generateFinalPredAndCreateBubbles()
|
+--> compare stage 0/1/2 predictions
+--> choose finalPred
+--> compute override bubbles
|
v
processNewPrediction()
|
+--> createFetchTargetEntry()
+--> speculative history update
+--> enqueue FetchTarget into FTQ
|
v
FTQ tail <- new FetchTarget


Fetch side
==========

cycle N+k
Fetch::tick()
|
+--> checkDecoupledFrontend()
| |
| +--> if FTQ has no fetching entry: stall with FTQBubble
|
+--> ftqFetchingTarget()
|
+--> lookupAndUpdateNextPC()
| |
| +--> if current PC == predBranchInfo.pc and predTaken
| | redirect to predicted target
| |
| +--> else
| sequential fetch within predEndPC
|
+--> consumeFetchTarget() when stream is exhausted
|
v
fetched instructions -> Decode


Feedback side
=============

Decode / Resolve / Commit
|
+--> branch mispredict discovered early
| |
| +--> controlSquash()
|
+--> non-control redirect / trap
| |
| +--> nonControlSquash() / trapSquash()
|
+--> resolve-time update if supported
| |
| +--> prepareResolveUpdateEntries()
| +--> resolveUpdate()
|
+--> commit(target_id)
|
+--> updateStatistics()
+--> updatePredictorComponents()
+--> FTQ front retires


History / Recovery relationship
===============================

prediction time:
finalPred --> speculative history update --> next s0PC / next prediction

wrong path:
squash --> recoverHistoryForSquash() --> reset predictor-side state

correct path:
resolve / commit --> update(target) --> train predictor tables

如果只看 BTBTAGE 的位置,可以压缩成下面这张图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cycle N:
s0PC/history -> putPCHistory()
-> stage 0 : UBTB
-> stage 1 : MBTB
-> stage 2 : BTBTAGE / BTBITTAGE / MGSC

cycle N+0..N+1:
may wait for override bubbles

cycle N+2 or later:
finalPred -> FetchTarget -> FTQ

later fetch cycles:
Fetch consumes FTQ head

later decode/commit cycles:
squash / resolve / commit feed back real outcome