Register xmm dan instruksi move

Register xmm 128-bit dengan lane internalnya serta pasangan movaps dan movups.

Daftar isi

Topik ini membahas register xmm, yaitu himpunan register 128-bit yang menjadi fondasi SIMD (Single Instruction, Multiple Data) pada x86-64, sebuah pendekatan yang menerapkan satu instruksi pada beberapa data sekaligus. Struktur register dan pembagian internalnya diperkenalkan terlebih dahulu, dilanjutkan dengan pasangan instruksi muat movaps dan movups beserta pasangan transfer integer movd dan movq. Seluruhnya digabungkan dalam satu program kecil yang diperiksa lewat gdb, sehingga isi register terlihat dalam keluaran asli debugger.

Enam belas register xmm 128-bit#

Prosesor x86-64 dengan dukungan SSE menyediakan enam belas register bernama xmm0 sampai xmm15. Masing-masing menyimpan 128 bit, dua kali lebar register umum seperti rax, dan berdiri terpisah dari register-register tersebut. Menulis ke xmm0 tidak mengubah nilai register mana pun yang lain, karena bit internalnya memang tidak tersentuh oleh register umum. Lebar 128 bit bukan angka karangan, melainkan dipilih agar satu register memuat tepat empat bilangan float 32-bit, ukuran standar float pada bahasa C.

Diagram berikut memperlihatkan pembagian internal satu register xmm.

Register xmm 128-bit16 register: xmm0–xmm15Lane 0bit 0-31Lane 1bit 32-63Lane 2bit 64-95Lane 3bit 96-127Empat lane 32-bit: dibaca sebagai empat float atau empat integer sekaligus
Register xmm 128-bit terbagi menjadi empat lane 32-bit

Keberadaan register ini juga tampak dari sisi konvensi pemanggilan. Kontrak System V yang digunakan Linux menempatkan argumen float pertama pada xmm0, sehingga compiler C menghasilkan kode yang menyentuh register ini setiap kali sebuah fungsi menerima atau mengembalikan float. Register xmm dengan demikian bukan fitur eksotis yang jarang digunakan, melainkan bagian tetap dari jalur data setiap program yang menghitung bilangan pecahan.

Lane dan penafsiran datanya#

Lane adalah potongan data berukuran tetap yang berbaris di dalam satu register lebar. Satu register xmm memuat empat lane 32-bit, dan keempatnya dapat dibaca sebagai empat float atau empat integer 32-bit. Bit yang tersimpan sama persis, sedangkan penafsirannya ditentukan oleh instruksi yang dipilih, bukan oleh isi register itu sendiri. Register yang sama dapat dimuat sebagai float pada satu instruksi dan dihitung sebagai integer pada instruksi berikutnya.

Instruksi yang mengolah seluruh lane sekaligus disebut packed, yang berarti satu operasi diterapkan pada seluruh paket nilai, sedangkan bentuk skalar hanya menyentuh lane terbawah. Perbedaan keduanya tampak dari akhiran nama instruksi, misalnya addps untuk penjumlahan packed dan addss untuk penjumlahan skalar. Kerja sehari-hari dengan register xmm didominasi bentuk packed, karena di situlah keuntungan lebarnya terasa, yaitu empat perhitungan selesai dalam satu instruksi tunggal.

Cara pandang yang berguna adalah memperlakukan satu register xmm sebagai satu baris array pendek yang berada di dalam prosesor. Data array di memori dimuat sebaris ke register, diolah, lalu disimpan kembali, dan pola muat, olah, simpan inilah kerangka dasar pengolahan vektor pada x86. Lebar empat lane membuat register xmm paling pas untuk data yang jumlahnya kelipatan empat, sedangkan data lain memerlukan perlakuan khusus pada lane sisanya.

movaps dan movups#

movaps menyalin 128 bit antara register xmm dan memori. Namanya dapat diurai sebagai move aligned packed single, yang sekaligus memuat tiga syarat dan fiturnya, yaitu alamat memori harus selaras (aligned), artinya kelipatan 16 byte, data diolah sebagai paket, dan tipe dasarnya single precision. movups mengurai move unaligned packed single, yang melakukan penyalinan yang sama tanpa syarat penyelarasan apa pun. Apabila alamatnya memang sudah selaras, kedua instruksi menghasilkan register yang identik bit demi bit, sehingga perbedaan keduanya murni terletak pada janji tentang alamat, bukan pada hasilnya. Apabila janji itu dilanggar, prosesor memicu fault, yaitu pelanggaran yang dilaporkan prosesor kepada sistem operasi dan lazimnya menghentikan program.

Untuk data integer, pasangan movd dan movq menyalin 32 bit atau 64 bit antara register umum dan lane terbawah sebuah register xmm. movq xmm2, rax memuat nilai rax ke 64 bit bawah xmm2 sekaligus mengosongkan 64 bit atasnya, sehingga keadaan awal register tujuan tidak ikut terbawa. Kedua instruksi ini menjadi jembatan ketika sebuah nilai integer tunggal harus berpindah ke register vektor untuk diolah lebih lanjut.

Memuat empat float dengan satu instruksi#

Program berikut memuat array empat float dua kali, sekali dengan movaps dan sekali dengan movups, lalu memuat konstanta integer 100 melalui movq. gdb menghentikan program setelah empat instruksi pertama selesai dan mencetak isi ketiga register yang terlibat. Perintah cetak p $xmm0.v4_float meminta gdb menafsirkan 128 bit register sebagai empat float 32-bit, sehingga urutan cetaknya mengikuti urutan lane dari array asalnya.

nasm register-xmm.asm
default rel

section .data
    align 16
suhu:   dd 21.5, 22.5, 23.5, 24.5

section .text
global _start

_start:
    movaps xmm0, [suhu]      ; muat empat float, alamat selaras
    movups xmm1, [suhu]      ; muat empat float, tanpa syarat selaras
    mov  rax, 100
    movq xmm2, rax           ; satu integer 64-bit ke lane bawah xmm2
    mov  eax, 60
    xor  edi, edi
    syscall
bash
# rakit dan tautkan program
nasm -f elf64 register-xmm.asm -o register-xmm.o
ld register-xmm.o -o register-xmm

# isi tiga register setelah empat instruksi pertama dijalankan
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi 4' -ex 'p $xmm0.v4_float' -ex 'p $xmm1.v4_float' -ex 'p $xmm2.v2_int64' ./register-xmm
text Keluaran
Breakpoint 1 at 0x401000

Breakpoint 1, 0x0000000000401000 in _start ()
0x0000000000401018 in _start ()
$1 = {21.5, 22.5, 23.5, 24.5}
$2 = {21.5, 22.5, 23.5, 24.5}
$3 = {100, 0}

Keluaran menunjukkan xmm0 dan xmm1 berisi {21.5, 22.5, 23.5, 24.5} dalam urutan yang sama seperti penulisan array, karena lane 0 selalu menampung elemen pertama. Dua register menghasilkan isi identik meskipun instruksinya berbeda, yang membuktikan bahwa movaps dan movups hanya berbeda pada syarat alamatnya. Register xmm2 terbaca {100, 0}, yaitu nilai 100 pada 64 bit bawah dan nol pada 64 bit atas sebagai akibat pengosongan movq. Nilai suhu 21,5 sampai 24,5 dipilih karena setiap nilainya terwakili dengan tepat pada float 32-bit, sehingga yang tampak di debugger persis sama dengan yang ditulis di sumbernya. Arahan align 16 pada section .data menjamin label suhu berada di alamat kelipatan 16, syarat yang dipenuhi movaps pada baris pertamanya.

Ringkasan#

  • Register xmm0 sampai xmm15 masing-masing menyimpan 128 bit dan terpisah dari register umum 64-bit.
  • Satu register xmm terbagi menjadi empat lane 32-bit yang dapat dibaca sebagai empat float atau empat integer.
  • movaps dan movups menyalin 128 bit dengan hasil yang identik, dengan perbedaan hanya pada syarat alamat selaras 16 byte.
  • movd dan movq menyalin integer 32-bit atau 64-bit antara register umum dan lane terbawah, dan movq mengosongkan bagian atas tujuannya.

Referensi#