Gambaran umum konvensi X64 ABI
Topik ini menjelaskan antarmuka biner aplikasi dasar (ABI) untuk x64, ekstensi 64-bit ke arsitektur x86. Ini mencakup topik seperti konvensi panggilan, tata letak jenis, tumpukan dan daftarkan penggunaan, dan banyak lagi.
Konvensi panggilan x64
Dua perbedaan penting antara x86 dan x64 adalah:
- Kemampuan pengalamatan 64-bit
- Enam belas 64-bit mendaftar untuk penggunaan umum.
Mengingat kumpulan register yang diperluas, x64 menggunakan konvensi panggilan __fastcall dan model penanganan pengecualian berbasis RISC.
Konvensi menggunakan __fastcall
register untuk empat argumen pertama, dan bingkai tumpukan untuk meneruskan lebih banyak argumen. Untuk detail tentang konvensi panggilan x64, termasuk penggunaan register, parameter tumpukan, nilai pengembalian, dan unwinding tumpukan, lihat konvensi panggilan x64.
Untuk informasi selengkapnya tentang __vectorcall
konvensi panggilan, lihat __vectorcall
.
Mengaktifkan pengoptimalan kompilator x64
Opsi pengkompilasi berikut membantu Anda mengoptimalkan aplikasi untuk x64:
Jenis x64 dan tata letak penyimpanan
Bagian ini menjelaskan penyimpanan jenis data untuk arsitektur x64.
Jenis skalar
Meskipun dimungkinkan untuk mengakses data dengan penyelarasan apa pun, menyelaraskan data pada batas alaminya, atau kelipatan batas alaminya, untuk menghindari kehilangan performa. Enum adalah bilangan bulat konstan dan diperlakukan sebagai bilangan bulat 32-bit. Tabel berikut ini menjelaskan definisi jenis dan penyimpanan yang direkomendasikan untuk data karena berkaitan dengan perataan menggunakan nilai perataan berikut:
- Byte - 8 bit
- Word - 16 bit
- Kata ganda - 32 bit
- Quadword - 64 bit
- Oktaword - 128 bit
Jenis skalar | Jenis data C | Ukuran penyimpanan (dalam byte) | Perataan yang direkomendasikan |
---|---|---|---|
INT8 |
char |
1 | Byte |
UINT8 |
unsigned char |
1 | Byte |
INT16 |
short |
2 | Word |
UINT16 |
unsigned short |
2 | Word |
INT32 |
int , long |
4 | Kata ganda |
UINT32 |
unsigned int , unsigned long |
4 | Kata ganda |
INT64 |
__int64 |
8 | Quadword |
UINT64 |
unsigned __int64 |
8 | Quadword |
FP32 (presisi tunggal) |
float |
4 | Kata ganda |
FP64 (presisi ganda) |
double |
8 | Quadword |
POINTER |
* | 8 | Quadword |
__m64 |
struct __m64 |
8 | Quadword |
__m128 |
struct __m128 |
16 | Oktaword |
Tata letak agregat dan gabungan x64
Jenis lain, seperti array, struktur, dan serikat, memiliki persyaratan penyelarasan yang lebih ketat yang memastikan penyimpanan agregat dan gabungan dan pengambilan data yang konsisten. Berikut adalah definisi untuk array, struktur, dan union:
Array
Berisi sekelompok objek data yang berdekatan yang diurutkan. Setiap objek disebut elemen. Semua elemen dalam array memiliki ukuran dan jenis data yang sama.
Struktur
Berisi sekelompok objek data yang diurutkan. Tidak seperti elemen array, anggota struktur dapat memiliki jenis dan ukuran data yang berbeda.
Union
Objek yang menyimpan salah satu dari sekumpulan anggota bernama. Anggota set bernama bisa dari jenis apa pun. Penyimpanan yang dialokasikan untuk gabungan sama dengan penyimpanan yang diperlukan untuk anggota terbesar dari serikat tersebut, ditambah padding apa pun yang diperlukan untuk penyelarasan.
Tabel berikut menunjukkan perataan yang sangat direkomendasikan untuk anggota skalar serikat dan struktur.
Jenis Skalar | Tipe Data C | Perataan yang Diperlukan |
---|---|---|
INT8 |
char |
Byte |
UINT8 |
unsigned char |
Byte |
INT16 |
short |
Word |
UINT16 |
unsigned short |
Word |
INT32 |
int , long |
Kata ganda |
UINT32 |
unsigned int , unsigned long |
Kata ganda |
INT64 |
__int64 |
Quadword |
UINT64 |
unsigned __int64 |
Quadword |
FP32 (presisi tunggal) |
float |
Kata ganda |
FP64 (presisi ganda) |
double |
Quadword |
POINTER |
* | Quadword |
__m64 |
struct __m64 |
Quadword |
__m128 |
struct __m128 |
Oktaword |
Aturan perataan agregat berikut ini berlaku:
Perataan array sama dengan perataan salah satu elemen array.
Perataan awal struktur atau serikat adalah perataan maksimum setiap anggota individu. Setiap anggota dalam struktur atau serikat harus ditempatkan pada penyelarasan yang tepat seperti yang didefinisikan dalam tabel sebelumnya, yang mungkin memerlukan padding internal implisit, tergantung pada anggota sebelumnya.
Ukuran struktur harus merupakan kelipatan integral dari perataannya, yang mungkin memerlukan padding setelah anggota terakhir. Karena struktur dan penyatuan dapat dikelompokkan dalam array, setiap elemen array dari struktur atau gabungan harus dimulai dan berakhir pada perataan yang tepat yang ditentukan sebelumnya.
Dimungkinkan untuk menyelaraskan data sedih agar lebih besar dari persyaratan penyelarasan selama aturan sebelumnya dipertahankan.
Pengkompilasi individu dapat menyesuaikan pengemasan struktur karena alasan ukuran. Misalnya, /Zp (Struct Member Alignment) memungkinkan untuk menyesuaikan pengemasan struktur.
Contoh perataan struktur x64
Empat contoh berikut masing-masing mendeklarasikan struktur atau serikat yang selaras, dan gambar yang sesuai menggambarkan tata letak struktur atau penyatuan tersebut dalam memori. Setiap kolom dalam gambar mewakili byte memori, dan angka dalam kolom menunjukkan perpindahan byte tersebut. Nama di baris kedua dari setiap gambar sesuai dengan nama variabel dalam deklarasi. Kolom berbayang menunjukkan padding yang diperlukan untuk mencapai perataan yang ditentukan.
Contoh 1
// Total size = 2 bytes, alignment = 2 bytes (word).
_declspec(align(2)) struct {
short a; // +0; size = 2 bytes
}
Contoh 2
// Total size = 24 bytes, alignment = 8 bytes (quadword).
_declspec(align(8)) struct {
int a; // +0; size = 4 bytes
double b; // +8; size = 8 bytes
short c; // +16; size = 2 bytes
}
Contoh 3
// Total size = 12 bytes, alignment = 4 bytes (doubleword).
_declspec(align(4)) struct {
char a; // +0; size = 1 byte
short b; // +2; size = 2 bytes
char c; // +4; size = 1 byte
int d; // +8; size = 4 bytes
}
Contoh 4
// Total size = 8 bytes, alignment = 8 bytes (quadword).
_declspec(align(8)) union {
char *p; // +0; size = 8 bytes
short s; // +0; size = 2 bytes
long l; // +0; size = 4 bytes
}
Bitfields
Bidang bit struktur dibatasi hingga 64 bit dan dapat berjenis int yang ditandatangani, int tidak ditandatangani, int64, atau int64 yang tidak ditandatangani. Bidang bit yang melewati batas jenis akan melewati bit untuk menyelaraskan bitfield ke perataan jenis berikutnya. Misalnya, bitfield bilangan bulat mungkin tidak melewati batas 32-bit.
Konflik dengan pengkompilasi x86
Jenis data yang lebih besar dari 4 byte tidak secara otomatis diratakan pada tumpukan saat Anda menggunakan kompilator x86 untuk mengkompilasi aplikasi. Karena arsitektur untuk pengkompilasi x86 adalah tumpukan selaras 4 byte, apa pun yang lebih besar dari 4 byte, misalnya, bilangan bulat 64-bit, tidak dapat secara otomatis diselaraskan ke alamat 8-byte.
Bekerja dengan data yang tidak ditandatangani memiliki dua implikasi.
Mungkin perlu waktu lebih lama untuk mengakses lokasi yang tidak sejajar daripada yang diperlukan untuk mengakses lokasi yang selaras.
Lokasi yang tidak ditandatangani tidak dapat digunakan dalam operasi yang saling mengunci.
Jika Anda memerlukan perataan yang lebih ketat, gunakan __declspec(align(N))
pada deklarasi variabel Anda. Ini menyebabkan pengkompilasi secara dinamis menyelaraskan tumpukan untuk memenuhi spesifikasi Anda. Namun, menyesuaikan tumpukan secara dinamis pada waktu proses dapat menyebabkan eksekusi aplikasi Anda yang lebih lambat.
x64 mendaftarkan penggunaan
Arsitektur x64 menyediakan untuk 16 register tujuan umum (selanjutnya disebut sebagai register bilangan bulat) serta 16 register XMM/YMM yang tersedia untuk penggunaan floating-point. Register volatil adalah register awal yang diduga oleh penelepon untuk dihancurkan melalui panggilan. Register nonvolatile diperlukan untuk mempertahankan nilainya di seluruh panggilan fungsi dan harus disimpan oleh penerima panggilan jika digunakan.
Mendaftarkan volatilitas dan pelestarian
Tabel berikut ini menjelaskan bagaimana setiap register digunakan di seluruh panggilan fungsi:
Daftar | Status | Menggunakan |
---|---|---|
RAX | Volatile | Mengembalikan register nilai |
RCX | Volatile | Argumen bilangan bulat pertama |
RDX | Volatile | Argumen bilangan bulat kedua |
R8 | Volatile | Argumen bilangan bulat ketiga |
R9 | Volatile | Argumen bilangan bulat keempat |
R10:R11 | Volatile | Harus dipertahankan sesuai kebutuhan oleh pemanggil; digunakan dalam instruksi syscall/sysret |
R12:R15 | Nonvolatile | Harus dipertahankan oleh penerima panggilan |
RDI | Nonvolatile | Harus dipertahankan oleh penerima panggilan |
RSI | Nonvolatile | Harus dipertahankan oleh penerima panggilan |
RBX | Nonvolatile | Harus dipertahankan oleh penerima panggilan |
RBP | Nonvolatile | Dapat digunakan sebagai penunjuk bingkai; harus dipertahankan oleh penerima panggilan |
RSP | Nonvolatile | Penunjuk tumpukan |
XMM0, YMM0 | Volatile | Argumen FP pertama; argumen jenis vektor pertama saat __vectorcall digunakan |
XMM1, YMM1 | Volatile | Argumen FP kedua; argumen jenis vektor kedua saat __vectorcall digunakan |
XMM2, YMM2 | Volatile | Argumen FP ketiga; argumen jenis vektor ketiga saat __vectorcall digunakan |
XMM3, YMM3 | Volatile | Argumen FP keempat; argumen tipe vektor keempat saat __vectorcall digunakan |
XMM4, YMM4 | Volatile | Harus dipertahankan sesuai kebutuhan oleh pemanggil; Argumen tipe vektor kelima saat __vectorcall digunakan |
XMM5, YMM5 | Volatile | Harus dipertahankan sesuai kebutuhan oleh pemanggil; argumen jenis vektor keenam saat __vectorcall digunakan |
XMM6:XMM15, YMM6:YMM15 | Nonvolatile (XMM), Volatil (bagian atas YMM) | Harus dipertahankan oleh penerima panggilan. Register YMM harus dipertahankan sesuai kebutuhan oleh pemanggil. |
Pada fungsi keluar dan pada entri fungsi ke panggilan Pustaka Runtime C dan panggilan sistem Windows, bendera arah dalam daftar bendera CPU diharapkan dihapus.
Penggunaan tumpukan
Untuk detail tentang alokasi tumpukan, perataan, jenis fungsi, dan bingkai tumpukan pada x64, lihat penggunaan tumpukan x64.
Prolog dan epilog
Setiap fungsi yang mengalokasikan ruang tumpukan, memanggil fungsi lain, menyimpan register nonvolatile, atau menggunakan penanganan pengecualian harus memiliki prolog yang batas alamatnya dijelaskan dalam data unwind yang terkait dengan entri tabel fungsi masing-masing, dan epilog di setiap keluar ke fungsi. Untuk detail tentang kode prolog dan epilog yang diperlukan pada x64, lihat prolog x64 dan epilog.
Penanganan pengecualian x64
Untuk informasi tentang konvensi dan struktur data yang digunakan untuk menerapkan penanganan pengecualian terstruktur dan perilaku penanganan pengecualian C++ pada x64, lihat penanganan pengecualian x64.
Rakitan intrinsik dan sebaris
Salah satu batasan untuk kompiler x64 adalah tidak ada dukungan perakitan sebaris. Ini berarti bahwa fungsi yang tidak dapat ditulis dalam C atau C++ harus ditulis sebagai subroutine atau sebagai fungsi intrinsik yang didukung oleh pengkompilasi. Fungsi tertentu sensitif terhadap performa sementara fungsi lain tidak. Fungsi sensitif performa harus diimplementasikan sebagai fungsi intrinsik.
Intrinsik yang didukung oleh kompilator dijelaskan dalam intrinsik Compiler.
Format gambar x64
Format gambar x64 yang dapat dieksekusi adalah PE32+. Gambar yang dapat dieksekusi (DLL dan EXE) dibatasi hingga ukuran maksimum 2 gigabyte, sehingga alamat relatif dengan perpindahan 32-bit dapat digunakan untuk mengatasi data gambar statis. Data ini mencakup tabel alamat impor, konstanta string, data global statis, dan sebagainya.