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

注意这段代码:

#include  void a(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } int main() { a(1,2,3); } 

之后 :

 gcc -S ac 

该命令在程序集中显示我们的源代码。

现在我们可以在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 

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

这是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,2,3)直接放在(更新的)堆栈指针(esp)的偏移位置。 编译器选择手动执行“推送”,结果相同。

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

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

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

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

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

奔腾指令集没有将常量推入堆栈的指令。 因此使用push会很慢:程序必须将常量放入寄存器并推送寄存器:

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

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

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