Assembly 为什么此过程的程序集中使用了%rax寄存器和8个参数?

Assembly 为什么此过程的程序集中使用了%rax寄存器和8个参数?,assembly,x86-64,compiler-optimization,cpu-registers,Assembly,X86 64,Compiler Optimization,Cpu Registers,我有以下C函数: void proc(long a1, long *a1p, int a2, int *a2p, short a3, short *a3p, char a4, char *a4p) { *a1p += a1; *a2p += a2; *a3p += a3; *a4p += a4; } 为了简单起见,我将其转换为x86_64汇编,并使用-Og标志最小化优化。它生成以下程序集

我有以下C函数:

void proc(long  a1, long  *a1p,
          int   a2, int   *a2p,
          short a3, short *a3p,
          char  a4, char  *a4p)
{
    *a1p += a1;
    *a2p += a2;
    *a3p += a3;
    *a4p += a4;
}
为了简单起见,我将其转换为x86_64汇编,并使用-Og标志最小化优化。它生成以下程序集:

proc:
        movq    16(%rsp), %rax
        addq    %rdi, (%rsi)
        addl    %edx, (%rcx)
        addw    %r8w, (%r9)
        movl    8(%rsp), %edx
        addb    %dl, (%rax)
        ret
我被装配的第一行弄糊涂了:movq 16%rsp,%rax。我知道%rax寄存器用于存储返回值。但是proc过程没有返回值。所以我很好奇为什么在这里使用这个寄存器,而不是%r9或其他不用于返回值的寄存器

我也很困惑这个指令相对于其他指令的位置。它首先出现,远远早于任何事情都需要它的目标寄存器%rax。实际上,直到最后一步才需要这个寄存器。它也出现在addq%rdi,%rsi之前,这是过程*a1p+=a1;中第一行代码的翻译

我缺少什么?

它只是使用scratch reg加载堆栈arg。RAX是scratch注册的首选。此函数没有返回值,因此RAX不特殊

提前安排加载通常是隐藏加载使用延迟的一个好主意,这样无序的exec就不必那么费力地隐藏它。请记住,这是经过优化的代码,因此每个C语句的指令不是单独的块。对于这样简单的事情,这是很好的未优化的。另见

R9将是一个更糟糕的选择,因为它已经被函数项上的另一个arg占用,限制了指令调度。更重要的是,因为addb%dl,%r9需要REX前缀,而addb%dl,%rax则不需要。因此,这将浪费代码大小

已经在使用的缺点不适用于R10或R11,就像RAX一样,它们是纯调用阻塞的,但不用于arg传递,但代码大小的缺点仍然存在

R9B甚至没有意义;堆栈arg是指针。加载到EDX后,唯一使用的字节寄存器是DL char a4

dword加载可以避免写入部分寄存器,并且不需要movzx/movzbl,因为调用者通常会写入整个qword,或者至少是dword,即使对于窄参数也是如此


编译器也可以更早地移动此加载,但选择不移动。但是添加%dl,%rax是%rax上的RMW,因此在从%rax加载的数据准备就绪之前,不需要dl数据。提前准备好RAX地址比DL数据更有价值,因为该地址正在用于另一个加载,而不是ALU->store。

为什么您认为使用%r9%r9b不是一个选项会比使用%RAX更好?它正在加载一个指针,以便以后可以取消对它的引用。@ErikEidt我的问题不是关于这一行程序集在做什么?,还有关于为什么它特别使用%rax寄存器的更多信息?为什么它出现在代码块的开头,而它所基于的C代码出现在末尾呢?@RossRidge give%rax是存储返回值的首选寄存器,看到它以另一种方式使用,我感到惊讶。这意味着读取反汇编代码需要我们在心里将%rax映射到另一个scratch reg,而不是返回值。我认为另一个寄存器不必是%r9,任何尚未使用的寄存器都可以实现相同的目标,而不需要额外的精神开销。然而,我知道在编译到汇编时,为了提高性能而牺牲可读性是必要的,所以我可能对编译器要求太多了。