Dalam Tour Manual Intel x86-64 , saya membaca
Mungkin fakta yang paling mengejutkan adalah bahwa instruksi seperti
MOV EAX, EBX
secara otomatis nol di atas 32 bitRAX
register.
Dokumentasi Intel (3.4.1.1 Register Tujuan Umum dalam Mode 64-Bit dalam Arsitektur Dasar manual) yang dikutip dari sumber yang sama memberi tahu kita:
- Operand 64-bit menghasilkan hasil 64-bit di register tujuan umum.
- Operand 32-bit menghasilkan hasil 32-bit, hasil perluasan nol ke 64-bit dalam register tujuan umum tujuan.
- Operand 8-bit dan 16-bit menghasilkan hasil 8-bit atau 16-bit. 56 bit atas atau 48 bit (masing-masing) dari register tujuan umum tujuan tidak dimodifikasi oleh operasi. Jika hasil dari operasi 8-bit atau 16-bit dimaksudkan untuk kalkulasi alamat 64-bit, secara eksplisit tandatangani-perpanjang register ke 64-bit penuh.
Dalam perakitan x86-32 dan x86-64, instruksi 16 bit seperti
mov ax, bx
jangan perlihatkan perilaku "aneh" seperti ini yang kata atas eax di nol.
Jadi: apa alasan mengapa perilaku ini diperkenalkan? Sekilas sepertinya tidak masuk akal (tetapi alasannya mungkin karena saya terbiasa dengan kebiasaan perakitan x86-32).
r32
operan tujuan nol 32 tinggi, bukan penggabungan. Misalnya, beberapa assembler akan menggantipmovmskb r64, xmm
denganpmovmskb r32, xmm
, menyimpan REX, karena versi tujuan 64-bit berperilaku sama. Meskipun bagian Operasi dari manual mencantumkan semua 6 kombinasi dest 32 / 64bit dan sumber 64/128 / 256b secara terpisah, ekstensi nol implisit dari formulir r32 menduplikasi ekstensi nol eksplisit dari formulir r64. Saya ingin tahu tentang implementasi HW ...xor eax,eax
atauxor r8d,r8d
cara terbaik untuk nol RAX atau R8 (menyimpan awalan REX untuk RAX, dan 64-bit XOR bahkan tidak ditangani secara khusus di Silvermont). Terkait: Bagaimana sebenarnya kinerja sebagian register pada Haswell / Skylake? Menulis AL tampaknya memiliki ketergantungan yang salah pada RAX, dan AH tidak konsistenJawaban:
Saya bukan AMD atau berbicara untuk mereka, tetapi saya akan melakukannya dengan cara yang sama. Karena memusatkan perhatian pada separuh tinggi tidak membuat ketergantungan pada nilai sebelumnya, CPU harus menunggu. The mendaftar mengubah nama Mekanisme akan dasarnya dikalahkan jika hal itu tidak dilakukan dengan cara itu.
Dengan cara ini Anda dapat menulis kode cepat menggunakan nilai 32-bit dalam mode 64-bit tanpa harus secara eksplisit memutuskan dependensi sepanjang waktu. Tanpa perilaku ini, setiap instruksi 32-bit dalam mode 64-bit harus menunggu sesuatu yang terjadi sebelumnya, meskipun bagian yang tinggi itu hampir tidak akan pernah digunakan. (Membuat
int
64-bit akan membuang jejak cache dan bandwidth memori; x86-64 paling efisien mendukung ukuran operan 32 dan 64-bit )Perilaku untuk ukuran operan 8 dan 16-bit adalah yang aneh. Kegilaan ketergantungan adalah salah satu alasan mengapa instruksi 16-bit dihindari sekarang. x86-64 mewarisi ini dari 8086 untuk 8-bit dan 386 untuk 16-bit, dan memutuskan untuk memiliki register 8 dan 16-bit bekerja dengan cara yang sama dalam mode 64-bit seperti yang mereka lakukan dalam mode 32-bit.
Lihat juga Mengapa GCC tidak menggunakan register parsial? untuk detail praktis tentang bagaimana penulisan ke register parsial 8 dan 16-bit (dan pembacaan register lengkap selanjutnya) ditangani oleh CPU sebenarnya.
sumber
Ini hanya menghemat ruang dalam instruksi, dan set instruksi. Anda dapat memindahkan nilai kecil langsung ke register 64-bit dengan menggunakan instruksi (32-bit) yang ada.
Ini juga menghindarkan Anda dari keharusan mengenkode nilai 8 byte
MOV RAX, 42
, ketikaMOV EAX, 42
dapat digunakan kembali.Pengoptimalan ini tidak begitu penting untuk operasi 8 dan 16 bit (karena lebih kecil), dan mengubah aturan di sana juga akan merusak kode lama.
sumber
XOR EAX, EAX
karenaXOR RAX, RAX
akan membutuhkan awalan REX.[rsi + edx]
tidak diizinkan). Tentu saja menghindari ketergantungan palsu / kios register sebagian (jawaban lain) adalah alasan utama lainnya.Tanpa nol yang meluas ke 64 bit, itu berarti pembacaan instruksi dari
rax
akan memiliki 2 dependensi untukrax
operannya (instruksi yang menulis keeax
dan instruksi yang menulis kerax
sebelumnya), ini berarti bahwa 1) ROB harus memiliki entri untuk beberapa dependensi untuk satu operand, yang berarti ROB akan membutuhkan lebih banyak logika dan transistor serta membutuhkan lebih banyak ruang, dan eksekusi akan lebih lambat menunggu dependensi kedua yang tidak perlu yang mungkin membutuhkan waktu lama untuk dieksekusi; atau alternatifnya 2), yang saya duga terjadi dengan instruksi 16 bit, tahap alokasi mungkin terhenti (yaitu jika RAT memiliki alokasi aktif untukax
penulisan daneax
pembacaan muncul, berhenti sampaiax
penulisan dihentikan).Satu-satunya keuntungan dari bukan zero extending adalah memastikan bit urutan yang lebih tinggi
rax
disertakan, misalnya, jika aslinya berisi 0xffffffffffffffff, hasilnya adalah 0xffffffff00000007, tetapi ada sedikit alasan bagi ISA untuk membuat jaminan ini dengan biaya seperti itu, dan kemungkinan besar manfaat ekstensi nol sebenarnya lebih dibutuhkan, jadi ini menghemat baris kode tambahanmov rax, 0
. Dengan menjamin itu akan selalu nol diperpanjang hingga 64 bit, kompiler dapat bekerja dengan aksioma ini dalam pikiran sementara dimov rdx, rax
,rax
hanya harus menunggu ketergantungan tunggal, yang berarti dapat memulai eksekusi lebih cepat dan berhenti, membebaskan unit eksekusi. Selain itu, ini juga memungkinkan idiom nol yang lebih efisien sepertixor eax, eax
nolrax
tanpa memerlukan byte REX.sumber
cmovbe
2 uops tapicmovb
1). Tetapi tidak ada CPU yang melakukan penggantian nama register parsial yang melakukannya seperti yang Anda sarankan. Sebaliknya mereka menyisipkan uop penggabungan jika sebagian reg diganti namanya secara terpisah dari reg penuh (yaitu "kotor"). Lihat Mengapa GCC tidak menggunakan register parsial? dan Bagaimana tepatnya kinerja sebagian register di Haswell / Skylake? Menulis AL tampaknya memiliki ketergantungan yang salah pada RAX, dan AH tidak konsistenThis gives a delay of 5 - 6 clocks. The reason is that a temporary register has been assigned to AL to make it independent of AH. The execution unit has to wait until the write to AL has retired before it is possible to combine the value from AL with the value of the rest of EAX
Saya tidak dapat menemukan contoh 'penggabungan uop' yang akan digunakan untuk menyelesaikan masalah ini, sama untuk kios bendera parsialmov al, [mem]
juga dengan beban fusi mikro + ALU- merge, hanya mengganti nama AH, dan UOP penggabungan AH masih bermasalah saja. Mekanisme penggabungan flag parsial dalam CPU ini bervariasi, misalnya Core2 / Nehalem masih berhenti untuk flag parsial, tidak seperti parsial-reg.