Sdílet prostřednictvím


Přehled konvencí X64 ABI

Toto téma popisuje základní binární rozhraní aplikace (ABI) pro x64, 64bitové rozšíření architektury x86. Zabývá se tématy, jako jsou konvence volání, rozložení typů, zásobník a využití registru a další.

Konvence volání x64

Mezi x86 a x64 jsou dva důležité rozdíly:

  • Možnosti 64bitového adresování
  • Šestnáct 64bitových registrů pro obecné použití.

Vzhledem k rozšířené sadě registrů používá x64 konvenci volání __fastcall a model zpracování výjimek založený na RISC.

Konvence __fastcall používá registry pro první čtyři argumenty a rámec zásobníku k předání více argumentů. Podrobnosti o konvenci volání x64, včetně registrace využití, parametrů zásobníku, návratových hodnot a odvíjení zásobníku, najdete v tématu konvence volání x64.

Další informace o __vectorcall konvenci volání naleznete v tématu __vectorcall.

Povolení optimalizace kompilátoru x64

Následující možnost kompilátoru vám pomůže optimalizovat aplikaci pro platformu x64:

Rozložení typu x64 a úložiště

Tato část popisuje úložiště datových typů pro architekturu x64.

Skalární typy

I když je možné přistupovat k datům s jakýmkoli zarovnáním, zarovnat data na jejich přirozené hranici nebo násobek jeho přirozené hranice, aby nedošlo ke ztrátě výkonu. Výčty jsou konstantní celá čísla a považují se za 32bitová celá čísla. Následující tabulka popisuje definici typu a doporučené úložiště pro data, která se týkají zarovnání pomocí následujících hodnot zarovnání:

  • Bajt – 8 bitů
  • Word – 16 bitů
  • Doubleword - 32 bitů
  • Čtyřúhelník – 64 bitů
  • Octaword – 128 bitů
Skalární typ Datový typ jazyka C Velikost úložiště (v bajtech) Doporučené zarovnání
INT8 char 0 Byte
UINT8 unsigned char 0 Byte
INT16 short 2 Word
UINT16 unsigned short 2 Word
INT32 int, long 4 Dvojitý meč
UINT32 unsigned int, unsigned long 4 Dvojitý meč
INT64 __int64 8 Čtyřúhelník
UINT64 unsigned __int64 8 Čtyřúhelník
FP32 (jednoduchá přesnost) float 4 Dvojitý meč
FP64 (dvojitá přesnost) double 8 Čtyřúhelník
POINTER * 8 Čtyřúhelník
__m64 struct __m64 8 Čtyřúhelník
__m128 struct __m128 16 Octaword

Agregace a sjednocení rozložení x64

Jiné typy, jako jsou pole, struktury a sjednocení, mají přísnější požadavky na zarovnání, které zajišťují konzistentní agregaci a sjednocovací úložiště a načítání dat. Tady jsou definice polí, struktur a sjednocení:

  • Pole

    Obsahuje uspořádanou skupinu sousedních datových objektů. Každý objekt se nazývá prvek. Všechny prvky v poli mají stejnou velikost a datový typ.

  • Struktura

    Obsahuje uspořádanou skupinu datových objektů. Na rozdíl od prvků pole můžou mít členy struktury různé datové typy a velikosti.

  • Sjednocení

    Objekt, který obsahuje libovolnou sadu pojmenovaných členů. Členy pojmenované sady můžou být libovolného typu. Úložiště přidělené sjednocení se rovná úložišti požadovanému pro největšího člena této sjednocení a veškeré odsazení potřebné pro zarovnání.

Následující tabulka uvádí důrazně doporučené zarovnání skalárních členů sjednocení a struktur.

Skalární typ Datový typ jazyka C Požadované zarovnání
INT8 char Byte
UINT8 unsigned char Byte
INT16 short Word
UINT16 unsigned short Word
INT32 int, long Dvojitý meč
UINT32 unsigned int, unsigned long Dvojitý meč
INT64 __int64 Čtyřúhelník
UINT64 unsigned __int64 Čtyřúhelník
FP32 (jednoduchá přesnost) float Dvojitý meč
FP64 (dvojitá přesnost) double Čtyřúhelník
POINTER * Čtyřúhelník
__m64 struct __m64 Čtyřúhelník
__m128 struct __m128 Octaword

Platí následující pravidla agregace zarovnání:

  • Zarovnání pole je stejné jako zarovnání jednoho z prvků pole.

  • Zarovnání začátku struktury nebo sjednocení je maximální zarovnání každého jednotlivého člena. Každý člen v rámci struktury nebo sjednocení musí být umístěn ve správném zarovnání, jak je definováno v předchozí tabulce, což může vyžadovat implicitní vnitřní odsazení v závislosti na předchozím členu.

  • Velikost struktury musí být násobek jeho zarovnání, který může vyžadovat odsazení za posledním členem. Vzhledem k tomu, že struktury a sjednocení lze seskupit do polí, musí každý prvek pole struktury nebo sjednocení začínat a končit na správném zarovnání dříve určeném.

  • Data je možné zarovnat tak, aby byla větší než požadavky na zarovnání, pokud jsou zachována předchozí pravidla.

  • Jednotlivý kompilátor může upravit balení struktury z důvodů velikosti. Například /Zp (zarovnání člena struktury) umožňuje úpravu balení struktur.

Příklady zarovnání struktury x64

Následující čtyři příklady každý deklaruje zarovnanou strukturu nebo sjednocení a odpovídající obrázky znázorňují rozložení této struktury nebo sjednocení v paměti. Každý sloupec na obrázku představuje bajt paměti a číslo ve sloupci označuje přesunutí tohoto bajtu. Název na druhém řádku každého obrázku odpovídá názvu proměnné v deklaraci. Stínované sloupce označují odsazení, které je nutné k dosažení zadaného zarovnání.

Příklad 1

// Total size = 2 bytes, alignment = 2 bytes (word).

_declspec(align(2)) struct {
    short a;      // +0; size = 2 bytes
}

Diagram znázorňující rozložení struktury 1 v příkladu

Příklad 2

// Total size = 24 bytes, alignment = 8 bytes (quadword).

_declspec(align(8)) struct {
    int a;       // +0; size = 4 bytes
    double b;    // +8; size = 8 bytes
    short c;     // +16; size = 2 bytes
}

Diagram znázorňující rozložení struktury 2 v příkladu

Příklad 3

// Total size = 12 bytes, alignment = 4 bytes (doubleword).

_declspec(align(4)) struct {
    char a;       // +0; size = 1 byte
    short b;      // +2; size = 2 bytes
    char c;       // +4; size = 1 byte
    int d;        // +8; size = 4 bytes
}

Diagram znázorňující rozložení struktury 3 v příkladu

Příklad 4

// Total size = 8 bytes, alignment = 8 bytes (quadword).

_declspec(align(8)) union {
    char *p;      // +0; size = 8 bytes
    short s;      // +0; size = 2 bytes
    long l;       // +0; size = 4 bytes
}

Diagram znázorňující rozložení sjednocení 4 v příkladu

Bitová pole

Bitová pole struktury jsou omezena na 64 bitů a můžou mít typ znaménků, bez znaménka, int64 nebo bez znaménka int64. Bitová pole, která překračují hranici typu, přeskočí bity, aby zarovná bitové pole na další zarovnání typu. Například celočíselná bitová pole nemusí překročit 32bitovou hranici.

Konflikty s kompilátorem x86

Datové typy větší než 4 bajty nejsou automaticky zarovnány do zásobníku, když ke kompilaci aplikace použijete kompilátor x86. Vzhledem k tomu, že architektura kompilátoru x86 je 4 bajtová sada, nesmí být automaticky zarovnaná 4 bajty, například 64bitové celé číslo, automaticky zarovnané na 8 bajtovou adresu.

Práce s nerovnanými daty má dva důsledky.

  • Přístup k nerovnaným umístěním může trvat déle, než trvá přístup k zarovnaným umístěním.

  • Nerovná umístění se nedají použít v vzájemně uzamčených operacích.

Pokud požadujete přísnější zarovnání, použijte __declspec(align(N)) u deklarací proměnných. To způsobí, že kompilátor dynamicky zarovná zásobník tak, aby splňoval vaše specifikace. Dynamické úpravy zásobníku za běhu však můžou způsobit pomalejší spouštění aplikace.

Využití registrace x64

Architektura x64 poskytuje 16 registrů pro obecné účely (dále označovaných jako celočíselné registry) a také 16 registrů XMM/YMM, které jsou k dispozici pro použití s plovoucí desetinou čárkou. Volatilní registry jsou pomocné registry, které volající předpokládá, že bude zničen v rámci volání. Nevolatilní registry jsou vyžadovány k zachování jejich hodnot v rámci volání funkce a musí být uloženy volaným, pokud je použit.

Registrace nestálosti a zachování

Následující tabulka popisuje, jak se jednotlivé registry používají napříč voláními funkcí:

Registrovat Stav Používání
RAX Nestálý Návratový registr hodnot
RCX Nestálý První celočíselná hodnota
RDX Nestálý Druhý celočíselná argument
R8 Nestálý Třetí celočíselná hodnota
R9 Nestálý Čtvrtý celočíselné argumenty
R10:R11 Nestálý Volající musí být zachován podle potřeby; použité v pokynech syscall/sysret
R12:R15 Netěkavý Musí být zachováno volanou
RDI Netěkavý Musí být zachováno volanou
RSI Netěkavý Musí být zachováno volanou
RBX Netěkavý Musí být zachováno volanou
RBP Netěkavý Lze použít jako ukazatel rámce; musí být zachována volanou
RSP Netěkavý Ukazatel zásobníku
XMM0, YMM0 Nestálý První argument FP; první argument typu vektoru při __vectorcall použití
XMM1, YMM1 Nestálý Druhý argument FP; druhý argument typu vektoru při __vectorcall použití
XMM2, YMM2 Nestálý Třetí argument FP; třetí argument typu vektoru při __vectorcall použití
XMM3, YMM3 Nestálý Čtvrtý argument FP; čtvrtý argument typu vektoru při __vectorcall použití
XMM4, YMM4 Nestálý Volající musí být zachován podle potřeby; argument pátého typu vektoru při __vectorcall použití
XMM5, YMM5 Nestálý Volající musí být zachován podle potřeby; šestý argument typu vektoru při __vectorcall použití
XMM6:XMM15, YMM6:YMM15 Nevolatil (XMM), Volatile (horní polovina YMM) Musí být zachována volanou. Registrace YMM musí být zachovány podle potřeby volajícím.

Při ukončení funkce a vstupu funkce do volání knihovny modulu runtime jazyka C a systémových volání systému Windows se očekává, že se vymaže směr příznaku v registru příznaků procesoru.

Využití zásobníku

Podrobnosti o přidělování, zarovnání, typech funkcí a rámech zásobníku na platformě x64 najdete v tématu o využití zásobníku x64.

Prolog a epilog

Každá funkce, která přiděluje prostor zásobníku, volá jiné funkce, ukládá nevolatilní registry nebo používá zpracování výjimek, musí mít prolog, jehož limity adres jsou popsány v datech odvíjení přidružených k příslušné položce tabulky funkcí a epilogy na každém výstupu funkce. Podrobnosti o požadovaném kódu prologu a epilogu na platformě x64 najdete v tématu x64 prolog a epilog.

x64 – ošetření výjimek

Informace o konvencích a datových strukturách používaných k implementaci strukturovaného zpracování výjimek a chování zpracování výjimek jazyka C++ v x64 najdete v tématu zpracování výjimek x64.

Vnitřní objekty a vložené sestavení

Jedním z omezení kompilátoru x64 není podpora vloženého assembleru. To znamená, že funkce, které nelze zapsat v jazyce C nebo C++, musí být buď zapsány jako podprogramy, nebo jako vnitřní funkce podporované kompilátorem. Některé funkce jsou citlivé na výkon, zatímco jiné ne. Funkce citlivé na výkon by měly být implementovány jako vnitřní funkce.

Vnitřní objekty podporované kompilátorem jsou popsány ve vnitřních objektech kompilátoru.

Formát obrázku x64

Formát spustitelné image x64 je PE32+. Spustitelné obrázky (knihovny DLL i EXE) jsou omezeny na maximální velikost 2 gigabajty, takže relativní adresování s 32bitovým posunem je možné použít k adresování statických dat obrázků. Tato data zahrnují tabulku adres importu, řetězcové konstanty, statická globální data atd.

Viz také

Konvence volání