__vectorcall
Specyficzne dla firmy Microsoft
Konwencja wywoływania __vectorcall określa, że argumenty funkcji są przekazywane do rejestrów, gdy jest to możliwe.__vectorcall używa więcej rejestrów dla argumentów niż __fastcall lub używa domyślnej konwencji wywoływania x64.Konwencja wywoływania __vectorcall jest obsługiwana tylko w kodzie macierzystym na procesorach x86 i x64, które zawierają Streaming SIMD Extensions 2 (SSE2) i powyżej.Użyj __vectorcall, aby przyspieszyć funkcje, które wychodzą za kilka argumentów liczb zmiennoprzecinkowych lub wektora SIMD i przeprowadź operacje, które wykorzystują argumenty załadowane w rejestrach.Poniższa lista zawiera funkcje, które są wspólne dla implementacji x 86 i x 64 z __vectorcall.Różnice opisano w dalszej części tego artykułu.
Element |
Implementacja |
---|---|
Konwencja dekorowania nazw języka C |
Po nazwach funkcji są umieszczone dwa znaki handlowego „po” (@@), po których następuje liczba bajtów (w zapisie dziesiętnym) na liście parametrów. |
Tłumaczenie przypadku konwencji |
Nie wykonano żadnego procesu tłumaczenia. |
Użycie opcji kompilatora /Gv powoduje, że każda funkcja w module jest skompilowana jako __vectorcall, chyba że funkcja jest funkcja członkowską, jest zadeklarowane ze sprzecznym atrybutem konwencji wywoływania, używa listy argumentów zmiennych vararg, lub jest określana nazwą main.
Możesz przekazać trzy rodzaje argumenty przez rejestr w funkcjach __vectorcall: wartości typu całkowitego, wartości typu wektorowego, wartości (HVA) agregat wektora jednorodnego.
Typ liczby całkowitej spełnia dwa wymogi: pasuje do rozmiaru rejestru macierzystego procesora — na przykład 4 bajty na komputerze z procesorem x86 lub 8 bajtów na komputerze x64 — i umożliwia konwertowanie na liczbę całkowitą długości rejestru i ponowną zmianę bez zmiany jego bitowej reprezentacji.Na przykład dowolny typ, którego poziom może być podwyższony do int w architekturze x86 (long long w architekturze x64) — na przykład char lub short— lub może być rzutowany na typ int (long long w architekturze x64) i z powrotem do oryginalnego typu bez zmiany typu liczby całkowitej.Typu całkowitoliczbowe obejmują wskaźnik, odniesieni i typy struct lub union o długości 4 bajty (8 bajtów w architekturze x64) lub mniej.Na platformach x64 większe typy struct i union są przekazywane przez odwołanie do pamięci przydzielonej przez obiekt wywołujący; w platformach x86 są przekazywane przez wartości na stosie.
Typem wektora jest typ zmiennoprzecinkowy — na przykład float lub double — lub typ wektora SIMD — na przykład __m128 lub __m256.
Typ HVA jest typu złożonego do czterech elementów danych, które mają identyczne typy wektorów.Typ HVA ma ten sam wymóg wyrównania jako typ wektora jego członków.Jest to przykład definicji HVA struct, która zawiera trzy identyczne typy wektorów i 32-bajtowe wyrównanie:
typedef struct {
__m256 x;
__m256 y;
__m256 z;
} hva3; // 3 element HVA type on __m256
Jawnie deklaruj swoje funkcje za pomocą słowa kluczowego __vectorcall w nagłówku plików, aby umożliwić oddzielnie skompilowanemu kodowi utworzenie łącza bez błędów.Funkcje muszą mieć prototypy, aby użyć specyfikatora __vectorcall, i nie mogą używać listy argumentów o zmiennej długości vararg.
Funkcja członka może być zadeklarowana za pomocą specyfikatora __vectorcall.Ukryty wskaźnik this jest przekazywany przez rejestr jako pierwszy argument typu liczba całkowita.
W przypadku komputerów ARM słowo kluczowe __vectorcall jest akceptowane i ignorowane przez kompilator.
W przypadku funkcji niestatycznych klas członków, jeśli funkcja jest zdefiniowana poza wierszem, modyfikator konwencji wywołania nie musi być określony w definicji poza wierszem.Oznacza to, że dla niestatycznych członków klasy przyjmowana jest konwencja wywołania określona podczas zgłoszenia w punkcie definicji.Biorąc pod uwagę tę definicję klasy:
struct MyClass {
void __vectorcall mymethod();
};
to:
void MyClass::mymethod() { return; }
jest równoważne do:
void __vectorcall MyClass::mymethod() { return; }
Modyfikator konwencja wywoływania __vectorcall musi zostać określony po utworzeniu wskaźnika do funkcji __vectorcall.Następny przykład tworzy typedef dla wskaźnika do funkcji __vectorcall, która pobiera cztery argumenty double i zwraca wartość __m256:
typedef __m256 (__vectorcall * vcfnptr)(double, double, double, double);
Konwencja __vectorcall x64
Konwencja wywoływania __vectorcall na x64 rozszerza standardową konwencję wywoływania x64, aby skorzystać z dodatkowych rejestrów.Zarówno argumenty typu liczba całkowita, jak i argumenty typu wektor są mapowane do rejestrów na podstawie pozycji na liście argumentów.Argumenty agregatów HVA są przydzielane do nieużywanych rejestrów wektorowych.
Jeśli którekolwiek z pierwszych czterech argumentów w porządku od lewej do prawej są argumentami typu liczba całkowita, są one przekazywane do rejestru, który odpowiada tej pozycji — RCX, RDX, R8 lub R9.Ukryty wskaźnik this jest traktowany jako pierwszy argument typu liczba całkowita.Jeśli argument HVA w jednym z pierwszych czterech argumentów nie może być przekazywany w dostępnych rejestrach, odwołanie do pamięci przydzielonej przez obiekt wywołujący jest przekazywane w odpowiednim rejestrze typu Liczba całkowita.Argumenty typu całkowitoliczbowego po czwartej pozycji parametru są przekazywane na stos.
Jeśli którekolwiek z pierwszych sześciu argumentów w porządku od lewej do prawej są argumentami typu wektor, są one przekazywane według wartości w rejestrach wektora SSE od 0 do 5, zgodnie z pozycją argumentu.Typy zmiennoprzecinkowe i __m128 są przekazywane w rejestrach XMM, a typy __m256 są przekazywane w rejestrach YMM.To różni się od standardowej konwencji wywoływania x 64, ponieważ typy wektorowe są przekazywane według wartości, a nie według odwołania, i używane są dodatkowe rejestry.Obszar stosu cienia przydzielony dla argumentów typu wektor jest ustalony na 8 bajtów, a opcja /homeparams nie ma zastosowania.Argumenty typu wektora w siódmej i dalszych pozycjach parametru są przekazywane na stosie przez odwołanie do pamięci przydzielonej przez obiekt wywołujący.
Po przydzieleniu wszystkich rejestracji dla argumentów wektora, dane członków argumentów HVA są przydzielane w kolejności rosnącej do nieużywanych wektorów rejestrujących XMM0 do XMM5 (lub YMM0 do YMM5, dla typów __m256) tak długo, jak rejestry są dostępne dla całego HVA.Jeśli niewystarczająca liczba rejestrów jest dostępna, argument agregatu HVA jest przekazywany przez odwołanie do pamięci przydzielonej przez obiekt wywołujący.Obszar stosu cienia dla argumentu HVA jest ustalony na 8 bajtów z zawartością niezdefiniowaną.Argumenty agregatów HVA są przypisane do rejestrów w kolejności od lewej do prawej na liście parametrów i mogą być w dowolnym miejscu.Argumenty agregatów HVA na jednej z czterej pierwszych pozycji argumentów , które nie są przypisane do rejestrów wektorowych, są przekazywane przez odwołanie w rejestrze całkowitoliczbowym, które odpowiada tej pozycji.Argumenty agregatu HVA przekazywane przez odwołanie po czwartej pozycji parametru są wypychane na stos.
Wyniki funkcji __vectorcall są zwracane przez wartość w rejestrach, jeżeli jest to możliwe.Wyniki typu liczba całkowita, włącznie ze strukturami lub związkami 8 bajtów lub mniej, są zwracane według wartości w RAX.Wyniki typu wektor są zwracane przez wartość w XMM0 lub YMM0, w zależności od rozmiaru.Wyniki agregatu HVA mają każdy element danych zwracany przez wartość w rejestrach XMM0:XMM3 lub YMM0:YMM3, w zależności od rozmiaru elementu.Typy wyników, które nie pasują do odpowiednich rejestrów są zwracane przez odniesienie do pamięci przydzielonej przez wywołującego.
Stos jest utrzymywany przez obiekt wywołujący w implementacji x 64 __vectorcall.Kod prologu i epilogu wywołującego przydziela i czyści stos dla wywołanej funkcji.Argumenty są wypychane na stosie od prawej do lewej, a cień obszaru stosu jest zarezerwowany dla argumentów przekazywanych do rejestrów.
Przykłady:
// 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);
}
Konwencja __vectorcall x86
Konwencja wywoływania __vectorcall kroczy za konwencją __fastcall dla argumentów typu liczba całkowita 32-bitowa i wykorzystuje rejestry wektora SSE dla typu wektora i argumentów HVA.
Pierwsze dwa argumenty typu liczba całkowita znalezione na liście parametrów od lewej do prawej są umieszczone odpowiednio w ECX i EDX.Ukryty wskaźnik this jest traktowany jako pierwszy argument typu liczba całkowita i przechodzi w ECX.Pierwsze sześć argumentów typu wektor jest przekazywanych przez wartość przez rejestry wektorów SSE 0-5, w rejestrach XMM lub YMM, w zależności od rozmiaru argumentu.
Pierwsze sześć argumentów typu wektor, w kolejności od lewej do prawej, jest przekazywanych przez wartość w rejestrach wektorów SSE 0 do 5.Typy zmiennoprzecinkowe i __m128 są przekazywane w rejestrach XMM, a typy __m256 są przekazywane w rejestrach YMM.Żaden obszar stosu w tle nie jest przydzielany do argumentów typu wektorowego przekazywanych przez rejestr.Argumenty typu wektora siódmego i kolejnych są przekazywane na stosie przez odwołanie do pamięci przydzielonej przez obiekt wywołujący.Ograniczenie błędu kompilatora C2719 nie dotyczy tych argumentów.
Po przydzieleniu wszystkich rejestracji dla argumentów wektora, dane członków argumentów HVA są przydzielane w kolejności rosnącej do nieużywanych wektorów rejestrujących XMM0 do XMM5 (lub YMM0 do YMM5, dla typów __m256) tak długo, jak rejestry są dostępne dla całego HVA.Jeśli niewystarczająca liczba rejestrów jest dostępna, argument agregatu HVA jest przekazywany w stosie przez odwołanie do pamięci przydzielonej przez obiekt wywołujący.Nie jest przydzielany żaden obszar stosu w tle argumentu agregatu HVA.Argumenty agregatów HVA są przypisane do rejestrów w kolejności od lewej do prawej na liście parametrów i mogą być w dowolnym miejscu.
Wyniki funkcji __vectorcall są zwracane przez wartość w rejestrach, jeżeli jest to możliwe.Wyniki typu liczba całkowita, włącznie ze strukturami lub związkami 4 bajtów lub mniej, są zwracane według wartości w EAX.Struktury typu całkowitoliczbowego lub sumy rozłączne o wielkości 8 bajtów lub mniej są zwracane przez wartość w parametrze EDX:EAX.Wyniki typu wektor są zwracane przez wartość w XMM0 lub YMM0, w zależności od rozmiaru.Wyniki agregatu HVA mają każdy element danych zwracany przez wartość w rejestrach XMM0:XMM3 lub YMM0:YMM3, w zależności od rozmiaru elementu.Inne typy wyników są zwracane przez odwołanie do pamięci przydzielonej przez obiekt wywołujący.
Implementacja x86 __vectorcall następuje po konwencji argumentów przesuniętych na stosie od prawej do lewej przez wywołującego, po czym wywołana funkcja czyści stos tuż przed zwróceniem.Tylko argumenty, które nie znajdują się w rejestrach, są wypychane na stos.
Przykłady:
// 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 specyficzny dla Microsoft