x86_64寄存器rax / eax / ax / al覆盖整个寄存器的内容
正如广泛宣传的那样,现代x86_64处理器有64位寄存器,可以以32位寄存器,16位寄存器甚至8位寄存器的后向兼容方式使用,例如:
0x1122334455667788 ================ rax (64 bits) ======== eax (32 bits) ==== ax (16 bits) == ah (8 bits) == al (8 bits)
这样的scheme可以从字面上理解,也就是说,人们总是只能使用指定的名称来访问寄存器的一部分,以便读写,这是非常合乎逻辑的。 实际上,对于高达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
这样的行为对我来说似乎是非常荒谬和不合逻辑的。 它看起来像试图写任何东西,以任何方式导致擦除高32位rax
寄存器。
所以,我有两个问题:
-
我相信这个尴尬的行为必须logging在某处,但我似乎无法find任何地方的详细解释(64位寄存器的高32位是如何擦除的)。 我写的
eax
总是擦拭rax
,还是比较复杂? 它适用于所有的64位寄存器,还是有一些例外?一个强烈的相关问题提到相同的行为,但是,唉,还有没有确切的文献引用。
换句话说,我想要一个指向这个行为的文档的链接。
-
这只是我或者整个事情似乎是非常奇怪和不合逻辑的(例如,eax-ax-ah-al,rax-ax-ah-al有一个行为,rax-eax有另一个行为)? 可能是我在这里错过了一些重要的一点,为什么这样执行?
对“为什么”的解释将不胜感激。
英特尔/ AMD处理器手册中记载的处理器型号对于现代内核的实际执行引擎而言是相当不完美的型号。 尤其是处理器寄存器的概念与现实不符,没有EAX或RAX寄存器这样的东西。
指令解码器的一个主要工作是将传统的x86 / x64指令转换为微处理器( RISC-like processor)的指令。 小指令易于同时执行并能够利用多个执行子单元。 允许多达6条指令同时执行。
为了做到这一点,处理器寄存器的概念也被虚拟化了。 指令解码器从一大堆寄存器中分配一个寄存器。 当指令退役时 ,该dynamic分配的寄存器的值被写回到当前保持RAX值的任何寄存器中。
为了使这些工作顺利高效地进行,允许多条指令同时执行,这些操作之间没有相互依赖关系是非常重要的。 而最糟糕的是寄存器值取决于其他指令。 EFLAGS注册是臭名昭着的,许多指令修改它。
同样的问题,你喜欢它的工作方式。 大问题,它需要两个寄存器值在指令退役时被合并。 创build一个将阻塞核心的数据依赖关系。 通过强制高32位为0,该依赖关系即刻消失,不再需要合并。 扭曲9的执行速度。