Тривиальная функция, которую я компилирую с помощью gcc и clang:
void test() {
printf("hm");
printf("hum");
}
$ gcc test.c -fomit-frame-pointer -masm=intel -O3 -S
sub rsp, 8
.cfi_def_cfa_offset 16
mov esi, OFFSET FLAT:.LC0
mov edi, 1
xor eax, eax
call __printf_chk
mov esi, OFFSET FLAT:.LC1
mov edi, 1
xor eax, eax
add rsp, 8
.cfi_def_cfa_offset 8
jmp __printf_chk
И
$ clang test.c -mllvm --x86-asm-syntax=intel -fomit-frame-pointer -O3 -S
# BB#0:
push rax
.Ltmp1:
.cfi_def_cfa_offset 16
mov edi, .L.str
xor eax, eax
call printf
mov edi, .L.str1
xor eax, eax
pop rdx
jmp printf # TAILCALL
Разница, которая меня интересует, заключается в том, что gcc использует sub rsp, 8
/add rsp, 8
для пролога функции, а clang использует push rax
/pop rdx
.
Почему компиляторы используют разные прологи функций? Какой вариант лучше? push
и pop
, безусловно, кодируют более короткие инструкции, но они быстрее или медленнее, чем add
и sub
?
Причина возни со стеком в первую очередь, по-видимому, заключается в том, что abi требует, чтобы rsp был выровнен по 16 байтам для неконечных процедур. Я не смог найти какие-либо флаги компилятора, которые их удаляют.
Судя по вашим ответам, кажется, что push & pop лучше. push rax + pop rdx = 1 + 1 = 2
против sub rsp, 8 + add rsp, 8 = 4 + 4 = 8
. Таким образом, первая пара экономит 6 байт без каких-либо затрат.
(%rsp + 8)
выровнено по 16B. (Я отредактировал большую часть этого комментария в своем ответе). - person Peter Cordes   schedule 21.07.2015