Передача аргументов в x86-64 System V ABI использует 8-байтовые «слоты» в стеке для аргументов, которые не помещаются в регистры.Все, что не кратно 8 байтам, будет иметь дыры (заполнение) перед следующим аргументом стека.
Это довольно стандартно для соглашений о вызовах для операционных систем / архитектур.Передача short
в 32-битном соглашении о вызовах будет использовать 4-байтовый слот стека (или займет весь 4-байтовый регистр, независимо от того, будет ли он расширяться до полной ширины регистра).
Ваши последние два вопроса действительно задают одно и то же:
Вы компилируете без оптимизации, поэтому для согласованной отладки каждой переменной, включая аргументы функции, нужен адрес памяти, где отладчик мог бы изменить значение при остановкев точке останова.Сюда входят main
argc
и argv
, а также аргументы регистра f1
.
Если вы определили main
как int main(void)
(что является одной из двух действительных сигнатур)для main
в реализациях на хосте C, другое - int main(int argc, char**argv)
), не будет входящих аргументов для разлива main.
Если вы скомпилировали с включенной оптимизацией, там 'не будь ничего из этого дерьма .См. Как удалить "шум" из выходных данных сборки GCC / clang? для предложений о том, как заставить компиляторы создавать asm, на которые приятно смотреть.например, из проводника компилятора Godbolt , скомпилированного с gcc -O3 -fPIC
1 , вы получите:
f1:
addl %esi, %edi # a2, tmp106 # tmp106 = a1 + a2
movl 8(%rsp), %eax # a7, tmp110
addl %edx, %edi # a3, tmp107
addl %ecx, %edi # a4, tmp108
addl %r8d, %edi # a5, tmp109
addl %r9d, %edi # a6, tmp110
addl %edi, %eax # tmp110, tmp110
addl 16(%rsp), %eax # a8, tmp112
addl 24(%rsp), %eax # a9, tmp113
addl $7, %eax #, tmp105 # c+d = constant 7
ret
(я использовал синтаксис AT & T вместо Intel, потому что вы использовали этов вашем вопросе)
IDK, почему именно gcc резервирует несколько больше стекового пространства, чем на самом деле нужно;это иногда происходит даже при включенной оптимизации.например, gcc main
выглядит следующим образом:
# gcc -O3
main:
subq $16, %rsp # useless; the space isn't used and it doesn't change stack alignment.
movl $6, %r9d
movl $5, %r8d
movl $4, %ecx
pushq $9
movl $3, %edx
movl $2, %esi
movl $1, %edi
pushq $8
pushq $7
call f1@PLT
xorl %eax, %eax # implicit return 0
addq $40, %rsp
ret
Вся лишняя хрень, которая происходит в вашей версии функции, является следствием антиоптимизаций, необходимых для согласованной отладки, которую вы получаетесо значением по умолчанию -O0
. (Последовательная отладка означает, что вы можете set
переменную, когда она остановлена на точке останова, и даже jump
для другой строки источника внутри той же функции, и программа все равно будет работать и работать каквы можете ожидать от абстрактной машины C. Таким образом, компилятор не может хранить ничего в регистрах между операторами или оптимизировать на основе чего-либо, кроме литеральных констант внутри оператора.)
-O0
также означает быструю компиляциюи не пытайтесь эффективно распределить пространство стека.
Сноска 1: -fPIC
не позволяет gcc оптимизировать вызов в main
.
Без этого, даже если__attribute__((noinline))
, он может видеть, что у функции нет побочных эффектов, поэтому он может просто пропустить вызов вместо того, чтобы включить его и оптимизировать.
Но-fPIC
означает создание кода для разделяемой библиотеки, что (при нацеливании на Linux) означает, что возможно взаимное расположение символов, поэтому компилятор не может предположить, что call f1@plt
фактически вызовет это определение f1
, итаким образом, не может оптимизировать, основываясь на отсутствии побочных эффектов.
clang, очевидно, предполагает, что он все еще может оптимизировать таким образом даже с -fPIC
, поэтому я предполагаю, что clang предполагает, что конфликтующие определения одной и той же функции недопустимыили что-то?Это может нарушить переопределения библиотечных функций LD_PRELOAD для вызовов из библиотеки.