Inline assembly
Native and freestanding programs can talk to the machine directly, with the
syscall builtin and with inline assembly.
#![freestanding]
fn write(fd: i64, buf: *u8, len: i64) -> i64 { return asm("syscall", out("rax") i64, in("rax") 1, in("rdi") fd, in("rsi") buf, in("rdx") len, clobber("rcx", "r11", "memory"))}
fn bswap(x: u64) -> u64 { return asm("mov {out}, {0}", "bswap {out}", out(reg) u64, in(reg) x)}
fn _start() -> i64 { write(1, "hi\n", 3) return 0}syscall
Section titled “syscall”syscall(n, a, b, ...) makes the raw Linux system call number n with up to
six arguments, integers or pointers, and returns the kernel’s result as an
i64 (a negative error number on failure). It works on x86_64 and aarch64,
but the numbers are the target’s: write is 1 on x86_64 and 64 on aarch64.
The sys module has them for the target being built for
(per-target modules):
import syssyscall(sys.WRITE, 1, "hi\n", 3) // write(1, "hi\n", 3)The same goes for asm, whose template is the target’s assembly: put it in a
module with one file per target.
asm(template..., operands...) is an expression of the output’s type, or
unit without an output.
- Template. One or more string lines, joined with newlines.
{0},{1}, … are the inputs in order and{out}the output. On x86_64 the syntax is Intel. - Output. At most one:
out("rax") Tin a given register, orout(reg) Tin one the compiler chooses. - Inputs.
in("rdi") xin a given register,in(reg) xin any, andin(out) xto start the output register withx, for instructions such asxchgorincthat update a register in place. Inputs are integers, bools, pointers or function values. - Clobbers.
clobber("rcx", "memory")lists registers the code changes and"memory"if it reads or writes memory. The flags are always clobbered.
A function value is a code pointer, so it can be an input and call {0} calls
it. lib/coro.jh builds stackful coroutines from a few lines of asm this way
(Standard library).
Limits
Section titled “Limits”- Native and freestanding only.
- Like
syscall,asmcannot run at compile time. - The template is passed to LLVM as is; a mistake in it is reported by LLVM’s assembler when the program is built (JIR).
See examples/asm.jh.