*移动消除*插槽在 Intel CPU 中如何工作?

Mar*_*oom 3 x86 intel cpu-architecture

Andreas Abel 和 Jan Reineke在他们描述 uiCA 的论文中讨论了移动消除

4.1.4移动消除。[...] 然而,这种消除动作并不总是成功的。[...]我们开发了微基准,使用这些计数器来分析移动消除何时成功。[...]
以下模型与我们的观察一致。处理器跟踪由多个架构寄存器使用的物理寄存器。我们说每个这样的物理寄存器占用一个消除槽。当相应的寄存器被覆盖后,消除槽被再次释放。* 一个周期内可以消除的移动指令数量既取决于可用消除槽的数量,也取决于前一个周期中成功消除的数量。

我对我不明白的部分进行了强调。

我认为给定的物理寄存器只能由单个体系结构寄存器从重命名到退出使用。我认为文本的含义暗示了其他情况,所以我正在努力理解移动消除槽是如何工作的(此时甚至寄存器重命名实际上是如何工作的)。

Pet*_*des 5

mov-elimination 的全部要点在于,不是分配一个新的 PRF 条目(物理寄存器文件)并运行 uop 来读取该值并将其写入该新条目(就像在Alder Lake P 核又名 Golden Cove 之前的lea rdx, [rcx+0]CPU 上那样)) ),mov rdx, rcx可以通过让 RDX 的 RAT 条目(寄存器分配表)指向与 RCX 此时相同的物理寄存器号来处理。

因此,整个想法是改变 PRF 条目在某个时刻作为单个架构寄存器的状态的规则。这可能会使跟踪 PRF 条目何时可以被释放、或者当两个架构寄存器都引用相同的物理寄存器时重命名稍后的微指令或其他一些复杂情况变得更加复杂。

“移动消除槽”是单独的资源,而不是 PRF 条目。它们的存在是为了解决英特尔遇到的任何额外的跟踪问题。mov当您覆盖稍后的目标时,移动消除槽将被释放,例如mov ecx, edx/not ecx立即释放所需的任何移动消除资源。

如果没有移动消除,你对它的工作原理是正确的;一个 PRF 条目保存仅写入一个架构寄存器的值,并且是在覆盖该寄存器之前读取该寄存器的任何微指令的输入依赖项。

除了 PRF 条目还为 FLAGS 条件代码留出空间外,因此在类似的指令add eax, ecx写入 FLAGS 和整数寄存器之后,RFLAGS 和 RCX 都指向相同的物理寄存器。后面的指令如mov-immediate, notorlea可以覆盖 gp 寄存器,只留下 CF 和 SPAZO 组 FLAGS 指向旧的物理寄存器。指令如cmpstc、 或add [mem], eax写入(部分)FLAGS 之类的

但这只是两件事(FLAGS、CF 和 SF/PF/AF/ZF/OF 又名 SPAZO的单独重命名部分)可能仍然可以引用 phys 寄存器,而不是 GP 整数寄存器。每个 phys reg 可能有 1 位来跟踪它是否仍然被 GP-integer reg 引用,当退役写入 GP-integer 寄存器的 uop 时,退役可以正确释放它们,也许只需检查 RAT 条目的退役状态对于标志。或者,也许每个 PRF 条目都有 3 位,每个 GP 整数、CF 和 SPAZO,作为退出的一种方式,以确定何时可以释放物理寄存器(当它退出一个微指令,该微指令会覆盖最后一个架构引用)它。)

BeeOnRope 建议,移动消除槽实际上是引用计数,而不是在每个 PRF 条目中进行完整的引用计数(在mov ecx, eax/ / ... 的情况下计数器最多可以计数到 15)。mov edx, eax

异或归零总是可以被消除,因为物理零寄存器永远不需要被释放,所以它不需要被引用计数。(整数和向量的物理零寄存器的存在是从 SnB 系列能够消除零微指令这一事实推断出来的。)


相关:x86的MOV真的可以“免费”吗?为什么我根本无法重现这个?其中提到了英特尔优化手册中提到的一些内容,即希望尽快覆盖寄存器副本的结果,以提高 mov-elimination 的成功率。但至少当时Intel并没有提及涉及哪些CPU资源限制的细节。

Skylake 比 Ivy Bridge 有更多的 mov 消除槽,因为我的测试表明它不会在测试用例中遇到瓶颈,他们用来说明及时覆盖 mov 的好处。

非常不幸的是,英特尔搞砸了 Ice Lake / Tiger Lake,不得不通过微码更新禁用其 mov-elimination(对于 GP-integer),因为覆盖了mov通常意味着它是关键路径延迟的一部分,与如果您的代码可以在没有 mov-elimination 的情况下在 CPU 上运行,您希望这样做。它在 Alder Lake 和 Rocket Lake 再次发挥作用。

在许多情况下,您很快就会覆盖副本和原始文件,因此可以在几条指令中保持目的地不变。理想情况下,避免长期不修改副本,除非这会花费更多的微指令或使 Ice Lake 上的关键路径延迟更糟。(例如,如果您保存副本并且只读取它。)下一个中断通常会导致所有寄存器无论如何都被保存/恢复,因此即使对于具有一些长的代码,这也不是一个可以“建立”的问题运行带有许多 mov 消除副本的循环。