Sdílet prostřednictvím


__vectorcall

Specifické pro Microsoft

Konvence volání __vectorcall určuje, že argumenty funkcí mají být předány v registrech, pokud je to možné.__vectorcall používá více registrů pro argumenty, než kolik používá __fastcall nebo výchozí konvence volání x64.Konvence volání __vectorcall je podporována pouze v nativním kódu u x86 a x64 procesorů, které disponují funkcí Streaming SIMD Extensions 2 (SSE2) a výše.Použijte __vectorcall k urychlení funkcí, které předávají několik vektorových argumentů s plovoucí desetinnou čárkou nebo SIMD a provádějí operace, které využívají argumenty načtené do registrů.Následující seznam obsahuje funkce, které jsou společné pro implementace x86 a x64 __vectorcall.Rozdíly jsou vysvětleny dále v tomto článku.

Element

Implementace

Konvence pro vzhled názvu C

Názvy funkcí jsou doplněny dvěma symboly "zavináč" (@@) následovanými počtem bajtů (v desítkové soustavě) v seznamu parametrů je přidán k názvům.

Úmluva překladu případu

Neprovádí se žádný překlad případu.

Použití možnosti kompilátoru /Gr způsobí, že každá funkce v modulu je kompilována jako __vectorcall, pokud není funkce členská funkce, je deklarována s konfliktním atributem konvence volání, používá seznam argumentů proměnných vararg nebo má název main.

Můžete předat tři typy argumentů podle registru ve funkcích __vectorcall: celočíselný typ hodnoty, typ vektoru hodnoty a agregát homogenního vektoru (HVA).

Typ celého čísla splňuje dva požadavky: vejde se do nativní velikosti registru procesoru – například 4 bajty v počítači x86 nebo 8 bajtů v počítači x64 – a lze jej převést na celé číslo délky registru a zpět beze změny jeho bitové reprezentace.Například libovolný typ, který může být povýšen na int na platformě x86 (long long na platformě x64) – například char nebo short- nebo který může být převeden na int (long long na platformě x64) a zpět na původní typ, bez změny, která je typu integer.Celočíselné typy zahrnují ukazatel, odkaz a typy struct nebo union 4 bajtů (8 bajtů na platformě x 64) nebo méně.Na platformě x64 platformy jsou typy struct a union předány podle odkazu v paměti přidělené volajícím; na platformách x86 jsou předávány podle hodnoty v zásobníku.

Typ vektoru je typ s plovoucí desetinnou čárkou, například float nebo double, nebo typ vektoru SIMD, například __m128 či __m256.

Typ HVA je složený typ z až čtyř datových členů, které mají identický vektorový typ.Typ HVA má stejný požadavek na zarovnání jako typ vektoru jeho členů.Toto je příklad definici HVA struct, která obsahuje tři totožné typy vektoru a má 32bajtové zarovnání:

typedef struct {
   __m256 x;
   __m256 y;
   __m256 z;
} hva3;    // 3 element HVA type on __m256

Deklarace vašich funkcí explicitně s klíčovým slovem __vectorcall v záhlaví souborů, aby bylo možné samostatně zkompilovat kód k propojení bez chyb.Musí být prototypem pomocí funkce __vectorcall a nelze použít seznam argumentů délky proměnné vararg.

Členská funkce může být deklarována pomocí specifikátoru __vectorcall.Skrytý ukazatel this je registrem odeslán jako první argument celočíselného typu.

Na počítačích ARM je __vectorcall kompilátorem přijat a ignorován.

U funkcí členů nestatické třídy, je-li funkce definovaná mimo řádek, volající konvenční modifikátor nemusí být stanoven na definici mimo řádek.To znamená, že pro nestatické členy se konvence volání zadaná během deklarace přejme během definice.Při této definici třídy:

struct MyClass {
   void __vectorcall mymethod();
};

toto:

void MyClass::mymethod() { return; }

je ekvivalentem tohoto:

void __vectorcall MyClass::mymethod() { return; }

Modifikátor konvence volání __vectorcall musí být určen při vytvoření ukazatele na funkci __vectorcall.Následující příklad vytvoří typedef pro ukazatel ve funkci __vectorcall, která přebírá čtyři argumenty double a vrátí hodnotu __m256:

typedef __m256 (__vectorcall * vcfnptr)(double, double, double, double);

Konvence __vectorcall na x64

Konvence volání __vectorcall na platformě x64 rozšiřuje standard x64 konvence volání, která tak využívá dalších registrů.Argumenty typu integer a argumenty typu vector jsou mapovány do registrů, které jsou založeny na umístění v seznamu argumentů.Nepoužité vektorové registry jsou přiděleny argumentům HVA.

Pokud některý z prvních čtyř argumentů v pořadí zleva doprava jsou argumenty typu celé číslo, jsou předány do registru, který odpovídá této pozici – RCX, RDX, R8 a R9.Skrytý ukazatel this je považován za první argument typu integer.Pokud argument HVA v jednom z prvních čtyř argumentů nelze předávat do dostupných registrů, odkaz na paměť přidělenou volajícímu je místo něho předán do odpovídajícího registru typu celého čísla.Argumenty typu celého čísla po pozici čtvrtého parametru jsou předány v zásobníku.

Pokud některý z prvních šesti argumentů v pořadí zleva doprava jsou argumenty vektorového typu, jsou předány podle hodnoty do registrů vektoru SSE 0 až 5 podle pozice argumentu.Plovoucí desetinná čárka a typy __m128 jsou předány v registrech XMM a typy __m256 jsou předány v registrech YMM.Tím se liší od standardní konvence volání x64, protože vektorové typy jsou předávány hodnotou místo pomocí odkazu a jsou použity další registry.Vystínovaný prostor zásobníku přidělený pro argumenty vektorového typu je nastaven na 8 bajtech a možnost /homeparams neplatí.Argumenty vektorového typu na sedmé a následujících pozicích parametru jsou předány do zásobníku s odkazem na paměť přidělenou volajícím.

Po přidělení registrů vektorovým argumentům budou datové členy argumentů HVA přiděleny vzestupně nepoužívaným vektorovým registrům XMM0 až XMM5 (nebo YMM0 až YMM5, pro typy __m256), dokud nebude k dispozici dostatek registrů pro celé HVA.Není-li k dispozici dostatek registrů, argument HVA je předán odkazem na paměť přidělenou volajícím.Stínovaný prostor zásobníku pro argument HVA je pevně ukotven na 8 bajtech s nedefinovaným obsahem.Argumenty HVA jsou přiřazeny k registrům v pořadí zleva doprava v seznamu parametrů a mohou být v jakékoliv pozici.Argumenty HVA v jedné z prvních čtyř pozic argumentů, které nejsou přiřazeny k vektorovým registrům, jsou předány podle odkazu v registru celých čísel, který odpovídá této pozici.Argumenty HVA předané podle odkazu po pozici čtvrtého parametru jsou poslány do zásobníku.

Výsledky funkcí __vectorcall jsou vrácené hodnotou v registrech, pokud je to možné.Hodnota v RAX vrátí výsledky celočíselného typu, včetně struktur nebo sjednocení 8 či méně bajtů.Výsledky vektorového typu jsou vráceny hodnotou v XMM0 nebo YMM0, v závislosti na velikosti.Výsledky HVA mají každý datový element vrácen podle hodnoty v registrech XMM0:XMM3 nebo YMM0:YMM3, v závislosti na velikosti elementu.Odkaz na paměť přidělenou volajícím vrátí typy výsledků, které se nehodí do příslušného registru.

Zásobník je volajícím udržován v implementaci x64 systému __vectorcall.Kód prologu a epilogu volajícího přiděluje a maže zásobník volané funkce.Argumenty jsou posunuty v zásobníku zprava doleva a je přiděleno stínové místo zásobníku pro argumenty předané v registrech.

Příklady:

// 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);
}

Konvence __vectorcall na x86

Konvence volání __vectorcall postupuje podle konvence __fastcall pro 32bitové argumenty typu celého čísla a využívá registrů vektoru SSE pro typ vektoru a argumenty HVA.

První argumenty celočíselného typu v seznamu parametrů zleva doprava jsou umístěny jeden do ECX a druhý do EDX.Skrytý ukazatel this je považován za první argument typu integer a je předáván v ECX.Prvních šest argumentů vektorového typu jsou předávány hodnotou prostřednictvím vektorového registru SSE 0 až 5 v registrech XMM nebo YMM v závislosti na velikost argumentu.

Prvních šest argumentů vektorových typů v pořadí zleva doprava jsou předávány hodnotou vektorového registru SSE 0 až 5.Plovoucí desetinná čárka a typy __m128 jsou předány v registrech XMM a typy __m256 jsou předány v registrech YMM.Pro argumenty argumentů typu vector předaných z registru není přiděleno místo ve stínovém zásobníku.Sedmý a následující argumenty vektorového typu jsou předány do zásobníku s odkazem na paměť přidělenou volajícím.Omezení chyby kompilátoru C2719 se nevztahuje na tyto argumenty.

Po přidělení registrů vektorovým argumentům budou datové členy argumentů HVA přiděleny vzestupně nepoužívaným vektorovým registrům XMM0 až XMM5 (nebo YMM0 až YMM5, pro typy __m256), dokud nebude k dispozici dostatek registrů pro celé HVA.Není-li k dispozici dostatek registrů, argument HVA je předán do stohu odkazem na paměť přidělenou volajícím.Pro argumenty HVA není přiděleno místo ve stínovém zásobníku.Argumenty HVA jsou přiřazeny k registrům v pořadí zleva doprava v seznamu parametrů a mohou být v jakékoliv pozici.

Výsledky funkcí __vectorcall jsou vrácené hodnotou v registrech, pokud je to možné.Hodnota v EAX vrátí výsledky celočíselného typu, včetně struktur nebo sjednocení 4 či méně bajtů.Hodnota EDX:EAX vrátí struktury celočíselného typu nebo sjednocení 8 či méně bajtů.Výsledky vektorového typu jsou vráceny hodnotou v XMM0 nebo YMM0, v závislosti na velikosti.Výsledky HVA mají každý datový element vrácen podle hodnoty v registrech XMM0:XMM3 nebo YMM0:YMM3, v závislosti na velikosti elementu.Ostatní typy výsledků jsou vráceny ve vztahu k paměti přidělené volajícím.

Implementace X 86 __vectorcall následuje konvenci argumentů předaných v zásobníku zprava doleva volajícím a volané funkce vymaže zásobníku těsně před vrácením.Pouze argumenty, které nejsou uloženy v registrech, jsou odeslány do zásobníku.

Příklady:

// 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);
}

Specifické pro End Microsoft

Viz také

Referenční dokumentace

Konvence předávání a pojmenování argumentů

Klíčová slova jazyka C++