Untuk memberikan contoh yang lebih jelas, pada x86_64, dikompilasi dengan -O
flag, fungsinya
pub fn leet(a : i128) -> i128 {
a + 1337
}
kompilasi ke
example::leet:
mov rdx, rsi
mov rax, rdi
add rax, 1337
adc rdx, 0
ret
(Posting asli saya u128
lebih baik daripada yang i128
Anda tanyakan. Fungsi mengkompilasi kode yang sama, demonstrasi yang baik yang ditandatangani dan tidak ditandatangani sama pada CPU modern.)
Daftar lainnya menghasilkan kode yang tidak dioptimalkan. Aman untuk melangkah melalui debugger, karena memastikan Anda dapat meletakkan breakpoint di mana saja dan memeriksa status variabel apa pun di setiap baris program. Lebih lambat dan sulit dibaca. Versi yang dioptimalkan jauh lebih dekat dengan kode yang benar-benar akan berjalan dalam produksi.
Parameter a
fungsi ini dilewatkan dalam sepasang register 64-bit, rsi: rdi. Hasilnya dikembalikan dalam pasangan register lain, rdx: rax. Dua baris kode pertama menginisialisasi jumlah ke a
.
Baris ketiga menambahkan 1337 ke kata input yang rendah. Jika ini meluap, ia membawa 1 di flag carry CPU. Baris keempat menambahkan nol pada kata tinggi input — ditambah 1 jika dijalankan.
Anda dapat menganggap ini sebagai penambahan sederhana dari nomor satu digit ke nomor dua digit
a b
+ 0 7
______
tetapi dalam basis 18.446.744.073.709.551.616. Anda masih menambahkan "digit" terendah terlebih dahulu, mungkin membawa 1 ke kolom berikutnya, lalu menambahkan digit berikutnya ditambah carry. Pengurangan sangat mirip.
Perkalian harus menggunakan identitas (2⁶⁴a + b) (2⁶⁴c + d) = 2¹²⁸ac + 2⁶⁴ (ad + bc) + bd, di mana masing-masing perkalian ini mengembalikan bagian atas produk dalam satu register dan bagian bawah produk dalam lain. Beberapa istilah tersebut akan dihapus, karena bit di atas 128 tidak cocok dengan a u128
dan dibuang. Meski begitu, ini membutuhkan sejumlah instruksi mesin. Divisi juga mengambil beberapa langkah. Untuk nilai yang ditandatangani, perkalian dan pembagian juga perlu mengubah tanda-tanda operan dan hasilnya. Operasi-operasi itu sama sekali tidak efisien.
Pada arsitektur lain, itu menjadi lebih mudah atau lebih sulit. RISC-V mendefinisikan ekstensi set instruksi 128-bit, meskipun setahu saya tidak ada yang menerapkannya dalam silikon. Tanpa ekstensi ini, manual arsitektur RISC-V merekomendasikan cabang bersyarat:addi t0, t1, +imm; blt t0, t1, overflow
SPARC memiliki kode kontrol seperti flag kontrol x86, tetapi Anda harus menggunakan instruksi khusus add,cc
,, untuk mengaturnya. MIPS, di sisi lain, mengharuskan Anda untuk memeriksa apakah jumlah dua bilangan bulat yang tidak ditandatangani benar-benar kurang dari satu operan. Jika demikian, penambahan meluap. Setidaknya Anda dapat mengatur register lain ke nilai carry bit tanpa cabang kondisional.