Penyelarasan dan akses tak selaras

Fault akses tak selaras, arahan penyelarasan data, dan pertimbangan memilih movups.

Daftar isi

Topik ini membahas penyelarasan (alignment) data untuk register xmm, yaitu persyaratan bahwa alamat memori tertentu harus berkelipatan 16 byte. Aturan align NASM dan jaminan stack dari konvensi Linux dibahas lebih dahulu, dilanjutkan dengan fault yang terjadi ketika movaps diarahkan ke alamat tak selaras, dan diakhiri pertimbangan memilih movups sebagai bentuk yang aman. Dua program kecil memperagakan kegagalan dan keberhasilan tersebut dengan kode keluar yang dapat direproduksi.

Aturan penyelarasan 16 byte#

Sebuah alamat disebut selaras 16 byte apabila habis dibagi 16, dan instruksi SSE yang memindahkan 128 bit antara register serta memori menuntut syarat itu pada operan memorinya. Alasan teknisnya terletak pada cara subsistem memori mengambil data dalam potongan tetap, sehingga akses yang berhenti tepat pada batas potongan dapat dijalankan dalam satu langkah. Prosesor menegakkan syarat ini secara keras, dan pelanggarannya bukan peringatan yang dapat diabaikan, melainkan fault yang menghentikan program.

NASM menyediakan arahan align 16 untuk menempatkan label berikutnya pada alamat kelipatan 16 dengan menambahkan byte pengisi apabila perlu. Aturan praktisnya, label yang berada tepat setelah array 16 byte pada section yang sudah selaras otomatis ikut selaras, sedangkan label atau alamat yang melompat ke tengah array tidak. Arahan ini hanya mengatur data statis, sedangkan alamat yang terbentuk saat program berjalan harus diperiksa atau diselaraskan secara terpisah. Konvensi Linux membantu pada sisi lain, karena stack dijamin selaras 16 byte pada titik masuk proses dan pada instruksi call menurut kontrak pemanggilan, sehingga slot lokal untuk data xmm dapat dipercaya alamatnya asalkan janji kontrak itu dijaga.

Fault pada movaps tak selaras#

movaps dengan alamat yang bersisa bila dibagi 16 memicu general protection fault, yaitu pelanggaran yang dilaporkan prosesor kepada kernel. Kernel Linux meneruskannya sebagai sinyal SIGSEGV ke program, dan shell menampilkannya sebagai kode keluar 139, yang merupakan 128 ditambah nomor sinyal SIGSEGV. Tidak ada nilai yang terbaca sebagian, tidak ada byte yang tergeser, dan program langsung berhenti pada instruksi yang bersangkutan.

Program berikut memuat array yang sama dua kali. Muat pertama menunjuk label data yang selaras dan berhasil, sedangkan muat kedua menunjuk data + 4, yaitu tengah array pada alamat bersisa 4, dan memicu fault. gdb membuktikan muat pertama selesai dengan isi yang utuh, kemudian program yang sama dijalankan langsung untuk memperlihatkan kode keluarnya.

nasm movaps-fault.asm
default rel

section .data
    align 16
data: dd 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0

section .text
global _start

_start:
    movaps xmm0, [data]       ; alamat selaras: berhasil
    movaps xmm1, [data + 4]   ; alamat bersisa 4: memicu fault
    mov  eax, 60
    xor  edi, edi
    syscall
bash
# rakit dan tautkan program
nasm -f elf64 movaps-fault.asm -o movaps-fault.o
ld movaps-fault.o -o movaps-fault

# muat pertama dari alamat selaras berhasil
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi 1' -ex 'p $xmm0.v4_float' ./movaps-fault

# muat kedua dari alamat bersisa 4 menghentikan program
sh -c './movaps-fault' 2>/dev/null; echo "exit=$?"
text Keluaran
Breakpoint 1 at 0x401000

Breakpoint 1, 0x0000000000401000 in _start ()
0x0000000000401007 in _start ()
$1 = {1, 2, 3, 4}
exit=139

Keluaran menunjukkan dua kejadian pada satu program. gdb melaporkan xmm0 berisi {1, 2, 3, 4} setelah muat pertama, sehingga array dan align 16-nya terbukti benar. Pada eksekusi langsung, program berhenti di muat kedua dan shell melaporkan exit=139, yaitu ciri khas SIGSEGV, sedangkan debugger menghentikan program tepat pada instruksi bermasalah, sehingga baris penyebabnya mudah ditemukan pada program nyata. Pesan Segmentation fault yang biasanya ditambahkan shell sengaja disenyapkan karena memuat nomor proses yang berubah antar eksekusi, sehingga hanya kode keluar yang deterministik yang ditampilkan. Skenario data + 4 bukan karangan, karena array yang dimulai dari tengah array lain memang menghasilkan alamat seperti itu.

movups sebagai pilihan aman#

movups melakukan penyalinan 128 bit yang sama tanpa menuntut apa pun tentang alamatnya, sehingga akses ke data + 4 yang menghentikan movaps berjalan lancar. Program berikut mengganti instruksinya dan memuat empat float yang sama, yaitu 2.0 sampai 5.0, dari tengah array.

nasm movups-aman.asm
default rel

section .data
    align 16
data: dd 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0

section .text
global _start

_start:
    movups xmm1, [data + 4]   ; tanpa syarat selaras: berhasil
    mov  eax, 60
    xor  edi, edi
    syscall
bash
# rakit dan tautkan program
nasm -f elf64 movups-aman.asm -o movups-aman.o
ld movups-aman.o -o movups-aman

# muat dari alamat bersisa 4 dengan movups berhasil
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi 1' -ex 'p $xmm1.v4_float' ./movups-aman
text Keluaran
Breakpoint 1 at 0x401000

Breakpoint 1, 0x0000000000401000 in _start ()
0x0000000000401007 in _start ()
$1 = {2, 3, 4, 5}

Keluaran menunjukkan {2, 3, 4, 5} pada xmm1, yaitu empat float yang berawal dari alamat bersisa 4, tanpa fault dan tanpa pengaturan tambahan. Penalti khas movups hanya muncul ketika akses melintasi batas baris cache, dan pada prosesor modern biayanya kecil, sedangkan movups terhadap data yang ternyata selaras berjalan sama cepatnya dengan movaps. Penalti lintas baris cache itu juga menjelaskan mengapa penyelarasan tetap diperhatikan pada data yang diakses sangat sering, meskipun fault tidak mungkin terjadi lagi. Perilaku itulah yang membuat compiler memilih movups untuk pointer yang alamatnya tidak diketahui pasti, dan menyimpan movaps untuk kasus yang penyelarasannya dapat dibuktikan, misalnya data yang dideklarasikan dengan align atau slot stack yang dijamin kontrak.

Ringkasan#

  • Alamat selaras 16 byte adalah alamat yang habis dibagi 16, dan instruksi muat atau simpan 128 bit yang bernama selaras menuntutnya secara keras.
  • movaps terhadap alamat tak selaras memicu fault yang diterjemahkan kernel menjadi SIGSEGV, yang tampak sebagai kode keluar 139.
  • movups menerima alamat apa pun, berjalan hampir sama cepatnya pada prosesor modern, dan menjadi pilihan utama ketika penyelarasan tidak diketahui.
  • Arahan align 16 pada data statis dan janji stack selaras dari konvensi Linux adalah dua sumber alamat selaras yang dapat diandalkan.

Referensi#