Accessing Information
CPU x86-64 punya 16 general-purpose register berukuran 64-bit. Register ini menyimpan data integer dan pointer.
Sejarah penamaan
Penamaan register berlapis karena evolusi instruction set:
| Generasi | Lebar | Nama register | Catatan |
|---|---|---|---|
| 8086 | 16-bit | %ax, %bx, %cx, %dx, %si, %di, %bp, %sp |
Nama mencerminkan fungsi (accumulator, counter, source/destination index, stack pointer) |
| IA32 | 32-bit | %eax … %ebp |
Prefix e = extended
|
| x86-64 | 64-bit | %rax … %rbp |
Prefix r
|
| x86-64 (baru) | 64-bit | %r8 … %r15 |
Konvensi baru, suffix d/w/b untuk 32/16/8-bit
|
Sub-register (nested boxes)
%rax, %eax, %ax, %al bukan empat register berbeda, melainkan satu register fisik yang diakses dengan lebar berbeda:
|<------------------- %rax (64 bit) ------------------->|
|<------ %eax (32 bit) ----->|
|<- %ax (16) ->|
|%al(8)|
- Operasi byte → least significant byte
- Operasi 16-bit → 2 byte terbawah
- Operasi 32-bit → 4 byte terbawah
- Operasi 64-bit → seluruh register
Ketika instruksi menghasilkan nilai kurang dari 8 byte ke register:
| Ukuran tulis | Efek ke byte sisanya |
|---|---|
| 1 byte / 2 byte | Tidak berubah |
| 4 byte | 4 byte atas di-nol-kan |
Contoh, dengan %rax = 0x1122334455667788:
movb $0xAA, %al # %rax = 0x11223344556677AA (sisanya utuh)
movw $0xAAAA, %ax # %rax = 0x112233445566AAAA (sisanya utuh)
movl $0xAAAA, %eax # %rax = 0x000000000000AAAA (4 byte atas = 0)
Kenapa? Aturan zero-upper diadopsi saat ekspansi IA32 → x86-64 supaya hasil tulisan tidak bergantung pada isi lama register (menghindari partial register dependency), sehingga eksekusi out-of-order lebih lancar. Compiler memanfaatkannya, misalnya mov $0x0,%eax untuk return 0.
Peran register (System V ABI)
| Register | Peran |
|---|---|
%rax |
Return value |
%rdi, %rsi, %rdx, %rcx, %r8, %r9 |
Argumen ke-1 sampai ke-6 (urut) |
%rsp |
Stack pointer (spesial, dipakai otomatis oleh push/pop/call/ret)
|
%rbx, %rbp, %r12–%r15 |
Callee saved: fungsi yang dipanggil wajib mengembalikan nilainya |
%r10, %r11 (+ register argumen & %rax) |
Caller saved: boleh dirusak oleh fungsi yang dipanggil |
Jembatan keledai urutan argumen: "Diane's Silk Dress Costs $89" → di, si, dx, cx, 8, 9.
Contoh: multstore
push %rbx # rbx callee-saved → simpan nilai lama
mov %rdx,%rbx # rdx = argumen ke-3 (dest), amankan di rbx
call mult2 # x, y masih di rdi & rsi
mov %rax,(%rbx) # *dest = hasil mult2 (rax)
pop %rbx # pulihkan rbx
ret
dest dipindah ke %rbx karena %rdx caller-saved (bisa dirusak mult2), sedangkan %rbx dijamin selamat melewati call.
3.4.1 Operand Specifiers
Tiga jenis operand
| Jenis | Contoh | Arti |
|---|---|---|
| Immediate | $-577, $0x1F |
Konstanta, nilai tertanam di instruksi |
| Register | %rax, %al |
Isi register, notasi R[ra]
|
| Memory | 0x104, (%rax), 8(%rax) |
Isi memori di effective address, notasi M[Addr]
|
Perhatian: $0x10 = angka 16, sedangkan 0x10 (tanpa $) = isi memori di alamat 16.
%rax vs (%rax)
%rax→ isi register itu sendiri (sepertipdi C)(%rax)→ isi register dipakai sebagai alamat, lalu baca memori di alamat itu (seperti*pdi C)
Kurung ( ) di AT&T = dereference. Di AT&T, register wajib pakai %; rax tanpa % akan dianggap nama simbol.
| Maksud | AT&T (gcc, CS:APP) | Intel (NASM, MASM) |
|---|---|---|
| Isi register | %rax |
rax
|
| Isi memori di alamat rax | (%rax) |
[rax]
|
| Konstanta | $5 |
5
|
Bentuk umum memory reference
Imm(rb, ri, s) → alamat = Imm + R[rb] + R[ri] · s
Imm: displacement konstantarb: base register (64-bit)ri: index register (64-bit)s: skala, hanya 1, 2, 4, atau 8 (ukuranchar,short,int,long/pointer)
Semua mode lain adalah kasus khusus dengan komponen yang dihilangkan:
| Form | Effective address | Nama |
|---|---|---|
Imm |
Imm |
Absolute |
(rb) |
R[rb] |
Indirect |
Imm(rb) |
Imm + R[rb] |
Base + displacement |
(rb,ri) |
R[rb] + R[ri] |
Indexed |
Imm(rb,ri) |
Imm + R[rb] + R[ri] |
Indexed |
(,ri,s) |
R[ri]·s |
Scaled indexed |
Imm(,ri,s) |
Imm + R[ri]·s |
Scaled indexed |
(rb,ri,s) |
R[rb] + R[ri]·s |
Scaled indexed |
Imm(rb,ri,s) |
Imm + R[rb] + R[ri]·s |
Scaled indexed |
Tambahan x86-64 yang tidak ada di tabel: RIP-relative, misalnya lea 0x0(%rip),%rdi, alamat dihitung relatif terhadap instruksi berikutnya (dipakai untuk data global/string literal).
Kegunaan: akses array dan struct
long a[100];
x = a[i];
Dengan a di %rdx dan i di %rcx:
movq (%rdx,%rcx,8), %rax # alamat = a + i*8
Satu instruksi tanpa perkalian terpisah. Untuk arr[i].field, offset field masuk ke Imm.
Cara menghitung nilai operand
Untuk memory operand selalu dua langkah:
- Hitung alamat:
Imm + R[rb] + R[ri]·s - Baca isi memori di alamat tersebut
Jebakan umum:
- Displacement tanpa
0xadalah desimal:260=0x104,16=0x10 - Koma di depan (
0xFC(,%rcx,4)) berarti tanpa base register - Perhatikan carry saat menjumlah hex:
0xFC + 4 = 0x100 - Jangan berhenti di alamat; jawaban adalah isi memorinya
Contoh (Practice Problem 3.1)
Data: %rax = 0x100, %rcx = 0x1, %rdx = 0x3; memori 0x100→0xFF, 0x104→0xAB, 0x108→0x13, 0x10C→0x11.
| Operand | Jenis | Alamat | Nilai |
|---|---|---|---|
%rax |
Register | — | 0x100
|
0x104 |
Absolute | 0x104 |
0xAB
|
$0x108 |
Immediate | — | 0x108
|
(%rax) |
Indirect | 0x100 |
0xFF
|
4(%rax) |
Base + displacement | 4 + 0x100 = 0x104 |
0xAB
|
9(%rax,%rdx) |
Indexed | 9 + 0x100 + 3 = 0x10C |
0x11
|
260(%rcx,%rdx) |
Indexed | 0x104 + 1 + 3 = 0x108 |
0x13
|
0xFC(,%rcx,4) |
Scaled indexed | 0xFC + 1·4 = 0x100 |
0xFF
|
(%rax,%rdx,4) |
Scaled indexed | 0x100 + 3·4 = 0x10C |
0x11
|