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