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
}