Eine triviale Funktion mit gcc und Klirren ich kompilieren:Warum verwenden llvm und gcc verschiedene Funktionsprotokolle auf 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
Und
$ 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
Der Unterschied mich interessiert ist, dass gcc sub rsp, 8
verwendet/add rsp, 8
für die Funktion Prolog und Klang verwendet push rax
/pop rdx
.
Warum verwenden die Compiler unterschiedliche Funktionsprologe? Welche Variante ist besser? push
und pop
codiert sicherlich zu kürzeren Anweisungen aber sind sie schneller oder langsamer als add
und sub
?
Der Grund für das Stack-Fiddling an erster Stelle scheint zu sein, dass das ABI erfordert, dass rsp 16 Bytes für Non-Leaf-Prozeduren ist. Ich konnte keine Compilerflags finden, die sie entfernen.
Ausgehend von Ihren Antworten scheint es, Push & Pop ist besser. push rax + pop rdx = 1 + 1 = 2
vs. sub rsp, 8 + add rsp, 8 = 4 + 4 = 8
. So spart das ehemalige Paar 6 Bytes ohne Kosten.
Es ist eine Frage der Wahl. Es ist schwer zu sagen, welche Variante besser ist. Wahrscheinlich sind beide Varianten in Bezug auf die Leistung ziemlich ähnlich. –
re: Ihre Bearbeitung. Ja, der ABI garantiert, dass bei Funktionseingabe "(% rsp + 8)" 16B ausgerichtet ist. (Ich habe den Großteil dieses Kommentars in meine Antwort übernommen). –