为什么 32 位 C 将所有函数参数直接压入堆栈,而 64 位 C 将前 6 个参数放入寄存器,其余参数压入堆栈?
所以 32 位堆栈看起来像:
...
arg2
arg1
return address
old %rbp
虽然 64 位堆栈看起来像:
...
arg8
arg7
return address
old %rbp
arg6
arg5
arg4
arg3
arg2
arg1
那么为什么 64 位 C 会这样做呢?将所有内容压入堆栈,而不是将前 6 个参数放入寄存器中,然后将它们移动到函数序言中的堆栈上,不是更容易吗?
而不是将前 6 个参数放入寄存器中只是为了将它们移动到函数序言中的堆栈上?
我正在查看 gcc 生成的一些代码,这就是它总是做的事情。
然后你忘了启用优化. gcc -O0
将一切都溢出到内存中 https://stackoverflow.com/questions/53366394/why-does-clang-produce-inefficient-asm-with-o0-for-this-simple-floating-point因此您可以在单步执行时使用调试器修改它们。这对于性能来说显然是可怕的,所以编译器不会这样做,除非你通过编译来强制它们这样做-O0
.
x86-64 System V 允许int add(int x, int y) { return x+y; }
编译为
lea eax, [rdi + rsi]
/ ret
,这就是编译器实际执行的操作,如您所见Godbolt 编译器浏览器 https://godbolt.org/z/rG7VTF.
Stack-args 调用约定缓慢且过时。 RISC 机器在 x86-64 存在之前就一直在使用寄存器参数调用约定,并且在仍然关心 32 位 x86 的操作系统(即 Windows)上,有更好的调用约定,例如__vectorcall
传递寄存器中的前 2 个整数参数。
i386 System V 尚未被取代,因为人们大多不太关心其他操作系统上的 32 位性能;我们只是使用 64 位代码和精心设计的 x86-64 System V 调用约定。
有关调用约定设计中寄存器参数和调用保留寄存器与调用破坏寄存器之间的权衡的更多信息,请参阅为什么不将函数参数存储在 XMM 向量寄存器中? https://stackoverflow.com/questions/33707228/why-not-store-function-parameters-in-float-registers,并且为什么 Windows64 使用与 x86-64 上所有其他操作系统不同的调用约定? https://stackoverflow.com/questions/4429398/why-does-windows64-use-a-different-calling-convention-from-all-other-oses-on-x86/35619528#35619528.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)