ferrule documentation[styled mode]
specrfcshome

inline assembly

Status: rfc


inline assembly

this feature is an rfc. the spec describes what it might be, not what's committed.


Overview

Ferrule provides typed inline assembly with explicit inputs, outputs, and clobbers.


Basic Syntax

asm <target>
  in  { <input bindings> }
  out { <output bindings> }
  clobber [<clobbered registers>]
  [volatile] "<assembly code>";

Example: RDTSC

function rdtsc() -> u64 effects [cpu] {
  asm x86_64
    in  {}
    out { lo: u32 in rax, hi: u32 in rdx }
    clobber [rcx, rbx]
    volatile "rdtsc";
  return (u64(hi) << 32) | u64(lo);
}

Input Bindings

Bind Ferrule values to registers:

function add_asm(a: u64, b: u64) -> u64 effects [cpu] {
  asm x86_64
    in  { a: u64 in rdi, b: u64 in rsi }
    out { result: u64 in rax }
    clobber []
    "mov rax, rdi\nadd rax, rsi";
  return result;
}

Output Bindings

Declare outputs with their types and registers:

out { name: Type in register }

Multiple outputs:

out { lo: u32 in eax, hi: u32 in edx }

Clobbers

Declare registers modified by the assembly:

clobber [rax, rbx, rcx, memory, flags]

Special clobbers:


Volatile

Mark assembly that must not be optimized away:

asm x86_64
  in {}
  out {}
  clobber [memory]
  volatile "mfence";

Supported Targets

TargetDescription
x86_6464-bit x86
x8632-bit x86
aarch6464-bit ARM
arm32-bit ARM
riscv6464-bit RISC-V
wasm32WebAssembly (limited)

Effect Requirement

Inline assembly requires the cpu effect:

function memory_fence() -> Unit effects [cpu] {
  asm x86_64
    in {}
    out {}
    clobber [memory]
    volatile "mfence";
}

Example: CPUID

function cpuid(leaf: u32) -> { eax: u32, ebx: u32, ecx: u32, edx: u32 } effects [cpu] {
  asm x86_64
    in  { leaf: u32 in eax }
    out { 
      out_eax: u32 in eax, 
      out_ebx: u32 in ebx, 
      out_ecx: u32 in ecx, 
      out_edx: u32 in edx 
    }
    clobber []
    "cpuid";
  return { 
    eax: out_eax, 
    ebx: out_ebx, 
    ecx: out_ecx, 
    edx: out_edx 
  };
}

Example: Atomic Compare-Exchange

function cas(ptr: *u64, expected: u64, desired: u64) -> Bool effects [cpu, atomics] {
  asm x86_64
    in  { 
      ptr: *u64 in rdi, 
      expected: u64 in rax, 
      desired: u64 in rcx 
    }
    out { success: u8 in al }
    clobber [memory, flags]
    volatile "lock cmpxchg [rdi], rcx\nsete al";
  return success !== 0;
}

Feature Gate

Inline assembly may be behind a feature gate in early toolchains:

// in Package.fe
target x86_64-linux {
  features = [inline_asm]
}