asm
Inline assembly and x86-64 JIT - build byte sequences, emit instructions, seal to executable memory, and call.
Load with: use asm
What this module does
asm lets you build x86-64 machine code from ilusm and execute it at runtime.
You create a program buffer, emit instructions into it (mov, add, sub, and, or, xor, push, pop, ret, nop),
then seal the buffer - which maps it into executable memory - and call it with up to 6 integer arguments.
Before sealing, the module checks that the host runtime has the required native capabilities:
__native_mmap_rw, __native_jit_put, __native_mprotect_rx, and __native_call.
Calling convention follows SysV AMD64: arguments go in rdi, rsi, rdx, rcx, r8, r9. Return value in rax.
Quick example - add two numbers
use asm
# Build a function: rdi + rsi -> rax, ret
p = asmne()
asmmo1(p, "rax", "rdi") # mov rax, rdi
asmad(p, "rax", "rsi") # add rax, rsi
asmre(p) # ret
jit = asmse(p) # seal to executable
result = asmca3(jit, 10, 32) # call with 2 args
prn(result) # 42
Functions
Setup and capability check
asmca()
Checks that the host runtime has all required JIT capabilities. Returns tru if checks pass, errors with the missing capability name if not. If __ilusm_caps is not set, assumes all capabilities are present.
asmne()
Creates a new empty program buffer. Returns a {b: buf} object you pass to all emit functions.
asmar()
Returns the SysV argument register order: ["rdi", "rsi", "rdx", "rcx", "r8", "r9"].
Register encoding
asmid(nm)
Returns the numeric register index for a register name. rax=0, rcx=1, rdx=2, rbx=3, rsp=4, rbp=5, rsi=6, rdi=7, r8–r15=8–15. Errors on unknown names.
Raw byte emission
asmpu(p, xs)
Emits a list of raw bytes into the program buffer. Use this for opcodes not covered by the helper functions.
Register instructions (r64)
All operate on 64-bit registers. REX.W prefix is always emitted. REX.R/B are set automatically for registers r8–r15.
asmmo(p, opc, d, s)
Emits a ModRM instruction with the given opcode, destination register d, and source register s. Handles REX prefix computation.
asmmo1(p, d, s)
mov r64, r64 - copies source register into destination.
asmad(p, d, s)
add r64, r64 - adds source to destination.
asmsu(p, d, s)
sub r64, r64 - subtracts source from destination.
asmsn(p, d, s)
and r64, r64 - bitwise AND.
asmso(p, d, s)
or r64, r64 - bitwise OR.
asmsx(p, d, s)
xor r64, r64 - bitwise XOR. Passing the same register for both d and s zeroes it.
asmmv(p, d, imm)
mov r64, imm32 - moves a 32-bit immediate (sign-extended to 64 bits) into a register. The immediate is encoded little-endian.
asmpu1(p, r)
push r64 - pushes a register onto the stack.
asmpo(p, r)
pop r64 - pops the top of the stack into a register.
asmre(p)
ret - emits a return instruction (0xC3).
asmno(p)
nop - emits a no-op (0x90).
Buffer operations
asmca1(p, q)
Appends the bytes from program buffer q onto program buffer p.
asmby(p)
Returns the program bytes as a list of integers.
asmbi(p)
Returns the program bytes as a bin view.
Sealing and calling
asmse(p)
Seals the program buffer - allocates executable memory (mmap with RW, then mprotect to RX), writes the bytes in, and returns a JIT handle. Runs capability check first.
asmca2(r, a0, a1, a2, a3, a4, a5)
Calls a JIT handle with up to 6 integer arguments. Returns the int64 return value. Pad unused args with 0.
asmca3(r, a, b)
Calls a JIT handle with 2 arguments. Shorthand for asmca2(r, a, b, 0, 0, 0, 0).
asmca4(r)
Calls a JIT handle with no arguments. Shorthand for asmca2(r, 0, 0, 0, 0, 0, 0).
Example programs
asmtr()
Builds, seals, and returns a JIT function that zeroes rax and returns - always returns 0.
asmta()
Builds, seals, and returns a JIT function that adds rdi and rsi and returns the result in rax.
Notes
- x86-64 only. The calling convention is SysV AMD64.
- The host runtime must expose
__native_mmap_rw,__native_jit_put,__native_mprotect_rx, and__native_call.asmca()checks these before sealing. asmcais overloaded:asmca()is the capability check,asmca1appends buffers,asmca2/asmca3/asmca4call JIT handles.