BRAIDGROUP
RESEARCH & DEV
22. Documentation

FFI

Extern FFI (C Bindings)

The extern fn keyword declares a function that is linked from an external C library. The compiler generates a C declaration that resolves at link time. Extern functions can accept primitives and return values. They are declared at the top level of a module.

extern fn srand(seed: int)
extern fn rand() -> int
extern fn sqrt(x: float) -> float
extern fn sin(x: float) -> float
extern fn cos(x: float) -> float

fn random_in_range(min: int, max: int) -> int {
    let r = rand()
    return (r % (max - min + 1)) + min
}

fn compute_distance(x1: float, y1: float, x2: float, y2: float) -> float {
    let dx = x2 - x1
    let dy = y2 - y1
    return sqrt(dx * dx + dy * dy)
}

Passing Primitives and Structs

Primitives (int, float, bool) are passed by value following the C ABI. Structs are passed by pointer in the current implementation. Strings are marshalled as null-terminated C strings.

extern fn puts(s: string) -> int
extern fn strlen(s: string) -> int
extern fn atoi(s: string) -> int

fn greet(name: string) {
    let msg = "Hello, " + name
    puts(msg)
}

fn to_int(s: string) -> int {
    return atoi(s)
}

Library Linking

Extern functions are linked against libraries specified in braid.toml or via the -l flag at build time. The Braid build system passes these to the C linker (gcc) during the C codegen pipeline.

# braid.toml
[project]
name = "myapp"
version = "1.0"

[dependencies]
libs = ["m", "pthread"]

Native FFI (Runtime Bindings)

The native fn keyword declares a function that is baked into the BraidVM runtime. These are C functions registered in the VM's native function table. Native functions are called via the OP_INVOKE_FFI bytecode instruction, which takes a library index, function index, and argument count.

native fn print_int(val: int)
native fn print_float(val: float)
native fn print_string(val: string)
native fn get_time() -> float
native fn sleep(ms: int)

fn benchmark(f: fn() -> int) -> int {
    let start = get_time()
    let result = f()
    let elapsed = get_time() - start
    print_string("Elapsed: ")
    print_float(elapsed)
    return result
}

ML-Oriented Native Functions

The Braid runtime includes native functions for ML operations, enabling zero-overhead calls into the C++ LLM runtime library. These are declared as native fn and linked directly into the VM.

native fn llm_model_create(d_model: int, d_ff: int, num_layers: int, vocab_size: int) -> int
native fn llm_model_free(model: int)
native fn llm_train_step(model: int, input_ids: int, labels: int, batch: int, seq: int) -> float
native fn llm_forward(model: int, input_ids: int, seq: int) -> int

fn create_llm() -> int {
    return llm_model_create(512, 1024, 6, 64)
}

fn train_model(model: int, input_ids: int, labels: int) -> float {
    return llm_train_step(model, input_ids, labels, 8, 8)
}

Calling Conventions

Extern C functions follow the platform C ABI (System V AMD64 on Linux/x64, Microsoft x64 on Windows). Native functions use BraidVM's internal calling convention, where arguments are pushed onto the VM stack and results are returned on the stack. The OP_INVOKE_FFI opcode handles marshalling between the VM stack and the C ABI.

Error Handling Across FFI

Both extern and native FFI calls can return nil to signal errors. Braid's type system allows checking the result before use.

extern fn fopen(path: string, mode: string) -> int

fn safe_open(path: string) {
    let handle = fopen(path, "r")
    if handle == nil {
        print_string("Failed to open file")
        return nil
    }
    return handle
}