C+
Systems · v0.0.14

Inline assembly

When you need an instruction the compiler will not emit on its own — a special register, a fence, a custom-ABI trampoline — drop to assembly with #asm. The template is a string; the compiler hands it to the backend and wires up the operands you describe.

unsafe {
    #asm("nop");
}

#asm is unsafe: the body is opaque to sema and the borrow checker, so it must sit inside an unsafe block.

Operands and clobbers

Operands are named, Rust-style. Each binds a template placeholder {name} to a C+ value with a direction and a register constraint:

fn add(a: i64, b: i64) -> i64 {
    let mut sum: i64 = 0;
    unsafe {
        #asm("add {s}, {a}, {b}",
            s = out(reg) sum,
            a = in(reg) a,
            b = in(reg) b,
            clobber("cc"));
    }
    return sum;
}
  • Direction. in reads a value into the instruction, out writes a result back, and inout does both through one register.
  • Constraint. reg lets the compiler pick a register — when you use reg, the matching {name} must appear in the template. Pin a specific register with a string instead, e.g. "x0".
  • Targets. An out or inout operand must name a mut variable. Operands are register-sized scalars.
  • clobber("...") tells the compiler which registers or flags ("cc", a named register) the block trashes, so it does not assume they survive.

#[naked] functions

A #[naked] function gets no compiler-generated prologue or epilogue. Its body is inline assembly that handles the ABI itself and returns on its own. This is the tool for entry stubs, trampolines, and custom-ABI shims where even the standard frame setup is in the way.

#[naked]
fn trampoline() {
    unsafe {
        #asm("ret");
    }
}

The body must be asm-only — any non-asm statement in a #[naked] function is E0909.

Module-level global assembly (a top-level #asm outside any function) is deferred to a later release. Today inline assembly lives inside function bodies.