sbpf-assembler 0.1.9

Assembler for SBPF (Solana BPF) assembly language
Documentation
WHITESPACE = _{ " " | "\t" | "\r" }
COMMENT    = _{ (";" | "#" | "//") ~ (!NEWLINE ~ ANY)* }

// Numbers (decimal and hex)
hex_number     = @{ "-"? ~ "0x" ~ ASCII_HEX_DIGIT+ ~ ("_" ~ ASCII_HEX_DIGIT+)* }
decimal_number = @{ "-"? ~ ASCII_DIGIT+ ~ ("_" ~ ASCII_DIGIT+)* }
number         = @{ hex_number | decimal_number }

// String literals.
string_content = @{ (!"\"" ~ ANY)* }
string_literal = ${ "\"" ~ string_content ~ "\"" }

// Registers
register      = @{ "r" ~ ("10" | ASCII_DIGIT) }
register_32   = @{ "w" ~ ("10" | ASCII_DIGIT) }
llvm_register = @{ register | register_32 }

// identifier is used when defining a name
identifier = @{
    !register ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")*
}
// symbol is used when referencing a previously defined name
symbol = @{ identifier }

// Numeric labels ( can be referenced with f or b)
numeric_label     = @{ ASCII_DIGIT+ }
numeric_label_ref = @{ numeric_label ~ ("f" | "b") }

// Expressions
bin_op     = { "+" | "-" | "*" | "/" }
term       = { "(" ~ expression ~ ")" | number | symbol }
expression = { term ~ (bin_op ~ term)* }

// Operand (full arithmetic expression support for label math, .equ constants, etc.)
operand = { expression }

// Memory offset.
memory_op     = { "+" | "-" }
memory_offset = { number | symbol }
mem_size      = { "u64" | "u32" | "u16" | "u8" }
memory_ref    = { "[" ~ register ~ (memory_op ~ memory_offset)+ ~ "]" }
llvm_memory_ref  = { "(" ~ register ~ (memory_op ~ memory_offset)? ~ ")" }

// Jump target
signed_number = @{ ("+" | "-")? ~ (hex_number | decimal_number) }
jump_target   =  { numeric_label_ref | signed_number | symbol }

// ============
// DIRECTIVES
// ============

// Global
globl_symbol    = { identifier }
directive_globl = { (".globl" | ".global") ~ globl_symbol }

// External
directive_extern = { ".extern" ~ symbol+ }

// Constant
directive_equ = { ".equ" ~ identifier ~ "," ~ expression }

// Sections
directive_section = {
    ".text"
  | ".data"
  | ".rodata"
}

// Data directives
directive_ascii = { ".ascii" ~ string_literal }
directive_byte  = { ".byte" ~ number ~ ("," ~ number)* }
directive_short = { (".short" | ".half") ~ number ~ ("," ~ number)* }
directive_word  = { ".word" ~ number ~ ("," ~ number)* }
directive_int   = { ".int" ~ number ~ ("," ~ number)* }
directive_long  = { ".long" ~ number ~ ("," ~ number)* }
directive_quad  = { ".quad" ~ number ~ ("," ~ number)* }

directive_inner = {
    directive_globl
  | directive_extern
  | directive_equ
  | directive_section
  | directive_ascii
  | directive_byte
  | directive_short
  | directive_word
  | directive_int
  | directive_long
  | directive_quad
}
directive       = { directive_inner ~ NEWLINE }

// ============================
// DEFAULT INSTRUCTIONS
// ============================

// Arithmetic and Logical Operations (64-bit)
alu_64_op       = {
    "add64"
  | "sub64"
  | "mul64"
  | "div64"
  | "sdiv64"
  | "mod64"
  | "smod64"
  | "or64"
  | "and64"
  | "xor64"
  | "mov64"
  | "lsh64"
  | "rsh64"
  | "arsh64"
  | "hor64"
  | "lmul64"
  | "uhmul64"
  | "udiv64"
  | "urem64"
  | "shmul64"
  | "srem64"
}
instr_alu64_imm = { alu_64_op ~ register ~ "," ~ operand }
instr_alu64_reg = { alu_64_op ~ register ~ "," ~ register }

// Arithmetic and Logical Operations (32-bit)
alu_32_op       = {
    "add32"
  | "sub32"
  | "mul32"
  | "div32"
  | "sdiv32"
  | "mod32"
  | "smod32"
  | "or32"
  | "and32"
  | "xor32"
  | "mov32"
  | "lsh32"
  | "rsh32"
  | "arsh32"
  | "lmul32"
  | "udiv32"
  | "urem32"
  | "srem32"
}
instr_alu32_imm = { alu_32_op ~ register ~ "," ~ operand }
instr_alu32_reg = { alu_32_op ~ register ~ "," ~ register }

// Negate operation
instr_neg32 = { "neg32" ~ register }
instr_neg64 = { "neg64" ~ register }

// Memory Load Operations
load_op    = { "ldxb" | "ldxh" | "ldxw" | "ldxdw" }
instr_load = { load_op ~ register ~ "," ~ memory_ref }
instr_lddw = { "lddw" ~ register ~ "," ~ operand }

// Memory store Operations
store_op_imm    = { "stb" | "sth" | "stw" | "stdw" }
store_op_reg    = { "stxb" | "stxh" | "stxw" | "stxdw" }
instr_store_imm = { store_op_imm ~ memory_ref ~ "," ~ operand }
instr_store_reg = { store_op_reg ~ memory_ref ~ "," ~ register }

// Control Flow Operations (Jumps)
jump_op           = {
    "jeq"
  | "jne"
  | "jgt"
  | "jge"
  | "jlt"
  | "jle"
  | "jsgt"
  | "jsge"
  | "jslt"
  | "jsle"
  | "jset"
}
instr_jump_imm    = { jump_op ~ register ~ "," ~ operand ~ "," ~ jump_target }
instr_jump_reg    = { jump_op ~ register ~ "," ~ register ~ "," ~ jump_target }
instr_jump_uncond = { "ja" ~ jump_target }

// Byte Swap Operations
endian_op    = { "be16" | "be32" | "be64" | "le16" | "le32" | "le64" }
instr_endian = { endian_op ~ register }

// ============================
// LLVM DIALECT INSTRUCTIONS
// ============================

// ALU operations
alu_op           = { "s>>=" | "s/=" | "s%=" | "<<=" | ">>=" | "+=" | "-=" | "*=" | "/=" | "|=" | "&=" | "^=" | "%=" | "=" }
instr_llvm_alu64 = { register ~ alu_op ~ (register | operand) }
instr_llvm_alu32 = { register_32 ~ alu_op ~ (register_32 | operand) }

// Negate operations
instr_llvm_neg64 = { register ~ "=" ~ "-" ~ register }
instr_llvm_neg32 = { register_32 ~ "=" ~ "-" ~ register_32 }

// Memory load operations
instr_llvm_load = { llvm_register ~ "=" ~ "*" ~ "(" ~ mem_size ~ "*" ~ ")" ~ llvm_memory_ref }
instr_llvm_lddw = { register ~ "=" ~ operand ~ "ll" }

// Memory store operation
instr_llvm_store_reg = { "*" ~ "(" ~ mem_size ~ "*" ~ ")" ~ llvm_memory_ref ~ "=" ~ llvm_register }
instr_llvm_store_imm = { "*" ~ "(" ~ mem_size ~ "*" ~ ")" ~ llvm_memory_ref ~ "=" ~ operand }

// Byte swap operations
instr_llvm_endian = { register ~ "=" ~ endian_op ~ register }

// Control flow operations
cmp_op                 = { "s>=" | "s<=" | "s>" | "s<" | "==" | "!=" | ">=" | "<=" | ">" | "<" | "&" }
instr_llvm_jump_uncond = { "goto" ~ jump_target }
instr_llvm_jump_reg    = { "if" ~ register ~ cmp_op ~ register ~ "goto" ~ jump_target }
instr_llvm_jump_imm    = { "if" ~ register ~ cmp_op ~ operand ~ "goto" ~ jump_target }

// ============================
// SHARED INSTRUCTIONS
// ============================

instr_call  = { "call" ~ symbol }
instr_callx = { "callx" ~ register }
instr_exit  = { "exit" }

// ============================
// PROGRAM STRUCTURE
// ============================

instr_default     = {
    (instr_lddw | instr_callx | instr_call | instr_exit | instr_neg32 | instr_neg64 | instr_alu64_imm | instr_alu64_reg | instr_alu32_imm | instr_alu32_reg | instr_load | instr_store_imm | instr_store_reg | instr_jump_imm | instr_jump_reg | instr_jump_uncond | instr_endian) ~ (NEWLINE | &EOI)
}
label_default     = { (identifier | numeric_label) ~ ":" ~ ((directive_inner | instr_default) | NEWLINE) }
statement_default = { NEWLINE | label_default | directive | instr_default }
program_default   = { SOI ~ statement_default* ~ EOI }

instr_llvm     = {
    (instr_llvm_lddw | instr_llvm_load | instr_llvm_store_reg | instr_llvm_store_imm | instr_llvm_endian | instr_llvm_neg64 | instr_llvm_neg32 | instr_llvm_alu64 | instr_llvm_alu32 | instr_llvm_jump_uncond | instr_llvm_jump_reg | instr_llvm_jump_imm | instr_callx | instr_call | instr_exit) ~ (NEWLINE | &EOI)
}
label_llvm     = { (identifier | numeric_label) ~ ":" ~ ((directive_inner | instr_llvm) | NEWLINE) }
statement_llvm = { NEWLINE | label_llvm | directive | instr_llvm }
program_llvm   = { SOI ~ statement_llvm* ~ EOI }

program = _{ program_default | program_llvm }