在 Long 模式下使用 64/32 位寄存器时可能会有任何惩罚吗?

May there be any penalties when using 64/32-bit registers in Long mode?

可能这甚至不是微优化而是纳米优化,但我对这个主题很感兴趣,我想知道在长模式下使用非本机寄存器大小时是否有任何惩罚?

我从各种来源了解到,部分寄存器更新(如 ax 而不是 eax)会导致 eflags 停顿并降低性能。但我不确定长模式。对于这种处理器操作模式,什么寄存器大小被认为是本机的? x86-64 仍然是 x86 架构的扩展,因此我相信 32 位仍然是原生的。还是我错了?

例如,指令如

sub eax, r14d

sub rax, r14

具有相同的大小,但使用其中任何一个时可能会有任何处罚吗? 在像下面这样的连续指令中混合寄存器大小时可能会有任何惩罚吗? (假设高位双字在所有情况下均为零)

sub ecx, eax
sub r14, rax

May there be any penalties when mixing 32 and 64-bit register sizes in consecutive instructions?

No, writing to a 32-bit register always zero-extends to the full register,因此 x86-64 避免了 32 位和 64 位指令的任何部分寄存器惩罚。

thus I believe 32 bits are still native.

是的,大多数指令的默认操作数大小为 32 位 (other than PUSH/POP)。 64 位需要 W 位设置为 1 的 REX 前缀。因此出于代码大小的原因更喜欢 32 位。这就是编译器使用 mov r32, imm32 作为静态数据地址的原因(因为默认代码模型要求代码和静态数据地址位于虚拟地址 space 的低 2GiB 中)。

这是 AMD 的设计选择。他们本可以选择其他方式,并需要一个前缀来获得 32 位操作数大小。由于长模式是一种单独的模式,因此 x86-64 机器代码可以与 x86-32 机器代码不同,但它需要。 AMD 选择最小化差异,以便他们可以在解码器中共享尽可能多的晶体管。你的结论是正确的,但是你的推理完全是假的。


partial register updates (like ax instead of eax) can cause eflags stall and degrade performance.

部分标志停顿与部分寄存器停顿分开。它们在内部的处理方式类似(EFLAGS 的单独重命名部分必须合并,就像修改后的 AX 必须与 EAX 的未修改高位字节合并一样)。 但一个不会导致另一个

# partial-reg stall
setcc   al           # leaves the upper 3 (or 7) bytes unmodified
add     edx, eax     # reads full EAX.  Older CPUs stall while merging

。 (Core2/Nehalem 停顿的周期比早期的 CPU 少,但在插入合并 uop 时仍会停顿 2 或 3c。Sandybridge 在插入合并 uop 时根本不会停顿。

(不同CPU上部分寄存器惩罚的另一个总结:,说的基本一样)。

AMD 在稍后读取完整寄存器时不会遭受部分寄存器停顿,而是部分寄存器写入和读取对完整寄存器有错误的依赖性。 (AMD CPU 本来就不单独重命名子寄存器。Intel P4 和 Silvermont / Knight's Landing 都是一样的。)

Intel Haswell/Skylake(也许还有 Ivybridge)根本不会将 alrax 分开重命名 ,因此他们永远不需要合并 low8 / low16 寄存器。但是 setcc al 对旧值有错误的依赖。他们仍然重命名并合并 ah。 (.)


# partial flag stall when reading a flag that didn't come from
# the last instruction to write any flags.
clc
# edi and esi = one-past-the-end of dst and src
# ecx = -count
bigInt_add:
    mov   eax, [esi+ecx*4]
    adc   [edi+ecx*4], eax   # reads CF, partial flag stall on 2nd and later iterations
    inc   ecx                # writes all flags except CF
    jl    bitInt_add         # loop upwards towards zero

有关 Intel pre-Sandybridge 与 Sandybridge 的部分标志问题的更多讨论,请参阅


另请参阅 Agner Fog's microarch pdf, and other links in the 标签 wiki,了解有关所有这些的更多详细信息。