Una funzione banale sto compilazione con gcc e clang:Perché llvm e gcc usano prologi di funzioni differenti su 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
E
$ 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
La differenza che mi interessa è che gcc usa sub rsp, 8
/add rsp, 8
per la funzione prolog e clang utilizza push rax
/pop rdx
.
Perché i compilatori utilizzano diversi prologhi di funzione? Quale variante è migliore? push
e pop
codificano sicuramente per istruzioni più brevi ma sono più veloci o più lenti di add
e sub
?
Il motivo per cui la pila di puntini sembra essere il fatto che l'abi richiede che rsp sia 16 byte allineati per le procedure non foglia. Non sono stato in grado di trovare alcun flag del compilatore che li rimuove.
A giudicare dalle vostre risposte, sembra che il push del pop & sia migliore. push rax + pop rdx = 1 + 1 = 2
vs. sub rsp, 8 + add rsp, 8 = 4 + 4 = 8
. Quindi la prima coppia risparmia 6 byte senza spese.
È una questione di scelta. È difficile dire quale variante è migliore. Probabilmente entrambe le varianti sono piuttosto simili in termini di prestazioni. –
re: la tua modifica. Sì, l'ABI garantisce che all'entrata della funzione, '(% rsp + 8)' è allineata a 16B. (Ho editato la maggior parte di questo commento nella mia risposta). –