【问题标题】:Conditional jump instructions in MSROM procedures?MSROM 程序中的条件跳转指令?
【发布时间】:2019-09-12 17:58:56
【问题描述】:

这与this问题有关

考虑一下,在现代英特尔 CPU 上,SEC 阶段是在微码中实现的,这意味着将使用烧录的密钥来验证 PEI ACM 上的签名。如果不匹配,则需要做某事,如果匹配,则需要做其他事情。鉴于这是作为 MSROM 过程实现的,因此必须有一种分支方式,但鉴于 MSROM 指令没有 RIP。

通常,当一个分支错误预测为被执行时,当指令退出时,ROB 将检查异常代码并因此将指令长度添加到 ROB 行的 RIP 或仅使用下一个 ROB 条目的 IP,这将导致前端在分支预测更新中被重新引导到该地址。对于 BOB,此功能现在已借给跳转执行单元。显然,这不会发生在 MSROM 例程中,因为前端与它无关。

我的想法是有一个特定的跳转指令,只有 MSROM 例程可以发出,它跳转到 MSROM 中的不同位置,并且可以配置为始终预测不执行 MSROM 分支指令以及何时执行分支单元遇到此指令并执行分支,它会产生异常代码并可能将特殊跳转目标连接到它,并且在退出时发生异常。或者,执行单元可以处理它并且它可以使用 BOB,但我的印象是 BOB 由分支指令 RIP 索引,然后还有一个事实是生成 MSROM 代码的异常通常在退休时处理;分支错误预测不需要我认为不需要的 MSROM,而是所有操作都是在内部执行的。

【问题讨论】:

    标签: x86 intel cpu-architecture branch-prediction micro-architecture


    【解决方案1】:

    微码分支显然很特别。

    根据 Andy Glew 对原始 P6 (What setup does REP do?) 的描述,英特尔的 P6 和 SnB 系列不支持微码分支的动态预测。鉴于 SnB 系列 rep-string 指令的类似性能,我认为这一 PPro 事实甚至适用于最新的 Skylake / CoffeeLake CPU1

    但是微码分支错误预测会受到惩罚,因此它们是静态(?)预测的。 (这就是为什么 rep movsb 在 ECX 中的低/中/高计数的启动成本以 5 个周期为增量,并且对齐与未对齐。)


    微编码指令在 uop 缓存中占用整行。 当它到达 IDQ 的前面时,它会接管发布/重命名阶段,直到完成发布微码 uops。(有关更多详细信息,另请参阅 How are microcodes executed during an instruction cycle?,以及来自性能事件描述的一些证据,例如 @ 987654332@ 表明 IDQ 可以接受来自 uop 缓存的新 uop问题/重命名阶段正在从微码序列器读取。)

    对于rep-string 指令,我认为循环的每次迭代都必须通过前端实际发出,而不仅仅是在后端循环内部并重用这些微指令。所以这涉及到来自 OoO 后端的反馈,以找出指令何时完成执行。

    我不知道当问题/重命名切换到从 MS-ROM 而不是 IDQ 读取 uops 时会发生什么的详细信息。

    即使每个 uop 没有自己的 RIP(作为单个微编码指令的一部分),我猜分支错误预测检测机制的工作方式与普通分支类似。

    rep movs 某些 CPU 上的设置时间似乎以 5 个周期为步长,具体取决于具体情况(小与大、对齐等)。如果这些来自微码分支错误预测,那似乎意味着错误预测惩罚是固定数量的周期,除非这只是rep movs 的特例。可能是因为 OoO 后端跟不上前端?并且从 MS-ROM 读取比从 uop 缓存读取更能缩短路径,从而使未命中惩罚如此之低。

    rep movsb 附近运行一些 OoO exec 可能会很有趣,例如有两条依赖的imul指令链,看看它是否(部分)serializes them like lfence。我们希望不会,但为了实现 ILP,后面的imul uops 必须在不等待后端耗尽的情况下发出。

    我在 Skylake (i7-6700k) 上做了一些实验。初步结果:95 字节及以下的副本大小便宜且被 IMUL 链的延迟隐藏,但它们基本上完全重叠。 96 字节或更大的复制大小会耗尽 RS,序列化两个 IMUL 链。 无论是 RCX=95 与 96 的 rep movsb 还是 RCX=23 与 987654341@ 都无关紧要. 24. 有关我的发现的更多总结,请参见 cmets 中的讨论;如果我有时间,我会发布更多详细信息。

    “耗尽 RS”行为是用 rs_events.empty_end:u 测量的,甚至每 rep movsb 变为 1 而不是 ~0.003。 other_assists.any:u 为零,所以它不是“辅助”,或者至少不算作一。

    如果微代码分支不支持通过 BoB 进行快速恢复,可能涉及的任何 uop 都只会在达到退休时检测到错误预测? 96 字节阈值可能是某些替代策略的截止值。 RCX=0 也会耗尽 RS,大概是因为它也是一种特殊情况。

    rep scas 测试会很有趣(它不支持快速字符串,只是缓慢而愚蠢的微代码。)

    Intel's 1994 Fast Strings patent 描述了 P6 中的实现。它没有 IDQ(因此现代 CPU 在阶段之间有缓冲区和 uop 缓存会有一些变化是有道理的),但他们描述的避免分支的机制很简洁,可能仍然用于现代 ERMSB:第一个n 复制迭代是后端的谓词微指令,因此可以无条件地发出它们。还有一个 uop 会导致后端将其 ECX 值发送到微码定序器,然后微码定序器使用它来提供正确数量的额外复制迭代。只是复制微指令(可能是 ESI、EDI 和 ECX 的更新,或者可能只在中断或异常时这样做),而不是微码分支微指令。

    这个最初的n uops 与阅读 RCX 后输入更多可能是我看到的 96 字节阈值;它带有一个额外的idq.ms_switches:urep movsb(从 4 个增加到 5 个)。

    https://eprint.iacr.org/2016/086.pdf 建议微码在某些情况下可以触发辅助,这可能是较大副本大小的现代机制,并且可以解释耗尽 RS(显然是 ROB),因为它只会触发当 uop committed(退休)时,它就像一个没有快速恢复的分支。

    执行单元可以通过将事件代码与微操作的结果相关联来发出辅助或发出故障信号。当微操作被提交时(第 2.10 节),事件代码会导致乱序调度程序压缩 ROB 中所有正在运行的微操作。事件代码被转发到微码定序器,它读取相应事件处理程序中的微操作"

    此专利与 P6 专利之间的区别在于,此辅助请求可能发生在来自后续指令的一些非微码微指令已经发出之后,预计微码指令仅在第一批微码指令中完成。或者,如果它不是来自微码的批次中的最后一个微指令,它可以用作选择不同策略的分支。

    但这就是为什么它必须刷新 ROB。

    我对 P6 专利的印象是,对 MS 的反馈发生在后续指令发出微指令之前,以便在需要时及时发出更多的微指令。如果我错了,那么也许它已经是 2016 年论文中描述的相同机制了。


    通常,当一个分支错误预测为被采用时,那么当指令退出

    自从 Nehalem 以来,英特尔实现了“快速恢复”,当预测错误的分支执行时开始恢复,而不是像例外一样等待它退休。

    这是在通常的 ROB 退休状态之上拥有一个分支订单缓冲区的关键,它允许您在任何其他类型的意外事件变得非推测性时回滚。 (What exactly happens when a skylake CPU mispredicts a branch?)


    脚注 1:IceLake 应该具有“快速短代表”功能,这可能是处理 rep 字符串的不同机制,而不是对微码的更改。例如也许像安迪这样的硬件状态机提到他希望自己一开始就设计。

    我没有任何关于性能特征的信息,但是一旦我们知道了一些事情,我们或许能够对新的实现做出一些猜测。

    【讨论】:

    • @PeterCordes @PeterCordes 这个patent 很有趣:摘要说发出指令来计算迭代次数,当它退出时,uop 辅助用于发出迭代次数的 uops。 This link 有一个关于 ucode 的部分,该部分链接了许多有趣的专利,并有证据表明 uop 序列在退休时被触发。 rep movsd 可能只在退休时触发 uop 辅助什么都不做......
    • @MargaretBloom:有趣;不过,这并不是那么简单。那篇论文说字符串指令“可以处理硬件中的小数组,并为更大的数组发出微码辅助”。我尝试在times 40 imul eax,eaxedx 链之间放置rep movsbmovsd 代替lfence(地址和计数在每次迭代时由mov 重置),并且时间上有很大的跳跃(减速: 191c/i 到 289c/i) 从 size=96 字节,无论是 movsd rcx=24 还是 movsb rcx=96。 idq.ms_switches:u 从每迭代 8 个跃升至 10 个。
    • @MargaretBloom:哎呀,这些数字在循环顶部带有 lfence,以隔离每个 rep movs / time T imul / rep movs / times T imul 交互。没有它,95 和 96 字节之间的差异会更加显着(2 个周期的因子),rs_events.empty_end:u 从每次迭代 2(大概rep movs 每次必须运行时都会以某种方式耗尽 RS)变得非常小,平均每迭代 0.003 个。但是other_assists.any:u正好是0,所以它不是字面上和FP助攻形式相同的助攻机制。
    • @MargaretBloom:我更新了How are microcodes executed during an instruction cycle?,提供了更多细节。我认为一些微码微码会导致 RS 耗尽(可能是因为微码分支丢失直到退休才能检测到?)。有了这个,我的解释适合一切。性能事件idq.ms_cyclesidq.ms_uops 的描述支持它:[微码序列器(MS)忙时Uops 传递到指令解码队列(IDQ)]。在我看来,这就像在前端正常输入 uops 时接管问题/重命名。
    • @PeterCordes 非常有趣!在我之前的评论中链接的 SGX 论文提到了 RS 耗尽(如果我没记错的话):“执行单元可以通过将事件代码与微操作的结果相关联来发出辅助或发出故障信号。当微操作被提交(第 2.10 节),事件代码导致乱序调度器压缩所有在 ROB 中进行中的微操作。事件代码被转发到微码排序器,读取相应事件处理程序中的微操作"
    【解决方案2】:

    英特尔已经为一些非常类似于汇编的微码功能申请了专利,其中包括:

    从 L1、L2 或 L3 执行(!!!!!!!!!!!!!!!!!!!!!!!!)。哎呀,他们申请了将“大”微码更新从大容量存储加载到 L3 中然后从那里更新的专利......-请注意,“专利”和“实现”是不同的,我不知道是否他们目前已经实现了除了从 L1 执行之外的任何东西。

    MCU 包中的 Opcode 和 Ucode(!) 部分(统一微处理器更新)——我们称之为“微码更新”的东西,但实际上有/可以有各种各样的东西,包括 PMU 固件更新、MCROM 补丁、非核心在处理器固件/ucode 更新过程之前/之后执行的参数更改、PWC 固件等。

    Ucode 上类似子程序的行为包括参数。条件分支,或者至少是条件循环,他们已经有很长一段时间了。

    微码的压缩和解压缩(未知是否可以直接从压缩状态“运行”,但专利似乎暗示它至少会用于优化 MCU 封装)。

    WRMSR/RDMSR 确实更像是一个 RPC 到 Ucode 中,而不是现在的任何其他东西,我想当他们发现他们需要一个新的 MSR 或对一种架构 MSR 行为(例如 LAPIC 基址寄存器,它必须被“守门”才能解决几年前成为新闻的 LAPIC 内存沉洞 SMM 安全漏洞)。

    因此,只需将其视为实现“公共”指令架构的硬件加速图灵完备 RISC 机器。

    【讨论】:

    • 是的,他们使用wrmsr 作为 Spectre 缓解机制的原因是,微代码更新可以添加一个全新的 MSR,其“处理程序”实际上会刷新分支预测缓存,因为它可以通过 ucode 更新添加它。但是添加一个全新的指令需要修改解码器,并且不能仅通过现有 CPU 的固件更新来完成。
    • 我不确定 RPC 是最好的描述,更好的类比是“系统调用”或管理程序调用来修改运行指令的机器的状态。但是,是的,WRMSR 是一个用于运行任意微代码以修改真正的 uop 执行机制的钩子。
    • 然而,这个问题(我认为)根本不是询问微码更新机制。它只是询问 MS-ROM 是如何工作的。当您说“从 L3 执行”时,您甚至是什么意思?很明显,微码完全在执行核心中,不存储在统一的缓存中,除非在微码更新期间。 我们知道微代码指令的执行是如何工作的:它的 IDQ 条目到达 IDQ 的前面,然后接管问题重命名阶段以从 MS-ROM 而不是 IDQ 读取。不涉及缓存。甚至没有直接的 uop-cache (DSB)。看我的回答。
    • (这里的问题至少有部分答案,但我认为它令人困惑和/或偏离正题。如果你这样介绍它就可以了。)
    • @PeterCordes 谢谢,关于 wrmsr 的好点;我在想,微码更新到底是如何减轻幽灵之类的。我只是真正理解 retpolines 的建议或以其他方式完全修改底层微架构,例如在 IBTB 中使用 PCID
    【解决方案3】:

    我现在知道的是,MSROM 静态预测分支,并将该预测用于下一个 IP 逻辑中的下一个微代码行。这些预测可能已经在存储在 MSROM 中的微指令中提供。

    对于更小和更频繁的 MSROM 例程,复杂的解码器可以发出 1-4 uop,然后将控制权交给 MSROM 以完成解码。否则,它会将控制权延迟传递给 MSROM。

    在优选实施例中,XLAT PLA 510-516 将一些更频繁使用的宏指令解码为微操作序列中的第一个 Cuop 中的一个、两个、三个或四个,这提供了高性能XLAT PLAs 510-516 中额外最小术语的成本。或者,对于一些不太常用的宏指令,四个 XLAT PLA 510-516 不发出 Cuops,而只是让 MS 单元 534 发出所有 Cuops。第二个替代方案的缺点是性能较低(即损失至少一个时钟周期),但可以在 XLAT PLA 510-516 中节省最小项(条目),这是一种设计折衷,可减少芯片空间以较低的性能为代价。这种折衷对于使用频率较低的指令或较长的微码流(其中一个额外时钟的重要性降低)很有用。

    来自宏指令502的操作码被提供给入口点PLA 530,该入口点PLA 530对操作码进行解码以生成进入微码ROM的入口点地址。生成的入口点地址被提供给MS单元534,MS单元534响应于入口点生成一系列Cuop。 MS单元534包括微码ROM(“UROM”),其包括为长指令流提供UROM Cuop的微码例程,在一些示例中这可能需要超过一百个UROM Cuop。 UROM 还包括辅助处理例程和其他微代码。

    其余的在这里回答:https://stackoverflow.com/a/65796517/7194773

    【讨论】:

    • 这听起来与我对 OoO exec(imul 链)在足够短的rep movs 附近发生的观察一致,但在超过某个阈值时根本没有发生。错误预测预测大小
    • 我在某处读到,MS uops 可以在某种程度上被预测,因此来自rep movs 的 uops 的初始突发对于任何大小达到限制的任何大小来说都足够加载/存储,后面的执行为如果结果是 RCX
    • @PeterCordes 我要解决的一件大事是某些微指令是否会停止解码器(或分配器),或者是否没有。还有你认为 uop 接管分配阶段的理论。我正在阅读的所有内容都表明 MSROM 会在线发射微指令,并且它们与来自常规解码器的其他“快速路径”微指令“打包”在一起。 AMD 专利 5,983,337 提到,如果该行仅包含 1 或 2 个微指令,则来自解码器的微指令与 MROM 的最后一行同时发布。 (它还讨论了 MROM 更新、MROM 过程中的异常、标记 MROM 指令等)
    • 我的意思是您的理论,即我不确定的 MSROM 会发出特殊的 uop。我认为IDQ.MS_DSB_UOPS 是来自 MS 的 MS 微指令,由 DSB启动,而不是 MITE 复杂解码器,而不是在 MS 忙于处理时由 DSB 传送到 IDQ 的微指令分配器。 LSD 可以包含来自 MSROM 的微指令,我们看到某些计数器,即idq.ms_uops,它谈论 MS 将微指令传送到 IDQ,因此我们知道 MSROM 微指令被传送到队列而不是直接传送到分配器。
    • 这表明你是正确的,解码器没有停止,并继续向IDQ发出微指令。有 23 个 MITE 微指令和 39 个 MSROM 微指令(我认为 38 个来自第二个 rdpmc)。所有 23 条微指令都发给 IDQ,而第一个 rdpmc 是从 MSROM 发出的。问题仍然是这些微指令如何在 IDQ 中保持程序顺序,这可能是您对它接管分配阶段并绕过 IDQ 的看法是正确的。 INST_RETIRED.ANY 显然是在计算第一个但不是最后一个 rdmpc
    猜你喜欢
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 2014-09-17
    • 2012-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多