4 min read #rust #unsafe-rust
On this page

Inline Assembly

The asm! macro allows Rust to directly embed target machine assembly instructions—constraints describe the input/output relationships of registers, and clobbers declare which registers are clobbered. Each assembly template is per-architecture (x86_64 and ARM64 syntaxes differ completely), essentially acting as a wall to the compiler optimizer saying "please do not optimize across this line."

asm! Basics

use std::arch::asm;
let x: u64;
unsafe { asm!("rdtsc", out("rax") _, out("rdx") _, lateout("rax") x); }
// TSC (Time Stamp Counter) = EDX:EAX
// out("rax") _  — upper 32 bits, value discarded (_)
// out("rdx") _  — lower 32 bits in rdx, discarded
// lateout("rax") x — value combined in x

asm! is a new macro introduced in Rust 1.59+ (replacing the old llvm_asm!). It performs compile-time checks for constraint correctness—register conflicts, invalid operands, and type mismatches are all reported at compile time.

Constraints: Input/Output Specifications

let mut a: u64 = 4;
let b: u64 = 4;
unsafe {
    asm!(
        "add {0}, {1}",              // {0} = a, {1} = b
        inlateout(reg) a,            // a: input + output, does not share register with inputs
        in(reg) b,                   // b: pure input
    );
}
  • in(reg): Input, compiler allocates a register and loads the value
  • out(reg): Output, reads the final value
  • inout(reg): Input + Output
  • lateout(reg): Output, does not share a register with inputs (used when input registers are clobbered)
  • inlateout(reg): Input + Output, does not share a register with inputs

Clobbers: Which Registers Are Modified

out and lateout automatically mark the corresponding registers as clobbered. However, if other functions are called, you need to mark the entire ABI's clobber set:

unsafe {
    asm!(
        "call my_function",
        clobber_abi("C"),            // All caller-saved registers of the C calling convention are marked as clobbered
    );
}

Per-Architecture Conditional Compilation

#[cfg(target_arch = "x86_64")]
unsafe { asm!("rdtsc", ...); }

#[cfg(target_arch = "aarch64")]
unsafe { asm!("mrs {}, cntvct_el0", out(reg) x); }  // ARM64 cycle counter

Options

asm!("nop", options(nomem, nostack, preserves_flags));
// nomem:    No memory read/write (compiler may not flush cache)
// nostack:  No push/pop to stack
// preserves_flags: Does not modify EFLAGS/RFLAGS
// pure:     No side effects (can be optimized away)
// readonly: Read-only memory

References

  • Rust Reference: inline assembly
  • Rust By Example: asm

Keywords: asm!, inline assembly, constraints, clobbers, lateout, clobber_abi, per-arch asm