分支目标缓冲区检测到什么分支预测错误?

What branch misprediction does the Branch Target Buffer detect?

我目前正在查看 CPU 管道的各个部分,这些部分可以检测分支预测错误。我发现这些是:

  1. 分支目标缓冲区(BPU CLEAR)
  2. 分支地址计算器(BA CLEAR)
  3. 跳转执行单元(不知道这里的信号名称??)

我知道 2 和 3 检测到什么,但我不明白 BTB 中检测到什么错误预测。 BAC 检测 BTB 在何处错误地预测了非分支指令的分支,在何处 BTB 未能检测到分支,或者 BTB 在何处错误预测了 x86 RET 指令的目标地址。执行单元评估分支并确定它是否正确。

在分支目标缓冲区检测到什么类型的错误预测?这里究竟检测到什么是错误预测?

我能找到的唯一线索是英特尔开发人员手册第 3 卷中的这个(底部的两个 BPU CLEAR 事件计数器):

BPU predicted a taken branch after incorrectly assuming that it was not taken.

这似乎意味着预测没有完成 "synchronously",而是 "asynchronously",因此 "after incorrectly assuming"??

更新:

罗斯,这是来自原始英特尔专利的 CPU 分支电路("reading" 的分支电路如何?):

我在任何地方都没有看到 "Branch Prediction Unit"?读过这篇论文的人会认为 "BPU" 是一种将 BTB 电路、BTB 缓存、BAC 和 RSB 组合在一起的懒惰方式吗?

所以我的问题仍然存在,哪个组件会发出 BPU CLEAR 信号?

这是个好问题!我认为它造成的混乱是由于英特尔奇怪的命名方案,这些方案经常使学术界的标准术语超载。我会尽量回答你的问题,并澄清我在评论中看到的困惑。

首先。我同意在标准计算机科学术语中,分支目标缓冲区不是分支预测器的同义词。然而,在 Intel 术语中,分支目标缓冲区 (BTB) [大写] 是特定的东西,包含预测器和分支目标缓冲区缓存 (BTBC),它只是一个 table 分支指令及其目标结果。这个 BTBC 就是大多数人理解的分支目标缓冲区 [小写]。那么什么是分支地址计算器 (BAC)?如果我们有 BTB,为什么还需要它?

因此,您了解现代处理器被分成具有多个阶段的流水线。无论这是一个简单的流水线处理器还是乱序超标量处理器,第一阶段通常是 ​​fetch,然后是 decode。在 fetch 阶段,我们所拥有的只是程序计数器 (PC) 中包含的当前指令的地址。我们使用 PC 从内存中加载字节并将它们发送到 decode 阶段。在大多数情况下,我们增加 PC 以加载后续指令,但在其他情况下,我们处理可以完全修改 PC 内容的控制流指令。

BTB的目的是猜测PC中的地址是否指向分支指令,如果是,PC中的下一个地址应该是什么?没关系,我们可以对条件分支使用预测器,对下一个地址使用 BTBC。如果预测正确,那就太好了!如果预测错了,那怎么办?如果 BTB 是我们拥有的唯一单元,那么我们将不得不等到分支到达管道的 issue/execute 阶段。我们将不得不冲洗管道并重新开始。但并非所有 情况都需要这么晚才解决。这就是分支地址计算器 (BAC) 的用武之地。

BTB 用于管道的 fetch 阶段,但 BAC 位于 decode 阶段。一旦我们获取的指令被解码,我们实际上有更多有用的信息。我们知道的第一个新信息是:"is the instruction I fetched actually a branch?" 在 fetch 阶段我们不知道,BTB 只能猜测,但在 decode 阶段我们 知道 肯定. BTB 有可能在指令实际上不是分支时预测分支;在这种情况下,BAC 将停止获取单元,修复 BTB,并重新启动正确的获取。

unconditional relativecall 这样的分支呢?这些可以在解码阶段进行验证。 BAC 将检查 BTB,查看 BTBC 中是否有条目,并将预测器设置为始终预测采用。对于 conditional 个分支,BAC 无法确认它们是否为 taken/not-taken,但它至少可以验证预测的地址并在地址预测错误的情况下更正 BTB。有时 BTB 根本不会 identify/predict 一个分支。 BAC 需要更正此错误并向 BTB 提供有关此指令的新信息。由于 BAC 没有自己的条件预测器,它使用一种简单的机制(采用后向分支,不采用前向分支)。

有人需要确认我对这些硬件计数器的理解,但我相信它们的意思如下:

  • BACLEAR.CLEARfetch中的BTB做坏时递增 decode 中的作业和 BAC 可以修复它。
  • BPU_CLEARS.EARLY 是 当 fetch 决定(错误地)加载下一个时递增 BTB 预测它实际上应该从加载之前的指令 取而代之的路径。这是因为 BTB 需要多个周期,而 fetch 使用该时间推测性地加载连续的指令块。这可能是因为英特尔使用了两个 BTB,一个速度快,另一个速度慢但更准确。需要更多的周期才能获得更好的预测。

这解释了为什么在 BTB 中检测到错误预测的惩罚是 2/3 个周期,而在 BAC 中检测到错误预测是 8 个周期。

事实 BPU_CLEARS.EARLY 描述表明,当 BPU 预测时会发生此事件,更正假设,这意味着前端有 3 个阶段。假设、预测和计算。我目前的猜测是,当预测是 'taken' 而不是未采用时,早期清除正在刷新甚至已知来自 L1 BTB 的预测阶段之前的管道阶段。

BTB 集包含 4 种方式,每 16 字节最多有 4 个分支(其中该集中的所有方式都标有相同的标记,指示特定的 16 字节对齐块)。每条路都有一个偏移量,指示地址的 4 个 LSB,因此是 16 字节内的字节位置。每个条目还有一个推测位、有效位、pLRU 位、一个推测局部 BHR、一个真实局部 BHR,并且每条路共享集合 BPT (PHT) 作为第二级预测。这可能与 GHR /推测 GHR 合金化。

我觉得BPU给uop缓存和指令缓存提供了64B的预测块(以前是32B,P6上是16B)。对于传统路由,它需要提供一个 64(或 32/16)字节预测块,这是一组 64 位掩码,标记预测的分支指令、预测方向以及哪个字节是分支目标。此信息将由 L1 BTB 提供,同时正在获取 64 字节行,这样从中读取的 16 字节对齐(IFU 始终为 16B)块根本没有使用的位将不会被获取指令预解码器(未使用是默认值,因为在预测块小于行大小的体系结构上,BPU 可能只为行的 16 或 32B 提供位掩码)。因此,BPU 提供预测掩码,used/unused mask(将第一个预测块中第一个分支之后和第二个预测块中的分支目标之前的字节标记为未使用,其余使用),预测方向掩码; ILD 提供分支指令掩码。预测块中第一个使用的字节是隐含的分支目标或从 uop 缓存 (DSP) 重新转向或切换到遗留管道 (MITE) 后指令流的开始。预测块中使用的字节构成预测 window.

这里是 P6 流水线。以此为例,早期清除发生在第 3 个周期 (13),此时进行了预测(并且读取了目标和条目类型,因此现在已知无条件分支目标以及条件分支目标和它们的预测)。使用 16 字节块集合中的第一个预测采用的分支目标。此时,它之前的 2 个管道阶段已经充满了从下一个连续的 16 字节块中提取和开始查找,这意味着如果有任何预测,它们需要被刷新(否则它不需要因为下一个连续的 16 字节块已经开始在它之前的管道阶段中查找),留下 2 个周期的间隙或气泡。缓存查找与 BTB 查找同时发生,因此 BTB 和缓存并行 2 管道阶段都必须刷新,而第三阶段不需要从缓存或 BTB 中刷新,因为 IP 已打开已确认的路径,是当前正在为下一个路径查找的 IP。事实上,在这个 P6 设计中,只有一个 one cycle bubble 用于这个早期清除,因为新的 IP 可以发送到第一级以在时钟的高边再次解码一组,而其他阶段正在脸红了。

这种流水线比在开始查找下一个 IP 之前等待预测更有用。这意味着每隔一个周期查找一次。这将给出每 2 个周期 16 个字节的预测吞吐量,即 8B/c。在 P6 流水线方案中,吞吐量在正确假设下为每周期 16 字节,在错误假设下为 8B/c。显然更快。如果我们假设 2/3 的假设对于 9 条指令中的 1 条指令是每块 4 条指令的采用分支是正确的,则这给出了每 ((1*0.666)+2*0.333)) =1.332 个周期而不是 16B 的 16B 吞吐量每 2 个周期。

如果这是真的,每一个分支都会导致提前清除。然而,当我在我的 KBL 上使用该事件时,情况并非如此。希望这个事件实际上是错误的,因为我的 KBL 应该不支持它,但确实显示了一个随机的低数字,所以希望它正在计算其他东西。这似乎也不被以下 https://gist.github.com/mattgodbolt/4e2cbb1c9aa97e0c5478 https://github.com/mattgodbolt/agner/blob/master/tests/branch.py 支持。鉴于 900k 指令和 100k 早期清除,如果你使用我对早期清除的定义然后查看他的代码,我不明白你怎么会有奇数个早期清除。如果我们假设 window 对于那个 CPU 是 32B,那么如果您在该宏中的每个分支指令上使用 4 的对齐,您每 8 条指令就会清除一次,因为 8 将适合 32B对齐 window.

我不确定为什么 Haswell and Ivy Bridge have such values for early and late clears but these events (0xe8) disappear starting with SnB,恰好与当 BTB 被统一为一个单一的结构时。看起来延迟清除计数器现在正在计算早期清除事件,因为它与 Arrandale CPU 上的早期清除事件的数量相同,而早期清除事件现在没有计数。我也不确定为什么 Nehalem 有一个 2 周期气泡用于早期清除,因为 L1 Nehalem BTB 的设计似乎与 P6 BTB 没有太大变化,每组 512 个条目和 4 种方式。这可能是因为更高的时钟速度导致它被分解成更多的阶段,因此 L1i 缓存延迟也更长。

晚晴(BPU_CLEARS.LATE)appears to happen at the ILD. In the diagram above, the cache lookup takes only 2 cycles. In more recent processors, it apparently takes 4 cycles. This allows another L2 BTB to be inserted and a lookup in it to take place. 'MRU bypass' and 'MRU conflicts' could just mean that there was a miss in the MRU BTB or it could also mean that the prediction in the L2 is different to the one in L1 in the event that it uses a different prediction algorithm and history file。在我的 KBL 上,它不支持任何一个事件,我总是为 ILD_STALL.MRU 而不是 BPU_CLEARS.LATE 得到 0。 3 周期气泡来自第 5 阶段(也是 ILD 阶段)的 BPU,在第 1 阶段的低边缘重新引导管道并冲洗第 2、3 和 4 阶段(这与引用的 4 个周期的 L1i 延迟一致,因为 L1i 查找发生在第 1-4 阶段,用于命中 + ITLB 命中)。一旦做出预测,BTB 就会使用所做的预测更新条目的推测本地 BHR 位。

BACLEAR 例如当 IQ 将 predictions-made 掩码与预解码器生成的分支指令掩码进行比较时,然后对于某些指令类型,如相对跳转,它将检查符号位以执行静态分支预测。我想静态预测一旦从预解码器进入 IQ 就会发生,这样立即进入解码器的指令就包含静态预测。现在被预测采取的分支在分支指令被解码时将导致BACLEAR_FORCE_IQ,因为当BAC计算相对条件分支指令的绝对地址时将没有目标来验证,这是需要的验证预计何时拍摄。

译码器的 BAC 在从指令本身计算绝对地址并将其与它进行比较后,还确保相关分支和直接分支具有正确的分支目标预测,如果不是,则发出 BACLEAR。对于相对跳跃,BAC 中的静态预测使用跳跃位移的符号位来静态预测采用/未采用(如果未进行预测),但如果 BTB 不支持 return 则覆盖所有已采用的预测 return 条目类型(它不在 P6 上并且不做预测,而是 BAC 使用 BPU 的 RSB 机制并且它是管道中确认 return 指令的第一个点)并覆盖所有寄存器在 P6 上采用的间接分支预测(因为没有 IBTB),因为它使用了更多分支被采用的统计数据。 BAC 计算绝对目标并将其从相对目标插入到 uop 并将 IP 增量插入到 uop 并将下降通过 IP (NLIP) 插入 BPU 的 BIT,这可能被标记到 uop,或者更可能是 BIT条目在相应的循环队列上工作,如果没有足够的 BIT 条目,它将停止,并且间接目标预测或已知目标被插入到 uop 64 位立即字段中。 These fields in the uop are used by the allocator for allocation into the RS/ROB later on. The BAC also informs the BTB of any spurious predictions (non branch instructions) that need their entries deallocating from the BTB. At the decoders, branch instructions are detected early in the logic (when prefixes are decoded and the instruction is examined to see if it can be decoded by the decoder) and the BAC is accessed in parallel with the rest. The BAC inserting the known or otherwise predicted target into the uop is known as converting an auop into a duop。预测被编码到 uop 操作码中。

BAC 可能指示 BTB 为检测到的分支指令的 IP 推测更新其 BTB。如果目标现在已知并且没有对其进行预测(意味着它不在缓存中) - 它仍然是推测性的,因为虽然分支目标是确定的,但它仍然可能在推测路径上,所以是标有投机位——这现在将立即提供早期引导,特别是对于现在进入管道的无条件分支,但也适用于有条件的,具有空白历史记录,因此将预测下一次不会被采用,而不必等到退休)。

The IQ 上面包含一个用于分支预测方向 (BTBP) 和进行分支预测/未进行预测 (BTBH) 的位掩码字段(以区分 BTBP 中的哪些 0 没有被采用,而不是没有预测)对于 IQ 行中的 8 个指令字节中的每一个以及分支指令的目标,这意味着每条 IQ 行只能有一个分支并结束该行。此图未显示预解码器生成的分支指令掩码,该掩码显示哪些指令实际上是分支,以便 IQ 知道它需要对哪些 not-made 预测进行预测(以及哪些根本不是分支指令) .

IQ 是一个连续的指令字节块,ILD 填充 8 位位掩码,用于标识每个宏指令的第一个操作码字节 (OpM) 和指令结束字节 (EBM),因为它将圆形字节包装到 IQ 中。它可能还提供指示它是复杂指令还是简单指令的位(正如许多 AMD 专利中 'predecode bits' 所建议的那样)。这些标记之间的间隙是后续指令的隐式前缀字节。我认为 IQ 的设计使得它在 IDQ/ROB 中发出的 uops 很少会超过 IQ,这样 IQ 中的头指针开始覆盖仍然标记在 IDQ 中等待分配的宏指令,以及何时它确实,有一个停顿,所以 IDQ 标签指回分配器访问的 IQ。我认为 ROB 也使用这个 uop 标签。如果 16 字节 * 40 个条目在 SnB 上的 IQ 在最坏情况下包含 40 个宏操作,在平均情况下为 320 个,在最佳情况下为 640 个。这些产生的 uops 数量会大得多,所以它很少会超过,而且当它超过时,我猜它会停止解码,直到更多指令退出。尾指针包含 ILD 最近分配的标签,头指针包含下一条等待退出的宏指令指令,读指针是解码器要使用的当前标签(向尾指针移动)。虽然,现在这变得很困难,因为路径中的一些(如果不是大多数)uop 来自 SnB 以来的 uop 缓存。如果 uops 没有用 IQ 条目标记(并且 IQ 中的字段直接插入到 uops 中),IQ 可能被允许超过后端,并且这将被检测到并且管道将从开始。

当分配器将分支micro-op的物理目的地(Pdst)分配到ROB中时,分配器将Pdst条目号提供给BPU。 BPU 将其插入由 BAC 分配的正确 BIT 条目(这可能位于尚未分配 Pdst 的活动 BIT 条目的循环队列的头部)。分配器还从 uop 中提取字段并将数据分配到 RS 中。

RS 包含一个字段,指示指令是分配器填充的 MSROM uop 还是常规 uop。分配器还将确认的绝对目标或预测的绝对目标插入到立即数据中,并作为源重命名标志寄存器(or just a flag bit),在间接分支的情况下,还有重命名的寄存器包含地址作为另一个来源。 PRF 方案中的 Pdst 将是 ROB 条目,作为 Pdst 将是退休 macro-RIP 或 micro-IP 寄存器。 JEU 将目标或 fallthrough 写入该寄存器(如果预测正确则可能不需要)。

当保留站向整数执行单元中的跳转执行单元派发分支micro-op时,保留站将对应分支micro-op的Pdst表项通知给BTB。作为响应,BTB 访问 BIT 中分支指令的相应条目,并读出落入 IP (NLIP),减去 RS 中的 IP delta,并解码为指向分支条目将要执行的集合updated/allocated.

将重命名的标志寄存器源 Pdst 的结果用于确定分支是否被采用,然后与调度程序中的操作码中的预测进行比较,此外,如果分支是间接的,则预测目标在BIT 与源 Pdst 中的地址(在调度和调度之前计算并在 RS 中可用)进行比较,现在知道是否做出了正确的预测以及目标是否正确.

JEU 将异常代码传播到 ROB 并刷新管道(JEClear——它在分配阶段之前刷新整个管道,并停止分配器)并在开始时重定向下一个 IP 逻辑管道适当地使用 fallthrough(在 BIT 中)/目标 IP(以及微序列器,如果它是微分支错误预测;指向管道开始的 RIP 在整个 MSROM 过程中都是相同的)。推测条目被释放,真正的 BHR 被复制到推测 BHR 中。如果 PRF 方案中存在 BOB,则 BOB 会为每个分支指令以及出现错误预测时拍摄 RAT 状态的快照。 JEU 将 RAT 状态回滚到该快照,分配器可以立即继续(这对于微分支预测错误特别有用,因为它更接近分配器,因此气泡不会被管道隐藏),而不是停止分配器并且必须等到退休才能知道退休 RAT 状态,并使用它来恢复 RAT,然后清除 ROB(ROClear,它会取消分配器)。使用 BOB,分配器可以开始发出新指令,同时陈旧的 uops 继续执行,并且当分支准备退出时,ROClear 仅清除退出的错误预测和新的 uops 之间的 uops。如果是MSROM uop,因为可能已经完成,所以pipeline的开始还是需要重新重定向到MSROM uop,不过这次会从重定向的microip开始(inli就是这样的)e 指令(并且它可能能够重播它的智商)。如果在 MSROM 异常中发生错误预测,那么它不需要重新引导管道,只需直接重定向它,因为它已经接管了 IDQ 问题,直到过程结束——对于内联问题,该问题可能已经结束。

ROB 中响应分支异常向量的 ROClear 实际上发生在 uops 退役时的第二退役阶段 RET2(实际上是典型退役流水线的 3 个阶段中的第 3 个阶段)。当 EOM uop(宏指令结束)标记退出时,宏指令仅退出并且异常仅触发并且宏指令 RIP 仅更新(具有新目标或增加 ROB 中的 IP 增量),即使非 EOM 指令写入它,它与其他寄存器不同,它不会立即写入 RRF——无论如何,分支 uop 很可能是解码器处理的典型分支宏指令中的最终 uop。如果这是 MSROM 程序中的微分支,则不会更新 BTB;它在退出时更新 uIP,直到过程结束才更新 RIP。

如果在 MSROM macroop 执行期间发生通用 non-mispredict 异常(即需要处理程序的异常),一旦它被处理,导致异常的 microip 将由处理程序恢复到 uIP 寄存器(如果调用时传递给处理程序),以及触发异常的宏指令的当前RIP,当异常处理结束时,取指令在这个RIP+uIP处恢复:宏指令在 MSROM 中重新获取并重新尝试,它从向它发出信号的 uIP 开始。复杂 non-MSROM 宏指令中先前 uops 的 RRF 写入(或 PRF 方案上的退休 RAT 更新)可能发生在 EOM uop 退休之前的周期中,这意味着 restart can happen at a certain uop within a regular complex macroop and not just a MSROM macroop, and in this case, the instruction flow is restarted at the BPU at the RIP, and the complex decoder is configured with valid / invalid bits on the PLA cuop outputs. The uIP for this regular complex instruction that is used to configure the complex decoder valid bits is a value between 0-3, which I think the ROB sets to 0 at each EOM and increments for each microop retired, so that the non-MSROM complex instructions can be addressed, and for MSROM complex instructions, the MSROM routine contains a uop that tells the ROB the uIP of that instruction. The architectural RIP register however, which is updated by the IP delta only when the EOM uop retires is still pointing to the current macroop because the EOM uop failed to retire), which only happens for exceptions but not hardware interrupts, which can't interrupt MSROM procedures or complex instruction mid retirement (software interrupts are similar and trigger at the EOM -- the trap MSROM handler performs a macrojump to the RIP of the software trap handler once it has finished).

BTB读取和标签比较发生在RET1,同时分支单元写回结果,然后在下一个周期,也许也在RET1期间(或者这可能在RET2完成),集合中的标签是比较,如果命中,则计算新的历史BHR;如果未命中,则需要在具有该目标的 IP 上分配一个条目。只有当 uop 按顺序退出后(在 RET2 中),结果才能放入真实历史中,并利用分支预测算法来更新需要更新的模式 table。如果分支需要分配,则利用替换策略来决定分配分支的方式。如果命中,则目标对于所有直接和相关分支都是正确的,因此在没有 IBTB 的情况下不必进行比较。推测位现在从条目中删除(如果存在)。最后,在下一个周期,该分支被BTB写控制逻辑写入BTB缓存中。 BTB 查找的第一部分可能能够在整个 RET1 中继续进行,然后可能会停止 BTB 写入流水线,直到等待写入 BTB 的 ROB 条目的阶段退出时的 RET2,否则,查找可能会解耦并且第一部分完成并将数据写入,例如,BIT,并且在 RET2 中,与退休的对应条目只是写回到 BTB(这意味着再次解码集合,再次比较标签,然后写入条目,所以可能不会)

如果 P6 有一个 uop 缓存,管道将是这样的:

  • 1H: select IP
  • 1L:BTB集解码+缓存集解码()+ITLB查找+uop缓存集解码
  • 2H:缓存读取+BTB读取+uop缓存读取
  • 2L:缓存标签比较+BTB标签比较+uop缓存标签比较;如果 uop 缓存命中,停止直到 uop 缓存可以发出,然后时钟门遗留解码管道
  • 3H:预测,如果被取,同花3H,2L,2H,1L
  • 如果采用 3L,则使用新 IP 开始 1L 以解码新集并继续当前 16 字节块,分支指令驻留在 4L

至于 uop 缓存,因为它已经过了 BAC 阶段,所以永远不会有伪分支或无条件分支的错误预测或 non-indirect 分支的错误目标. uop 缓存将使用来自 BPU 的 used/unused 掩码为从这些字节开始的指令发出 uops,并将使用预测方向掩码将宏分支 uops 更改为预测未采用/预测采用的宏分支 uop(T/NT 预测被插入到 uop 本身)。如果预测到它被占用,那么它将停止为那个 64B 对齐的块(以前也是 32B,以前是 16B)发出微指令,并等待管道中紧随其后的下一个 window。 uop 缓存将知道哪些 uops 是分支,并且可能静态预测未对所有 non-predictions 采取,或者可能具有更高级的静态预测。来自 IBTB 的间接目标预测被插入到 uop 立即字段中如果这个分支也被预测采用,那么它将等待下一个 BPU 预测块。我想 uop 缓存会创建 BIT 条目并更新 BTB 中的预测,以确保 uop 缓存和 MITE(传统解码)uops 以正确的顺序更新历史记录。