__vectorcall
Microsoft'a Özgü
Çağırma __vectorcall
kuralı, mümkün olduğunda, işlevlere yönelik bağımsız değişkenlerin yazmaçlara geçirileceklerini belirtir. __vectorcall
bağımsız değişkenler __fastcall
için veya varsayılan x64 çağırma kuralı kullanımından daha fazla yazmaç kullanır. __vectorcall
Çağırma kuralı yalnızca Streaming SIMD Extensions 2 (SSE2) ve üzerini içeren x86 ve x64 işlemcilerde yerel kodda desteklenir. Çeşitli kayan nokta veya SIMD vektör bağımsız değişkenlerini geçiren işlevleri hızlandırmak ve yazmaçlara yüklenen bağımsız değişkenlerden yararlanan işlemler gerçekleştirmek için kullanın __vectorcall
. Aşağıdaki listede, x86 ve x64 uygulamalarında ortak olan özellikler gösterilmektedir __vectorcall
. Farklılıklar bu makalenin devamında açıklanmıştır.
Öğe | Uygulama |
---|---|
C name-decoration kuralı | İşlev adları iki "at" işareti (@@) ve ardından parametre listesindeki bayt sayısı (ondalık) ile son eklenmiştir. |
Durum çevirisi kuralları | Büyük/küçük harf çevirisi yapılmaz. |
Derleyici seçeneğinin /Gv
kullanılması, işlev bir üye işlev olmadığı, çakışan bir çağırma kuralı özniteliğiyle bildirilmediği, değişken bağımsız değişken listesi kullandığı vararg
veya adına main
sahip olmadığı sürece modüldeki her işlevin derlenmesine __vectorcall
neden olur.
İşlevlere kaydederek __vectorcall
üç tür bağımsız değişken geçirebilirsiniz: tamsayı türü değerleri, vektör türü değerleri ve homojen vektör toplama (HVA) değerleri.
Tamsayı türü iki gereksinimi karşılar: işlemcinin yerel yazmaç boyutuna (örneğin, x86 makinesinde 4 bayt veya x64 makinesinde 8 bayt) sığar ve bit gösterimini değiştirmeden kayıt uzunluğu ve geri tamsayıya dönüştürülebilir. Örneğin, x86'da (long long
x64'te) char
short
yükseltilebilen int
veya (x64'te) olarak atanabilen int
veya değişiklik olmadan özgünlong long
türüne geri dönebilen her tür bir tamsayı türüdür. Tamsayı türleri işaretçi, başvuru ve struct
union
4 bayt (x64'te 8 bayt) veya daha küçük türleri içerir. x64 platformlarında, daha büyük struct
ve union
türler çağıran tarafından ayrılan belleğe başvuru ile geçirilir; x86 platformlarında bunlar yığındaki değere göre geçirilir.
Vektör türü bir kayan nokta türüdür(örneğin, veya float
double
) ya da SIMD vektör türüdür; örneğin, __m128
ya da __m256
.
HVA türü, aynı vektör türlerine sahip en fazla dört veri üyesinin bileşik türüdür. HVA türü, üyelerinin vektör türüyle aynı hizalama gereksinimine sahiptir. Bu, üç özdeş vektör türü içeren ve 32 bayt hizalaması olan bir HVA struct
tanımı örneğidir:
typedef struct {
__m256 x;
__m256 y;
__m256 z;
} hva3; // 3 element HVA type on __m256
Ayrı olarak derlenmiş kodun __vectorcall
hatasız bağlanmasına izin vermek için üst bilgi dosyalarında anahtar sözcüğüyle işlevlerinizi açıkça bildirin. İşlevlerin kullanmak __vectorcall
için prototipi oluşturulmalıdır ve değişken uzunluğu bağımsız değişken listesini kullanamaz vararg
.
Bir üye işlevi tanımlayıcı kullanılarak __vectorcall
bildirilebilir. Gizli this
işaretçi, yazmaç tarafından ilk tamsayı türü bağımsız değişkeni olarak geçirilir.
ARM makinelerinde, __vectorcall
derleyici tarafından kabul edilir ve yoksayılır. ARM64EC'da, __vectorcall
derleyici tarafından desteklenmez ve reddedilir.
Statik olmayan sınıf üyesi işlevleri için, işlev satır dışı olarak tanımlanırsa, çağıran kural değiştiricinin satır dışı tanımında belirtilmesi gerekmez. Diğer bir ifadeyle, statik olmayan sınıf üyeleri için bildirim sırasında belirtilen çağırma kuralı tanım noktasında varsayılır. Bu sınıf tanımını ele alalım:
struct MyClass {
void __vectorcall mymethod();
};
bu:
void MyClass::mymethod() { return; }
şuna eşdeğerdir:
void __vectorcall MyClass::mymethod() { return; }
İşlev işaretçisi __vectorcall
__vectorcall
oluşturulduğunda çağıran kural değiştirici belirtilmelidir. Sonraki örnek, dört double
bağımsız değişken alan ve bir değer döndüren bir __vectorcall
işlev işaretçisi için bir __m256
oluştururtypedef
:
typedef __m256 (__vectorcall * vcfnptr)(double, double, double, double);
Derleyici seçeneği/Za
(Dil uzantılarını devre dışı bırak) belirtilmediği sürece, _vectorcall
önceki sürümlerle uyumluluk için __vectorcall
bir eş anlamlıdır.
x64'te __vectorcall kuralı
__vectorcall
x64'te çağrı kuralı, ek yazmaçlardan yararlanmak için standart x64 çağrı kuralını genişletir. Hem tamsayı türü bağımsız değişkenleri hem de vektör türü bağımsız değişkenleri, bağımsız değişken listesindeki konuma göre kayıtlarla eşlenir. HVA bağımsız değişkenleri kullanılmayan vektör yazmaçlarına ayrılır.
Soldan sağa doğru sıralı ilk dört bağımsız değişkenden herhangi biri tamsayı türü bağımsız değişkenleri olduğunda, bunlar bu konuma karşılık gelen yazmaçta geçirilir: RCX, RDX, R8 veya R9. Gizli this
işaretçi ilk tamsayı türü bağımsız değişkeni olarak değerlendirilir. İlk dört bağımsız değişkenden birinde yer alan bir HVA bağımsız değişkeni kullanılabilir yazmaçlara geçirilemediğinde, bunun yerine karşılık gelen tamsayı türü yazmacında çağıran tarafından ayrılan belleğe başvuru geçirilir. Dördüncü parametre konumundan sonraki tamsayı türü bağımsız değişkenleri yığına geçirilir.
Soldan sağa sırasıyla ilk altı bağımsız değişkenden herhangi biri vektör türü bağımsız değişkenleri olduğunda, bunlar bağımsız değişken konumuna göre 0 ile 5 arasında SSE vektör yazmaçlarındaki değere göre geçirilir. Kayan nokta ve __m128
türler XMM kayıtlarında geçirilir ve __m256
türler YMM kayıtlarında geçirilir. Vektör türleri başvuru yerine değere göre geçirildiğinden ve ek yazmaçlar kullanıldığından bu, standart x64 çağırma kuralından farklıdır. Vektör türü bağımsız değişkenleri için ayrılan gölge yığın alanı 8 bayt olarak sabittir ve /homeparams
seçenek geçerli değildir. Yedinci ve sonraki parametre konumlarındaki vektör türü bağımsız değişkenleri, çağıran tarafından ayrılan belleğe başvuru olarak yığına geçirilir.
Yazmaçlar vektör bağımsız değişkenleri için ayrıldıktan sonra, HVA bağımsız değişkenlerinin veri üyeleri, tüm HVA için yeterli sayıda yazmaç bulunduğu sürece kullanılmayan vektör kayıtlarında XMM0'yi XMM5'e (veya YMM0'ye YMM5'e __m256
) artan düzende ayrılır. Yeterli yazmaç yoksa, HVA bağımsız değişkeni çağıran tarafından ayrılan belleğe başvuru ile geçirilir. HVA bağımsız değişkeninin yığın gölge alanı, tanımsız içerikle 8 bayt olarak sabitlenmiştir. HVA bağımsız değişkenleri, parametre listesinde soldan sağa doğru sırasıyla kayıtlara atanır ve herhangi bir konumda olabilir. Vektör yazmaçlarına atanmamış ilk dört bağımsız değişken konumundan birinde yer alan HVA bağımsız değişkenleri, bu konuma karşılık gelen tamsayı yazmaçtaki başvuruyla geçirilir. Dördüncü parametre konumundan sonra başvuru tarafından geçirilen HVA bağımsız değişkenleri yığına gönderilir.
İşlevlerin __vectorcall
sonuçları mümkün olduğunda yazmaçlardaki değere göre döndürülür. Yapılar veya 8 bayt veya daha az birleşimler de dahil olmak üzere tamsayı türünün sonuçları RAX'taki değere göre döndürülür. Vektör türü sonuçları boyuta bağlı olarak XMM0 veya YMM0'deki değere göre döndürülür. HVA sonuçları, öğe boyutuna bağlı olarak XMM0:XMM3 veya YMM0:YMM3 kayıtlarında değer tarafından döndürülen her veri öğesine sahiptir. Karşılık gelen yazmaçlara sığmayan sonuç türleri, çağıran tarafından ayrılan belleğe başvuruyla döndürülür.
Yığın, x64 uygulamasında __vectorcall
çağıran tarafından korunur. Çağıran prolog ve epilog kodu çağrılan işlev için yığını ayırır ve temizler. Bağımsız değişkenler sağdan sola yığına gönderilir ve yazmaçlarda geçirilen bağımsız değişkenler için gölge yığın alanı ayrılır.
Örnekler:
// 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);
}
x86'da __vectorcall kuralı
__vectorcall
Çağırma kuralı, 32 bit tamsayı türü bağımsız değişkenleri için kuralı izler __fastcall
ve vektör türü ve HVA bağımsız değişkenleri için SSE vektör yazmaçlarından yararlanır.
Parametre listesinde soldan sağa bulunan ilk iki tamsayı türü bağımsız değişkeni sırasıyla ECX ve EDX'e yerleştirilir. Gizli this
işaretçi ilk tamsayı türü bağımsız değişkeni olarak kabul edilir ve ECX'te geçirilir. İlk altı vektör türü bağımsız değişkeni, bağımsız değişken boyutuna bağlı olarak XMM veya YMM kayıtlarında 0 ile 5 arasında SSE vektör yazmaçları aracılığıyla değere göre geçirilir.
Soldan sağa sırasıyla ilk altı vektör türü bağımsız değişkeni, 0 ile 5 arasında SSE vektör yazmaçlarındaki değere göre geçirilir. Kayan nokta ve __m128
türler XMM kayıtlarında geçirilir ve __m256
türler YMM kayıtlarında geçirilir. Yazmaç tarafından geçirilen vektör türü bağımsız değişkenleri için gölge yığın alanı ayrılmaz. Yedinci ve sonraki vektör türü bağımsız değişkenleri çağıran tarafından ayrılan belleğe başvuru olarak yığına geçirilir. C2719 derleyici hatasının sınırlaması bu bağımsız değişkenler için geçerli değildir.
Yazmaçlar vektör bağımsız değişkenleri için ayrıldıktan sonra, HVA bağımsız değişkenlerinin veri üyeleri, tüm HVA için yeterli sayıda yazmaç olduğu sürece kullanılmayan vektör yazmaçları XMM0'yi XMM5'e (veya türler için __m256
YMM0'ye YMM5'e) kaydetmek üzere artan düzende ayrılır. Yeterli yazmaç yoksa, HVA bağımsız değişkeni çağıran tarafından ayrılan belleğe başvuru olarak yığına geçirilir. HVA bağımsız değişkeni için yığın gölge alanı ayrılmaz. HVA bağımsız değişkenleri, parametre listesinde soldan sağa doğru sırasıyla kayıtlara atanır ve herhangi bir konumda olabilir.
İşlevlerin __vectorcall
sonuçları mümkün olduğunda yazmaçlardaki değere göre döndürülür. 4 bayt veya daha küçük yapılar veya birleşimler de dahil olmak üzere tamsayı türünün sonuçları EAX'deki değer tarafından döndürülür. EDX:EAX içindeki değer tarafından 8 bayt veya daha az tamsayı türü yapıları veya birleşimleri döndürülür. Vektör türü sonuçları boyuta bağlı olarak XMM0 veya YMM0'deki değere göre döndürülür. HVA sonuçları, öğe boyutuna bağlı olarak XMM0:XMM3 veya YMM0:YMM3 kayıtlarında değer tarafından döndürülen her veri öğesine sahiptir. Diğer sonuç türleri, çağıran tarafından ayrılan belleğe başvuruyla döndürülür.
x86 uygulaması __vectorcall
, çağıranın sağdan sola yığına gönderilen bağımsız değişkenlerin kuralını izler ve çağrılan işlev, döndürmeden hemen önce yığını temizler. Yalnızca yazmaçlara yerleştirilmeyen bağımsız değişkenler yığına gönderilir.
Örnekler:
// 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);
}
Microsoft'a Özgü Son
Ayrıca bkz.
Bağımsız Değişkeni Geçirme ve Adlandırma Kuralları
Anahtar Sözcükler