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

Poo*_*oya 17 c assembly gcc callstack calling-convention

注意这段代码:

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

int main()
{
    a(1,2,3); 
}
Run Code Online (Sandbox Code Playgroud)

之后 :

gcc -S a.c
Run Code Online (Sandbox Code Playgroud)

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

现在我们可以在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
Run Code Online (Sandbox Code Playgroud)

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

Jes*_*ter 18

这是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. 
Run Code Online (Sandbox Code Playgroud)

显然-maccumulate-outgoing-args默认启用,覆盖-mpush-args.显式编译-mno-accumulate-outgoing-args确实会返回PUSH到此方法.

  • 一个更好的问题是为什么这个膨胀的选项`-maccumulate-outgoing-args`不会被`-Os`自动禁用. (4认同)
  • 更新:`-maccumulate-outgoing-args`在1月份被禁用为默认[`-mtune = generic`](https://gcc.gnu.org/ml/gcc-patches/2014-01/msg00008.html) 2014年,现在没有[stack-engines](http://stackoverflow.com/questions/36631576/what-is-the-stack-engine-in-the-sandybridge-microarchitecture)的CPU非常罕见.(可能应该早点完成). (2认同)

Ben*_*tto 8

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

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


use*_*515 6

gcc进行各种优化,包括根据优化的特定CPU的执行速度选择指令.您会注意到,像x *= nSHL,ADD和/或SUB的混合经常会被替换,特别是当n是常数时; 而MUL只在SHL-ADD-SUB组合的平均运行时间(和缓存/等脚印)超过MUL的时候使用,或者n不是常量(因此使用带有shl-add-sub的循环会来昂贵).

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