为什么GCC使用Mov而不是推入函数调用?

所以我有这个C程序示例。

int worship(long john) { return 0 * john; } int main() { return worship(666); } 

该程序集(基本上)看起来像这样:

 worship(long): pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movl $0, %eax popq %rbp ret main: pushq %rbp movq %rsp, %rbp movl $666, %edi call worship(long) popq %rbp ret 

我在阅读堆栈粉碎时遇到了这个问题。 在assemblyworship(long):它说movq %rdi, -8(%rbp)我希望它是基于我到目前为止阅读的所有内容使用pushq 。 这是GCC将参数推入堆栈的新方法吗?如果有的话,我可以使用编译器标志来切换它吗?

GCC手册说,

 -mpush-args 

在调用函数时,推送指令将用于传递传出参数。 默认情况下启用。

 -mno-push-args 

使用PUSH操作存储传出参数。 此方法较短,通常与使用SUB / MOV操作的方法一样快,默认情况下启用。 在某些情况下,由于改进了调度并减少了依赖性,因此禁用它可能会提高性能。

 -maccumulate-outgoing-args 

如果启用,将在函数序言中计算传出参数所需的最大空间量。 这在大多数现代CPU上更快,因为当优选的堆栈边界不等于2时,依赖性降低,调度改进并且堆栈使用减少。缺点是代码大小显着增加。 这个开关意味着-mno-push-args。

偶数-mpush-args默认启用它会被-maccumulate-outgoing-args覆盖,默认情况下启用它。 明确编译传递选项-mno-accumulate-outgoing-args可能会更改要push的指令。

像GCC这样的编译器是由那些非常仔细地考虑如何使经常使用的代码片段(如函数调用/返回)尽可能高效的人编写的。 当然,他们的解决方案针对一般情况,在特殊情况下可能会有更好的选择。