Konvensi panggilan x64

Bagian ini menjelaskan proses dan konvensi standar yang digunakan satu fungsi (pemanggil) untuk melakukan panggilan ke fungsi lain (penerima panggilan) dalam kode x64.

Untuk informasi selengkapnya tentang __vectorcall konvensi panggilan, lihat __vectorcall.

Default konvensi panggilan

Antarmuka Biner Aplikasi (ABI) x64 menggunakan konvensi panggilan cepat empat-daftar secara default. Ruang dialokasikan pada tumpukan panggilan sebagai penyimpanan bayangan bagi caleg untuk menyimpan register tersebut.

Ada korespondensi satu-ke-satu yang ketat antara argumen panggilan fungsi dan register yang digunakan untuk argumen tersebut. Argumen apa pun yang tidak pas dalam 8 byte, atau bukan 1, 2, 4, atau 8 byte, harus diteruskan oleh referensi. Satu argumen tidak pernah tersebar di beberapa register.

Tumpukan register x87 tidak digunakan. Ini dapat digunakan oleh penerima panggilan, tetapi menganggapnya volatil di seluruh panggilan fungsi. Semua operasi floating point dilakukan menggunakan 16 register XMM.

Argumen bilangan bulat diteruskan dalam register RCX, RDX, R8, dan R9. Argumen floating point diteruskan dalam XMM0L, XMM1L, XMM2L, dan XMM3L. Argumen 16-byte diteruskan oleh referensi. Passing parameter dijelaskan secara rinci dalam Passing Parameter. Register ini, dan RAX, R10, R11, XMM4, dan XMM5, dianggap volatil, atau berpotensi diubah oleh callee saat pengembalian. Penggunaan register didokumenkan secara rinci dalam x64 register usage dan Caller/callee saved registers.

Untuk fungsi yang diprototi, semua argumen dikonversi ke jenis penerima panggilan yang diharapkan sebelum diteruskan. Pemanggil bertanggung jawab untuk mengalokasikan ruang untuk parameter penerima panggilan. Pemanggil harus selalu mengalokasikan ruang yang cukup untuk menyimpan empat parameter register, bahkan jika penerima panggilan tidak mengambil parameter sebanyak itu. Konvensi ini menyederhanakan dukungan untuk fungsi bahasa C yang tidak diprototi dan fungsi vararg C/C++. Untuk fungsi vararg atau tidak diprototi, nilai floating point apa pun harus diduplikasi dalam register tujuan umum yang sesuai. Parameter apa pun di luar empat pertama harus disimpan di tumpukan setelah penyimpanan bayangan sebelum panggilan. Detail fungsi Vararg dapat ditemukan di Varargs. Informasi fungsi yang tidak diprototi dirinci dalam fungsi Tidak Diprototi.

Penjajaran

Sebagian besar struktur selaras dengan keselarasan alaminya. Pengecualian utama adalah penunjuk tumpukan dan malloc atau alloca memori, yang selaras dengan 16 byte untuk membantu performa. Perataan di atas 16 byte harus dilakukan secara manual. Karena 16 byte adalah ukuran perataan umum untuk operasi XMM, nilai ini harus berfungsi untuk sebagian besar kode. Untuk informasi selengkapnya tentang tata letak dan perataan struktur, lihat jenis x64 dan tata letak penyimpanan. Untuk informasi tentang tata letak tumpukan, lihat penggunaan tumpukan x64.

Unwindability

Fungsi daun adalah fungsi yang tidak mengubah register non-volatil. Fungsi non-daun dapat mengubah RSP non-volatil, misalnya, dengan memanggil fungsi. Atau, ini dapat mengubah RSP dengan mengalokasikan ruang tumpukan tambahan untuk variabel lokal. Untuk memulihkan register non-volatil saat pengecualian ditangani, fungsi non-daun diannotasi dengan data statis. Data menjelaskan cara melepas fungsi dengan benar pada instruksi arbitrer. Data ini disimpan sebagai pdata, atau data prosedur, yang pada gilirannya mengacu pada xdata, data penanganan pengecualian. xdata berisi informasi unwinding, dan dapat menunjuk ke pdata tambahan atau fungsi handler pengecualian.

Prolog dan epilog sangat dibatasi sehingga dapat dijelaskan dengan benar dalam xdata. Penunjuk tumpukan harus tetap selaras 16 byte di wilayah kode apa pun yang bukan bagian dari epilog atau prolog, kecuali dalam fungsi daun. Fungsi daun dapat dilepaskan hanya dengan mensimulasikan pengembalian, sehingga pdata dan xdata tidak diperlukan. Untuk detail tentang struktur prolog dan epilog fungsi yang tepat, lihat prolog x64 dan epilog. Untuk informasi selengkapnya tentang penanganan pengecualian, dan penanganan pengecualian dan melepas pdata dan xdata, lihat penanganan pengecualian x64.

Pengoperasian parameter

Secara default, konvensi panggilan x64 meneruskan empat argumen pertama ke fungsi dalam register. Register yang digunakan untuk argumen ini bergantung pada posisi dan jenis argumen. Argumen yang tersisa akan didorong pada tumpukan dalam urutan kanan-ke-kiri.

Argumen bernilai bilangan bulat di empat posisi paling kiri masing-masing diteruskan dalam urutan kiri-ke-kanan dalam RCX, RDX, R8, dan R9. Argumen kelima dan yang lebih tinggi diteruskan pada tumpukan seperti yang dijelaskan sebelumnya. Semua argumen bilangan bulat dalam register dibenarkan kanan, sehingga penerima panggilan dapat mengabaikan bit atas register dan hanya mengakses bagian dari register yang diperlukan.

Setiap argumen floating-point dan presisi ganda dalam empat parameter pertama diteruskan dalam XMM0 - XMM3, tergantung pada posisi. Nilai floating-point hanya ditempatkan dalam bilangan bulat mendaftarkan RCX, RDX, R8, dan R9 ketika ada argumen varargs. Untuk detailnya, lihat Varargs. Demikian pula, register XMM0 - XMM3 diabaikan ketika argumen yang sesuai adalah tipe bilangan bulat atau pointer.

__m128 jenis, array, dan string tidak pernah diteruskan oleh nilai langsung. Sebagai gantinya, pointer diteruskan ke memori yang dialokasikan oleh pemanggil. Struktur dan penyatuan ukuran 8, 16, 32, atau 64 bit, dan __m64 jenis, diteruskan seolah-olah bilangan bulat dengan ukuran yang sama. Struktur atau penyatuan ukuran lain diteruskan sebagai penunjuk ke memori yang dialokasikan oleh pemanggil. Untuk jenis agregat ini yang diteruskan sebagai penunjuk, termasuk __m128, memori sementara yang dialokasikan pemanggil harus selaras 16 byte.

Fungsi intrinsik yang tidak mengalokasikan ruang tumpukan, dan tidak memanggil fungsi lain, terkadang menggunakan register volatil lainnya untuk meneruskan argumen register tambahan. Pengoptimalan ini dimungkinkan dengan pengikatan ketat antara pengkompilasi dan implementasi fungsi intrinsik.

Penerima panggilan bertanggung jawab untuk mencadangkan parameter register ke ruang bayangan mereka jika diperlukan.

Tabel berikut ini meringkas bagaimana parameter diteruskan, berdasarkan jenis dan posisi dari kiri:

Jenis parameter kelima dan lebih tinggi keempat ketiga detik paling kiri
Floating-point stack XMM3 XMM2 XMM1 XMM0
Integer stack R9 R8 RDX RCX
Agregat (8, 16, 32, atau 64 bit) dan __m64 stack R9 R8 RDX RCX
Agregat lain, sebagai penunjuk stack R9 R8 RDX RCX
__m128, sebagai penunjuk stack R9 R8 RDX RCX

Contoh argumen melewati 1 - semua bilangan bulat

func1(int a, int b, int c, int d, int e, int f);
// a in RCX, b in RDX, c in R8, d in R9, f then e pushed on stack

Contoh argumen melewati 2 - semua float

func2(float a, double b, float c, double d, float e, float f);
// a in XMM0, b in XMM1, c in XMM2, d in XMM3, f then e pushed on stack

Contoh argumen melewati 3 - ints campuran dan float

func3(int a, double b, int c, float d, int e, float f);
// a in RCX, b in XMM1, c in R8, d in XMM3, f then e pushed on stack

Contoh argumen melewati 4 - __m64, , __m128dan agregat

func4(__m64 a, __m128 b, struct c, float d, __m128 e, __m128 f);
// a in RCX, ptr to b in RDX, ptr to c in R8, d in XMM3,
// ptr to f pushed on stack, then ptr to e pushed on stack

Varargs

Jika parameter diteruskan melalui vararg (misalnya, argumen elipsis), maka konvensi lolos parameter register normal berlaku. Konvensi itu termasuk menumpahkan argumen kelima dan yang lebih baru ke tumpukan. Ini adalah tanggung jawab penerima panggilan untuk mencadangkan argumen yang memiliki alamat mereka diambil. Hanya untuk nilai floating-point, baik register bilangan bulat maupun register floating-point harus berisi nilai , jika penerima panggilan mengharapkan nilai dalam register bilangan bulat.

Fungsi yang tidak diprototi

Untuk fungsi yang tidak sepenuhnya diprototi, pemanggil meneruskan nilai bilangan bulat sebagai bilangan bulat dan nilai floating-point sebagai presisi ganda. Hanya untuk nilai floating-point, baik register bilangan bulat maupun register floating-point berisi nilai float jika penerima panggilan mengharapkan nilai dalam register bilangan bulat.

func1();
func2() {   // RCX = 2, RDX = XMM1 = 1.0, and R8 = 7
   func1(2, 1.0, 7);
}

Mengembalikan nilai

Nilai pengembalian skalar yang dapat masuk ke dalam 64 bit, termasuk jenisnya __m64 , dikembalikan melalui RAX. Jenis non-skalar termasuk float, ganda, dan jenis vektor seperti __m128, __m128i, __m128d dikembalikan dalam XMM0. Status bit yang tidak digunakan dalam nilai yang dikembalikan dalam RAX atau XMM0 tidak terdefinisi.

Jenis yang ditentukan pengguna dapat dikembalikan berdasarkan nilai dari fungsi global dan fungsi anggota statis. Untuk mengembalikan jenis berdasarkan nilai yang ditentukan pengguna dalam RAX, harus memiliki panjang 1, 2, 4, 8, 16, 32, atau 64 bit. Ini juga tidak boleh memiliki konstruktor, destruktor, atau operator penugasan salinan yang ditentukan pengguna. Ini tidak dapat memiliki anggota data privat atau non-statis yang dilindungi, dan tidak ada anggota data non-statis dari jenis referensi. Ini tidak dapat memiliki kelas dasar atau fungsi virtual. Dan, hanya dapat memiliki anggota data yang juga memenuhi persyaratan ini. (Definisi ini pada dasarnya sama dengan jenis POD C++03. Karena definisi telah berubah dalam standar C++11, kami tidak menyarankan penggunaan std::is_pod untuk pengujian ini.) Jika tidak, pemanggil harus mengalokasikan memori untuk nilai yang dikembalikan dan meneruskan penunjuk ke dalamnya sebagai argumen pertama. Argumen yang tersisa kemudian digeser satu argumen ke kanan. Penunjuk yang sama harus dikembalikan oleh penerima panggilan di RAX.

Contoh-contoh ini menunjukkan bagaimana parameter dan nilai pengembalian diteruskan untuk fungsi dengan deklarasi yang ditentukan:

Contoh nilai pengembalian 1 - hasil 64-bit

__int64 func1(int a, float b, int c, int d, int e);
// Caller passes a in RCX, b in XMM1, c in R8, d in R9, e pushed on stack,
// callee returns __int64 result in RAX.

Contoh nilai pengembalian 2 - hasil 128-bit

__m128 func2(float a, double b, int c, __m64 d);
// Caller passes a in XMM0, b in XMM1, c in R8, d in R9,
// callee returns __m128 result in XMM0.

Contoh nilai pengembalian 3 - hasil jenis pengguna menurut penunjuk

struct Struct1 {
   int j, k, l;    // Struct1 exceeds 64 bits.
};
Struct1 func3(int a, double b, int c, float d);
// Caller allocates memory for Struct1 returned and passes pointer in RCX,
// a in RDX, b in XMM2, c in R9, d pushed on the stack;
// callee returns pointer to Struct1 result in RAX.

Contoh nilai pengembalian 4 - jenis pengguna hasil menurut nilai

struct Struct2 {
   int j, k;    // Struct2 fits in 64 bits, and meets requirements for return by value.
};
Struct2 func4(int a, double b, int c, float d);
// Caller passes a in RCX, b in XMM1, c in R8, and d in XMM3;
// callee returns Struct2 result by value in RAX.

Register tersimpan penelepon/penerima panggilan

X64 ABI mempertimbangkan register RAX, RCX, RDX, R8, R9, R10, R11, dan XMM0-XMM5 volatile. Saat ini, bagian atas YMM0-YMM15 dan ZMM0-ZMM15 juga volatil. Pada AVX512VL, register ZMM, YMM, dan XMM 16-31 juga volatil. Ketika dukungan AMX ada, register petak peta TMM volatil. Pertimbangkan register volatil yang dihancurkan pada panggilan fungsi kecuali jika tidak dapat diprovable oleh analisis seperti pengoptimalan seluruh program.

X64 ABI mempertimbangkan untuk mendaftarkan nonvolatile RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, dan XMM6-XMM15. Mereka harus disimpan dan dipulihkan oleh fungsi yang menggunakannya.

Pointer fungsi

Penunjuk fungsi hanyalah penunjuk ke label fungsi masing-masing. Tidak ada persyaratan daftar isi (TOC) untuk penunjuk fungsi.

Dukungan floating-point untuk kode yang lebih lama

MMX dan floating-point stack registers (MM0-MM7/ST0-ST7) dipertahankan di seluruh sakelar konteks. Tidak ada konvensi panggilan eksplisit untuk register ini. Penggunaan register ini sangat dilarang dalam kode mode kernel.

FPCSR

Status register juga menyertakan kata kontrol FPU x87. Konvensi pemanggilan menentukan register ini menjadi nonvolatile.

Register kata kontrol FPU x87 diatur menggunakan nilai standar berikut pada awal eksekusi program:

Daftar[bit] Pengaturan
FPCSR[0:6] Pengecualian menutupi semua 1 (semua pengecualian ditutupi)
FPCSR[7] Dicadangkan - 0
FPCSR[8:9] Kontrol Presisi - 10B (presisi ganda)
FPCSR[10:11] Kontrol pembulatan - 0 (bulat ke terdekat)
FPCSR[12] Kontrol tak terbatas - 0 (tidak digunakan)

Penerima panggilan yang memodifikasi salah satu bidang dalam FPCSR harus memulihkannya sebelum kembali ke pemanggilnya. Selain itu, penelepon yang telah memodifikasi salah satu bidang ini harus memulihkannya ke nilai standar mereka sebelum memanggil penerima panggilan, kecuali dengan perjanjian penerima panggilan mengharapkan nilai yang dimodifikasi.

Ada dua pengecualian untuk aturan tentang non-volatilitas bendera kontrol:

  • Dalam fungsi di mana tujuan yang didokumentasikan dari fungsi yang diberikan adalah untuk memodifikasi bendera FPCSR nonvolatile.

  • Ketika sangat benar bahwa pelanggaran aturan ini menghasilkan program yang berulah sama dengan program yang tidak melanggar aturan, misalnya, melalui analisis seluruh program.

MXCSR

Status register juga mencakup MXCSR. Konvensi pemanggilan membagi register ini menjadi bagian volatil dan bagian nonvolatile. Bagian volatil terdiri dari enam bendera status, di MXCSR[0:5], sementara sisa register, MXCSR[6:15], dianggap nonvolatile.

Bagian nonvolatile diatur ke nilai standar berikut pada awal eksekusi program:

Daftar[bit] Pengaturan
MXCSR[6] Denormal adalah nol - 0
MXCSR[7:12] Pengecualian menutupi semua 1 (semua pengecualian ditutupi)
MXCSR[13:14] Kontrol pembulatan - 0 (bulat ke terdekat)
MXCSR[15] Siram ke nol untuk aliran bawah bertopeng - 0 (nonaktif)

Penerima panggilan yang memodifikasi salah satu bidang nonvolatile dalam MXCSR harus memulihkannya sebelum kembali ke pemanggilnya. Selain itu, penelepon yang telah memodifikasi salah satu bidang ini harus memulihkannya ke nilai standar mereka sebelum memanggil penerima panggilan, kecuali dengan perjanjian penerima panggilan mengharapkan nilai yang dimodifikasi.

Ada dua pengecualian untuk aturan tentang non-volatilitas bendera kontrol:

  • Dalam fungsi di mana tujuan yang didokumentasikan dari fungsi yang diberikan adalah untuk memodifikasi bendera MXCSR nonvolatile.

  • Ketika sangat benar bahwa pelanggaran aturan ini menghasilkan program yang berulah sama dengan program yang tidak melanggar aturan, misalnya, melalui analisis seluruh program.

Jangan asumsi tentang status bagian volatil MXCSR register di seluruh batas fungsi, kecuali dokumentasi fungsi secara eksplisit menjelaskannya.

setjmp/longjmp

Saat Anda menyertakan setjmpex.h atau setjmp.h, semua panggilan ke setjmp atau longjmp mengakibatkan unwind yang memanggil destruktor dan __finally panggilan. Perilaku ini berbeda dari x86, di mana termasuk setjmp.h menghasilkan __finally klausul dan destruktor yang tidak dipanggil.

Panggilan untuk setjmp mempertahankan penunjuk tumpukan saat ini, register non-volatil, dan register MXCSR. Panggilan untuk longjmp kembali ke situs panggilan terbaru setjmp dan mengatur ulang penunjuk tumpukan, register non-volatil, dan pendaftaran MXCSR, kembali ke status sebagai dipertahankan oleh panggilan terbaru setjmp .

Baca juga

Konvensi perangkat lunak x64