Topik ini memetakan tiga puluh dua register RV64 beserta nama ABI-nya, membuktikan peran khusus x0 melalui program nyata, lalu membedah enam format encoding instruksi RISC-V. Setelah membacanya, keluaran objdump pada topik-topik lain dapat dibaca sampai ke susunan bitnya, dan asal-usul pseudo-instruction yang tampak seperti instruksi tunggal menjadi jelas. Nama ABI seperti a0, t0, dan ra lebih sering muncul pada keluaran assembler daripada nama x0 hingga x31, sehingga keduanya perlu dikuasai sekaligus.
Tiga puluh dua register dan nama ABI-nya#
RV64 menyediakan tiga puluh dua register serbaguna 64-bit yang bernomor x0 sampai x31. Berbeda dengan x86-64 yang mewarisi nama bersejarah seperti rax dan rsp, RISC-V menggunakan penamaan fungsional yang ditetapkan konvensi pemanggilan, yaitu Application Binary Interface (ABI). Kode sumber assembly RISC-V hampir selalu menggunakan nama ABI karena nama itulah yang menyatakan peran register dalam pemanggilan fungsi, bukan nomor perangkat kerasnya.
Tabel berikut merangkum seluruh register dalam kelompok peran yang sama. Baris yang berisi rentang nomor berarti seluruh register dalam rentang itu memiliki peran dan disiplin yang sama.
| Register | Nama ABI | Peran |
|---|---|---|
x0 | zero | konstanta nol yang dibaca tetap, tulisannya diabaikan |
x1 | ra | alamat kembali (return address) |
x2 | sp | stack pointer |
x3 | gp | global pointer untuk data statis |
x4 | tp | thread pointer |
x5 sampai x7 | t0 sampai t2 | register sementara |
x8 | s0 atau fp | register terselamatkan, sering menjadi frame pointer |
x9 | s1 | register terselamatkan |
x10 sampai x17 | a0 sampai a7 | argumen dan nilai balikan |
x18 sampai x27 | s2 sampai s11 | register terselamatkan |
x28 sampai x31 | t3 sampai t6 | register sementara |
Kelompok t (temporary) dan a (argument) bersifat caller-saved, yakni nilainya boleh dirusak oleh fungsi yang dipanggil sehingga pemanggil wajib menyelamatkannya sendiri. Kelompok s (saved) bersifat callee-saved, yakni fungsi yang dipanggil wajib memulihkan nilainya sebelum kembali. Pembagian ini diuraikan beserta buktinya pada topik ABI RISC-V, sedangkan gp dan tp jarang disentuh pada program assembly mandiri karena digunakan sistem perangkat lunak yang lebih besar.
x0 sebagai register nol#
x0 adalah register paling istimewa di antara tiga puluh dua register ini karena isinya dikawal perangkat keras. Pembacaan x0 selalu menghasilkan nol, sedangkan penulisannya diabaikan begitu saja tanpa pesan error. Register nol menghilangkan banyak bentuk instruksi yang sebenarnya redundan, karena setiap instruksi yang butuh konstanta nol cukup menyebut x0 sebagai operannya.
Konsekuensinya, beberapa instruksi yang tampak berbeda sebenarnya adalah instruksi yang sama. Salinan register mv rd, rs dirakit menjadi addi rd, rs, 0, pemuatan konstanta kecil li rd, imm dirakit menjadi addi rd, x0, imm, dan nop dirakit menjadi addi x0, x0, 0. Program berikut memanfaatkan x0 sekaligus membuktikan bahwa tulisan ke x0 tidak berdampak apa pun.
.global _start
.text
_start:
addi s0, x0, 125 # s0 berisi 125
addi x0, s0, 9 # penulisan ke x0 diabaikan perangkat keras
add a0, x0, s0 # x0 terbaca sebagai nol, sehingga a0 = 125
li a7, 93 # syscall exit
ecall# rakit dan tautkan program
riscv64-linux-gnu-as -o x0-tetap-nol.o x0-tetap-nol.asm
riscv64-linux-gnu-ld -o x0-tetap-nol x0-tetap-nol.o
# jalankan di qemu, kode keluarnya nilai a0
qemu-riscv64 ./x0-tetap-nol; echo "exit=$?"exit=125Kode keluar 125 membuktikan dua hal sekaligus. Penulisan addi x0, s0, 9 tidak mengubah isi x0, dan pembacaan x0 pada add a0, x0, s0 tetap menghasilkan nol, sehingga a0 berisi 125 dan itulah nilai yang diteruskan ke syscall exit. Nomor syscall pada Linux riscv64 juga berbeda dari x86-64, yaitu exit bernomor 93 alih-alih 60, dan nomor tersebut diisi ke a7 sebelum ecall.
Pseudo-instruction dan perakitannya#
Nama-nama seperti mv, li, dan nop disebut pseudo-instruction, yaitu mnemonic yang disediakan demi kenyamanan, tidak memiliki encoding sendiri, dan dirakit assembler menjadi instruksi nyata. Pembahasan berikut membedah perakitan ini sekaligus memperkenalkan enam format encoding, karena setiap instruksi nyata harus mengikuti salah satunya. Berkas berikut memuat satu instruksi untuk setiap format, lalu beberapa pseudo-instruction.
.global _start
.text
_start:
add t0, t1, t2 # format R
addi t0, t1, 5 # format I
sw t0, 8(sp) # format S
beq t0, t1, selesai # format B
lui t3, 0x12345 # format U
jal ra, selesai # format J
mv a1, a0 # pseudo-instruction salin register
li a2, 123 # pseudo-instruction muat konstanta 12 bit
li t0, 0x123456789 # pseudo-instruction muat konstanta 35 bit
nop # pseudo-instruction tanpa operasi
selesai:
ret # pseudo-instruction kembali# rakit sebagai RV64I agar pseudo-instruction terlihat bentuk 4 byte-nya
riscv64-linux-gnu-as -march=rv64i -o encoding.o encoding.asm
# tampilkan byte encoding setiap instruksi
riscv64-linux-gnu-objdump -d encoding.o
encoding.o: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <_start>:
0: 007302b3 add t0,t1,t2
4: 00530293 addi t0,t1,5
8: 00512423 sw t0,8(sp)
c: 02628463 beq t0,t1,34 <selesai>
10: 12345e37 lui t3,0x12345
14: 020000ef jal 34 <selesai>
18: 00050593 mv a1,a0
1c: 07b00613 li a2,123
20: 000922b7 lui t0,0x92
24: a2b2829b addiw t0,t0,-1493 # 91a2b <selesai+0x919f7>
28: 00d29293 slli t0,t0,0xd
2c: 78928293 addi t0,t0,1929
30: 00000013 nop
0000000000000034 <selesai>:
34: 00008067 retBaris-baris pseudo-instruction memperlihatkan perakitannya secara langsung. mv a1, a0 menempati 4 byte bernilai 0x00050593, dan byte itu terbaca sebagai format I dengan rs1 bernomor 10 yaitu a0, rd bernomor 11 yaitu a1, serta immediate bernilai 0, yaitu konstanta yang menyatu di dalam instruksi. Instruksi aslinya adalah addi a1, a0, 0. Pola yang sama berlaku untuk li a2, 123 yang menjadi addi a2, x0, 123, nop yang menjadi addi x0, x0, 0 dengan encoding 0x00000013, dan ret yang menjadi jalr dengan encoding 0x00008067.
Konstanta besar membutuhkan perlakuan berbeda. li t0, 0x123456789 tidak muat dalam satu instruksi apa pun, sehingga assembler merakitnya menjadi empat instruksi, yaitu lui, addiw, slli, dan addi. Nilai 0x123456789 bahkan tidak muat dalam pola lui ditambah addi karena batasan tanda pada immediate, sehingga assembler memilih urutan lain yang hasilnya sama. Pseudo-instruction menyerahkan seluruh perhitungan ini kepada assembler, dan itulah alasan li digunakan hampir di mana-mana pada kode sumber.
Enam baris pertama berisi enam format berbeda, dan objdump menampilkannya apa adanya karena semuanya instruksi nyata. Opsi -march=rv64i dipilih agar setiap pseudo-instruction dirakit dalam bentuk 4 byte yang terbaca, sedangkan pada profil dengan ekstensi C assembler bebas memilih bentuk 2 byte seperti c.mv dan c.li, sebagaimana ditunjukkan pada topik filosofi RISC-V.
Enam format encoding#
Setiap instruksi RISC-V berukuran 32 bit dan mengikuti salah satu dari enam format. Enam format itu ada karena satu alasan praktis, yaitu jumlah bit yang dibutuhkan setiap jenis instruksi tidak sama, sedangkan ukuran total harus tetap. Diagram berikut menampilkannya berdampingan dengan posisi bit yang konsisten.
Bidang-bidangnya memiliki arti yang tetap pada semua format. opcode bersama funct3 dan funct7 mengklasifikasikan instruksi, rd serta rs1 dan rs2 menyimpan nomor register sepanjang 5 bit untuk tiga puluh dua register, dan imm menyimpan immediate tersebut. Format I memuat immediate 12 bit utuh, format S membelahnya menjadi dua bagian agar posisi rs1, rs2, dan opcode tidak bergeser dari format lain, dan format U atau J memuat immediate 20 bit.
Pembacaan encoding dapat diverifikasi pada baris addi t0, t1, 5 yang byte-nya 0x00530293. Susunan bitnya adalah immediate 000000000101, rs1 bernilai 00110 yaitu t1, funct3 bernilai 000, rd bernilai 00101 yaitu t0, dan opcode bernilai 0010011 untuk kelompok aritmetika immediate. Format B dan J menyimpan immediate dengan urutan bit yang tampak teracak, karena bit tanda ditempatkan paling atas dan bit-bit lain diposisikan agar pengawatan perangkat keras tetap sederhana. Immediate cabang pada kedua format itu hanya muat 12 bit ditambah bit tanda, sehingga rentangnya terbatas dan konsekuensinya dibahas pada topik cabang.
Ringkasan#
- RV64 memiliki tiga puluh dua register 64-bit yang dikenal lewat nama ABI, dengan kelompok t dan a bersifat caller-saved serta kelompok s bersifat callee-saved.
- x0 dibaca sebagai nol dan tulisannya diabaikan, sehingga
mv,li, dannophanyalah pseudo-instruction yang dirakit menjadiaddi. - Konstanta yang tidak muat dalam satu instruksi dirakit menjadi beberapa instruksi oleh assembler, misalnya pola
luidan pengikutnya. - Setiap instruksi 32 bit mengikuti format R, I, S, B, U, atau J, dengan bidang opcode dan funct sebagai klasifikasinya serta immediate yang lebarnya berbeda-beda.
Referensi#
- RISC-V ISA Manual — RISC-V International — Bab “RV64I Base Integer Instruction Set” mendefinisikan enam format encoding dan peran x0.
- GNU Binutils Documentation — Sourceware
— Dokumentasi opsi
-marchassembler dan tampilan keluaran objdump.