Jak funkce vrací hodnoty v x86-64 podle System V ABI
Kompilátor x86-64 podle System V ABI používá registry rax a rdx pro návrat prvních dvou hodnot typu integer. Pro struktury platí pravidla klasifikace: INTEGER pro malé velikosti, MEMORY pro velké. To určuje, zda se hodnota předá přímo nebo prostřednictvím skrytého ukazatele v rdi.
Zvažme základní případ návratu int:
one_plus_one():
mov DWORD PTR [rbp-4], 2
mov eax, DWORD PTR [rbp-4]
ret
Hodnota je načtena do eax (nižších 32 bitů rax). Podobně pro 64bitové typy se používá celý registr rax.
Malé struktury do 16 bajtů: návrat přes registry
Malé struktury se umísťují do rax a rdx. Příklad struktury o velikosti 16 bajtů:
struct nums {
std::int64_t first{};
std::int64_t second{};
};
nums construct() {
nums ret{10, 120};
return ret;
}
Vygenerovaný kód:
construct():
mov QWORD PTR [rbp-16], 10
mov QWORD PTR [rbp-8], 120
mov rax, QWORD PTR [rbp-16]
mov rdx, QWORD PTR [rbp-8]
ret
Pole jsou rozdělena postupně: first do rax, second do rdx. Pořadí v paměti odpovídá little-endian s klesajícím směrem zásobníku.
- Klasifikace
INTEGER: Až dva 64bitové pole se umístí do registrů. - Zarovnání: Pole jsou zarovnána po 8 bajtech.
- Omezení: Celková velikost ≤ 16 bajtů pro přímý návrat.
Velké struktury: skrytý argument v rdi
Struktury větší než 16 bajtů jsou klasifikovány jako MEMORY. Volající kód vyhradí paměť a předá adresu v rdi jako první (skrytý) argument.
Příklad struktury o velikosti 32 bajtů:
struct many_nums {
std::int64_t first{};
std::int64_t second{};
std::int64_t third{};
std::int64_t fourth{};
};
many_nums construct_scary() {
many_nums temp{10, 20, 30, 40};
return temp;
}
Ve funkci construct_scary:
construct_scary():
mov QWORD PTR [rbp-8], rdi
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rax], 10
mov QWORD PTR [rax+8], 20
mov QWORD PTR [rax+16], 30
mov QWORD PTR [rax+24], 40
ret
Opakované mov rax, [rbp-8] jsou artefakt -O0. Optimalizovaný kód používá rdi přímo.
Ve main:
main:
sub rsp, 32
lea rax, [rbp-32]
mov rdi, rax
call construct_scary
ret
- Vyhrazení paměti:
sub rsp, sizenebo na zásobníku/paměti dynamické. - Předání adresy: Do
rdipřed voláním. - Klasifikace
MEMORY: Podle System V ABI pro typy >16 bajtů nebo ne-INTEGER.
Pravidla klasifikace návratových hodnot
System V ABI definuje třídy:
- INTEGER: Do 16 bajtů, skalární nebo agregované pole ≤2×64 bitů →
rax/rdx. - MEMORY: >16 bajtů, SSE-zabaleno >2×128 bitů, ne-INTEGER → skrytý
rdi. - SSE: Floating-point do 128 bitů →
xmm0(zde nepopsáno).
Kompilátor (GCC/Clang) generuje kód podle těchto pravidel. Pro ladění použijte -O0 pro viditelné mezivýsledky.
Co je důležité
- Registry
rax/rdxpro návrat hodnot typu integer ≤16 bajtů. - Skrytý argument
rdipro tříduMEMORY: volající vyhradí paměť. - System V ABI standardizuje chování na Linuxu/macOS.
- Optimalizace (
-O2+) minimalizují nadbytečnémov. - Ověřte pomocí
objdump -dnebo Godbolt.
— Editorial Team
Zatím žádné komentáře.