以 RAX/EAX/AX/AL/AH 寄存器作为目的地的加法是否更快?

Is an addition with carry faster with RAX/EAX/AX/AL/AH registers as destination?

在英特尔文档中,我们有 ADC 的下一个定义:

Op/En    Operand 1           Operand 2  .....

RM       ModRM:reg (r, w)    ModRM:r/m (r)
MR       ModRM:r/m (r, w)    ModRM:reg (r)
MI       ModRM:r/m (r, w)    imm8
I        AL/AX/EAX/RAX       imm8

现在 asm 代码的一个小例子:

asm (         
    "adc    -Ox12(%rbp), %rax  \n\t"  //1
    "adc    -Ox12(%rbp), %rdx  \n\t"  //2
    "adc    -Ox12(%rbp), %r8   \n\t"  //3
    "adc    -Ox12(%rbp), %R11  \n\t"  //4

    "adc    %r8 , %rdx  \n\t"  //5
    "adc    %r8 , %rax  \n\t"  //6

    "adc     , %rdx   \n\t"  //7
    "adc     , %rax   \n\t"  //8
);

你能告诉我每组中哪条指令最快吗?为什么? 我有这个问题,因为在 Intel 中他们引用 %RAX 寄存器。另一个慢吗?

注意:对于下面的所有内容,我假设是现代 80x86(过去 10 年左右的任何内容)。

对于第一组;第一条指令导致高速缓存未命中或依赖性停顿的可能性(非常小)增加(由 RBP、RAX 或进位标志被引导到它的指令修改引起)。

对于所有其他指令,都依赖于 eflags(它们必须等到知道前一条指令的进位标志),它们都会受到同样的影响。更具体地说,我希望 "carry flag dependency" 将每条指令的执行限制为 1 个周期(没有并行发生的指令)。那是最有可能的瓶颈。

使用的寄存器没有区别(除了依赖于以前使用的寄存器)。

甚至 adc , %rax 也不能有效地使用特殊的 rax-only 编码
REX.W + 15 id ADC RAX, imm32.

  • REX.W + 15 03 00 00 00 是 6 个字节。 (adc rax, imm32)
  • REX.W + 83 mod/rm 03 是 4 个字节。 (adc r/m32, imm8,其中 mod/rm 字节将 rax 编码为目标,reg 字段中的 /2 是操作码的一部分。立即源操作共享第一个操作码字节。)

The (16bit version of) both encodings were introduced with 8086. See the link in the 维基百科。显然,累加器应该一直用于所有事情,and/or 他们没有考虑未来的指令集扩展,所以他们认为在特殊的 al 和 [= 上花费那么多操作码是值得的23=] 所有 ALU 立即数指令的版本。

如果您查看双操作数整数 ALU 指令(andorsubtest 等),每一个都有一个特殊的- 用于 alax/eax/rax 目的地的更短字节编码,具有全尺寸立即操作数。 (即 imm32,而不是 imm8 符号扩展为 32 或 64b)。所以每条指令有两个额外的操作码。

这只影响 x86 代码大小。一旦指令被解码,它们 运行 的方式就没有进一步的区别了。请参阅 http://agner.org/optimize/ 以了解有关 CPU 内部结构的更多信息。

AMD64 可以将这些排除在 64 位模式之外,释放更多编码 space,但他们可能对取消 32 位并不乐观。如果您希望指令在 32 位和 64 位模式下工作,如果两种模式的编码相同,则需要较少的解码器晶体管。不过,他们本可以将编码 space 用于 setcc r32 之类的。没有花哨的新 SIMD 功能,只是取消了一些基本指令。在标志设置操作之前,如果没有 xor,您几乎永远不能使用 setcc 将整个寄存器归零。无论如何,AMD 错过了从 x86 中删除一些杂物的绝好机会。


有趣的事实:在 Broadwell / Skylake(以及更高版本?)上,具有直接编码 adc 的特殊情况 AL/AX/EAX/RAX 实际上 更慢。参见

这也可能适用于早期 Sandybridge / Haswell 上的 adc al,0。 (adc eax, 0 不会使用该编码。)