当前 O3 前端里的 FTQ 的定位是:

  1. BAC(Branch Address Calculation) 生成 FetchTarget 后,用于在 BACFetch 之间传递取指目标的队列。
  2. FetchTarget 的承载体,用来描述一段连续的 fetch 范围,以及该范围退出点的预测信息。
  3. 一个临时托管 BPU PredictorHistory 的结构,用于桥接“预测发生时刻”与“预译码拿到真实 branch/seqNum 时刻”之间的时间差。

当前实现中:

  • BACFTQ 的生产者。
  • FetchFTQ 的消费者。
  • BPU 是预测与恢复逻辑的真正拥有者。
  • FTQFetchTarget 和尚未正式入账的 PredictorHistory 的过渡驻留点。

与 FTQ 直接相关的文件是:

  • ftq.hh/cc:定义 FetchTargetFTQ
  • bac.hh/cc:负责生成 FetchTarget、把它放入 FTQ、在 fetch 阶段把历史迁回 BPU。
  • fetch.cc:负责消费 FTQ 头部的 FetchTarget

前端组织方式

BAC 被拆成两部分

BAC 的职责:

  1. Branch prediction / FTQ feeding 部分
    • 在 decoupled frontend 模式下作为一个独立活动阶段运行。
    • 负责利用 BTB 扫描地址流、发现可能的 branch、调用 BPU 产生预测,并构造 FetchTarget 插入 FTQ
  2. PC update / predecode 协调部分
    • Fetch 紧耦合
    • Fetch 每预译码一条指令,就调用 BAC::updatePC() 决定下一条 PC
    • 在 decoupled 模式下,真正把 FTQ 里暂存的预测历史迁回 BPU,也发生在这里

源码注释也明确指出,这种组织方式并不完美,但目的是把 decoupling 逻辑主要收敛在 BAC 内,而不是进一步复杂化 Fetch

Coupled frontend 与 decoupled frontend 的差异

当前代码同时支持两种模式:

Coupled frontend

  • Fetch 在预译码到 branch 时,直接调用 bpu->predict(...)
  • 不需要 FTQ 介入预测流程。
  • 预测和 Fetch 更同步。

Decoupled frontend

  • BAC 提前基于 BTB 扫描地址流,先生成 FetchTarget
  • Fetch 后续只按 FTQ 头项取指。
  • Fetch 在预译码到 branch 后,不再重新做一次预测,而是从 FTQ 里取出已生成的预测历史并“正式入账”。

本文重点讨论的是第二种,也就是 FTQ 真正参与的模式。


5. FetchTarget 的数据模型

5.1 FetchTarget 的含义

FetchTarget 可以近似理解成一个“前端预测驱动的连续取指块”。它和传统基本块 basic block 有相似性,但并不完全等价:

  • 它有一个 startPC 和一个 endPC
  • 它通常在一个 branch 处结束。
  • 它也可能不以 branch 结束,而只是因为达到了允许的最大搜索宽度。
  • 它还可能包含 surprise branch,也就是 BAC 当时看不到、只能在后续 predecode 才发现的 branch。

5.2 FetchTarget 的关键字段

FetchTarget 核心包含以下信息:

  • startPC

    • 本 fetch target 的起始地址。
  • endPC

    • 本 fetch target 的结束地址,对应退出 instruction 的 PC。
  • predPC

    • 如果退出 instruction 是 branch,则这是该退出 branch 对应的预测目标地址。
    • 如果退出 instruction 不是 branch,那么这个字段仍会在 finalize() 时被填入“下一个 FT 的起点”。
  • ftSeqNum

    • FetchTarget 自己的编号。
    • 注意这不是指令的 seqNum,因为 FT 是在真正生成动态指令之前就构建出来的。
  • tid

    • 所属线程。
  • is_branch

    • 退出 instruction 是否是 branch。
  • taken

    • 退出 branch 是否预测为 taken。
  • bpuHistory

    • 最关键字段。
    • 这是一个 BPredUnit::PredictorHistory *,用于在 FT 等待于 FTQ 期间暂存该 fetch target 对应的分支预测历史。

5.3 为什么 bpuHistory 需要挂在 FetchTarget 上

因为在 decoupled frontend 中,预测发生得更早,而真正动态指令的 seqNum 更晚才出现。

在 gem5 当前实现里:

  • BPU 的正式历史管理以指令序号 seqNum 为核心。
  • 但 BAC 构造 FT 的那一刻,还没有真实的动态指令,自然拿不到最终 seqNum

于是实现采用了一个过渡策略:

  1. 在 BAC 生成 FT 并调用 bpu->predict(...) 时,先拿到一份 PredictorHistory
  2. 这份历史不立即放进 BPU 的 predHist 正式队列。
  3. 它先挂到 ft->bpuHistory 上,在 FTQ 中临时保存。
  4. Fetch 真正预译码到那个 branch,拿到动态指令和真实 seqNum 后,再把这份历史转移回 BPU 主历史队列。

这个设计是整个 FTQ/BPU 协同机制的核心。


6. FTQ 的职责与状态

6.1 FTQ 的基本职责

FTQ 本身非常“瘦”。

它不负责:

  • 生成预测。
  • 更新预测器状态。
  • 计算恢复后的历史。
  • 解释 branch 类型。

它主要负责:

  • 存放 FetchTarget
  • 提供头尾插入和弹出。
  • 提供一些状态控制接口,如 invalidate()lock()
  • 在 squash 时清空队列。
  • 为外部逻辑提供遍历接口,以便 BAC 回滚 FTQ 中仍未正式转入 BPU 的历史。

6.2 FTQ 的状态

当前实现中,FTQ 具有如下状态:

  • Valid

    • 可以安全读取和消费队列。
  • Invalid

    • 当前队列中的 fetch target 不再可信。
    • 进入该状态后,需要依靠 resteer/squash 来恢复。
  • Locked

    • 头部 fetch target 仍然可用,但其后的项不再可信。
    • 常出现在复杂指令或多 branch micro-op 的保守处理路径中。

代码里还有 Full 相关概念,但严格来说“是否已满”主要通过 size >= numEntries 计算,不是一个单独用于外部语义判断的常驻状态机值;BAC 侧会据此进入 FTQFull 阻塞状态。

6.3 FTQ 的几个关键接口

insert()

  • FetchTarget 压入队尾。
  • BAC 调用。

readHead()

  • 返回当前头部 FetchTarget
  • 如果 FTQInvalid 或队列为空,则返回 nullptr

isHeadReady()

  • 只有在队列非空且不为 Invalid 时才为真。
  • Fetch 在 decoupled 模式下会依赖这个条件来判断能否继续取指。

popHead()

  • 消费当前 FTQ 头项。
  • 这是一个非常关键的保护点。
  • 如果头项仍然携带 bpuHistory,则弹出失败,并将 FTQ 置为 Invalid

这等价于一个重要不变量:

任意 FetchTarget 在被真正弹出 FTQ 之前,挂在它上面的 bpuHistory 必须已经被转移走,或者在 squash 路径中被清空。

squash()

  • 清空队列。
  • 但它内部假设所有 FetchTarget 上的 bpuHistory 已经先被外部清掉。
  • 因此正常顺序是:
    1. BAC 先调用 squashBpuHistories() 倒序回滚历史。
    2. 再调用 ftq->squash() 清队列。

7. BAC 如何生成 FetchTarget 并填充 FTQ

7.1 输入:BAC 维护自己的前端 PC

在 decoupled frontend 中,BAC 维护自己的 bacPC
这个 PC 不是 Fetch 正在使用的那个 fetch PC,而是“预测生产侧”的 PC。

也就是说:

  • Fetch 消费 FTQ 中已有的 fetch targets。
  • BAC 则在后台持续尝试生成后续 fetch targets。

7.2 搜索 branch 的方式

BAC::generateFetchTargets() 的基本策略是:

  1. 从当前 bacPC 开始。
  2. 逐地址检查 BTB 是否命中。
  3. 如果未命中,则继续向前扫描,直到:
    • 找到 branch,或
    • 扫描宽度达到 fetchTargetWidth

这里的一个关键设计选择是:

BAC 并不依赖预译码来识别 branch,而是纯粹依赖 BTB 来发现“这里看起来有 branch”。

这意味着:

  • 只有进入过 BTB 的 branch,BAC 才能在生成 FT 时看到。
  • never-taken branch、首次遇到的 branch、被 BTB 驱逐的 branch,都可能在 BAC 阶段完全不可见。

7.3 找到 branch 后如何预测

一旦 BTB 命中:

  1. BAC 通过 BTBGetInst() 取出对应的 StaticInst
  2. 调用 BAC::predict()
  3. BAC::predict() 内部进一步调用 bpu->predict(...)
  4. BPU 返回预测方向,同时更新传入的 pc 为预测目标或 fallthrough。
  5. BPU 生成的 PredictorHistory 不进入 BPU 正式历史队列,而是直接挂到 ft->bpuHistory

7.4 完成 FetchTarget

无论是否发现 branch,BAC 都会调用 curFT->finalize(...),设置:

  • endPC
  • is_branch
  • taken
  • predPC

随后将该 FT 插入 FTQ。

7.5 下一 FT 的起点如何确定

如果当前 branch 预测为 taken:

  • 下一个 FT 的起点就是预测目标 predPC

如果当前 branch 预测为 not-taken,或根本没有 branch:

  • 下一个 FT 的起点就是顺序地址。

也就是说,FTQ 中的每个 FT 实际上隐含构成了一条由 BPU 提前规划出来的 fetch 路径。


8. Fetch 如何消费 FTQ

8.1 Fetch 只处理头部 FT

Fetch 在 decoupled 模式下,首先会检查 FTQ 是否 ready:

  • 若 FTQ 为空或无效,则 Fetch 进入 FtqWait
  • 若 FTQ 可用,则读取头部 FetchTarget

8.2 当前 PC 必须落在当前 FT 的范围内

Fetch 会验证:

  • 当前 fetch PC 是否在 curFT->inRange(...) 范围内。

如果不在范围内,说明前端状态与 FTQ 内容已经不同步,需要触发 bacResteer() 进行恢复。

这说明 FTQ 不只是一个“建议列表”,而是当前前端取指路径的强约束。

8.3 Fetch 在 FT 范围内顺序取指

只要当前 PC 仍然位于 curFT 内:

  • Fetch 就继续从 I-cache / decoder 中取出指令。
  • 每生成一条 DynInst,都调用 bac->updatePC(instruction, next_pc, curFT)

这时 FTQ 头项相当于给 Fetch 提供了一个“当前有效取指段”的边界条件。


9. Fetch 预译码到 branch 后,历史如何从 FTQ 转回 BPU

这是当前设计中最重要、也最容易误解的一步。

9.1 预译码到 control instruction 时

Fetch 发现当前 instruction 是 control instruction:

  • 在 coupled 模式下,直接在这里调用 bpu->predict(...)
  • 在 decoupled 模式下,不再重新预测,而是调用 BAC::updatePreDecode(...)

9.2 updatePreDecode 的基本动作

updatePreDecode() 会做以下事情:

情况 A:这是当前 FT 的 exit branch,且 FT 上有 bpuHistory

这说明:

  • BAC 之前已经对这个 branch 做过预测。
  • 现在只是到了真正的预译码阶段,需要把历史正式转入 BPU。

流程是:

  1. ft->bpuHistory 取出。
  2. 给这份 history 填入真实 seqNum
  3. 调用 bpu->insertPredictorHistory(...) 插入 BPU 主历史队列。
  4. 按 history 中记录的预测结果更新 fetch PC

这一步实际上完成了“临时历史 -> 正式历史”的生命周期切换。

情况 B:FT 上没有对应 history

这通常意味着:

  • BAC 生成 FT 时根本没发现这个 branch。
  • 常见原因是 BTB miss、首次出现、从未 taken、或者 BTB entry 被替换。

此时当前实现不会立即认为这是错误,而是采用一个保守恢复机制:

  1. 新建一个 PredictorHistory
  2. 调用 bpu->branchPlaceholder(...) 构造 predictor-specific 的占位历史。
  3. 假设该 branch 的预测是 not-taken。
  4. 先把这份 dummy history 插入 BPU 正式历史体系。
  5. 如果后续 decode/commit 发现真实情况不是这样,再依赖 squash 路径纠正。

这一策略非常关键,因为它保证了即使 BAC 事先没看到 branch,前端和预测器历史仍能在之后恢复到一致状态。

9.3 为什么不能在 BAC 预测时直接插入 BPU 主历史

原因主要有三个:

  1. 当时没有真实 seqNum

    • BPU 主历史以动态指令序号管理 in-flight branch。
  2. 当时 branch 类型信息可能不够稳定

    • 特别是复杂 instruction / micro-op 场景下,单纯依赖 BTB 中保存的静态信息可能还不够安全。
  3. 需要允许“预测已发生,但尚未被真正消费”

    • FTQ 本质上就是为了支持这种 decoupling。

10. FetchTarget 何时被弹出

10.1 一个 FT 不一定以 branch 结束

当前实现中,一个 FT 结束的条件可以是:

  • 找到 branch。
  • 达到最大搜索宽度。
  • 在复杂 micro-op 场景下触发特殊处理后被保守截断。

因此 FT 的退出 instruction 不一定是 branch。

10.2 何时认为当前 FT 已被消费完成

在 decoupled 模式下,如果满足以下任一条件,当前 ft 指针会被置空:

  • 当前 instruction 是 FT 的 exit instruction,且若是 micro-op 则必须是最后一个 micro-op。
  • FTQ 已变为 not ready。

一旦当前 FT 被置空,Fetch 就尝试:

  1. ftq->popHead()
  2. 若成功,读取下一个 FT
  3. 若失败,则认为状态异常并触发 bacResteer()

10.3 popHead 为什么可能失败

popHead() 失败的核心条件是:

  • 头部 FetchTarget 上仍残留 bpuHistory

这意味着某个关键步骤没有按预期发生,例如:

  • exit branch 的 history 没有在 updatePreDecode() 中成功取走。
  • 或者发生了复杂 corner case,导致 FT 被消费结束,但对应分支历史还没有完成迁移。

当前实现对这种情况的态度非常保守:

  • 不尝试在 popHead() 时“补救性处理”历史。
  • 而是直接把 FTQ 置 Invalid,让外层走 squash/resteer 恢复。

这是一个明确的安全优先选择。


11. FTQ 与 BPU 的真实边界

11.1 BPU 负责什么

BPredUnit 负责:

  • 执行方向预测。
  • 结合 BTB / RAS / indirect predictor 得到目标。
  • 创建和维护 PredictorHistory
  • 在 commit 进行训练更新。
  • 在 decode/commit/fetch squash 时恢复历史。

11.2 FTQ 负责什么

FTQ 不负责预测语义本身,只负责:

  • 暂存未来要给 Fetch 使用的取指目标。
  • 暂存还没“正式编号”的 PredictorHistory

因此可以把两者关系概括为:

  • BPUowner of predictor state
  • FTQtemporary carrier of prediction products

11.3 为什么要有这种边界

这是 decoupled frontend 的直接代价。

一旦希望:

  • 让预测早于真实 fetch/predecode 发生,
  • 并且让 BAC 与 Fetch 分离,

就必然需要一个中间层来保存“已经算出来但还没有完全消费”的预测产物。

在当前实现里,这个中间层就是 FetchTarget + FTQ


12. squash、mispredict、resteer 时的恢复流程

12.1 恢复顺序

当前实现对恢复流程非常谨慎,核心顺序通常是:

  1. 先回滚 FTQ 中尚未正式进入 BPU 主历史队列的历史。
  2. 再清空 FTQ。
  3. 再根据 decode/commit/fetch 反馈,对 BPU 正式历史进行 squash/update。
  4. 重设 BAC 的 PC,重新开始生成 FT。

12.2 squashBpuHistories 的作用

BAC::squashBpuHistories() 会:

  • 倒序遍历 FTQ。
  • 对每个仍带有 bpuHistory 的 FT,调用 bpu->squashHistory(...)
  • 清空对应 ft->bpuHistory

之所以倒序,是因为这些 history 代表的是按预测路径逐步累积的 speculative history update,恢复时必须反向回滚。

12.3 FTQ::squash 的前提

FTQ::squash() 内部对所有 FT 都断言:

  • ft->bpuHistory == nullptr

这意味着 FTQ::squash() 不是一个包含全部恢复语义的“一站式接口”,而只是一个在历史已清空前提下的队列清空动作。

这进一步体现出当前实现的层次:

  • 历史恢复归 BAC/BPU。
  • 队列清空归 FTQ。

12.4 不同来源的 squash

当前实现会从多个来源触发恢复:

  • commit

    • 例如真实 branch mispredict 在提交时被确认。
  • decode

    • 例如更早发现的控制流纠错。
  • fetch

    • 例如前端发现当前 FT 与实际 PC 不一致,或在消费 FT 过程中检测到异常。

不管来源是什么,BAC 先处理 FTQ 中的悬挂历史,再让 BPU 继续处理正式历史。


13. BTB miss 与 surprise branch 的处理

这是理解当前 FTQ 设计时不可绕过的一点。

13.1 BAC 对 branch 可见性的前提

在 decoupled frontend 里,BAC 依赖 BTB 来发现 branch。

也就是说:

  • BTB hit 才能让 BAC 在生成 FT 时“看到”一个 branch。

所以如果某个 branch:

  • 首次出现,
  • 从未被 taken,
  • 被 BTB 驱逐,

那么 BAC 在生成 FT 时会把它当作普通顺序 instruction,根本不会提前预测它。

13.2 这类 branch 在何时被发现

只有在后续 Fetch 真正把对应 bytes 送进 decoder,预译码出 control instruction 时,系统才意识到:

这里其实有一个 BAC 当时没看到的 branch。

13.3 branchPlaceholder 的意义

这就是 branchPlaceholder() 存在的根本原因。

对于这类“漏检 branch”,系统仍然需要:

  • 给 predictor 一个可恢复的占位点,
  • 让后续 squash 能正确对齐历史,
  • 避免 predictor 的 speculative history 与真实前端路径彻底脱节。

因此当前实现采用:

  • 先插入一个 placeholder history,
  • 默认按 not-taken 继续,
  • 后续若错,再由 decode/commit squash 修正。

这是一个典型的保守一致性设计。


14. 复杂指令与多 branch micro-op 的处理

当前 FTQ/BPU 关系中最复杂的 corner case 是:

  • 一个 instruction 含多个 branch 语义,
  • 或 branch 不在最后一个 micro-op,
  • 或 BTB 中保存的 branch 类型与最终预译码出的真实类型不一致。

当前实现对这些情况采取的是非常保守的策略。

14.1 branch type mismatch

如果从 FT 上取下来的 history 所记录的 branch 类型,与当前预译码得到的 branch 类型不同:

  • 认为当前预测历史不可信。
  • 先把 history 放回 FT。
  • 立刻回滚 FTQ 中所有悬挂历史。
  • 将 FTQ 置 Locked

这样做的含义是:

  • 不再信任当前队列后续项。
  • 等当前复杂 instruction 处理完,再通过 resteer 重建前端状态。

14.2 多 branch complex instruction

对于包含多个 branch 的复杂 instruction,当前实现只在 BAC 生成 FT 时预测“第一个可见 branch”。

如果在后续 predecode 发现:

  • 当前 micro-op 并不是最后一个,
  • 但已经没有可用 history,

则会:

  1. 先回滚 FTQ 中现有悬挂历史。
  2. 将 FTQ lock
  3. 对当前 instruction 重新做一次新预测。

这表明当前实现并不试图为复杂 instruction 提供完美的细粒度多 branch FT 建模,而是通过锁定和重建来保守处理。

14.3 这种策略的特点

优点:

  • 实现简单。
  • 一致性风险较低。
  • 对少见复杂场景足够稳妥。

代价:

  • 会频繁走 lock -> squash/resteer 路径。
  • 不适合用来精确模拟非常激进、能在复杂 macro-op 内做高精度 branch streaming 的工业前端。

15. FTQ 的关键不变量

以下不变量对当前实现至关重要:

15.1 FTQ 头项在弹出前不能残留 bpuHistory

这是最重要的不变量。

若违反:

  • popHead() 直接失败。
  • FTQ 置 Invalid
  • 外层必须走恢复路径。

15.2 FTQ 清空前,所有悬挂历史必须已被回滚并清空

FTQ::squash() 明确依赖这个前提。

15.3 当前 fetch PC 必须落在当前 FT 范围内

若不在:

  • 说明 FTQ 与实际前端路径已经脱节。
  • 必须 resteer。

15.4 对于由 BAC 预先预测的 exit branch,history 转入 BPU 主历史只能发生一次

也就是说,ft->bpuHistory 的所有权转移路径必须明确:

  • 要么在 updatePreDecode() 中被迁走并插入 BPU。
  • 要么在 squash 路径中被回滚并清空。

不能重复消费,也不能遗失。

15.5 BAC 生成 FT 时看到的 branch 集合,不等于最终 decoder 看到的 branch 集合

这不是 bug,而是设计前提。

因此 placeholder、lock、invalidate、resteer 都是必需机制,而不是补丁式残留。


16. 当前实现的优点

16.1 将预测生产与取指消费解耦

这是引入 FTQ 的根本收益:

  • BAC 可以提前规划 fetch path。
  • Fetch 可以更像一个按 target 执行的消费者。

16.2 缩短 fetch 关键路径

在 decoupled 模式下,预测不必等到 Fetch 预译码到 branch 才发生。
从模型语义上,前端取指路径的生成被前移到了 BAC 阶段。

16.3 允许 BPU 驱动的更精确 prefetch / target generation

源码注释也明确提到,这种设计利于由 BPU 引导的 fetch target 级别 prefetch。

16.4 对异常场景采用安全优先的恢复策略

当前实现对不一致的容忍方式不是“尽量在线修复”,而是:

  • 发现问题,
  • 标记 invalid/locked,
  • 走 squash/resteer 恢复。

这种策略对模拟器来说通常比激进在线修补更稳健。


17. 当前实现的限制与代价

17.1 强依赖 BTB 的 branch 可见性

这是当前 decoupled 设计最本质的限制。

若 branch 不在 BTB 中:

  • BAC 根本看不到。
  • 只能在后续 predecode 补救。

因此这份实现并不是“完全凭静态指令流就能独立构建 FT”的前端模型,而是“BTB-guided FT generation”。

17.2 FTQ 和 BPU 耦合较深

虽然 FTQ 本身结构轻量,但整个机制依赖多个跨模块约定:

  • ft->bpuHistory 何时生成。
  • 何时迁移。
  • 何时回滚。
  • popHead() 前必须清空。

这使得 BAC、FTQ、BPU 三者之间存在明显的隐式协作协议。

17.3 复杂指令场景依赖保守恢复

当前实现面对多 branch complex instruction 时,不是做精细建模,而是:

  • lock
  • invalidate
  • squash
  • resteer

这对功能正确性是合理的,但对前端微结构时序细节的建模精度有限。

17.4 FTQ 不是完全自治的数据结构

FTQ 并不知道如何处理 BPU 历史。

它只是一个容器:

  • 外部先回滚 history,
  • 它再清队列。

因此 FTQ 的接口语义本身并不闭合,必须结合 BAC/BPU 才能正确使用。


18. 与 TAGE 预测器的关系

当前仓库里的 TAGE 预测器本体没有显式多周期预测延迟建模,而是同步返回预测结果。

放到 FTQ 设计中看,其关系是:

  1. BAC 在生成 FT 时,通过 BPredUnit::predict() 调用条件分支预测器。
  2. 如果条件预测器配置为 TAGE,则 TAGE::lookup() / TAGE::predict() 会同步返回方向预测。
  3. BPU 再结合 BTBRAS、indirect predictor 形成完整控制流预测。
  4. 这些预测结果被封装进 FetchTargetbpuHistory,进入 FTQ。

因此:

  • TAGE 决定“方向预测结果”。
  • BPredUnit 决定“完整 branch prediction 结果”。
  • FTQ 承载“被 BAC 预先规划出来的 fetch path”。

换句话说,FTQ 并不直接关心底层条件预测器是 TAGEgshare 还是别的实现;它只消费由 BPredUnit 整合后的预测产物。


19. 一条完整的数据流示例

以下给出一条典型 decoupled frontend 路径,帮助理解各组件分工。

步骤 1:BAC 从 bacPC 开始扫描

  • 检查若干顺序地址是否在 BTB 中命中。
  • 找到一个 branch 地址。

步骤 2:BAC 调用 BPU 预测

  • 从 BTB 中取出 StaticInst
  • 调用 bpu->predict(...)
  • 得到 taken/not-taken 和 target。
  • 同时获得一份 PredictorHistory

步骤 3:构建 FetchTarget

  • 设置 startPC/endPC/predPC/is_branch/taken
  • PredictorHistory 挂到 ft->bpuHistory
  • FetchTarget 插入 FTQ。

步骤 4:Fetch 读取 FTQ 头项

  • 检查当前 fetch PC 是否处于 FT 范围内。
  • 在 FT 范围内顺序取指并预译码。

步骤 5:Fetch 预译码到 branch

  • 调用 bac->updatePC(...)
  • 在 decoupled 模式下进一步调用 updatePreDecode(...)

步骤 6:history 从 FTQ 转回 BPU

  • 若这是 FT 的 exit branch,则取下 ft->bpuHistory
  • 补上真实 seqNum
  • 调用 bpu->insertPredictorHistory(...)

步骤 7:FetchTarget 完成消费

  • 当 FT 的退出 instruction 被处理完后,当前 FT 被视为消费结束。
  • Fetch 调用 ftq->popHead()
  • 若无残留 bpuHistory,则成功弹出。

步骤 8:后续训练与恢复

  • 若预测正确,commit 时 bpu->update(...) 正常训练。
  • 若预测错误,decode/commit/fetch 任何一方都可能触发 squash。
  • BAC 先回滚 FTQ 中尚未入账的历史,再清空 FTQ,重建路径。

20. 审查结论

基于当前源码,可以得到如下结论:

20.1 FTQ 的角色非常明确

FTQ 在当前实现中是:

  • BACFetch 之间的 FetchTarget 队列;
  • 同时也是尚未正式进入 BPU 主历史体系的 PredictorHistory 的临时载体。

它不是 predictor 本体,也不是 predictor 训练队列。

20.2 当前实现逻辑上是自洽的

从源码逻辑看,FTQBACFetchBPU 之间的分工和恢复路径是闭合的:

  • 预测先发生;
  • 历史先挂 FT;
  • 预译码后正式入账;
  • 出错时先回滚 FTQ 悬挂历史,再清队列,再修正正式历史。

整体设计是自洽的。

20.3 这是一种“保守而稳健”的 decoupled frontend 建模

它的主要特征是:

  • 使用 BTB-guided 的 fetch target 生成;
  • 允许 branch 漏检并通过 placeholder 补救;
  • 对复杂 corner case 优先选择 lock/invalidate/squash/resteer
  • 把一致性置于建模激进度之前。

20.4 最大限制在于 BTB 可见性和复杂指令精细建模

这套实现适合模拟一种:

  • 有 decoupled branch prediction,
  • 但仍然高度依赖 BTB 发现 branch,
  • 对复杂 branch packing 采取保守恢复策略

的前端。

如果未来希望建模更激进的工业级前端,可能需要进一步增强:

  • 非 BTB 依赖的 branch discoverability;
  • complex instruction / multi-branch micro-op 的细粒度建模;
  • FTQ 与 predictor history 的所有权语义封装。

21. 简要术语表

  • BAC

    • Branch Address Calculation stage,当前实现里同时承担 decoupled prediction feeding 与 fetch-side PC update 的协调职责。
  • BPU

    • Branch Prediction Unit,即 BPredUnit
  • FT

    • Fetch Target。
  • FTQ

    • Fetch Target Queue。
  • PredictorHistory

    • BPU 为单条 branch 预测维护的历史对象,支持后续 update/squash/recovery。
  • BTB

    • Branch Target Buffer,当前 decoupled FT 生成的 branch 可见性基础。
  • Placeholder

    • 为 BAC 漏检 branch 构造的占位历史,用于后续恢复和纠偏。
  • Resteer

    • 前端发现 FTQ 路径与实际取指路径不一致后,重定向 BAC/fetch 路径并恢复状态的机制。