Assembly
Pengenalan Assembly

Pengenalan Assembly

Contoh Program

Sekarang mari kita lihat bagaimana register (r0-r10), region memori, dan instruksi bekerja bersama dalam program yang sebenarnya. Kita akan mulai dengan program sBPF paling sederhana untuk memahami alur eksekusi fundamental.

NoOp-program

Program "NoOp" (No Operation) sangat cocok untuk pembelajaran karena menunjukkan struktur program yang esensial tanpa kerumitan:

  • Bagaimana program menerima input (dalam register r1)
  • Bagaimana program mengembalikan hasil (dalam register r0)
  • Alur masuk/keluar dasar yang diikuti setiap program sBPF
  • Bagaimana Rust dikompilasi menjadi assembly sBPF

Meskipun program ini "tidak melakukan apa-apa," program ini menunjukkan dasar yang menjadi landasan setiap program Solana.

Sekarang kita telah mengetahui operasi dasar sBPF, mari kita lihat seperti apa operasi tersebut dalam program nyata (meskipun sangat kecil).

Pinocchio NoOp

Di bawah ini adalah "noop" berkinerja tinggi yang ditulis dengan Pinocchio. Yang dilakukannya hanyalah mengembalikan keberhasilan:

rust
#![no_std]
use pinocchio::{entrypoint::InstructionContext, lazy_program_entrypoint, no_allocator, nostd_panic_handler, ProgramResult};
 
lazy_program_entrypoint!(process_instruction);
 
nostd_panic_handler!();
no_allocator!();
 
fn process_instruction(
    _context: InstructionContext, // wrapper around the input buffer
 ) -> ProgramResult {
    Ok(())
}

Jika kita membangun kode ini dengan cargo build-sbf --dump, kita akan mendapatkan dump ELF yang memberikan informasi tentang binary kita di direktori /target/deploy/.

Kemudian kita perlu mencari bagian .text — bagian dari binary kita tempat kode yang dapat dieksekusi disimpan.

pinocchio_noop-dump.txt
Disassembly of section .text
0000000000000120 <entrypoint>
     120 b7 00 00 00 00 00 00 00      	mov64 r0, 0x0
     128 95 00 00 00 00 00 00 00      	exit

Mari kita uraikan apa arti byte-byte heksadesimal ini menggunakan format instruksi yang telah kita pelajari:

Ini adalah instruksi pertama: 120 b7 00 00 00 00 00 00 00

  • Alamat: 0x120 (dalam region teks yang dimulai pada 0x100000000)
  • Opcode: 0xb7 = mov64 dengan nilai immediate
  • dst: 0x00 = register r0
  • src: 0x00 = tidak digunakan (operasi immediate)
  • offset: 0x0000 = tidak digunakan
  • imm: 0x00000000 = nilai immediate 0

Dan ini adalah instruksi kedua: 128 95 00 00 00 00 00 00 00

  • Alamat: 0x128 (8 byte setelah instruksi pertama)
  • Opcode: 0x95 = exit/return
  • Semua field lainnya: 0x00 = tidak digunakan untuk exit

Offset 0x120 adalah tempat di mana linker memutuskan untuk menempatkan fungsi entrypoint dalam bagian .text file ELF. File ELF dimulai dengan header, tabel bagian, dan metadata lain yang mengambil ~0x120 byte pertama (288 byte). Kode yang dapat dieksekusi sebenarnya muncul setelah semua informasi pembukuan tersebut.

Assembly NoOp

Jika kita mendisassembly binary untuk mengubahnya kembali menjadi sBPF Assembly yang dapat dikompilasi, kodenya akan terlihat seperti ini:

sbpf
.globl entrypoint
entrypoint:
    mov64 r0, 0x00   // r0 <- success
    exit             // finish, return r0

Mari kita uraikan kodenya:

  • .globl entrypoint: Ini adalah direktif assembler yang memberi tahu linker untuk membuat simbol entrypoint terlihat secara global. Runtime Solana mencari simbol ini untuk mengetahui di mana harus mulai mengeksekusi program Anda.
  • entrypoint: Ini adalah label yang menandai alamat memori di mana eksekusi program dimulai. Ketika runtime memuat program Anda, ia melompat ke alamat ini.
  • mov64 r0, 0x00: Memindahkan nilai langsung 0 ke register r0. Karena r0 adalah register return, ini menyiapkan kode return sukses.
  • exit: Mengakhiri eksekusi program dan mengembalikan nilai dalam r0 ke runtime.

mengembalikan 0 berarti kita mengembalikan status sukses untuk program.

Ini adalah program yang sangat kecil, dengan hanya 2 instruksi yang mengkonsumsi hanya 2 compute unit (CU) untuk dieksekusi yang memetakan dengan sempurna ke kode Rust kita:

  • Kita mendefinisikan fungsi entrypoint
  • Kita mengembalikan Ok(()) yang dievaluasi menjadi 0
  • Compiler menghasilkan instruksi mov64 dan exit yang sesuai

Optimized Assembly NoOp

Namun, kita dapat mengoptimalkan ini lebih lanjut. Karena runtime Solana menginisialisasi r0 ke 0 secara default, kita dapat menghilangkan instruksi mov64 yang redundan:

sbpf
.globl entrypoint
entrypoint:
    exit

Versi yang dioptimalkan ini:

  • Hanya membutuhkan 1 unit komputasi (pengurangan 50%!)
  • Menghasilkan hasil yang identik (r0 masih berisi 0)

Inilah mengapa memahami assembly membantu dalam optimasi!

Optimasi ini dimungkinkan karena kita mengetahui status register awal — sesuatu yang tidak selalu dimanfaatkan oleh compiler Rust. Memahami assembly sBPF memungkinkan Anda mengidentifikasi dan menghilangkan inefisiensi seperti ini dalam kode yang kritis terhadap performa.

Daftar Isi
Lihat Sumber
Blueshift © 2025Commit: 1e001ec