__vectorcall
Sezione specifica Microsoft
La __vectorcall
convenzione di chiamata specifica che gli argomenti delle funzioni devono essere passati nei registri, quando possibile. __vectorcall
usa più registri per gli argomenti rispetto __fastcall
all'uso della convenzione di chiamata x64 predefinita. La convenzione di chiamata __vectorcall
è supportata solo nel codice nativo su processori x86 e x64 che includono Streaming SIMD Extensions 2 (SSE2) e versioni successive. Usare __vectorcall
per velocizzare le funzioni che passano diversi argomenti vettoriali a virgola mobile o SIMD ed eseguono operazioni che sfruttano gli argomenti caricati nei registri. L'elenco seguente mostra le funzionalità comuni alle implementazioni x86 e x64 di __vectorcall
. Le differenze sono descritte più avanti in questo articolo.
Elemento | Implementazione |
---|---|
Convenzione della decorazione dei nomi C | I nomi delle funzioni sono suffissi con due segni "at" (@@) seguiti dal numero di byte (in decimale) nell'elenco dei parametri. |
Convenzione della conversione maiuscolo/minuscolo e viceversa | Non viene effettuata alcuna conversione maiuscolo/minuscolo. |
L'uso dell'opzione del /Gv
compilatore fa sì che ogni funzione nel modulo venga compilata come __vectorcall
a meno che la funzione non sia una funzione membro, venga dichiarata con un attributo convenzione di chiamata in conflitto, usi un vararg
elenco di argomenti di variabile o abbia il nome main
.
È possibile passare tre tipi di argomenti registrando nelle funzioni: valori di tipo integer, valori di tipo vettore e valori di aggregazione vettoriale omogenea (HVA).__vectorcall
Un tipo integer soddisfa due requisiti: si adatta alle dimensioni native del registro del processore, ad esempio 4 byte in un computer x86 o 8 byte in un computer x64, ed è convertibile in un numero intero di lunghezza del registro e di nuovo senza modificare la relativa rappresentazione di bit. Ad esempio, qualsiasi tipo che può essere promosso a int
in x86 (long long
su x64), ad esempio un char
oggetto o short
, o che può essere eseguito il cast a int
(long long
su x64) e tornare al tipo originale senza modificare è un tipo integer. I tipi integer includono puntatore, riferimento e struct
tipi union
di 4 byte (8 byte su x64) o meno. Nelle piattaforme x64, i tipi più grandi struct
e union
vengono passati per riferimento alla memoria allocata dal chiamante. Nelle piattaforme x86 vengono passati per valore nello stack.
Un tipo di vettore è un tipo a virgola mobile, ad esempio o float
double
, o un tipo di vettore SIMD, __m128
ad esempio o __m256
.
Un tipo HVA è un tipo composto da membri dati (fino a quattro) con tipi vettore identici. Un tipo HVA ha lo stesso requisito di allineamento del tipo vettore dei relativi membri. Questo è un esempio di definizione HVA struct
che contiene tre tipi di vettore identici e ha un allineamento a 32 byte:
typedef struct {
__m256 x;
__m256 y;
__m256 z;
} hva3; // 3 element HVA type on __m256
Dichiarare le funzioni in modo esplicito con la __vectorcall
parola chiave nei file di intestazione per consentire il collegamento di codice compilato separatamente senza errori. Le funzioni devono essere prototipo per l'uso __vectorcall
di e non possono usare un vararg
elenco di argomenti a lunghezza variabile.
È possibile dichiarare una funzione membro usando l'identificatore __vectorcall
. Il puntatore nascosto this
viene passato da register come primo argomento di tipo integer.
Nei computer ARM, __vectorcall
viene accettato e ignorato dal compilatore. In ARM64EC, __vectorcall
non è supportato e rifiutato dal compilatore.
Per le funzioni membro di classi non statiche, se la funzione viene definita non inline, il modificatore della convenzione di chiamata non deve essere specificato nella definizione non inline. Questo significa che per i membri non statici della classe, la convenzione di chiamata specificata durante la dichiarazione è presunta in corrispondenza della definizione. Data la definizione di classe seguente
struct MyClass {
void __vectorcall mymethod();
};
il codice seguente
void MyClass::mymethod() { return; }
equivale a questo
void __vectorcall MyClass::mymethod() { return; }
Il __vectorcall
modificatore della convenzione di chiamata deve essere specificato quando viene creato un puntatore a una __vectorcall
funzione. Nell'esempio seguente viene creato un oggetto typedef
per un puntatore a una __vectorcall
funzione che accetta quattro double
argomenti e restituisce un __m256
valore:
typedef __m256 (__vectorcall * vcfnptr)(double, double, double, double);
Per la compatibilità con le versioni precedenti, _vectorcall
è un sinonimo di __vectorcall
a meno che non sia specificata l'opzione /Za
del compilatore (Disabilita estensioni del linguaggio).
Convenzione __vectorcall su piattaforme x64
La __vectorcall
convenzione di chiamata su x64 estende la convenzione di chiamata x64 standard per sfruttare i vantaggi di registri aggiuntivi. Sia gli argomenti tipo Integer che gli argomenti tipo vettore vengono mappati ai registri in base alla posizione nell'elenco di argomenti. Gli argomenti HVA vengono allocati nei registri vettoriali inutilizzati.
Quando uno dei primi quattro argomenti, in ordine da sinistra a destra, sono argomenti tipo Integer, vengono passati nel registro corrispondente a tale posizione, ovvero RCX, RDX, R8 o R9. Un puntatore nascosto this
viene considerato come il primo argomento di tipo integer. Quando non è possibile passare un argomento HVA in uno dei primi quattro argomenti nei registri disponibili, viene passato un riferimento alla memoria allocata dal chiamante nel registro dei tipi integer corrispondente. Gli argomenti tipo Integer dopo la posizione del quarto parametro vengono passati allo stack.
Quando uno dei primi sei argomenti, in ordine da sinistra a destra, sono argomenti tipo vettore, vengono passati per valore nei registri vettoriali SSE da 0 a 5 in base alla posizione dell'argomento. I tipi e __m128
a virgola mobile vengono passati nei registri XMM e __m256
i tipi vengono passati nei registri YMM. In questo caso esiste una differenza rispetto alla convenzione di chiamata x64 standard, poiché i tipi vettore sono passati per valore anziché per riferimento e vengono utilizzati registri aggiuntivi. Lo spazio dello stack di ombreggiature allocato per gli argomenti di tipo vettore è fisso a 8 byte e l'opzione /homeparams
non si applica. Gli argomenti tipo vettore nelle posizioni del settimo parametro e successive vengono passati nello stack per riferimento alla memoria allocata dal chiamante.
Dopo l'allocazione dei registri per gli argomenti vettoriali, i membri dati degli argomenti HVA vengono allocati, in ordine crescente, ai registri vettoriali inutilizzati XMM0 a XMM5 (o da YMM0 a YMM5, per __m256
i tipi), purché siano disponibili registri sufficienti per l'intera HVA. Se non sono disponibili registri sufficienti, l'argomento HVA verrà passato per riferimento alla memoria allocata dal chiamante. Lo spazio dello shadow stack per un argomento HVA è fissato a 8 byte con contenuto non definito. Gli argomenti HVA vengono assegnati ai registri in ordine da sinistra a destra nell'elenco dei parametri e possono trovarsi in qualsiasi posizione. Gli argomenti HVA in una delle prime quattro posizioni non assegnati ai registri vettoriali vengono passati per riferimento nel registro di tipi Integer corrispondente a tale posizione. Gli argomenti HVA passati per riferimento dopo la posizione del quarto parametro vengono inseriti nello stack.
I risultati delle __vectorcall
funzioni vengono restituiti dal valore nei registri, quando possibile. I risultati di tipo Integer, inclusi i valori struct o union di 8 byte o meno, vengono restituiti per valore in RAX. I risultati di tipo vettore vengono restituiti per valore in XMM0 o in YMM0, a seconda della dimensione. Nei risultati HVA ogni elemento dati viene restituito per valore nei registri da XMM0 a XMM3 o da YMM0 a YMM3, in base alle dimensioni dell'elemento. I tipi di risultati che non rientrano nei registri corrispondenti vengono restituiti per riferimento alla memoria allocata dal chiamante.
Lo stack viene gestito dal chiamante nell'implementazione x64 di __vectorcall
. Nel codice di prologo ed epilogo del chiamante viene allocato e pulito lo stack per la funzione chiamata. Gli argomenti vengono inseriti nello stack da destra a sinistra e viene allocato lo spazio dello shadow stack per gli argomenti passati nei registri.
Esempi:
// crt_vc64.c
// Build for amd64 with: cl /arch:AVX /W3 /FAs crt_vc64.c
// This example creates an annotated assembly listing in
// crt_vc64.asm.
#include <intrin.h>
#include <xmmintrin.h>
typedef struct {
__m128 array[2];
} hva2; // 2 element HVA type on __m128
typedef struct {
__m256 array[4];
} hva4; // 4 element HVA type on __m256
// Example 1: All vectors
// Passes a in XMM0, b in XMM1, c in YMM2, d in XMM3, e in YMM4.
// Return value in XMM0.
__m128 __vectorcall
example1(__m128 a, __m128 b, __m256 c, __m128 d, __m256 e) {
return d;
}
// Example 2: Mixed int, float and vector parameters
// Passes a in RCX, b in XMM1, c in R8, d in XMM3, e in YMM4,
// f in XMM5, g pushed on stack.
// Return value in YMM0.
__m256 __vectorcall
example2(int a, __m128 b, int c, __m128 d, __m256 e, float f, int g) {
return e;
}
// Example 3: Mixed int and HVA parameters
// Passes a in RCX, c in R8, d in R9, and e pushed on stack.
// Passes b by element in [XMM0:XMM1];
// b's stack shadow area is 8-bytes of undefined value.
// Return value in XMM0.
__m128 __vectorcall example3(int a, hva2 b, int c, int d, int e) {
return b.array[0];
}
// Example 4: Discontiguous HVA
// Passes a in RCX, b in XMM1, d in XMM3, and e is pushed on stack.
// Passes c by element in [YMM0,YMM2,YMM4,YMM5], discontiguous because
// vector arguments b and d were allocated first.
// Shadow area for c is an 8-byte undefined value.
// Return value in XMM0.
float __vectorcall example4(int a, float b, hva4 c, __m128 d, int e) {
return b;
}
// Example 5: Multiple HVA arguments
// Passes a in RCX, c in R8, e pushed on stack.
// Passes b in [XMM0:XMM1], d in [YMM2:YMM5], each with
// stack shadow areas of an 8-byte undefined value.
// Return value in RAX.
int __vectorcall example5(int a, hva2 b, int c, hva4 d, int e) {
return c + e;
}
// Example 6: HVA argument passed by reference, returned by register
// Passes a in [XMM0:XMM1], b passed by reference in RDX, c in YMM2,
// d in [XMM3:XMM4].
// Register space was insufficient for b, but not for d.
// Return value in [YMM0:YMM3].
hva4 __vectorcall example6(hva2 a, hva4 b, __m256 c, hva2 d) {
return b;
}
int __cdecl main( void )
{
hva4 h4;
hva2 h2;
int i;
float f;
__m128 a, b, d;
__m256 c, e;
a = b = d = _mm_set1_ps(3.0f);
c = e = _mm256_set1_ps(5.0f);
h2.array[0] = _mm_set1_ps(6.0f);
h4.array[0] = _mm256_set1_ps(7.0f);
b = example1(a, b, c, d, e);
e = example2(1, b, 3, d, e, 6.0f, 7);
d = example3(1, h2, 3, 4, 5);
f = example4(1, 2.0f, h4, d, 5);
i = example5(1, h2, 3, h4, 5);
h4 = example6(h2, h4, c, h2);
}
Convenzione __vectorcall su piattaforme x86
La __vectorcall
convenzione di chiamata segue la __fastcall
convenzione per gli argomenti di tipo integer a 32 bit e sfrutta i registri dei vettori SSE per gli argomenti vector type e HVA.
I primi due argomenti di tipo Integer presenti nell'elenco di parametri da sinistra a destra vengono inseriti rispettivamente in ECX e in EDX. Un puntatore nascosto this
viene considerato come il primo argomento di tipo integer e viene passato in ECX. I primi sei argomenti di tipo vettore vengono passati per valore attraverso i registri vettoriali SSE da 0 - 5 oppure nei registri YMM o XMM in base alle dimensioni dell'argomento.
I primi sei argomenti tipo vettore, in ordine da sinistra a destra, vengono passati per valore nei registri vettoriali SSE da 0 a 5. I tipi e __m128
a virgola mobile vengono passati nei registri XMM e __m256
i tipi vengono passati nei registri YMM. Nessuno spazio dello shadow stack viene allocato per gli argomenti tipo vettore passati dal registro. Il settimo argomento di tipo vettore e quelli successivi vengono passati nello stack per riferimento alla memoria allocata dal chiamante. La limitazione dell'errore del compilatore C2719 non si applica a questi argomenti.
Dopo l'allocazione dei registri per gli argomenti vettoriali, i membri dati degli argomenti HVA vengono allocati in ordine crescente per registrare i vettori inutilizzati XMM0 a XMM5 (o da YMM0 a YMM5, per __m256
i tipi), purché siano disponibili registri sufficienti per l'intera appliance virtuale di protezione hardware. Se non sono disponibili registri sufficienti, l'argomento HVA verrà passato sullo stack per riferimento alla memoria allocata dal chiamante. Nessuno spazio dello stack shadow viene allocato per un argomento HVA. Gli argomenti HVA vengono assegnati ai registri in ordine da sinistra a destra nell'elenco dei parametri e possono trovarsi in qualsiasi posizione.
I risultati delle __vectorcall
funzioni vengono restituiti dal valore nei registri, quando possibile. I risultati di tipo Integer, inclusi i valori struct o union di 4 byte o meno, vengono restituiti per valore in EAX. I valori struct o union di tipo Integer di 8 byte o meno vengono restituiti per valore nei registri da EDX a EAX. I risultati di tipo vettore vengono restituiti per valore in XMM0 o in YMM0, a seconda della dimensione. Nei risultati HVA ogni elemento dati viene restituito per valore nei registri da XMM0 a XMM3 o da YMM0 a YMM3, in base alle dimensioni dell'elemento. Altri tipi di risultati vengono restituiti per riferimento alla memoria allocata dal chiamante.
L'implementazione x86 di __vectorcall
segue la convenzione degli argomenti inseriti nello stack da destra a sinistra dal chiamante e la funzione chiamata cancella lo stack subito prima che venga restituito. Solo gli argomenti non posizionati nei registri sono inviati allo stack.
Esempi:
// crt_vc86.c
// Build for x86 with: cl /arch:AVX /W3 /FAs crt_vc86.c
// This example creates an annotated assembly listing in
// crt_vc86.asm.
#include <intrin.h>
#include <xmmintrin.h>
typedef struct {
__m128 array[2];
} hva2; // 2 element HVA type on __m128
typedef struct {
__m256 array[4];
} hva4; // 4 element HVA type on __m256
// Example 1: All vectors
// Passes a in XMM0, b in XMM1, c in YMM2, d in XMM3, e in YMM4.
// Return value in XMM0.
__m128 __vectorcall
example1(__m128 a, __m128 b, __m256 c, __m128 d, __m256 e) {
return d;
}
// Example 2: Mixed int, float and vector parameters
// Passes a in ECX, b in XMM0, c in EDX, d in XMM1, e in YMM2,
// f in XMM3, g pushed on stack.
// Return value in YMM0.
__m256 __vectorcall
example2(int a, __m128 b, int c, __m128 d, __m256 e, float f, int g) {
return e;
}
// Example 3: Mixed int and HVA parameters
// Passes a in ECX, c in EDX, d and e pushed on stack.
// Passes b by element in [XMM0:XMM1].
// Return value in XMM0.
__m128 __vectorcall example3(int a, hva2 b, int c, int d, int e) {
return b.array[0];
}
// Example 4: HVA assigned after vector types
// Passes a in ECX, b in XMM0, d in XMM1, and e in EDX.
// Passes c by element in [YMM2:YMM5].
// Return value in XMM0.
float __vectorcall example4(int a, float b, hva4 c, __m128 d, int e) {
return b;
}
// Example 5: Multiple HVA arguments
// Passes a in ECX, c in EDX, e pushed on stack.
// Passes b in [XMM0:XMM1], d in [YMM2:YMM5].
// Return value in EAX.
int __vectorcall example5(int a, hva2 b, int c, hva4 d, int e) {
return c + e;
}
// Example 6: HVA argument passed by reference, returned by register
// Passes a in [XMM1:XMM2], b passed by reference in ECX, c in YMM0,
// d in [XMM3:XMM4].
// Register space was insufficient for b, but not for d.
// Return value in [YMM0:YMM3].
hva4 __vectorcall example6(hva2 a, hva4 b, __m256 c, hva2 d) {
return b;
}
int __cdecl main( void )
{
hva4 h4;
hva2 h2;
int i;
float f;
__m128 a, b, d;
__m256 c, e;
a = b = d = _mm_set1_ps(3.0f);
c = e = _mm256_set1_ps(5.0f);
h2.array[0] = _mm_set1_ps(6.0f);
h4.array[0] = _mm256_set1_ps(7.0f);
b = example1(a, b, c, d, e);
e = example2(1, b, 3, d, e, 6.0f, 7);
d = example3(1, h2, 3, 4, 5);
f = example4(1, 2.0f, h4, d, 5);
i = example5(1, h2, 3, h4, 5);
h4 = example6(h2, h4, c, h2);
}
End Microsoft Specific
Vedi anche
Passaggio di argomenti e convenzioni di denominazione
Parole chiave