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

时间:2016-10-19 21:21:14

标签: assembly optimization x86 micro-optimization

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

我从各种来源获悉,部分注册更新(如ax而不是eax)可能导致eflags停顿并降低性能。但我不确定长模式。对于此处理器操作模式,哪个寄存器大小被视为原生? x86-64仍然是x86架构的扩展,因此我相信32位仍然是原生的。或者我错了吗?

例如,

等说明
sub eax, r14d

sub rax, r14

具有相同的尺寸,但在使用其中任何一种时可能会有任何处罚吗? 在如下连续指令中混合寄存器大小时可能会有任何处罚吗? (假设高dword在所有情况下均为零)

sub ecx, eax
sub r14, rax

1 个答案:

答案 0 :(得分:9)

  

在连续指令中混合使用32位和64位寄存器时,是否会有任何处罚?

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

  

因此我相信32位仍然是原生的。

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

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

  

部分寄存器更新(如ax而不是eax)可能导致eflags停滞并降低性能。

部分标志档位与部分档案档位分开。它们在内部进行类似处理(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

Zeroing EAX ahead of the flag-setting and setcc with xor eax,eax avoids the partial-register penalty entirely。 (Core2 / Nehalem比早期的CPU停止的周期更少,但在插入合并的uop时仍然会停止2或3c.Sandybridge在插入合并uop时根本不会失速。)

(关于不同CPU的部分寄存器处罚的另一个摘要:Why doesn't GCC use partial registers?,说的基本相同)。

AMD在以后读取完整寄存器时不会遇到部分寄存器停顿,而是部分寄存器写入和读取对整个寄存器具有错误的依赖性。 (AMD CPU不会首先单独重命名子寄存器。英特尔P4和Silvermont / Knight的登陆方式相同。)

英特尔Haswell / Skylake(也许是Ivybridge)根本不会alrax分开重命名,因此他们永远不需要合并low8 / low16寄存器。但是setcc al对旧值具有错误的依赖性。他们仍然重命名并合并ah。 (的 Details on HSW/SKL partial-reg performance 即可。)

# 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

有关英特尔预Sandybridge与Sandybridge之间部分标记问题的更多讨论,请参阅this Q&A

另请参阅Agner Fog's microarch pdf以及标记wiki中的其他链接,了解有关所有这些内容的详细信息。