Inline Assembly

Inline assembly is Sec's explicit escape hatch for machine operations that cannot be expressed adequately through ordinary Sec code or a compiler/platform-known primitive. It allows real target instructions to appear inside Sec source while keeping the compiler responsible for the surrounding type, ownership, borrowing, effect, stack, target and control-flow model.

Inline assembly is deliberately narrow. It is not a second programming language inside Sec and it is not a way to bypass the compiler. The assembly text describes the target instructions; the structured Sec contract around it tells the compiler what crosses the boundary and what machine state may change.

unsafe {
    asm {
        "syscall"

        inputs:
            rax(number)

        outputs:
            rax(result)

        clobbers:
            rcx
            r11
            memory
    }
}
Rule: Inline assembly escapes Sec's ordinary instruction vocabulary. It does not escape Sec's compiler model.

Purpose

Inline assembly is appropriate for operations such as:

  • system-call and supervisor-call instructions;
  • special CPU instructions without a normal Sec primitive;
  • target-specific startup or runtime-free platform glue;
  • low-level hardware operations where a canonical platform primitive does not yet exist;
  • ABI adapters and carefully constrained machine-state operations.

When a normal Sec operation or compiler-known platform primitive already expresses the required semantics, that form is preferred. A canonical primitive can expose stronger machine-independent guarantees than opaque user assembly can safely claim.

Core Principle

The compiler continues to own the surrounding semantics:

  • type checking;
  • unsafe obligations;
  • ownership and borrowing;
  • effects;
  • control flow and cleanup;
  • stack accounting;
  • target and execution-context validation;
  • interrupt restrictions;
  • link dependencies and diagnostics.

Where the compiler does not have a declared semantic contract, it remains conservative. It does not infer correctness-critical properties merely by recognizing a mnemonic.

Target Machine Assembly

The template inside asm is real assembly for the selected build target. Register names, instruction names and addressing forms therefore come from the target's canonical assembler dialect.

An asm block does not select its own architecture. It is validated against the already resolved CompilationPlan.

// On an amd64 target:
asm {
    "syscall"
    ...
}

The same source cannot silently reinterpret syscall, rax or another target concept according to the compiler host. Cross compilation always uses the selected target.

Not MLIR or LLVM IR

Inline assembly is machine assembly. It is not source syntax for injecting Sec MLIR or LLVM IR.

Sec source asm
    != Sec MLIR injection
    != LLVM IR injection

An LLVM-based backend may eventually materialize a Sec asm operation through LLVM's inline-assembly facilities, but LLVM constraint strings and LLVM-specific flags are backend details. They are not the Sec source contract.

Unsafe Boundary

Inline assembly is an unsafe operation and therefore requires an explicit unsafe context.

Declaring the surrounding function as unsafe fn does not make its body an implicit unsafe context.

unsafe fn ReadMachineState() uint64 {
    unsafe {
        asm {
            "..."
            outputs:
                rax(result)
        }
    }

    return result
}

Unsafe does not disable type checking, borrowing, effects, stack analysis, ISR analysis, target validation or cleanup requirements. It accepts the specific proof obligations of the machine boundary; it does not mean “trust everything”.

Structured asm Block

A Sec asm operation consists conceptually of two parts:

target assembly template
+
compiler-visible Sec contract

The contract may describe:

  • inputs and outputs;
  • fixed registers or other target operand constraints;
  • clobbers;
  • memory and external effects;
  • control-flow behavior;
  • stack effects;
  • symbol dependencies;
  • target, feature and execution-environment requirements.

The assembler template itself is not a complete compiler contract. Correctness-critical facts must be represented explicitly or supplied by a stronger compiler/platform-known primitive.

Inputs

An input contains an ordinary Sec expression. The expression is type-checked, evaluated exactly once before the asm operation, and then materialized according to the declared target operand constraint.

inputs:
    rdi(argument)

The backend may choose how to materialize that value, but it must not duplicate the evaluation of argument merely because register allocation requires movement.

Physical Constraints

In a fixed-register operand such as rax(number), rax is a physical target constraint. It is not a Sec variable.

Targets may expose operand categories equivalent to:

  • fixed register;
  • register class;
  • immediate;
  • memory operand;
  • address operand;
  • target-defined machine operand class.

Constraint names resolve against the selected target. An x86-64 register name is invalid for an AArch64 compilation plan even if the compiler happens to run on x86-64.

Direct Operand Types

Sec 0.1 keeps the direct asm boundary intentionally small. Direct machine operands are limited to low-level representations that have an unambiguous machine meaning.

Allowed categoryNotes
int8 / uint8Fixed-width integer operands.
int16 / uint16Fixed-width integer operands.
int32 / uint32Fixed-width integer operands.
int64 / uint64Fixed-width integer operands.
int128 / uint128Only when the target operand class supports that width.
float32 / float64Only when the target operand class supports the representation.
RawPtr[T]When the target supports the corresponding raw address width and address space.

High-level values such as strings, structs, unions, arrays, collections, references, interfaces, closures and shaped values do not cross the ordinary register asm boundary directly. They must be explicitly decomposed into the primitive representation the machine operation actually requires.

Exact Widths

A fixed-width asm operand requires a Sec value with the same logical bit width. Inline assembly performs no implicit integer widening, narrowing, sign extension or zero extension.

// Conceptual rule:
32-bit machine operand -> int32 or uint32
64-bit machine operand -> int64 or uint64

If a conversion is intended, perform it explicitly in Sec before crossing the asm boundary.

Target-sized int and uint are not direct fixed-width asm register operands merely because their resolved size happens to match the current target. Low-level ABI code should use the explicit fixed-width type required by the machine contract.

RawPtr Operands

RawPtr[T] is the canonical raw-address operand when assembly needs an address. Passing a raw pointer does not create a safe reference, establish ownership, extend lifetime or prove that the addressed memory is valid.

Assembly also does not acquire hardware authority merely because a pointer value can be placed in a machine register. Address-space and execution-context requirements remain part of the selected platform contract.

Outputs

An output names a physical location whose final representation produces a new low-level Sec value after normal completion of the asm operation.

outputs:
    rax(result)

The output is not available before the assembly executes. The physical register width does not by itself determine the Sec output type; the Sec contract must resolve the output type explicitly or unambiguously.

Ordinary asm outputs remain low-level. They do not directly fabricate safe references, owned resources, constrained nominal values or high-level aggregates. Construct or validate those values after returning to ordinary Sec semantics.

Ownership Boundary

Moving bits into or out of registers does not itself copy or transfer Sec ownership. Ordinary inline assembly has no implicit consuming operand semantics.

The same physical register may be both an input and an output:

inputs:
    rax(number)

outputs:
    rax(result)

This means the initial machine value comes from number and the final machine value produces result. It does not mean that the Sec variable number is mutated in place.

Clobbers

A clobber declares a machine resource that may be destroyed by the assembly operation and whose final value is not represented as a Sec output.

clobbers:
    rcx
    r11

Registers already declared as outputs do not also need to appear as clobbers merely because they are written. Conversely, raw inline assembly has no hidden target-specific default clobber set. If the actual instruction sequence destroys a compiler-visible machine resource, the contract must say so.

Target clobbers may also include condition flags, floating-point status, vector state or other target-defined machine resources.

Observable by Default

Ordinary asm is observable by default. If execution reaches a live asm operation, the compiler must preserve that occurrence even when its outputs are unused.

This does not make the containing function or module globally live. An unreachable private function may still be removed according to normal link reachability.

Observable asm is also not a global optimizer barrier. Independent surrounding code may still move or optimize when data, memory, machine-state and ordering contracts allow it.

No asm volatile

Sec 0.1 does not define:

asm volatile
volatile asm
@volatile asm

The word volatile belongs to physical-storage access semantics. Inline asm observability is a separate concept and is already the default for ordinary asm.

memory Clobber

The special memory clobber says that the operation may access memory whose complete footprint is not otherwise described precisely.

clobbers:
    memory

This forces conservative compiler memory reasoning around the operation. It does not mean Acquire, Release or SeqCst, and it does not emit a CPU fence, cache operation, DMA handoff or device-completion sequence.

Rule: memory is a compiler-visible unknown-memory effect, not a hardware synchronization primitive.

Compiler vs Hardware Ordering

Compiler ordering and hardware ordering are distinct. An asm memory effect may prevent the compiler from reordering memory operations across the boundary while still emitting no physical fence instruction.

Likewise, hardware ordering and hardware completion are separate. A fence, read-back or target-specific completion instruction only provides a positive Sec guarantee when the owning platform contract states exactly what relation it establishes.

The compiler must not infer a completion guarantee merely because it recognizes a familiar instruction mnemonic.

Atomics and Synchronization

A machine instruction can be physically atomic without automatically becoming a proven Sec atomic operation. Raw assembly does not create a second memory model.

If Sec relies on an operation for synchronization, race freedom or a memory-order proof, the stronger semantics must come from a canonical Atomic or platform primitive contract. The same principle applies to interrupt masking: raw mask-manipulation instructions do not automatically establish a proven Sec critical section.

Control Flow

Local labels, loops and branches are allowed inside one asm operation when the target assembler supports them. That internal machine control flow must remain inside the asm boundary.

Ordinary inline assembly must not:

  • jump from asm into an arbitrary Sec basic block;
  • jump from Sec into an internal asm label;
  • use a machine ret to return directly from the enclosing Sec function;
  • use an interrupt/exception-return instruction as a normal source-level ISR return.

A truly non-returning asm operation requires an explicit compiler-visible NoReturn or equivalent semantic contract so that the Sec control-flow graph becomes terminal at that point.

Stack and ABI

Ordinary asm executes inside the surrounding Sec function's resolved ABI. The compiler still owns the prologue, epilogue, frame layout, stack alignment, return convention and required callee-saved preservation.

On normal fallthrough, the stack pointer must be restored to the state required by the enclosing function. Listing the stack pointer as a clobber is not enough to authorize a persistent stack change.

Temporary stack usage is possible only when the compiler-visible contract can describe the required final delta, maximum additional stack consumption and alignment facts needed by stack analysis. Unknown or unbounded stack use cannot satisfy a context that requires a finite positive stack proof.

Permanent stack switching or naked machine entry/exit is not ordinary inline assembly. It requires a stronger compiler/platform contract.

Symbols and Relocations

Ordinary Sec calls remain preferred when normal call semantics are sufficient. When assembly must refer to a Sec, foreign or platform symbol, the dependency must remain compiler-visible so the linker can preserve reachability, relocation and ABI semantics.

Source assembly should not depend on final compiler-mangled symbol spelling as its canonical link model. Local labels inside the asm template remain assembler-local and do not become Sec declarations.

Assembler Directives

Inline assembly may use target-approved local directives and raw instruction encodings when the target contract allows them. It must not use ordinary inline asm to take over the compiler's object model or build system.

Forms equivalent to the following are not ordinary asm responsibilities:

.section
.global
.weak
.org
.include
.incbin

Compiler-owned debug and unwind metadata are similarly not ordinary source asm. If the program needs a global symbol, separate section, startup root or object dependency, use the language/project/linking mechanism that owns that concept.

Target Validation

The target assembler being able to encode an instruction is not sufficient proof that the instruction is valid for the selected CompilationPlan.

Validation may additionally require:

  • a CPU feature or ISA extension;
  • a particular execution mode;
  • target availability;
  • platform authority;
  • a compatible security state;
  • a compatible ABI or environment contract.

Inline assembly and assembler directives cannot silently enable a CPU feature absent from the compilation plan.

Privilege and Authority

Unsafe is not privilege. A privileged instruction remains invalid when the current execution environment does not provide the required authority, even if the instruction is valid for the processor and encodable by the assembler.

ISA-valid != userspace-valid
unsafe    != privileged

The same principle applies to hardware authority, address-space access and security modes. Assembly cannot create capabilities that the selected platform profile does not provide.

System Calls and Environment Transitions

Inline assembly may implement architectural transitions such as system calls, supervisor calls, hypercalls, firmware calls, secure-monitor calls, debug traps or target-defined RTOS kernel entries.

Such a transition is not an ordinary Sec function call. Its input locations, output locations, clobbers, memory effects, blocking behavior, privilege transition, fault behavior and raw error convention come from the platform contract.

One instruction can therefore have strong effects. Instruction count says nothing about whether an operation may block, perform I/O, mutate external state or terminate control flow.

Raw Results and Faults

Assembly transports machine-level values. A platform convention such as “negative integer means errno” does not automatically produce a Sec Result.

A higher-level Sec or platform wrapper interprets the raw result and constructs Result, Option or another domain type using normal Sec rules.

Likewise, page faults, bus faults, illegal instructions, alignment faults, privilege faults and architectural traps do not automatically become ordinary Err(...) propagation. Their behavior is defined by the target, runtime or interrupt model.

Effects and Analysis

Inline assembly contributes effects to the normal Sec effect analysis. Depending on the declared or platform-known contract, those facts may include effects equivalent to:

  • MayPanic;
  • MayAllocate;
  • MayBlock;
  • MaySuspend;
  • MayIO;
  • MayAccessVolatile;
  • MayMutateExternalState;
  • MayUseNondeterministicInput;
  • machine-state or target-specific trusted effects.

Raw asm does not become pure merely because the source contains no Sec-level function calls. A stronger pure contract requires an explicit compiler/core/platform-known semantic guarantee.

Inline Assembly in ISRs

Inline assembly remains subject to the complete ISR contract. Using asm inside an ISR does not waive noPanic, noAlloc, noBlock, stack, target-authority, race, deadlock or runtime-context requirements.

Raw assembly that manipulates interrupt masks also does not automatically prove the exclusion relation required by ISR or race analysis. Canonical platform primitives are preferred when the program needs positive masking, ordering or completion guarantees.

Cross Compilation

Inline assembly is interpreted for the target, never for the compiler host. If Sec is running on Linux/amd64 while building bare-metal Cortex-M4 code, the assembly dialect, register set, features, ABI and privilege model are those of the Cortex-M4 target.

Host assembler capabilities must never silently influence target semantics. The CLI and LSP must resolve the same target facts from the same CompilationPlan.

Lowering Pipeline

Inline assembly remains a first-class semantic operation until all analyses that depend on its contract have consumed or preserved the required information.

Sec source asm
        |
        v
resolved inline-assembly operation
        |
        v
Semantic IR
        |
        v
Sec MLIR / target lowering
        |
        v
backend inline asm / target assembler
        |
        v
machine encoding

A backend must reject a valid Sec contract it cannot represent faithfully rather than silently weakening operand width, clobber, observability, control-flow, ordering, completion or symbol semantics.

Diagnostics and Tooling

Sec distinguishes three broad diagnostic classes:

  1. Sec contract errors — such as wrong operand width, forbidden operand type, missing unsafe context or illegal control flow;
  2. target assembler errors — such as an unknown mnemonic, invalid instruction form or illegal addressing mode;
  3. backend/materialization errors — where a valid Sec contract cannot be represented correctly by the selected backend.

Multiline assembler diagnostics should map back to the original Sec source line inside the asm template. User-facing diagnostics speak in Sec terms such as register, operand type, width, clobber, target, symbol and effect rather than exposing LLVM constraint syntax as the primary model.

Complete Example

The following illustrates the intended shape of a Linux/amd64 raw syscall wrapper using fixed-width machine ABI values. The wrapper remains unsafe because it exposes a raw execution-environment transition rather than a typed higher-level OS API.

#target(os: "linux", arch: "amd64")

unsafe fn RawSyscall1(number: uint64, arg1: uint64) int64 {
    unsafe {
        asm {
            "syscall"

            inputs:
                rax(number)
                rdi(arg1)

            outputs:
                rax(result)

            clobbers:
                rcx
                r11
                memory
        }
    }

    return result
}

The source explicitly describes the fixed machine registers used by the ABI and the machine resources destroyed by the transition. The raw scalar result remains a raw scalar. Any Linux error decoding belongs in a higher-level wrapper.

Current Implementation Status

The inline-assembly rulebook is normative, while implementation is currently partial.

Implemented today:

  • the parser recognizes asm statements and the structured asm block form;
  • assembly template, inputs, outputs and clobbers are retained by the parser;
  • semantic analysis requires an explicit unsafe context;
  • asm input expressions are visited and output bindings are created in the current model;
  • the legacy LLVM path can emit LLVM inline asm with observable/side-effect semantics;
  • fixed-register inputs, one fixed-register output and explicit clobbers have initial lowering support;
  • Linux/amd64 raw syscall code already exercises the structured form.

Important current mismatches with the normative model include:

  • the legacy LLVM path currently supports only one output and hardcodes it as i64;
  • current lowering coerces asm inputs to i64 instead of enforcing exact Sec operand widths;
  • pointer operands are not yet preserved as complete target-aware RawPtr contracts;
  • current lowering may add implicit rcx/r11 clobbers, which the normative model forbids;
  • the current frontend still requires an output in cases where instruction-only observable asm should be legal;
  • target register classes, feature requirements, privilege, dialect, stack and full effect contracts are not yet modeled end-to-end through CompilationPlan;
  • the complete first-class Semantic IR / Sec MLIR asm operation is still pending.
Rule: Manual examples in this chapter describe the normative Sec 0.1 model. Existing compiler acceptance must not be used to infer a different language rule when the current backend is still partial.

Best Practices

  • Prefer ordinary Sec code or compiler/platform-known primitives whenever they express the operation correctly.
  • Keep asm blocks small and give the compiler the most precise contract available.
  • Use explicit fixed-width integer types at machine ABI boundaries.
  • Use RawPtr[T] for raw addresses rather than silently converting addresses to integers.
  • Declare every destroyed compiler-visible machine resource as a clobber unless it is already an output.
  • Do not invent asm volatile; ordinary Sec asm is already observable.
  • Do not treat memory as a CPU fence or synchronization primitive.
  • Do not rely on raw atomic instructions or interrupt masking as positive Sec synchronization proof.
  • Do not return from a Sec function using a machine ret inside ordinary asm.
  • Do not permanently switch stacks inside ordinary inline assembly.
  • Do not create sections, global symbols or hidden build dependencies through ordinary asm directives.
  • Validate machine features and privilege against the selected target, not against the compiler host.
  • Wrap raw syscall or firmware conventions in ordinary Sec APIs that construct typed errors and values.

Summary

  • Inline assembly is an explicit unsafe target-machine escape hatch.
  • It escapes Sec's instruction vocabulary, not Sec's compiler model.
  • The assembly template uses the canonical dialect of the selected target.
  • Sec inline assembly is neither MLIR nor LLVM IR source injection.
  • An unsafe fn still needs an explicit unsafe context around asm.
  • Inputs are ordinary Sec expressions evaluated exactly once before the asm operation.
  • Fixed registers and operand classes are physical target constraints, not Sec variables.
  • Direct operand types are intentionally limited to fixed-width machine primitives and RawPtr.
  • Inline assembly performs no implicit integer width conversion.
  • Outputs produce new low-level Sec values after normal completion.
  • Physical register movement does not implicitly create or transfer Sec ownership.
  • Clobbers explicitly describe machine resources destroyed without a corresponding output.
  • Ordinary asm is observable by default and Sec 0.1 has no asm volatile.
  • memory is conservative compiler memory reasoning, not a hardware fence.
  • Compiler ordering, hardware ordering and hardware completion are separate concepts.
  • Raw machine atomicity does not automatically create Sec synchronization proof.
  • Local machine control flow may remain inside one asm operation, but ordinary asm cannot jump into or out of the Sec CFG.
  • The compiler retains ownership of the surrounding function ABI and stack discipline.
  • Inline assembly cannot create absent CPU features, privilege or platform authority.
  • Raw syscall results and machine faults remain distinct from typed Sec Result handling.
  • Inline assembly contributes normal Sec effects and remains subject to ISR, stack, race and deadlock analysis.
  • Cross compilation validates assembly for the target, never for the compiler host.
  • The normative rulebook is complete, while current implementation remains partial.