__vectorcall
Блок, относящийся только к системам Майкрософт
Соглашение __vectorcall
о вызовах указывает, что аргументы функций передаются в регистрах, когда это возможно. __vectorcall
использует больше регистров для аргументов, чем __fastcall
используется соглашение о вызовах x64 по умолчанию. Соглашение о вызовах __vectorcall
поддерживается только в машинном коде для процессоров x86 и x64, в которых используется набор инструкций SSE2 и выше. Используется для ускорения функций, которые передают несколько аргументов вектора с плавающей запятой или SIMD и выполняют операции, которые используют __vectorcall
аргументы, загруженные в регистры. В следующем списке показаны функции, которые являются общими __vectorcall
для реализаций x86 и x64. Различия объясняются ниже в этом разделе.
Элемент | Внедрение |
---|---|
Соглашение об оформлении имен C | Имена функций суффиксируются двумя знаками at (@) и числом байтов (в десятичном) списке параметров. |
Соглашение о преобразовании регистра | Преобразование регистра не выполняется. |
/Gv
Использование параметра компилятора приводит к компиляции каждой функции в модуле, если __vectorcall
функция не является функцией-членом, объявляется с конфликтующим атрибутом соглашения о вызове, использует vararg
список аргументов переменной или имеет имяmain
.
Можно передать три типа аргументов, регистрируя в __vectorcall
функциях: целочисленные значения типа, значения векторного типа и однородные значения агрегатных векторов (HVA).
Целочисленный тип удовлетворяет двум требованиям: он соответствует собственному размеру регистра процессора ( например, 4 байта на компьютере x86 или 8 байтах на компьютере x64), и он преобразуется в целое число регистра и обратно, не изменяя его битовое представление. Например, любой тип, который может быть повышен до int
x86 (long long
в x64) (например, или char
short
может быть приведение int
к (long long
на x64) и обратно к исходному типу без изменений является целым типом. Целые типы включают указатель, ссылку и struct
union
типы 4 байта (8 байт в x64) или меньше. На платформах x64 большие struct
и union
типы передаются по ссылке на память, выделенную вызывающим объектом; на платформах x86 они передаются по значению в стеке.
Тип вектора — это тип с плавающей запятой( например, a float
или double
SIMD-векторный тип, __m128
например или __m256
.
Тип HVA — это составной тип, в котором может содержаться до 4 элементов данных, имеющих идентичные векторные типы. Тип HVA предъявляет то же требование к выравниванию, что и векторный тип его членов. Это пример определения HVA struct
, содержащего три идентичных типа векторов и имеющий 32-байтовое выравнивание:
typedef struct {
__m256 x;
__m256 y;
__m256 z;
} hva3; // 3 element HVA type on __m256
Объявите функции явным образом с __vectorcall
помощью ключевое слово в файлах заголовков, чтобы разрешить отдельно скомпилированный код связываться без ошибок. Функции должны быть прототипами для использования __vectorcall
и не могут использовать список аргументов переменной vararg
длины.
Функция-член может быть объявлена __vectorcall
с помощью описателя. Скрытый this
указатель передается регистром в качестве первого аргумента типа целочисленного типа.
На компьютерах __vectorcall
ARM принимается и игнорируется компилятором. В ARM64EC не __vectorcall
поддерживается и отклоняется компилятором.
Если используется внестрочное определение нестатической функции-члена класса, то модификатор соглашения о вызовах не должен быть задан во внестрочном определении. То есть для нестатических членов класса считается, что соглашение о вызовах, указанное во время объявления, было сделано в точке определения. Рассмотрим следующее определение класса:
struct MyClass {
void __vectorcall mymethod();
};
вид:
void MyClass::mymethod() { return; }
эквивалентен следующему:
void __vectorcall MyClass::mymethod() { return; }
Модификатор __vectorcall
соглашения о вызовах должен быть указан при создании указателя на __vectorcall
функцию. В следующем примере создается typedef
указатель __vectorcall
на функцию, которая принимает четыре double
аргумента и возвращает __m256
значение:
typedef __m256 (__vectorcall * vcfnptr)(double, double, double, double);
Для совместимости с предыдущими версиями является синонимом__vectorcall
, _vectorcall
если не указан параметр /Za
компилятора (отключить расширения языка).
Соглашение о вызовах __vectorcall для архитектуры x64
Соглашение __vectorcall
о вызове x64 расширяет стандартное соглашение о вызовах x64, чтобы воспользоваться дополнительными регистрами. Аргументы как целочисленного, так и векторного типа сопоставляются с регистрами исходя из позиции в списке аргументов. Аргументы HVA назначаются неиспользуемым векторным регистрам.
Если любые из первых четырех аргументов в порядке слева направо являются целочисленными, они передаются в регистрах, соответствующих их позициям: RCX, RDX, R8 или R9. Скрытый this
указатель рассматривается как первый аргумент целочисленного типа. Если аргумент HVA в одном из первых четырех аргументов не может быть передан в доступных регистрах, ссылка на выделенную вызывающим память передается в соответствующем регистре целочисленного типа. Аргументы целочисленного типа, расположенные за четвертой позицией в списке параметров, передаются в стеке.
Если любые из первых шести аргументов в порядке слева направо являются аргументами векторного типа, они передаются по значению в векторных регистрах SSE 0–5, соответствующих их позициям. С плавающей запятой и __m128
типы передаются в регистрах XMM, а __m256
типы передаются в регистрах YMM. В этом состоит отличие от стандартного соглашения о вызовах x64, так как векторные типы передаются по значению, а не по ссылкам; кроме того, используются дополнительные регистры. Пространство теневого стека, выделенное для аргументов типа вектора, исправлено на 8 байт, и /homeparams
параметр не применяется. Аргументы векторного типа начиная с седьмой позиции в списке параметров передаются в стеке по ссылке на память, выделенную вызывающим объектом.
После выделения регистров для векторных аргументов члены данных аргументов HVA выделяются в порядке возрастания для неиспользуемых регистров векторов XMM0 до XMM5 (или YMM0 до YMM5 для __m256
типов), если для всего HVA достаточно регистров. Если регистров недостаточно, аргумент HVA передается по ссылке на память, выделенную вызывающим объектом. Пространство теневого стека, выделенное для аргументов типа HVA, имеет фиксированный размер 8 байт с неопределенным содержимым. Аргументы HVA назначаются регистрам в порядке слева направо в списке параметров и могут находиться в любой позиции. Аргументы HVA, находящиеся на одной из первых четырех позиций в списке аргументов и не назначенные векторным регистрам, передаются по ссылке в целочисленном регистре, соответствующем их позициям. Аргументы HVA, переданные по ссылке после четвертой позиции в списке параметров, помещаются в стек.
Результаты функций возвращаются по значению __vectorcall
в регистрах, когда это возможно. Результаты целочисленных типов, включая struct и union размером 8 байт и менее, возвращаются по значению в RAX. Результаты векторного типа возвращаются по значению в XMM0 или YMM0 в зависимости от их размера. В результатах HVA каждый элемент данных возвращается по значению в регистрах XMM0:XMM3 или YMM0:YMM3 в зависимости от размера элемента. Типы результатов, которые не помещаются в соответствующее регистры, возвращаются по ссылке на память, выделенную вызывающим объектом.
Стек поддерживается вызывающим элементом в реализации __vectorcall
x64. Код пролога и эпилога вызывающего объекта выделяет и очищает стек для вызываемой функции. Аргументы помещаются в стек в порядке справа налево, а для аргументов, передаваемых в регистрах, выделяется пространство теневого стека.
Примеры:
// 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);
}
Соглашение о вызовах __vectorcall для архитектуры x86
Соглашение __vectorcall
о вызове __fastcall
следует соглашению для 32-разрядных аргументов целочисленного типа и использует регистры векторов SSE для типов векторов и аргументов HVA.
Первые два целочисленных аргумента, встреченные в списке параметров в порядке слева направо, помещаются в регистры ECX и EDX соответственно. Скрытый this
указатель обрабатывается как первый аргумент целочисленного типа и передается в ECX. Первые шесть аргументов векторного типа передаются по значению через векторные регистры SSE 0–5 в регистры XMM или YMM в зависимости от размера аргумента.
Первые шесть аргументов векторного типа в порядке слева направо передаются по значению в векторных регистрах SSE 0–5. С плавающей запятой и __m128
типы передаются в регистрах XMM, а __m256
типы передаются в регистрах YMM. Для аргументов векторного типа, которые передаются через регистр, пространство теневого стека не выделяется. Аргументы векторного типа начиная с седьмой позиции в списке передаются в стек по ссылке на память, выделенную вызывающим объектом. Ограничение ошибки компилятора C2719 не применяется к этим аргументам.
После выделения регистров для векторных аргументов элементы данных аргументов HVA выделяются в порядке возрастания для неиспользуемых регистров векторов XMM0 до XMM5 (или YMM0 до YMM5 для __m256
типов), если для всего HVA достаточно регистров. Если регистров недостаточно, аргумент HVA передается в стек по ссылке на память, выделенную вызывающим объектом. Пространство теневого стека аргументу HVA не выделяется. Аргументы HVA назначаются регистрам в порядке слева направо в списке параметров и могут находиться в любой позиции.
Результаты функций возвращаются по значению __vectorcall
в регистрах, когда это возможно. Результаты целочисленных типов, включая structs и union размером 4 байт и менее, возвращаются по значению в EAX. Структуры или объединения целочисленного типа размером 8 байт или менее возвращаются по значению в регистрах EDX:EAX. Результаты векторного типа возвращаются по значению в XMM0 или YMM0 в зависимости от их размера. В результатах HVA каждый элемент данных возвращается по значению в регистрах XMM0:XMM3 или YMM0:YMM3 в зависимости от размера элемента. Результаты других типов возвращаются по ссылке на память, выделенную вызывающим объектом.
Реализация x86 __vectorcall
соответствует соглашению аргументов, отправленных в стек справа налево вызывающей стороной, и вызываемая функция очищает стек непосредственно перед возвратом. В стек передаются только те аргументы, которые не помещаются в регистры.
Примеры:
// 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
См. также
Передача аргументов и соглашения об именовании
Ключевые слова
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по