Trywialny funkcja Mam kompilacji z gcc i brzękiem:Dlaczego llvm i gcc używają różnych prologów funkcji na x86 64?
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
I
$ 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
Różnica Jestem zainteresowany jest gcc używa sub rsp, 8
/add rsp, 8
dla funkcji prolog i clang używa push rax
/pop rdx
.
Dlaczego kompilatory używają różnych prologów funkcji? Który wariant jest lepszy? push
i pop
z pewnością koduje krótsze instrukcje, ale czy są one szybsze czy wolniejsze niż add
i sub
?
Powodem, dla którego chodzi przede wszystkim o manipulowanie stertami, wydaje się, że abi wymaga, aby rsp miał 16 bajtów wyrównanych dla procedur bez liści. Nie mogłem znaleźć żadnych flag kompilatora, które je usunęły.
Sądząc z odpowiedzi, wygląda na to, że pop jest lepszy. push rax + pop rdx = 1 + 1 = 2
vs. sub rsp, 8 + add rsp, 8 = 4 + 4 = 8
. Tak więc pierwsza para oszczędza 6 bajtów bez żadnych kosztów.
Jest to kwestia wyboru. Trudno powiedzieć, który wariant jest lepszy. Prawdopodobnie oba warianty są dość podobne pod względem wydajności. –
re: twoja edycja. Tak, ABI gwarantuje, że przy wprowadzaniu funkcji "(% rsp + 8)" jest wyrównane do 16B. (Poprawiłem większość tego komentarza w mojej odpowiedzi). –