在x86-64 Tour of Intel Manuals中,我读到
也许最令人惊讶的事实是,诸如
MOV EAX, EBX
这样的指令会自动将RAX
寄存器的高32位清零。
在同一来源引用的英特尔文档(手册基本体系结构中64位模式下的3.4.1.1通用寄存器)告诉我们:
在目标通用register.
在x86-32和x86-64汇编中,16位指令,如
mov ax, bx
不要表现出这种“奇怪”的行为,即eax的上一个单词被归零。
因此:引入这种行为的原因是什么?乍一看,这似乎不合逻辑(但原因可能是我习惯了x86-32汇编的怪癖)。
发布于 2012-06-24 19:53:08
我不是AMD,也不是他们的代言人,但我也会以同样的方式去做。因为将上半部分置零不会创建对前一个值的依赖关系,所以CPU必须等待。如果不这样做,register renaming机制基本上就会失败。
这样,您就可以在64位模式下使用32位值编写快速代码,而不必总是显式地破坏依赖关系。如果没有这种行为,64位模式中的每一条32位指令都必须等待之前发生的事情,即使高位部分几乎永远不会被使用。(将int
设为64位会浪费缓存空间和内存带宽;x86-64 most efficiently supports 32 and 64-bit operand sizes)
8位和16位操作数大小的行为很奇怪。依赖的疯狂是现在避免16位指令的原因之一。x86-64继承了8086的8位和386的16位寄存器,并决定让8位和16位寄存器在64位模式下的工作方式与在32位模式下的工作方式相同。
有关实际CPU如何处理对8位和16位部分寄存器的写入(以及后续读取完整寄存器)的实际详细信息,请参阅Why doesn't GCC use partial registers?。
发布于 2012-06-24 19:50:14
它只是节省了指令和指令集的空间。您可以使用现有的(32位)指令将较小的立即值移动到64位寄存器。
当MOV EAX, 42
可以重用时,它还使您不必为MOV RAX, 42
编码8字节值。
这种优化对于8位和16位op并不重要(因为它们较小),而且更改规则也会破坏旧代码。
发布于 2020-04-01 03:20:09
如果不将零扩展到64位,这将意味着从rax
读取的指令将对其rax
操作数(写入eax
的指令和之前写入rax
的指令)具有两个依赖项,这将导致partial register stall,当有3个可能的宽度时,这将开始变得棘手,因此它有助于rax
和eax
写入完整寄存器,这意味着64位指令集不会引入任何新的部分重命名层。
mov rdx, 1
mov rax, 6
imul rax, rdx
mov rbx, rax
mov eax, 7 //retires before add rax, 6
mov rdx, rax // has to wait for both imul rax, rdx and mov eax, 7 to finish before dispatch to the execution units, even though the higher order bits are identical anyway
非零扩展的唯一好处是确保包含rax
的高位,例如,如果最初包含0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff通过保证它总是被零扩展到64位,编译器可以在mov rdx, rax
中使用这个公理,而rax
只需要等待它的单个依赖项,这意味着它可以更快地开始执行和退出,从而释放执行单元。此外,它还允许更有效的零习惯用法,如xor eax, eax
to zero rax
,而不需要REX字节。
https://stackoverflow.com/questions/11177137
复制相似问题