Соглашение о вызове функций C: почему movl вместо pushl?

Я не понимаю, почему следующие строки используют movl для передачи данных ниже указателя стека, созданного GCC.

movl    -4(%ebp), %eax      # -4(%ebp) <- local variable 1
movl    8(%ebp), %edx       # 8(%ebp)  <- first parameter
movl    %edx, 8(%esp)       # ??? WHY NOT:   pushl %edx
movl    %eax, 4(%esp)       # ??? WHY NOT:   pushl %eax
movl    -8(%ebp), %eax      # ??? WHY NOT:   pushl -8(%ebp)
movl    %eax, (%esp)
call    athena
movl    %eax, f

(полный код)

Я предполагаю, что этот код пытается передать 3 параметра для вызова функции. Но почему он не использует pushl. Каково использование этого кода и как это работает?


person miho    schedule 26.04.2014    source источник
comment
Я не знаю, почему это так работает. Может потому, что код похож на x86-64, в котором вы передаете параметры через регистры? (так, например, они будут иметь только значение загрузки в.. инструкцию). Кроме того, вы видели этот вопрос? stackoverflow.com/questions/22267767/   -  person Marco    schedule 26.04.2014
comment
PUSH — это устаревшая инструкция, которая плохо работает на современных ядрах. Он имеет косвенную зависимость от значения регистра ESP. Это затрудняет выполнение не по порядку. С МОВ таких проблем нет. Как указано в руководстве по оптимизации Intel, правило кодирования 25.   -  person Hans Passant    schedule 26.04.2014
comment
Здесь чего-то не хватает? Я ожидал бы, что где-то до того, как movels будет sub %esp,xxx, где xxx - это размер передаваемых параметров? А затем, когда звонок возвращается вскоре после этого, add %esp,xxx?   -  person lurker    schedule 26.04.2014
comment
Также обратите внимание, что он не ниже указателя стека, а на самом деле выше. В gcc есть опции для управления этим поведением, см. -mpush-args и -maccumulate-outgoing-args в руководстве. Они также дают некоторые пояснения там.   -  person Jester    schedule 26.04.2014


Ответы (1)


Ганс Пассант ответил правильно. Коды операций push/pop можно разбить на две микрооперации, которые выполняют перемещение памяти и увеличение/уменьшение указателя стека. Если указатель стека — или любой другой указатель — обновляется, а затем сразу же используется в следующем коде операции, обычно происходит остановка выполнения. При доступе к отдельным ячейкам памяти через указатель стека - как в вашем примере - не будет остановки, и операции можно будет связать, что позволит им выполняться одновременно.

Любой суперскалярный тип ЦП попытается выполнить несколько кодов операций за один цикл, если их результаты/источники не имеют ничего общего друг с другом. Компилятор делает для вас кое-что, чтобы ускорить выполнение, что было бы довольно трудоемко сделать вручную. Коды операций могут занимать больше места, чем push-уведомления, но они будут выполняться примерно в два раза быстрее — при прочих равных условиях.

person user3575124    schedule 26.04.2014