x86_64寄存器rax / eax / ax / al覆盖完整寄存器内容

时间:2014-08-22 20:57:47

标签: assembly x86-64 cpu-registers zero-extension

正如广泛宣传的那样,现代x86_64处理器具有64位寄存器,可以向后兼容的方式用作32位寄存器,16位寄存器甚至8位寄存器,例如:

0x1122334455667788
  ================ rax (64 bits)
          ======== eax (32 bits)
              ====  ax (16 bits)
              ==    ah (8 bits)
                ==  al (8 bits)

这样的方案可以从字面上理解,即,总是可以使用指定的名称仅访问寄存器的一部分用于读取或写入目的,并且这将是高度逻辑的。实际上,对于高达32位的所有内容都是如此:

mov  eax, 0x11112222 ; eax = 0x11112222
mov  ax, 0x3333      ; eax = 0x11113333 (works, only low 16 bits changed)
mov  al, 0x44        ; eax = 0x11113344 (works, only low 8 bits changed)
mov  ah, 0x55        ; eax = 0x11115544 (works, only high 8 bits changed)
xor  ah, ah          ; eax = 0x11110044 (works, only high 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  al, al          ; eax = 0x11112200 (works, only low 8 bits cleared)
mov  eax, 0x11112222 ; eax = 0x11112222
xor  ax, ax          ; eax = 0x11110000 (works, only low 16 bits cleared)

然而,一旦我们得到64位的东西,事情似乎相当尴尬:

mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  eax, 0x55556666         ; actual:   rax = 0x0000000055556666
                             ; expected: rax = 0x1111222255556666
                             ; upper 32 bits seem to be lost!
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
mov  ax, 0x7777              ;           rax = 0x1111222233337777 (works!)
mov  rax, 0x1111222233334444 ;           rax = 0x1111222233334444
xor  eax, eax                ; actual:   rax = 0x0000000000000000
                             ; expected: rax = 0x1111222200000000
                             ; again, it wiped whole register

这种行为对我来说似乎非常荒谬和不合逻辑。看起来试图通过任何方式向eax写任何内容都会导致擦除高{32} rax寄存器。

所以,我有两个问题:

  1. 我相信这种尴尬的行为必须记录在某处,但我似乎无法在任何地方找到详细的解释(64位寄存器的高32位是如何被擦除的)。我是否正确写eax总是擦rax,或者它更复杂?它是否适用于所有64位寄存器,或者有一些例外?

    strongly related question提到了同样的行为,但是,唉,再次没有对文档的确切引用。

    换句话说,我想要指明此行为的文档链接。

  2. 这只是我或整个事情似乎真的很奇怪和不合逻辑(即eax-ax-ah-al,rax-ax-ah-al有一种行为,而rax-eax有另一种行为)?可能我在这里错过了一些重要的观点,为什么它会像那样实施?

    解释"为什么"我将非常感激。

1 个答案:

答案 0 :(得分:70)

英特尔/ AMD处理器手册中记录的处理器型号对于现代核心的真实执行引擎来说是一个非常不完美的模型。特别是,处理器寄存器的概念与现实不匹配,没有EAX或RAX寄存器这样的东西。

指令解码器的一个主要工作是将传统的x86 / x64指令转换为类似RISC的处理器的 micro-ops 指令。易于同时执行并能够利用多个执行子单元的小指令。允许同时执行多达6条指令。

为了实现这一目标,处理器寄存器的概念也是虚拟化的。指令解码器从一大堆寄存器中分配寄存器。当指令 retired 时,该动态分配寄存器的值将被写回当前包含RAX值的任何寄存器。

为了使这项工作顺利有效地进行,允许许多指令同时执行,这些操作不具有相互依赖性是非常重要的。最糟糕的是,寄存器值取决于其他指令。 EFLAGS寄存器是臭名昭着的,很多指令都会修改它。

喜欢它的工作方式相同的问题。很大的问题,它需要在指令退役时合并两个寄存器值。创建一个阻塞核心的数据依赖关系。通过强制高32位为0,该依赖性立即消失,不再需要合并。 Warp 9执行速度。