Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit onderwerp wordt de binaire interface van de basistoepassing (ABI) voor x64 beschreven, de 64-bits extensie voor de x86-architectuur. Het behandelt onderwerpen zoals de aanroepconventie, type indeling, en het gebruik van de stack en registers, en meer.
x64-aanroepconventies
Twee belangrijke verschillen tussen x86 en x64 zijn:
- 64-bits adresseringsmogelijkheid
- Zestien 64-bits registers voor algemeen gebruik.
Gezien de uitgebreide registratieset maakt x64 gebruik van de __fastcall aanroepende conventie en een op RISC gebaseerd uitzonderingsverwerkingsmodel.
De __fastcall conventie gebruikt registers voor de eerste vier argumenten en het stackframe om meer argumenten door te geven. Zie x64-oproepconventie voor meer informatie over de x64-aanroepconventie, waaronder registergebruik, stackparameters, retourwaarden en stack-afwikkeling.
Zie voor meer informatie over de __vectorcall oproepconventie __vectorcall.
Optimalisatie van x64-compiler inschakelen
Met de volgende compileroptie kunt u uw toepassing optimaliseren voor x64:
x64-type en opslagindeling
In deze sectie wordt de opslag van gegevenstypen voor de x64-architectuur beschreven.
Scalaire typen
Hoewel het mogelijk is om toegang te krijgen tot gegevens met elke uitlijning, kunt u gegevens uitlijnen op de natuurlijke grens, of een veelvoud van de natuurlijke grens, om prestatieverlies te voorkomen. Enums zijn constante gehele getallen en worden behandeld als 32-bits gehele getallen. In de volgende tabel worden de typedefinitie en aanbevolen opslag voor gegevens beschreven, omdat deze betrekking hebben op uitlijning met behulp van de volgende uitlijningswaarden:
- Byte - 8 bits
- Word - 16 bits
- Dubbelwoord - 32 bits
- Quadwoord - 64 bits
- Octaword - 128 bits
| Scalaire type | C-gegevenstype | Opslaggrootte (in bytes) | Aanbevolen uitlijning |
|---|---|---|---|
INT8 |
char |
1 | Byte |
UINT8 |
unsigned char |
1 | Byte |
INT16 |
short |
2 | Woord |
UINT16 |
unsigned short |
2 | Woord |
INT32 |
int, long |
4 | Dubbelwoord |
UINT32 |
unsigned int, unsigned long |
4 | Dubbelwoord |
INT64 |
__int64 |
8 | Quadword |
UINT64 |
unsigned __int64 |
8 | Quadword |
FP32 (enkele precisie) |
float |
4 | Dubbelwoord |
FP64 (dubbele precisie) |
double |
8 | Quadword |
POINTER |
* | 8 | Quadword |
__m64 |
struct __m64 |
8 | Quadword |
__m128 |
struct __m128 |
16 | Octaword |
indeling x64 aggregaat en samenvoeging
Andere typen, zoals matrices, structs en samenvoegingen, hebben strengere uitlijningsvereisten die zorgen voor consistente opslag en samenvoeging en het ophalen van gegevens. Dit zijn de definities voor matrix, structuur en samenvoeging:
Array
Bevat een geordende groep aangrenzende gegevensobjecten. Elk object wordt een element genoemd. Alle elementen in een matrix hebben dezelfde grootte en hetzelfde gegevenstype.
Structuur
Bevat een geordende groep gegevensobjecten. In tegenstelling tot de elementen van een matrix kunnen de leden van een structuur verschillende gegevenstypen en -grootten hebben.
Unie
Een object dat één van een set benoemde leden bevat. De leden van de benoemde set kunnen van elk type zijn. De opslag die is toegewezen voor een samenvoeging is gelijk aan de opslag die is vereist voor het grootste lid van die samenvoeging, plus eventuele opvullingen die vereist zijn voor uitlijning.
In de volgende tabel ziet u de sterk aanbevolen uitlijning voor de scalaire leden van vakbonden en structuren.
| Scalaire type | C-gegevenstype | Vereiste uitlijning |
|---|---|---|
INT8 |
char |
Byte |
UINT8 |
unsigned char |
Byte |
INT16 |
short |
Woord |
UINT16 |
unsigned short |
Woord |
INT32 |
int, long |
Dubbelwoord |
UINT32 |
unsigned int, unsigned long |
Dubbelwoord |
INT64 |
__int64 |
Quadword |
UINT64 |
unsigned __int64 |
Quadword |
FP32 (enkele precisie) |
float |
Dubbelwoord |
FP64 (dubbele precisie) |
double |
Quadword |
POINTER |
* | Quadword |
__m64 |
struct __m64 |
Quadword |
__m128 |
struct __m128 |
Octaword |
De volgende regels voor samenvoeging zijn van toepassing:
De uitlijning van een matrix is hetzelfde als de uitlijning van een van de elementen van de matrix.
De uitlijning van het begin van een structuur of een samenvoeging is de maximale uitlijning van elk afzonderlijk lid. Elk lid binnen de structuur of samenvoeging moet op de juiste uitlijning worden geplaatst, zoals gedefinieerd in de vorige tabel, waarvoor mogelijk impliciete interne opvulling vereist is, afhankelijk van het vorige lid.
De structuurgrootte moet een integraal veelvoud van de uitlijning zijn, wat na het laatste lid mogelijk opvulling vereist. Aangezien structuren en samenvoegingen kunnen worden gegroepeerd in matrices, moet elk matrixelement van een structuur of samenvoeging beginnen en eindigen op de juiste uitlijning die eerder is bepaald.
Het is mogelijk om gegevens op een zodanige manier uit te lijnen dat deze groter zijn dan de uitlijningsvereisten zolang de vorige regels worden gehandhaafd.
Een individuele compiler kan de verpakking van een structuur om grootteredenen aanpassen. Met /Zp (Struct Member Alignment) kan bijvoorbeeld de verpakking van structuren worden aangepast.
Voorbeelden van uitlijning van x64-structuur
De volgende vier voorbeelden declareren elk een uitgelijnde structuur of samenvoeging en de bijbehorende afbeeldingen illustreren de indeling van die structuur of samenvoeging in het geheugen. Elke kolom in een afbeelding vertegenwoordigt een byte geheugen en het getal in de kolom geeft de verplaatsing van die byte aan. De naam in de tweede rij van elke afbeelding komt overeen met de naam van een variabele in de declaratie. De gearceerde kolommen geven opvulling aan die nodig is om de opgegeven uitlijning te bereiken.
Voorbeeld 1
// Total size = 2 bytes, alignment = 2 bytes (word).
_declspec(align(2)) struct {
short a; // +0; size = 2 bytes
}
Voorbeeld 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
}
In het diagram ziet u 24 bytes geheugen. Lid a, een int, neemt bytes 0 tot en met 3 in beslag. Het diagram toont opvulling voor bytes 4 tot en met 7. Lid b, een double, neemt bytes 8 tot en met 15 in beslag. Element c, een short, beslaat bytes 16 tot en met 17. Bytes 18 tot en met 23 worden niet gebruikt.
Voorbeeld 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
}
In het diagram ziet u 12 bytes geheugen. Lid a, een character, bevindt zich op byte 0. Byte 1 is opvulling. Lid b, een korte, neemt bytes 2 tot en met 4 in beslag. Lid c, een karakter, bevindt zich in byte 4. Bytes 5 tot en met 7 zijn opvulling. Lid d, een int, neemt bytes 8 tot en met 11 in beslag.
Voorbeeld 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
}
In het diagram ziet u 8 bytes geheugen. Lid p, een karakter, neemt byte 0 in beslag. Lid s, een short, neemt bytes 0 tot en met 1 in beslag. Lid l, een long, neemt bytes 0 tot en met 3 in beslag. Bytes 4 tot en met 7 zijn opvulling.
Bitfields
Structuurbitvelden zijn beperkt tot 64 bits en kunnen van het type ondertekend, niet-ondertekende int, int64 of niet-ondertekende int64 zijn. Bitvelden die de typegrens overschrijden, slaan bits over om het bitveld uit te lijnen op de volgende typeuitlijning. Bitvelden met gehele getallen kunnen bijvoorbeeld geen 32-bits grens overschrijden.
Conflicten met de x86-compiler
Gegevenstypen die groter zijn dan 4 bytes, worden niet automatisch uitgelijnd op de stack wanneer u de x86-compiler gebruikt om een toepassing te compileren. Omdat de architectuur voor de x86-compiler een 4 byte uitgelijnde stack is, kan alles wat groter is dan 4 bytes, bijvoorbeeld een 64-bits geheel getal, niet automatisch worden uitgelijnd op een 8-byteadres.
Het werken met niet-uitgelijnde gegevens heeft twee gevolgen.
Het kan langer duren om niet-uitgelijnde locaties te openen dan nodig is voor toegang tot uitgelijnde locaties.
Niet-uitgelijnde locaties kunnen niet worden gebruikt in vergrendelde bewerkingen.
Als u striktere uitlijning nodig hebt, gebruikt u __declspec(align(N)) bij uw variabele declaraties. Dit zorgt ervoor dat de compiler de stack dynamisch uitlijnt om te voldoen aan uw specificaties. Het dynamisch aanpassen van de stack tijdens runtime kan echter leiden tot een tragere uitvoering van uw toepassing.
x64-gebruik registreren
De x64-architectuur biedt 16 registers voor algemeen gebruik (hierna geheel getalregisters genoemd) en 16 XMM/YMM-registers die beschikbaar zijn voor gebruik met drijvende komma. Vluchtige registers zijn scratchregisters die door de aanroeper worden verondersteld om gedurende een oproep te worden vernietigd. Niet-vluchtige registers zijn nodig om hun waarden te behouden bij een functieaanroep en moeten door de ontvanger van de oproep worden opgeslagen indien gebruikt.
Volatiliteit en behoud registreren
In de volgende tabel wordt beschreven hoe elk register wordt gebruikt voor functie-aanroepen:
| Registreren | Toestand | Gebruik |
|---|---|---|
| RAX | Vluchtig | Register van retourwaarde |
| RCX | Vluchtig | Eerste geheel getalargument |
| RDX | Vluchtig | Tweede geheel getalargument |
| R8 | Vluchtig | Derde geheel getal argument |
| R9 | Vluchtig | Vierde geheelgetal-argument |
| R10:R11 | Vluchtig | Moet door de oproeper indien nodig worden bewaard; gebruikt in syscall/sysret-instructies |
| R12:R15 | Niet-tevreden | Moet bewaard blijven door de geroepen |
| RDI | Niet-tevreden | Moet bewaard blijven door de geroepen |
| RSI | Niet-tevreden | Moet bewaard blijven door de geroepen |
| RBX | Niet-tevreden | Moet bewaard blijven door de geroepen |
| RBP | Niet-tevreden | Kan worden gebruikt als framepointer; moet worden bewaard door de aangeroepene |
| RSP | Niet-tevreden | Stackpointer |
| XMM0, YMM0 | Vluchtig | Eerste FP-argument; eerste vector-type argument wanneer __vectorcall wordt gebruikt |
| XMM1, YMM1 | Vluchtig | Tweede FP-argument; tweede vector-type argument wanneer __vectorcall wordt gebruikt |
| XMM2, YMM2 | Vluchtig | Derde FP-argument; argument derde vectortype wanneer __vectorcall wordt gebruikt |
| XMM3, YMM3 | Vluchtig | Vierde FP-argument; vierde vectortype-argument wanneer __vectorcall wordt gebruikt |
| XMM4, YMM4 | Vluchtig | Moet bewaard blijven indien nodig door de beller; argument van het vijfde vectortype wanneer __vectorcall wordt gebruikt |
| XMM5, YMM5 | Vluchtig | Moet indien nodig door de aanroeper bewaard blijven; zesde vectortype-argument wanneer __vectorcall wordt gebruikt |
| XMM6:XMM15, YMM6:YMM15 | Niet-vluchtig (XMM), Vluchtig (bovenste helft van YMM) | Moet worden bewaard door de opgeroepene. YMM-registers moeten zo nodig worden bewaard door de beller. |
Bij het afsluiten van een functie en aan het begin van een functie voor C Runtime Library-aanroepen en Windows-systeemoproepen wordt verwacht dat de richtingsvlag in het cpu-vlaggenregister is gewist.
Stackgebruik
Zie het gebruik van x64-stack voor meer informatie over stacktoewijzing, uitlijning, functietypen en stackframes op x64.
Prolog en epilog
Elke functie die stackruimte toewijst, andere functies aanroept, niet-compatibele registers opslaat of uitzonderingsafhandeling gebruikt, moet een prolog hebben waarvan de adreslimieten worden beschreven in de afwikkelgegevens die zijn gekoppeld aan de betreffende functietabelvermelding en epilogs bij elke uitgang naar een functie. Zie x64 prolog en epilog voor meer informatie over de vereiste prolog- en epilog-code op x64.
x64-uitzonderingsafhandeling
Zie x64-uitzonderingsafhandeling voor informatie over de conventies en gegevensstructuren die worden gebruikt voor het implementeren van gestructureerde uitzonderingsafhandeling en het gedrag van C++-uitzonderingsafhandeling op de x64.
Intrinsiek en inlineassembly
Een van de beperkingen voor de x64-compiler is geen inline assembly-ondersteuning. Dit betekent dat functies die niet kunnen worden geschreven in C of C++ moeten worden geschreven als subroutines of als intrinsieke functies die door de compiler worden ondersteund. Bepaalde functies zijn prestatiegevoelig, terwijl andere niet. Prestatiegevoelige functies moeten worden geïmplementeerd als intrinsieke functies.
De door de compiler ondersteunde intrinsieke functies worden beschreven in Compiler intrinsics.
x64-afbeeldingsindeling
De x64 uitvoerbare afbeeldingsindeling is PE32+. Uitvoerbare installatiekopieën (zowel DLL's als EXE's) zijn beperkt tot een maximale grootte van 2 gigabyte, dus relatieve adressering met een 32-bits verplaatsing kan worden gebruikt om statische afbeeldingsgegevens aan te pakken. Deze gegevens omvatten de importadrestabel, tekenreeksconstanten, statische globale gegevens, enzovoort.