90

正如广泛宣传的那样,现代 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任何方式写入任何内容都会导致擦除rax寄存器的高 32 位。

所以,我有两个问题:

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

    一个高度相关的问题提到了相同的行为,但是,遗憾的是,再次没有对文档的确切引用。

    换句话说,我想要一个指向指定此行为的文档的链接。

  2. 只是我还是整个事情看起来真的很奇怪和不合逻辑(即 eax-ax-ah-al、rax-ax-ah-al 有一种行为,而 rax-eax 有另一种行为)?可能是我在这里遗漏了一些关于为什么要这样实施的关键点?

    对“为什么”的解释将不胜感激。

4

1 回答 1

89

Intel/AMD 处理器手册中记录的处理器模型对于现代内核的实际执行引擎来说是一个非常不完美的模型。特别是,处理器寄存器的概念与现实不符,没有 EAX 或 RAX 寄存器之类的东西。

指令解码器的一项主要工作是将遗留的 x86/x64 指令转换为微操作,即类 RISC 处理器的指令。易于并发执行并能够利用多个执行子单元的小指令。允许同时执行多达 6 条指令。

为了实现这一点,处理器寄存器的概念也被虚拟化了。指令解码器从一大堆寄存器中分配一个寄存器。当指令退出时,该动态分配的寄存器的值被写回到当前保存 RAX 值的任何寄存器。

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

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

于 2014-08-22T21:56:16.320 回答