为什么x86-64 System V调用约定在寄存器而不是堆栈中传递args?

Al *_*ith 0 assembly gcc x86-64 calling-convention

为什么32位C将所有函数参数直接推送到堆栈上,而64位C将前6个参数放入寄存器而其余的放在堆栈中?

所以32位堆栈看起来像:

...
arg2
arg1
return address
old %rbp
Run Code Online (Sandbox Code Playgroud)

虽然64位堆栈看起来像:

...
arg8
arg7
return address
old %rbp
arg6
arg5
arg4
arg3
arg2
arg1
Run Code Online (Sandbox Code Playgroud)

那么为什么64位C会这样做呢?将所有内容都推送到堆栈而不是将前6个参数放在寄存器中以便将它们移动到函数序言中的堆栈中是不是更容易?

Pet*_*des 5

而不是将前6个参数放在寄存器中,只是为了将它们移动到函数序言中的堆栈中?

我正在查看gcc生成的一些代码,这就是它一直以来所做的.

然后你忘了启用优化. gcc -O0将所有内容溢出到内存中,以便您可以在单步执行时使用调试器对其进行修改.这对于性能来说显然是可怕的,因此编译器不会这样做,除非你通过编译强制它们-O0.

x86-64 System V允许int add(int x, int y) { return x+y; }编译到
lea eax, [rdi + rsi]/ ret,这是编译器实际执行的操作,正如您在Godbolt编译器资源管理器中看到的那样.

Stack-args调用约定很慢且过时.RISC机器一直在使用register-args调用约定,因为在x86-64存在之前,在仍然关心32位x86(即Windows)的操作系统上,有更好的调用约定,例如__vectorcall传递寄存器中的前2个整数args.

i386 System V尚未被替换,因为人们大多不关心其他操作系统上的32位性能; 我们只使用64位代码和精心设计的x86-64 System V调用约定.

有关在调用约定设计中寄存器args和调用保留寄存器与调用寄存器寄存器之间的折衷的更多信息,请参阅为什么不在浮点寄存器中存储函数参数?,以及为什么Windows64在x86-64上使用来自所有其他操作系统的不同调用约定?.