повторная инициализация% rax при очистке.
Оба компилятора обнуляют EAX, потому что в C ++ (и C99) int main()
имеет неявное return 0;
в нижней части функции.
gcc ищет только оптимизацию глазка xor-zeroing при -O2 и выше, , но вы компилируете по умолчанию gcc -O0
(режим отладки /нет оптимизации / даже не храните переменные в регистрах между операторами.) ICC (и clang) используют обнуление xor даже при -O0
.
Оба mov $0, %eax
и xor %eax,%eax
"инициализируют" RAX, т.е.сломать любую зависимость от старого значения.mov $0, %eax
- неэффективный способ.
(или без -g
ICC может по умолчанию установить значение -O2
, но это не меняет выбор пролога / эпилога. Он по-прежнему устанавливает режим быстрой математики ивызывает специальные функции Intel для инициализации в верхней части main
. Вы можете более легко просмотреть вывод asm компилятора в проводнике компилятора Godbolt . Он неявно передает -g
, поэтому ICC там определенно по умолчанию равен -O0
.)
push %rbp
mov %rsp,%rbp
Это создает кадр стека с RBP.GCC -O1
и выше включает -fomit-frame-pointer
, поэтому gcc не будет тратить впустую инструкции на это в обычной функции.
ICC все еще создает кадр стека в main
, потому что он хочет выровнять стек с помощью128. (И кадр стека - это самый простой способ восстановить стек в конце main
после неизвестного смещения, поэтому main
может вернуться).
# ICC stack over-alignment code:
and $0xffffffffffffff80,%rsp # round RSP down to the next multiple of 128
sub $0x80,%rsp # and reserve 128 bytes
# missed optimization: add $-0x80, %rsp could use an imm8 instead of imm32
Я не знаю, почему ICC выравнивает стек в main
.128 - это размер красной зоны в x86-64 SysV ABI, но это может быть совпадением.Это означало бы, что материал, добавленный в main
, не будет беспокоиться о пересечении страниц для местных жителей в красной зоне.(Размер строки кэша составляет 64 ББ, размер страницы - 4 КБ).
ABI System V x86-64 гарантирует только 16-байтовое выравнивание стека, поэтому будущие вызовы функций не сохранят 128-байтовое выравнивание.(GCC не выравнивает стек, потому что main
уже вызывается с 16-байтовым выравниванием стека.)
Если бы вы выбрали любое другое имя функции вместо main
, вы бы нея не вижу много странных вещей.
sub $0x10,%rsp
GCC резервирует 16 байтов стекового пространства для int x,y,z
(и поддерживает выравнивание стека 16 байт после push rbp
).int
занимает 4 байта в x86-64 SysV ABI. GCC хранит их в памяти, потому что вы скомпилировали с отключенной оптимизацией.
Если бы вы скомпилировали с -O2
, g ++ сохранил бы переменную в регистрах и использовал только sub $8, %rsp
для выравниваниястек на 16 после ввода функции (вместо нажатия чего-либо).
Или с -mtune=haswell
или чем-то, я думаю, что недавний gcc мог бы push %rax
вместо использования sub
для выравнивания стека.
leave
против mov %rbp,%rsp
/ pop %rbp
.
GCC предпочитает leave
для удаления кадра стека, если RSP еще не указывает насохраненное значение RBP.(В противном случае он просто использует pop rbp
).
leave
- 3 мопа, согласно тестированию Agner Fog на процессорах Intel, но это может включать в себя стековую синхронизацию при тестировании спина к спине.Я не проверил себя.mov
/ pop
- всего 2 мопа.
leave
мне кажется хорошим выбором для оптимизации;IDK, если в этом случае выбор настроек Intel остался от более старых процессоров, где многопользовательские инструкции могли бы более легко вызвать проблемы с декодированием, или если они действительно протестировали и обнаружили, что для Haswell / Skylake лучше всего две отдельные инструкции.
В конце концов, что такое nopl 0x0(%rax)
?... Я думал, что retq была последней инструкцией main.
RET является последней инструкцией в main
.Long-NOP является частью дополнения между функциями из директивы .p2align 4
компилятора.