在一条指令中使用与操作数相同的寄存器是否合法?

3 assembly x86-64 instruction-set instructions

例如,下面是一条 X86-64 指令:

movq (%rdi),%rdi
Run Code Online (Sandbox Code Playgroud)

它读取内容(由 指向%rdi)并将此内容设置为%rdi,这在时序逻辑中是否有效?

或者我们是否必须将其分配给不同的寄存器并将其移回:

movq (%rdi), %rsi
movq rsi, %rdi
Run Code Online (Sandbox Code Playgroud)

Eri*_*idt 5

在同一条指令中重新调整寄存器的用途不仅合法而且非常普遍。

在机器代码中,CPU 寄存器速度快,因此是首选存储,但数量有限。而在高级语言中,您不能用完变量(在需要时声明一个新变量)——编译器和汇编语言程序员倾向于将使用不重叠的变量映射到同一个存储。

此外,在机器代码中,我们会看到许多在高级语言中看不到的短期变量——它们被称为临时变量或表达式临时变量。像a[i]机器代码这样的简单表达式需要计算a + i * 4,这可能需要一个临时变量来保存ai或者i * 4等等。这些临时变量是短期变量,如果在后续表达式中不再需要它们,那么保存它们的 CPU 寄存器可以立即重新用于其他用途。


Pet*_*des 5

某些 ISA 对某些指令有限制,例如较旧的 ARM 在将相同的 reg 用于umull. 这些危险总是记录在指令集参考手册1 中

Intel 和 AMD 的 x86 手册没有记录任何此类限制(push sp8086 / 186 2除外),因此您始终可以假设 x86-64 指令在写入任何输出操作数之前读取其所有输入操作数,就像您期望的那样. (无论如何,这都是 CPU 内部管道的正常状态。)


脚注 1:例如,Keil 的 ISA 参考UMULL{S}{cond} RdLo, RdHi, Rn, Rm 说明
Rn 必须与 ARMv6 之前的体系结构中的 RdLo 和 RdHi 不同。
(该指令执行全乘法,如 x86 mul,但具有显式输出对而不是 EDX:EAX,并且具有 2 个显式源。)

如果 ISA 存在任何奇怪的限制,通常涉及乘法;例如 MIPS 也有很多奇怪的地方multu及其 HI:LO 寄存器对。这个旧的 MIPS R3000 手册解释说,即使发生中断,一个multuright after anmflo仍然可以启动,从而导致从中断恢复后的损坏状态。在开始另一个乘法之前,您需要通过 2 条指令将一个乘法结果读取回正常的 reg。另请参阅Raymond Chen 的博客

这些软件“危害”与https://en.wikipedia.org/wiki/Hazard_(computer_architecture)是同一种概念,但它们是硬件无法处理的情况,而不是无序的情况exec 需要检测。

x86 没有任何这些软件危害;我只提到其他 ISA 作为比较和兴趣点。

脚注2

早期的 x86 CPU 是微码的,而不是流水线的,而微码的实现push实际上对最早的 x86 CPU 暴露了一种奇怪的效果。请参阅英特尔手册条目中的 pre-286 注释push

对于 Intel 286 及以上的 IA-32 处理器,PUSH ESP 指令将 ESP 寄存器的值压入在执行指令之前存在的值。(对于 IA-32 架构的 Intel 64 架构、实地址和虚拟 8086 模式也是如此。)对于 Intel® 8086 处理器,PUSH SP 指令推送 SP 寄存器的新值(即它已经减少了 2)。

但是对于 286 及更高版本(包括所有 x86-64 CPU),它的工作方式就像在写入任何操作数之前读取所有操作数一样,包括 forpush rsppush [rsp + 8]

PUSH ESP 指令压入 ESP 寄存器的值,因为它在指令执行之前就存在。如果 PUSH 指令使用内存操作数,其中 ESP 寄存器用于计算操作数地址,则在 ESP 寄存器递减之前计算操作数的地址。

pop还具有记录的事实pop rsp确实堆栈指针减量第一,写入负载结果到寄存器之前。所以pop rsp= mov rsp, [rsp],没有add rsp, 8之后。(这在文本中有描述。英特尔的伪代码不使用临时代码,因此它没有正确描述这种特殊情况。)


顺便说一句,您可以在编译器输出中找到很多类似的指令示例。例如,当源是具有寄存器寻址模式的内存操作数时,您正在通过链表或树进行指针追踪(如果这是在循环中,否则您只是在遍历间接级别时重用寄存器) .

int foo(int ****p) {
   int tmp = ****p;
   return tmp * 2;
}
Run Code Online (Sandbox Code Playgroud)

clang 10.1 -O3 为 x86-64编译:

foo(int****):
        mov     rax, qword ptr [rdi]
        mov     rax, qword ptr [rax]
        mov     rax, qword ptr [rax]         # write-only destination
        mov     eax, dword ptr [rax]
        add     eax, eax                     # EAX += EAX,  read-write destination
        ret
Run Code Online (Sandbox Code Playgroud)