【问题标题】:How are microcodes executed during an instruction cycle?在指令周期内如何执行微码?
【发布时间】:2019-10-06 16:06:02
【问题描述】:

从开放资源中我可以得出结论,微码大约是可以由 CPU 直接执行的东西,负责执行指令代码。此外Wikipedia 表示指令代码的每次执行都会经过一个获取-解码-执行指令周期。但是,我找不到任何参考资料来解释在这个三阶段周期中如何完成微码执行。所以我的问题是,微码执行和指令周期的关系是什么?在指令执行的获取、解码和执行阶段,微码是如何工作的?

此外,stackoverflow anwser 表示,在现代 Intel CPU 中,即使是最简单的指令(如 DIVMOV)也会在执行前编译成微码,因此最好有人能用此类 CPU 的示例进行解释,如果这确实是真的。

【问题讨论】:

  • 大多数 mov 变体在大多数微架构上都没有微编码,而 div 实际上是一个非常复杂的指令。你写的答案是关于movs,它与mov完全不同,也相当复杂。
  • 你可能想看youtube.com/…
  • cisc 之类的 intel 往往是微编码的,而 risc 往往不是,有点意思。就像接受“开门”的命令并将其分解为插入钥匙,转动钥匙,转动把手,推门,进入房间,取出钥匙,推门。当您拥有 cisc 时,您可能希望使用内存运算符上的一些偏移量在内存操作数和寄存器之间进行相加,并将结果放入内存,这是处理所有计算内存地址读取的所有步骤操作数,执行加法,将结果存储在内存中。
  • 从历史上看 cisc 是有道理的,看看 Visual 6502 项目,微码存在于 rom 中,所以如果你愿意,你可以设计更简单的微码,如果你更喜欢驱动机器,可以设计状态机,然后希望没有要旋转芯片,您可以更改放入 rom 中的内容。没有意识到他们可以只制造一台简单的机器并将所有代码保留在外面。我们后来学到的东西。
  • fetch 执行解码是原始的,您不时会看到它,但可以将其视为解释它的教科书方式,实际上您可以/确实拥有比这更多的状态。微码本身确实需要以某种形式获取、解码和执行。

标签: assembly cpu cpu-architecture


【解决方案1】:

div 并不简单,它是最难计算的整数运算之一!它是在 Intel CPU 上进行微编码的,与 movadd/sub 甚至 imul 不同,它们在现代 Intel 上都是单微指令。请参阅https://agner.org/optimize/ 获取指令表和微架构指南。 (有趣的事实:AMD Ryzen 没有对div 进行微编码;它只有 2 个微指令,因为它必须写入 2 个输出寄存器。Piledriver 和后来的版本也可以实现 32 位和 64 位除法 2 微指令。)

所有指令都解码为 1 或更多微指令(大多数程序中的大多数指令在当前 CPU 上为 1 微指令)。在 Intel CPU 上解码为 4 或更少微指令的指令被描述为“非微编码”,因为它们不使用特殊的 MSROM 机制来处理多指令指令。


没有将 x86 指令解码为 uops 的 CPU 使用简单的 3 阶段 fetch/decode/exec 循环,因此您问题的部分前提是没有意义的。再次,请参阅 Agner Fog 的微架构指南。

您确定要询问现代英特尔 CPU 吗?一些较旧的 CPU 是内部微编码的,尤其是非流水线 CPU,其中执行不同指令的过程可以以不同的顺序激活不同的内部逻辑块。 控制这一点的逻辑也称为微码,但它与流水线乱序 CPU 上下文中该术语的现代含义不同。

如果您正在寻找,请参阅 retrocomputing.SE 上的 How was microcode implemented in retro processors?,了解 6502 和 Z80 等非流水线 CPU,其中记录了一些微码内部计时周期。 p>


如何在现代 Intel CPU 上执行微编码指令?

当微编码的“间接微指令”到达 Sandybridge 系列 CPU 中的 IDQ 头部时,它会接管发布/重命名阶段,并从微码序列器 MS-ROM 向其提供微指令直到指令发出了所有的微指令,然后前端才能继续向乱序的后端发出其他微指令。

IDQ 是提供问题/重命名阶段的指令解码队列(它将微指令从前端发送到无序的后端)。它缓冲来自 uop 缓存 + 传统解码器的 uop,以吸收气泡和爆裂。这是David Kanter's Haswell block diagram 中的 56 uop 队列。 (但这表明微代码仅在队列之前被读取,这与英特尔对某些性能事件的描述不匹配1,或者对于运行数据相关的微指令数)。

(这可能不是 100% 准确,但至少可以作为大多数性能影响的心理模型2。对于性能可能还有其他解释到目前为止我们观察到的效果。)

这只发生在需要超过 4 个微指令的指令上;在普通解码器中需要 4 次或更少解码来分离 uops 并且可以正常发出的指令。例如xchg eax, ecx 在现代英特尔上是 3 微指令:Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? 详细介绍了我们可以弄清楚这些微指令实际上是什么。

微编码指令的特殊“间接”微指令在解码微指令缓存中占用一整行,即 DSB (potentially causing code-alignment performance issue)。我不确定他们是否只在从 uop 缓存和/或传统解码器 IDQ 提供问题阶段的队列中获取 1 个条目。无论如何,我编造了术语“间接uop”来描述它。它实际上更像是一条尚未解码的指令或指向 MS-ROM 的指针。 (可能一些微码指令可能是一对“普通”微指令和一个微码指针;这可以解释它自己占用一整条微指令缓存线。)

我很确定它们在到达队列头部之前不会完全扩展,因为一些微编码指令是可变数量的微指令,具体取决于寄存器中的数据。值得注意的是rep movs,它基本上实现了memcpy。事实上,这很棘手。根据对齐方式和大小使用不同的策略,rep movs 实际上需要进行一些条件分支。但它跳转到不同的 MS-ROM 位置,而不是不同的 x86 机器代码位置(RIP 值)。见Conditional jump instructions in MSROM procedures?

Intel's fast-strings patent 还揭示了 P6 中的原始实现:第一个 n 复制迭代在后端进行预测;并给后端时间将ECX的值发送给MS。从那以后,如果需要更多,微码定序器可以发送正确数量的复制微指令,而无需在后端进行分支。也许处理几乎重叠的 src 和 dst 或其他特殊情况的机制毕竟不是基于分支,但 Andy Glew 确实提到了缺乏微码分支预测作为实现的一个问题。所以我们知道它们很特别。那是在 P6 天的时候; rep movsb 现在更复杂了。

根据指令的不同,它可能会或可能不会在整理出要执行的操作时耗尽无序后端的预留站(即调度程序)。 rep movs 对大于 96 字节的副本执行此操作不幸的是,在 Skylake 上(根据我对性能计数器的测试,将 rep movs 放在 imul 的独立链之间)。这可能是由于与常规分支不同的错误预测的微码分支。也许分支错过快速恢复对它们不起作用,所以直到它们退休才检测/处理它们? (有关更多信息,请参阅微码分支问答)。


rep movsmov 非常不同。普通的movmov eax, [rdi + rcx*4] 一样是单个微指令,即使具有复杂的寻址模式。 mov 存储是 1 个微融合 uop,包括可以按任一顺序执行的存储地址和存储数据 uop,将数据和物理地址写入存储缓冲区,以便存储可以在指令后提交到 L1d从无序的后端退出并变得非投机性。 rep movs 的微码将包含许多加载和存储微指令。


脚注 1

我们知道 Skylake 上有像 idq.ms_dsb_cycles 这样的表演活动:

[当微码序列器[原文如此] (MS) 忙时,由解码流缓冲区 (DSB) 启动的微指令被传送到指令解码队列 (IDQ) 时的周期]

如果微码只是输入 IDQ 前端的第三种可能的微指令来源,那将毫无意义。但是有一个事件的描述听起来像这样:

idq.ms_switches
[来自 DSB(解码流缓冲区)或 MITE(传统 解码管道)到微码排序器]

我认为这实际上意味着当问题/重命名阶段切换到从微码定序器而不是 IDQ(它保存来自 DSB 和/或 MITE 的 uops)时,它很重要。并不是说 IDQ 会切换其传入微指令的来源。

脚注 2

为了测试这个理论,我们可以构建一个测试用例,在微编码指令之后有很多容易预测到冷 i-cache 行的跳转,并查看前端在跟踪缓存未命中和将微指令排队到大rep scasb 执行期间的 IDQ 和其他内部缓冲区。

SCASB 不支持快速字符串,因此速度非常慢,并且每个周期不会占用大量内存。我们希望它在 L1d 中命中,因此时间是高度可预测的。可能几个 4k 页面足以让前端跟踪大量 i-cache 未命中。我们甚至可以将连续的虚拟页面映射到同一个物理页面(例如,从用户空间在文件上使用mmap

如果微码指令后面的 IDQ 空间可以在执行时被后面的指令填满,这就为前端在需要时从更多 i-cache 行获取更多空间留出了空间。然后,我们可以希望通过运行rep scasb 以及一系列跳转来检测总周期和/或其他性能计数器的差异。在每次测试之前,在包含跳转指令的行上使用clflushopt

要以这种方式测试rep movs,我们可能会使用虚拟内存来将连续页面映射到同一个物理页面,再次为我们提供加载+存储的 L1d 命中,但 dTLB 延迟将难以控制。甚至可以在无填充模式下使用 CPU 启动,但这很难使用,并且需要自定义“内核”才能将结果放在可见的地方。

我非常有信心我们会在微编码指令接管前端时发现微指令进入 IDQ(如果它还没有满的话)。有一个 perf 事件

idq.ms_uops
[微码时将 Uops 传送到指令解码队列 (IDQ) 序列器 (MS) 正忙]

和其他 2 个事件,例如仅计算来自 MITE(旧版解码)的微指令或来自 DSB(微指令缓存)的微指令。英特尔对这些事件的描述与我对微码指令(“间接 uop”)如何接管发布阶段以从微码定序器/ROM 读取 uop 而前端的其余部分继续执行其向IDQ 的另一端,直到填满为止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-13
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-27
    • 2014-07-26
    • 1970-01-01
    相关资源
    最近更新 更多