Condividi tramite


Panoramica delle convenzioni ABI x64

Questo argomento descrive l'interfaccia ABI (Application Binary Interface) di base per x64, l'estensione a 64 bit per l'architettura x86. Vengono trattati argomenti come la convenzione di chiamata, il layout dei tipi, lo stack e registrare l'utilizzo e altro ancora.

Convenzioni di chiamata x64

Due importanti differenze tra x86 e x64 sono:

  • Funzionalità di indirizzamento a 64 bit
  • Sedici registri a 64 bit per uso generico.

Dato il set di registri espanso, x64 usa la convenzione di chiamata __fastcall e un modello di gestione delle eccezioni basato su RISC.

La __fastcall convenzione usa i registri per i primi quattro argomenti e lo stack frame per passare più argomenti. Per informazioni dettagliate sulla convenzione di chiamata x64, tra cui l'utilizzo del registro, i parametri dello stack, i valori restituiti e la rimozione dello stack, vedere convenzione di chiamata x64.

Per altre informazioni sulla convenzione di __vectorcall chiamata, vedere __vectorcall.

Abilitare l'ottimizzazione del compilatore x64

L'opzione del compilatore seguente consente di ottimizzare l'applicazione per x64:

Layout di archiviazione e tipo x64

Questa sezione descrive l'archiviazione dei tipi di dati per l'architettura x64.

Tipi scalari

Anche se è possibile accedere ai dati con qualsiasi allineamento, allineare i dati sul limite naturale o un multiplo del limite naturale per evitare perdite di prestazioni. Le enumerazioni sono numeri interi costanti e vengono considerati numeri interi a 32 bit. La tabella seguente descrive la definizione del tipo e l'archiviazione consigliata per i dati in quanto riguarda l'allineamento usando i valori di allineamento seguenti:

  • Byte - 8 bit
  • Word - 16 bit
  • Doubleword - 32 bit
  • Quadword - 64 bit
  • Octaword - 128 bit
Tipo scalare tipo di dati C Dimensioni di archiviazione (in byte) Allineamento consigliato
INT8 char 1 Byte
UINT8 unsigned char 1 Byte
INT16 short 2 Word
UINT16 unsigned short 2 Word
INT32 int, long 4 Doppia parola
UINT32 unsigned int, unsigned long 4 Doppia parola
INT64 __int64 8 Quadword
UINT64 unsigned __int64 8 Quadword
FP32 (precisione singola) float 4 Doppia parola
FP64 (precisione doppia) double 8 Quadword
POINTER * 8 Quadword
__m64 struct __m64 8 Quadword
__m128 struct __m128 16 Octaword

Layout di aggregazione e unione x64

Altri tipi, ad esempio matrici, struct e unioni, hanno requisiti di allineamento più rigorosi che garantiscono un'aggregazione coerente e l'archiviazione dell'unione e il recupero dei dati. Ecco le definizioni per matrice, struttura e unione:

  • Matrice

    Contiene un gruppo ordinato di oggetti dati adiacenti. Ogni oggetto viene chiamato elemento . Tutti gli elementi all'interno di una matrice hanno le stesse dimensioni e tipo di dati.

  • Struttura

    Contiene un gruppo ordinato di oggetti dati. A differenza degli elementi di una matrice, i membri di una struttura possono avere tipi di dati e dimensioni diversi.

  • Popolare

    Oggetto che contiene uno qualsiasi di un set di membri denominati. I membri del set denominato possono essere di qualsiasi tipo. Lo spazio di archiviazione allocato per un'unione è uguale allo spazio di archiviazione necessario per il membro più grande di tale unione, oltre a qualsiasi spaziatura interna necessaria per l'allineamento.

La tabella seguente illustra l'allineamento fortemente consigliato per i membri scalari di unioni e strutture.

Tipo scalare Tipo di dati C Allineamento obbligatorio
INT8 char Byte
UINT8 unsigned char Byte
INT16 short Word
UINT16 unsigned short Word
INT32 int, long Doppia parola
UINT32 unsigned int, unsigned long Doppia parola
INT64 __int64 Quadword
UINT64 unsigned __int64 Quadword
FP32 (precisione singola) float Doppia parola
FP64 (precisione doppia) double Quadword
POINTER * Quadword
__m64 struct __m64 Quadword
__m128 struct __m128 Octaword

Si applicano le regole di allineamento di aggregazione seguenti:

  • L'allineamento di una matrice è uguale all'allineamento di uno degli elementi della matrice.

  • L'allineamento dell'inizio di una struttura o di un'unione è l'allineamento massimo di qualsiasi singolo membro. Ogni membro all'interno della struttura o dell'unione deve essere posizionato in corrispondenza dell'allineamento corretto, come definito nella tabella precedente, che può richiedere spaziatura interna implicita, a seconda del membro precedente.

  • Le dimensioni della struttura devono essere un multiplo integrale dell'allineamento, che può richiedere la spaziatura interna dopo l'ultimo membro. Poiché le strutture e le unioni possono essere raggruppate in matrici, ogni elemento di matrice di una struttura o di un'unione deve iniziare e terminare con l'allineamento corretto determinato in precedenza.

  • È possibile allineare i dati in modo tale da essere maggiori dei requisiti di allineamento purché vengano mantenute le regole precedenti.

  • Un singolo compilatore può modificare la compressione di una struttura per motivi di dimensioni. Ad esempio, /Zp (Struct Member Alignment) consente di regolare la compressione delle strutture.

Esempi di allineamento della struttura x64

I quattro esempi seguenti dichiarano una struttura o un'unione allineata e le figure corrispondenti illustrano il layout di tale struttura o unione in memoria. Ogni colonna di una figura rappresenta un byte di memoria e il numero nella colonna indica lo spostamento di tale byte. Il nome nella seconda riga di ogni figura corrisponde al nome di una variabile nella dichiarazione. Le colonne ombreggiate indicano la spaziatura interna necessaria per ottenere l'allineamento specificato.

Esempio 1

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

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

Diagramma che mostra il layout della struttura di esempio 1.

Esempio 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
}

Diagramma che mostra il layout della struttura di esempio 2.

Esempio 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
}

Diagramma che mostra il layout della struttura di esempio 3.

Esempio 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
}

Diagramma che mostra il layout dell'unione di esempio 4.

Campi di bit

I campi di bit della struttura sono limitati a 64 bit e possono essere di tipo signed int, unsigned int, int64 o unsigned int64. I campi di bit che superano il limite del tipo ignorano i bit per allineare il campo di bit all'allineamento del tipo successivo. Ad esempio, i campi di bit integer potrebbero non superare un limite a 32 bit.

Conflitti con il compilatore x86

I tipi di dati di dimensioni maggiori di 4 byte non vengono allineati automaticamente nello stack quando si usa il compilatore x86 per compilare un'applicazione. Poiché l'architettura per il compilatore x86 è uno stack allineato a 4 byte, qualsiasi valore maggiore di 4 byte, ad esempio un numero intero a 64 bit, non può essere allineato automaticamente a un indirizzo a 8 byte.

L'uso di dati non allineati ha due implicazioni.

  • L'accesso a posizioni non idonee potrebbe richiedere più tempo rispetto all'accesso alle posizioni allineate.

  • Non è possibile usare posizioni non idonee nelle operazioni interlock.

Se è necessario un allineamento più rigoroso, usare __declspec(align(N)) nelle dichiarazioni di variabili. In questo modo il compilatore deve allineare dinamicamente lo stack in base alle specifiche. Tuttavia, la regolazione dinamica dello stack in fase di esecuzione può causare un rallentamento dell'esecuzione dell'applicazione.

Utilizzo della registrazione x64

L'architettura x64 fornisce 16 registri per utilizzo generico (qui di seguito denominati registri interi), oltre a 16 registri XMM/YMM disponibili per l'uso a virgola mobile. I registri volatili sono registri temporanei che il chiamante suppone vengano eliminati con una chiamata. I registri non volatili devono conservare i relativi valori durante le chiamate di funzione e, se usati, devono essere salvati dal chiamante.

Registrare volatilità e conservazione

Nella tabella seguente viene descritto il modo in cui ogni registro viene usato durante le chiamate di funzione:

Registrazione Status Utilizzo
RAX Volatile Registro del valore restituito
RCX Volatile Primo argomento Integer
RDX Volatile Secondo argomento Integer
R8 Volatile Terzo argomento Integer
R9 Volatile Quarto argomento Integer
R10:R11 Volatile Deve essere mantenuto in base alle esigenze del chiamante. Viene usato nelle istruzioni syscall/sysret.
R12:R15 Non volatile Deve essere mantenuto dal chiamato.
RDI Non volatile Deve essere mantenuto dal chiamato.
RSI Non volatile Deve essere mantenuto dal chiamato.
RBX Non volatile Deve essere mantenuto dal chiamato.
RBP Non volatile Può essere usato come puntatore ai frame. Deve essere mantenuto dal chiamato.
RSP Non volatile Puntatore dello stack
XMM0, YMM0 Volatile Primo argomento FP; primo argomento di tipo vettore quando si usa __vectorcall.
XMM1, YMM1 Volatile Secondo argomento FP; secondo argomento di tipo vettore quando si usa __vectorcall.
XMM2, YMM2 Volatile Terzo argomento FP; terzo argomento di tipo vettore quando si usa __vectorcall.
XMM3, YMM3 Volatile Quarto argomento FP; quarto argomento di tipo vettore quando si usa __vectorcall.
XMM4, YMM4 Volatile Deve essere mantenuto in base alle esigenze del chiamante; quinto argomento tipo vettore quando si usa __vectorcall.
XMM5, YMM5 Volatile Deve essere mantenuto in base alle esigenze del chiamante; sesto argomento di tipo vettore quando si usa __vectorcall.
XMM6:XMM15, YMM6:YMM15 Non volatile (XMM), volatile (metà superiore di YMM) Deve essere mantenuto dal chiamato. I registri YMM devono essere mantenuti in base alle esigenze del chiamante.

All'uscita dalla funzione e alla voce della funzione alle chiamate della libreria di runtime C e alle chiamate di sistema di Windows, è previsto che il flag di direzione nel registro dei flag cpu venga cancellato.

Uso dello stack

Per informazioni dettagliate sull'allocazione dello stack, l'allineamento, i tipi di funzione e gli stack frame in x64, vedere Utilizzo dello stack x64.

Prologo ed epilogo

Ogni funzione che alloca spazio dello stack, chiama altre funzioni, salva registri non volatile o usa la gestione delle eccezioni deve avere un prologo i cui limiti di indirizzo sono descritti nei dati di rimozione associati alla rispettiva voce di tabella delle funzioni ed epilogi a ogni uscita di una funzione. Per informazioni dettagliate sul codice di prologo ed epilogo richiesto su x64, vedere prologo ed epilogo x64.

Gestione delle eccezioni x64

Per informazioni sulle convenzioni e sulle strutture di dati usate per implementare la gestione delle eccezioni strutturata e il comportamento di gestione delle eccezioni C++ in x64, vedere Gestione delle eccezioni x64.

Intrinseci e assembly inline

Uno dei vincoli per il compilatore x64 non è supportato dall'assembler inline. Ciò significa che le funzioni che non possono essere scritte in C o C++ dovranno essere scritte come subroutine o come funzioni intrinseche supportate dal compilatore. Alcune funzioni sono sensibili alle prestazioni, mentre altre non lo sono. Le funzioni sensibili alle prestazioni devono essere implementate come funzioni intrinseche.

Gli intrinseci supportati dal compilatore sono descritti in Funzioni intrinseche del compilatore.

Formato immagine x64

Il formato di immagine eseguibile x64 è PE32+. Le immagini eseguibili (DLL e EXEs) sono limitate a una dimensione massima di 2 gigabyte, quindi è possibile usare l'indirizzamento relativo con uno spostamento a 32 bit per gestire i dati delle immagini statiche. Questi dati includono la tabella degli indirizzi di importazione, le costanti stringa, i dati globali statici e così via.

Vedi anche

Convenzioni di chiamata