xs-gem5: Frontend
本文分析对应的 xs-gem5 版本只考虑 commit
103c503233
xs-gem5 前端总体流程是一个“预测生产”和“取指消费”解耦的前端控制流系统:
- predictor 侧从
s0PC与 speculative histories 出发,启动一轮 block 级预测 - 多个 predictor component 并行工作,并按各自的 latency 把结果写入不同
stage - 晚到但更准确的 stage 可以覆盖早到的 stage
- 最终结果被封装成一个
FetchTarget FetchTarget进入FTQ- fetch 再从
FTQ头部取出该 target,并在其覆盖的 stream 内顺序取指 - 当真实执行结果返回后,前端通过
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.hhsrc/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:
UBTBAheadBTBMicroTAGEMBTBBTBTAGERASBTBITTAGEMGSC
这一顺序在 src/cpu/pred/btb/decoupled_bpred.cc 中可见
这个顺序会影响:
componentIdx的分配FetchTarget.predMetas中 metadata 的布局- tracing 与 profiling 的 source 编号
每线程 predictor state
每个 thread 维护一组 predictor 侧的 speculative state,核心字段包括:
s0PCpredsOfEachStages0Historys0PHistorys0BwHistorys0LHistoryfinalPrednumOverrideBubblesvalidpredictionsquashingblockPredictionPending
这些状态属于 predictor 侧,而不是 fetch queue 侧。
核心数据结构
1. FullBTBPrediction
定义在 src/cpu/pred/btb/common.hh
它表示“一次 block 级预测的聚合结果”,包含:
bbStart:预测 block 的起始 PCbtbEntries:按程序顺序排列的控制流候选condTakens:条件分支的 direction 结果indirectTargets:间接跳转的目标地址结果returnTarget:由RAS提供的返回目标tageInfoForMgscs:供MGSC使用的 TAGE provider/confidence 信息predSource:最终结果来自哪个 stageoverrideReason:为什么晚到 stage 覆盖了早到 stages1Source、s3Source: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
FetchTarget 是 FTQ 中真正流动的单元。它既是:
- fetch 阶段使用的 stream descriptor
- 也是 predictor 更新时使用的 checkpoint
它包含几类信息:
预测态:
startPCpredTakenpredEndPCpredBranchInfopredBTBEntries
执行/解析态:
exeTakenexeBranchInforesolvedsquashTypesquashPC
更新训练态:
updateNewBTBEntryupdateIsOldEntryupdateEndInstPCupdateBTBEntries
预测时快照:
predMetashistoryphistorybwhistorylhistorypreviousPCspredTickpredSourceoverrideReason
所以 FetchTarget 是连接以下三件事的桥梁:
- predictor-time speculation
- fetch-time consumption
- resolve/commit-time training
3. FetchTargetQueue
定义在 src/cpu/pred/btb/ftq.hh
关键语义如下:
front():队头,最老的 targetfetching():当前正被 fetch 消费的 targetfinishTarget():fetch 完成对一个 target 的消费commitTarget():commit 退休一个 targetsquashAfter():在squash后删掉错误路径上的后续 target
FTQ 是 predictor 与 fetch 之间最关键的边界。
流水线顶层流程
1. Fetch 侧处理
在 Fetch::tick() 中,顺序是:
- 先处理 signal、redirect、
squash - 再调用
dbpbtb->tick()推进 predictor 侧 - 最后 fetch 从
FTQ消费 target 并真正取指
所以 predictor 生产 FetchTarget 的动作,总是在 fetch 当拍消费前完成。
2. DecoupledBPUWithBTB::tick()
从行为上看,它每拍做四件事:
- 处理
squash留下的 predictor 内部清理逻辑 - 如果当前 thread 没有
validprediction且FTQ未满,则发起新的 prediction - 尝试把已经形成的 prediction 封装成
FetchTarget - 如果有
override bubble,则消耗一个 bubble
这说明 predictor 侧本身就是一个小流水,而不是 fetch 的一个即时子函数。
预测流程
1. 发起预测:requestNewPrediction
入口函数: DecoupledBPUWithBTB::requestNewPrediction
核心行为是:
- 初始化
predsOfEachStage[i]的tid与bbStart - 对每个 component 调用:
putPCHistory(s0PC, s0History, predsOfEachStage) - 让各 component 根据自己的 latency 把结果写入不同 stage
- 调用
generateFinalPredAndCreateBubbles() - 设置
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
流程是:
- 遍历所有 stage 的
FullBTBPrediction - 从最后一级往前找,选出最晚且有效的预测作为
finalPred - 用
FullBTBPrediction::match()比较前面 stage 是否与最终结果一致 - 找到第一个与最终结果一致的 stage
- 根据这个 stage 位置计算
numOverrideBubbles
含义很直接:
stage 0是最快但通常最粗糙的预测- 晚到的 stage 更准确
- 如果后面的 stage 覆盖了前面的结果,就要插入
override bubble
4. 等待 override bubble
在 processNewPrediction() 中,如果 numOverrideBubbles > 0,则不允许立即把 finalPred 入 FTQ
而是每拍把 bubble 计数减一,直到 bubble 消耗完,才允许入队
硬件语义:
- 晚到的精确预测可以纠正早到的粗糙预测
- 纠正需要前端为 pipeline consistency 付出 bubble
5. 构造 FetchTarget
入口函数:DecoupledBPUWithBTB::createFetchTargetEntry
它会:
- 从
finalPred中提取 block 级信息 - 决定是否 hit、是否 predict taken
- 把
finalPred.btbEntries和选中的predBranchInfo写入FetchTarget - 保存当前所有 histories
- 保存所有 component 的
predMeta - 记录
predSource与overrideReason - 初始化默认 execute 结果,使其与 prediction 一致
6. 进行 speculative history update
入口函数:DecoupledBPUWithBTB::updateHistoryForPrediction
- 对每个 component 调用
specUpdateHist(...) - 如果该 component 声明
needMoreHistories,则继续调用:specUpdatePHist(...)specUpdateBwHist(...)specUpdateIHist(...)specUpdateLHist(...)
- 从
finalPred中提取:- global history update 信息
- backward history update 信息
- path history update 信息
- 更新:
s0Historys0BwHistorys0PHistorys0LHistory
- 向
historyManager记录一条 speculative history
作用是:
- 下一次 stream prediction 发生时,可以看见当前这次 prediction 的结果
- 即便真实执行尚未返回,predictor 也已经开始沿着 speculative path 前进
7. 入 FTQ
在 processNewPrediction() 中,如果:
validprediction == true- 没有待消耗的
override bubble FTQ没满
那么 predictor 会:
- 把
FetchTarget插入FTQ - 把
s0PC更新为finalPred.getTarget(predictWidth) - 清掉当前这份
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
- 取出当前
ftqFetchingTarget(tid)作为stream - 若当前 instruction PC 恰好等于
stream.predBranchInfo.pc且predTaken为真,则跳到预测目标 - 否则就在
stream.predEndPC以内顺序取指 - 如果整个 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():
- 保存 folded history snapshot 到
meta - 从
getDelay()开始遍历stagePreds - 读取对应 stage 中已有的
btbEntries - 对 conditional entry 调用
lookupHelper(...) - 把方向结果写入
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 historypath historybackward historylocal history
在当前实现中,它们对应:
s0Historys0PHistorys0BwHistorys0LHistory
为什么要有多类 history?
不同 predictor 对上下文的需求不同:
TAGE依赖 branch outcome history 与 folded path informationITTAGE需要 target-sensitive contextMGSC会融合多类 historypath history可以区分 outcome 类似但执行路径不同的情形
speculative update 的原则
一旦一个 stream 被预测并进入 FTQ,这些 history 就会立即被推测更新,而不是等到执行之后再统一更新
目的是:后续 stream prediction 可以看到前面 prediction 的影响
recovery 的原则
如果后续证明 speculative path 错了,那么系统通过:
- 保存下来的
predMeta - 保存下来的 histories
- 真正执行得到的 outcome
把 predictor-side state 恢复到正确路径上
squash 与恢复流程
系统主要处理三类 squash:
control squashnon-control squashtrap squash
公共逻辑收敛在 handleSquash(...) 中
squash
流程是:
- 标记 thread 进入
squashing状态 - 在
FTQ中找到被squash的对应FetchTarget - 把真实执行结果写入这个 target
- 调用
ftq.squashAfter(target_id)删掉错误路径上的后续 target - 调用
recoverHistoryForSquash(...)恢复各类 history - 清空临时 stage prediction
- 把
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 恢复逻辑
resolve 与 commit 阶段的训练
训练分两类:
resolve-time updatecommit-time update
resolve-time update
某些 component 支持在 branch 解析时提前更新。
相关路径包括:
prepareResolveUpdateEntriesresolveUpdatecanResolveUpdatedoResolveUpdate
这里使用了 two-phase check:
- 先检查所有支持
resolved update的 component 是否都 ready - 全部 ready 后,再统一执行更新
这样可以避免某些 component 先更新、另一些 component 因冲突没更新,导致状态不一致
commit-time update
对普通 component 而言,训练发生在 commit(target_id, tid) 中。
对每个退休的 FetchTarget,系统会:
- 更新统计信息
- 计算
updateEndInstPC - 生成
updateBTBEntries - 如有需要,让
MBTB构造新 entry - 对所有
!getResolvedUpdate()的 component 调用update(target)
因此可以把 FetchTarget 看成整条 frontend 的统一训练包。
1 | Time ------> |
如果只看 BTBTAGE 的位置,可以压缩成下面这张图:
1 | cycle N: |