Share via


Vue d’ensemble des conventions ABI x64

Cette rubrique décrit l’interface binaire d’application de base (ABI) pour x64, l’extension 64 bits à l’architecture x86. Il aborde des rubriques telles que la convention d’appel, la disposition de type, la pile et l’utilisation de l’inscription, etc.

Conventions d’appel x64

Deux différences importantes entre x86 et x64 sont les suivantes :

  • Fonctionnalité d’adressage 64 bits
  • Seize registres 64 bits pour une utilisation générale.

Étant donné le jeu d’inscriptions développé, x64 utilise la convention d’appel __fastcall et un modèle de gestion des exceptions basé sur RISC.

La __fastcall convention utilise des registres pour les quatre premiers arguments et la trame de pile pour passer d’autres arguments. Pour plus d’informations sur la convention d’appel x64, notamment l’utilisation de l’inscription, les paramètres de pile, les valeurs de retour et le déroulement de la pile, consultez la convention d’appel x64.

Activer l’optimisation du compilateur x64

L’option de compilateur suivante vous aide à optimiser votre application pour x64 :

Disposition du type x64 et du stockage

Cette section décrit le stockage des types de données pour l’architecture x64.

Types scalaires

Bien qu’il soit possible d’accéder aux données avec n’importe quel alignement, d’aligner les données sur sa limite naturelle ou de plusieurs de ses limites naturelles, afin d’éviter toute perte de performances. Les énumérations sont des entiers constants et sont traitées comme des entiers 32 bits. Le tableau suivant décrit la définition de type et le stockage recommandé pour les données, car il se rapporte à l’alignement à l’aide des valeurs d’alignement suivantes :

  • Octet - 8 bits
  • Word - 16 bits
  • Doubleword - 32 bits
  • Quadword - 64 bits
  • Octaword - 128 bits
Type scalaire Type de données C taille Stockage (en octets) Alignement recommandé
INT8 char 1 Byte
UINT8 unsigned char 1 Byte
INT16 short 2 Word
UINT16 unsigned short 2 Word
INT32 int, long 4 Doubleword
UINT32 unsigned int, unsigned long 4 Doubleword
INT64 __int64 8 Quadword
UINT64 unsigned __int64 8 Quadword
FP32 (précision unique) float 4 Doubleword
FP64 (double précision) double 8 Quadword
POINTER * 8 Quadword
__m64 struct __m64 8 Quadword
__m128 struct __m128 16 Octaword

Disposition d’agrégation et d’union x64

D’autres types, tels que les tableaux, les structs et les unions, ont des exigences d’alignement plus strictes qui garantissent une agrégation cohérente et un stockage d’union et une récupération des données. Voici les définitions de tableau, de structure et d’union :

  • Tableau

    Contient un groupe ordonné d’objets de données adjacents. Chaque objet est appelé élément. Tous les éléments d’un tableau ont la même taille et le même type de données.

  • Structure

    Contient un groupe ordonné d’objets de données. Contrairement aux éléments d’un tableau, les membres d’une structure peuvent avoir différents types de données et tailles.

  • Union

    Objet qui contient un ensemble de membres nommés. Les membres du jeu nommé peuvent être de n’importe quel type. Le stockage alloué pour une union est égal au stockage requis pour le plus grand membre de cette union, ainsi que tout remplissage requis pour l’alignement.

Le tableau suivant présente l’alignement fortement recommandé pour les membres scalaires des unions et des structures.

Type scalaire Type de données C Alignement requis
INT8 char Byte
UINT8 unsigned char Byte
INT16 short Word
UINT16 unsigned short Word
INT32 int, long Doubleword
UINT32 unsigned int, unsigned long Doubleword
INT64 __int64 Quadword
UINT64 unsigned __int64 Quadword
FP32 (précision unique) float Doubleword
FP64 (double précision) double Quadword
POINTER * Quadword
__m64 struct __m64 Quadword
__m128 struct __m128 Octaword

Les règles d’alignement d’agrégation suivantes s’appliquent :

  • L’alignement d’un tableau est identique à l’alignement de l’un des éléments du tableau.

  • L’alignement du début d’une structure ou d’une union est l’alignement maximal de n’importe quel membre individuel. Chaque membre de la structure ou de l’union doit être placé à son alignement approprié tel que défini dans le tableau précédent, ce qui peut nécessiter un remplissage interne implicite, en fonction du membre précédent.

  • La taille de la structure doit être un multiple intégral de son alignement, ce qui peut nécessiter un remplissage après le dernier membre. Étant donné que les structures et les unions peuvent être regroupées dans des tableaux, chaque élément de tableau d’une structure ou d’une union doit commencer et se terminer à l’alignement approprié précédemment déterminé.

  • Il est possible d’aligner les données de telle sorte qu’elles soient supérieures aux exigences d’alignement tant que les règles précédentes sont conservées.

  • Un compilateur individuel peut ajuster l’emballage d’une structure pour des raisons de taille. Par exemple, /Zp (alignement des membres de struct) permet d’ajuster l’emballage des structures.

Exemples d’alignement de structure x64

Les quatre exemples suivants déclarent chacun une structure ou une union alignée, et les figures correspondantes illustrent la disposition de cette structure ou union en mémoire. Chaque colonne d’une figure représente un octet de mémoire, et le nombre de la colonne indique le déplacement de cet octet. Le nom de la deuxième ligne de chaque figure correspond au nom d’une variable dans la déclaration. Les colonnes ombrées indiquent le remplissage requis pour atteindre l’alignement spécifié.

Exemple 1

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

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

Diagram showing the example 1 structure layout.

Exemple 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 showing the example 2 structure layout.

Exemple 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 showing the example 3 structure layout.

Exemple 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 showing the example 4 union layout.

Champs de bits

Les champs de bits de structure sont limités à 64 bits et peuvent être de type signé int, intsigned int, int64 ou int64 non signé. Les champs de bits qui traversent la limite de type ignorent les bits pour aligner le champ de bits sur l’alignement de type suivant. Par exemple, les champs de bits entiers peuvent ne pas traverser une limite 32 bits.

Conflits avec le compilateur x86

Les types de données dont la taille est supérieure à 4 octets ne sont pas alignés automatiquement sur la pile lorsque vous utilisez le compilateur x86 pour compiler une application. Étant donné que l’architecture du compilateur x86 est une pile alignée sur 4 octets, tout ce qui dépasse 4 octets, par exemple, un entier 64 bits, ne peut pas être aligné automatiquement sur une adresse de 8 octets.

L’utilisation de données non alignées a deux implications.

  • L’accès aux emplacements non alignés peut prendre plus de temps que nécessaire pour accéder aux emplacements alignés.

  • Les emplacements non alignés ne peuvent pas être utilisés dans les opérations interblocées.

Si vous avez besoin d’un alignement plus strict, utilisez-la __declspec(align(N)) sur vos déclarations de variable. Ainsi, le compilateur aligne dynamiquement la pile pour répondre à vos spécifications. Toutefois, l’ajustement dynamique de la pile au moment de l’exécution peut entraîner une exécution plus lente de votre application.

Inscription x64 de l’utilisation

L’architecture x64 fournit 16 registres à usage général (appelés registres entiers ci-après) ainsi que 16 registres XMM/YMM disponibles pour une utilisation à virgule flottante. Les registres volatils sont des registres de travail censés être détruits après un appel. Les registres non volatils doivent conserver leurs valeurs tout au long d'un appel de fonction et être enregistrés par l'appelé s'il les utilise.

Inscrire la volatilité et la conservation

Le tableau suivant explique comment chaque registre est utilisé dans les appels de fonction :

Inscrire État Utilisation
RAX Volatil Registre des valeurs de retour
RCX Volatil Premier argument entier
RDX Volatil Deuxième argument entier
R8 Volatil Troisième argument entier
R9 Volatil Quatrième argument entier
R10:R11 Volatil Doit être conservé si nécessaire par l'appelant ; utilisé dans les instructions syscall/sysret
R12:R15 Non volatil Doit être conservé par l'appelé
RDI Non volatil Doit être conservé par l'appelé
RSI Non volatil Doit être conservé par l'appelé
RBX Non volatil Doit être conservé par l'appelé
RBP Non volatil Peut être utilisé comme pointeur de frame ; doit être conservé par l'appelé
RSP Non volatil Pointeur de pile
XMM0, YMM0 Volatil Premier argument FP ; premier argument de type vectoriel quand __vectorcall est utilisé
XMM1, YMM1 Volatil Deuxième argument FP ; deuxième argument de type vectoriel quand __vectorcall est utilisé
XMM2, YMM2 Volatil Troisième argument FP ; troisième argument de type vectoriel quand __vectorcall est utilisé
XMM3, YMM3 Volatil Quatrième argument FP ; quatrième argument de type vectoriel quand __vectorcall est utilisé
XMM4, YMM4 Volatil Doit être conservé si nécessaire par l’appelant ; cinquième argument de type vectoriel quand __vectorcall est utilisé
XMM5, YMM5 Volatil Doit être conservé si nécessaire par l'appelant ; sixième argument de type vectoriel quand __vectorcall est utilisé
XMM6:XMM15, YMM6:YMM15 Non volatil (XMM), volatil (moitié supérieure de YMM) Doit être conservé par l’appelé. Les registres YMM doivent être conservés si nécessaire par l'appelant.

Lors de la sortie de la fonction et de l’entrée de fonction vers les appels de la bibliothèque runtime C et les appels système Windows, l’indicateur de direction dans le registre des indicateurs de processeur est censé être effacé.

Utilisation de la pile

Pour plus d’informations sur l’allocation de pile, l’alignement, les types de fonctions et les trames de pile sur x64, consultez l’utilisation de la pile x64.

Prologue et épilogue

Chaque fonction qui alloue de l’espace de pile, appelle d’autres fonctions, enregistre des registres nonvolatiles ou utilise une gestion des exceptions doit avoir un prolog dont les limites d’adresse sont décrites dans les données de déroulement associées à l’entrée de table de fonctions respectives et aux épilogues à chaque sortie d’une fonction. Pour plus d’informations sur le prologue et le code d’épilogue requis sur x64, consultez prologue et épilogue x64.

Gestion d’exceptions x64

Pour plus d’informations sur les conventions et les structures de données utilisées pour implémenter la gestion des exceptions structurées et le comportement de gestion des exceptions C++ sur le x64, consultez gestion des exceptions x64.

Intrinsèques et assembly inline

L’une des contraintes pour le compilateur x64 n’est pas prise en charge de l’assembleur inline. Cela signifie que les fonctions qui ne peuvent pas être écrites en C ou C++ doivent être écrites en tant que sous-routines ou en tant que fonctions intrinsèques prises en charge par le compilateur. Certaines fonctions respectent les performances, tandis que d’autres ne le sont pas. Les fonctions sensibles aux performances doivent être implémentées en tant que fonctions intrinsèques.

Les intrinsèques prises en charge par le compilateur sont décrites dans les intrinsèques du compilateur.

Format d’image x64

Le format d’image exécutable x64 est PE32+. Les images exécutables (DLL et EXEs) sont limitées à une taille maximale de 2 gigaoctets, de sorte que l’adressage relatif avec un déplacement 32 bits peut être utilisé pour traiter les données d’image statiques. Ces données incluent la table d’adresses d’importation, les constantes de chaîne, les données globales statiques, et ainsi de suite.

Voir aussi

Conventions d’appel