为什么它使用movl而不是push?

时间:2010-12-26 17:42:35

标签: c assembly stack

注意这段代码:

#include <stdio.h>
void a(int a, int b, int c)
{
    char buffer1[5];
    char buffer2[10];
}

int main()
{
    a(1,2,3); 
}

之后:

gcc -S a.c

该命令在汇编中显示我们的源代码。

现在我们可以在main函数中看到,我们从不使用“push”命令来推送参数 一个函数进入堆栈。它用“movel”代替那个

main:
 pushl %ebp
 movl %esp, %ebp
 andl $-16, %esp
 subl $16, %esp
 movl $3, 8(%esp)
 movl $2, 4(%esp)
 movl $1, (%esp)
 call a
 leave

为什么会这样? 他们之间有什么区别?

5 个答案:

答案 0 :(得分:17)

以下是gcc手册的内容:

-mpush-args
-mno-push-args
    Use PUSH operations to store outgoing parameters. This method is shorter and usually
    equally fast as method using SUB/MOV operations and is enabled by default. 
    In some cases disabling it may improve performance because of improved scheduling
    and reduced dependencies.

 -maccumulate-outgoing-args
    If enabled, the maximum amount of space required for outgoing arguments will be
    computed in the function prologue. This is faster on most modern CPUs because of
    reduced dependencies, improved scheduling and reduced stack usage when preferred
    stack boundary is not equal to 2. The drawback is a notable increase in code size.
    This switch implies -mno-push-args. 

显然,-maccumulate-outgoing-args默认启用,覆盖-mpush-args。使用-mno-accumulate-outgoing-args进行明确编译确实会在此处恢复为PUSH方法。

答案 1 :(得分:8)

该代码只是将常量(1,2,3)直接放在(更新的)堆栈指针(esp)的偏移位置。编译器选择手动执行“推送”,结果相同。

“push”既设置数据又更新堆栈指针。在这种情况下,编译器将其减少到只有堆栈指针的一次更新(相对于三次)。一个有趣的实验是尝试更改函数“a”以只接受一个参数,并查看指令模式是否发生变化。

答案 2 :(得分:6)

gcc可以进行各种优化,包括根据要优化的特定CPU的执行速度选择指令。您会注意到x *= n之类的东西经常被SHL,ADD和/或SUB的混合所取代,尤其是当n是常数时;而MUL仅在SHL-ADD-SUB组合的平均运行时间(和缓存/等脚印)超过MUL的时候使用,或者n不是常量(因此使用带有shl的循环) add-sub会花费更多的钱。)

在函数参数的情况下:MOV可以通过硬件并行化,而PUSH则不能。 (由于esp寄存器的更新,第二个PUSH必须等待第一个PUSH完成。)在函数参数的情况下,MOV可以并行运行。

答案 3 :(得分:2)

这是OS X上的任何机会吗?我在某处读到它要求堆栈指针以16字节边界对齐。这可能解释了这种代码生成。

我找到了这篇文章:http://blogs.embarcadero.com/eboling/2009/05/20/5607

答案 4 :(得分:0)

Pentium指令集没有将常量压入堆栈的指令。所以使用push会很慢:程序必须将常量放入寄存器并推送寄存器:

...
movl $1, %eax
pushl %eax
...

因此编译器检测到使用movl的速度更快。 我想你可以尝试使用变量而不是常量调用函数:

int x;
scanf("%d", &x); // make sure x is not a constant
a(x, x, x);