---
title: Inline Assembly
url: https://doc.liz6.com/en/rust/07-unsafe-rust/03-inline-assembly
locale: en
area: rust
tags:
- rust
- unsafe-rust
date: 2026-06-30
modified: 2026-07-16
description: 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."
---

# 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

```rust
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

```rust
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:

```rust
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

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

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

## Options

```rust
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*
