Подтвердить что ты не робот

Почему gcc использует movl вместо push для передачи аргументов функции?

обратите внимание на этот код:

#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

эта команда показывает наш исходный код в сборке.

теперь мы можем видеть в основной функции, мы никогда не используем команду "push", чтобы проталкивать аргументы функция a в стек. и вместо этого он использовал "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

Почему это происходит? какая разница между ними?

4b9b3361

Ответ 1

Вот что руководство 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, здесь.


Обновление 2019 года: современные процессоры имеют эффективную технологию push/pop по сравнению с Pentium M.
-mno-accumulate-outgoing-args (и использование push) в конце концов стали значением по умолчанию для -mtune=generic в январе 2014 года.

Ответ 2

Этот код просто непосредственно помещает константы (1, 2, 3) в позиции смещения из (обновленного) указателя стека (esp). Компилятор выбирает "push" вручную с тем же результатом.

"push" обе устанавливают данные и обновляют указатель стека. В этом случае компилятор сокращает это только до одного обновления указателя стека (по сравнению с тремя). Интересный эксперимент состоял бы в том, чтобы попытаться изменить функцию "а", чтобы принять только один аргумент, и посмотреть, изменяется ли шаблон инструкции.

Ответ 3

gcc выполняет всевозможные оптимизации, включая выбор инструкций, основанных на скорости выполнения конкретного процессора, для которого оптимизирован. Вы заметите, что такие вещи, как x *= n, часто заменяются сочетанием SHL, ADD и/или SUB, особенно когда n является константой; в то время как MUL используется только тогда, когда средняя продолжительность выполнения (и кеш /etc. footprints) комбинации SHL-ADD-SUB будет превышать значение MUL, или n не является константой (и, таким образом, использует циклы с shl-add- sub будет дороже).

В случае аргументов функции: MOV может быть распараллелирован аппаратным обеспечением, в то время как PUSH не может. (Второй PUSH должен дождаться завершения первого PUSH из-за обновления регистра esp.) В случае аргументов функции MOV могут запускаться параллельно.

Ответ 4

Возможно ли это на OS X? Я где-то читал, что требуется, чтобы указатель стека был выровнен по 16-байтовым границам. Возможно, это объяснит такое генерирование кода.

Я нашел статью: http://blogs.embarcadero.com/eboling/2009/05/20/5607

Ответ 5

Набор инструкций Pentium не имеет инструкции для нажатия константы в стек. Поэтому использование push было бы медленным: программе пришлось бы поместить константу в регистр и нажать регистр:

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

Итак, компилятор обнаруживает, что использование movl выполняется быстрее. Я думаю, вы можете попробовать назвать свою функцию переменной вместо константы:

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