> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stoffelmpc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Stoffel VM Implementation

> Architecture notes for Stoffel VM runtime state, bytecode loading, execution, table memory, hooks, and MPC effects.

Stoffel VM is the register-based runtime used by the CLI, StoffelLang compiler, Rust SDK, and lower-level integration surfaces. It is implemented primarily by two crates in the `stoffel` repository:

| Crate                     | Purpose                                                                                                                        |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `crates/stoffel-vm-types` | Shared compiler/runtime data model: values, instructions, registers, activations, functions, and `.stflb` bytecode.            |
| `crates/stoffel-vm`       | Runtime execution engine, standard library, MPC builtins, async MPC effect scheduling, storage, networking internals, and FFI. |

Application docs should start with the CLI and Rust SDK. This page is for understanding the implementation boundary beneath those surfaces.

## Execution model

<img src="https://mintcdn.com/stoffellabs/WsJVEVCX73RSUEma/images/diagrams/vm-execution-model.svg?fit=max&auto=format&n=WsJVEVCX73RSUEma&q=85&s=3904911081be82c388b652e51f6e8ea5" alt="Stoffel VM execution model showing bytecode loading, instruction dispatch, clear and secret register spaces, runtime stores, builtins, and MPC hooks." width="1200" height="675" data-path="images/diagrams/vm-execution-model.svg" />

The runtime executes lowered/resolved instructions, not raw source text. Symbolic `Instruction` values are useful at compiler/direct-construction boundaries; `ResolvedInstruction` values and packed runtime instructions are used for efficient execution.

## VirtualMachine and VMState

`VirtualMachine` is a public wrapper around `VMState`.

`VirtualMachine::new()` uses the VM builder defaults:

* registers the standard library;
* registers MPC builtins;
* uses the default register layout;
* uses in-memory table storage;
* uses stdout as the output sink;
* starts without a configured MPC engine or local storage backend.

`VirtualMachine::without_builtins()` creates an empty VM for tests or custom embedding.

`VMState` owns the runtime state:

| Component              | Role                                                        |
| ---------------------- | ----------------------------------------------------------- |
| Program registry       | VM and foreign function definitions.                        |
| Activation stack       | Current call frames and return flow.                        |
| Runtime function cache | Lowered instruction data per active frame.                  |
| Register layout        | Maps absolute register operands to clear or secret banks.   |
| Table memory           | Object and array storage backend.                           |
| Foreign object store   | Rust objects exposed to VM code.                            |
| Hook manager           | Optional execution/debug hooks.                             |
| MPC runtime            | Optional engine metadata and online operation handle.       |
| Output sink            | Destination used by `print`.                                |
| Local storage          | Optional app-provided storage backend for `LocalStorage.*`. |

When the runtime needs to execute multiple functions or MPC tasks concurrently, it can clone an empty execution state that shares immutable program metadata while getting independent activation/table state. Configured local storage remains shared.

## Registers and frames

The bytecode ABI uses absolute register indices. The default layout treats `r0..r15` as clear registers and `r16..` as secret registers. `r0` is the return register.

Each function has a frame register count. Registration normalizes that count so the frame is wide enough for:

* at least the return register;
* all parameters;
* every referenced clear or secret register.

A call frame stores:

* local variables;
* the register file;
* captured upvalues;
* a volatile argument stack;
* a stable spill area;
* compare flag;
* instruction pointer;
* optional closure metadata.

The distinction between argument stack and spill area matters:

* `PUSHARG` pushes onto the volatile argument stack for calls;
* `LD` reads from that argument stack;
* `STS` / `LDS` access a separate per-frame spill area used by the register allocator.

## Clear and secret bank transitions

The register file stores clear and secret banks separately. The layout maps absolute bytecode registers into a bank and bank-local address.

A write or move across banks may have MPC meaning:

| Transition      | Runtime behavior                                        |
| --------------- | ------------------------------------------------------- |
| clear → clear   | normal value copy                                       |
| secret → secret | share/value copy                                        |
| clear → secret  | clear value is represented as a share value when needed |
| secret → clear  | reveal/open operation; may yield an async MPC effect    |

Pending reveals are stored as register-slot state until the online MPC operation completes. They are not ordinary `Value` variants.

## Values and table memory

The VM value model includes scalar values, typed table handles, closures, foreign object handles, unit, and share values.

Object and array storage is abstracted behind `TableMemory`. The default backend is an in-memory `ObjectStore`, but the trait boundary is designed so future backends, including access-tracking or ORAM-like storage, can preserve read/write metadata. For this reason, execution-path table reads go through mutable table-memory APIs even when a read looks logically immutable.

Arrays are 0-indexed. The default array implementation uses dense inline storage for small numeric indices plus an extra field map for large or non-numeric keys, with a cached length hint.

## Functions and calls

A `VMFunction` carries:

* name;
* parameter names;
* upvalue names;
* optional parent function;
* register count;
* labels;
* symbolic instructions, or resolved instructions with constants and call targets.

During registration/loading, the VM validates labels and registers, resolves control-flow targets, interns constants, and resolves call targets. Call-target lookup is cached inside VM state to avoid repeated function-name lookups on hot call paths.

VM calls and foreign-function calls share the same call machinery. Method-style calls are resolved against canonical builtin names and receiver types before dispatch.

## Hooks and debugging

The VM has an optional hook system for debugging and tooling. When no hooks are enabled, the runtime takes a fast path and does not build hook snapshots.

Hook events can cover:

* before/after instruction execution;
* register reads/writes with absolute and bank-local register information;
* variable and upvalue access;
* object and array field access;
* function calls;
* closure creation;
* stack push/pop.

Use this hook layer for runtime instrumentation instead of adding logging to hot execution paths.

## Async MPC effects

The VM can execute ordinary local instructions synchronously. When execution reaches work that needs an MPC engine, async execution yields a typed effect and resumes after the engine returns a result.

Examples of operations that can yield effects:

* client input sharing;
* secret multiplication;
* secret boolean bit operations;
* opening/revealing a share;
* `Share.batch_open`;
* `Share.random` / `Share.random_int`;
* `MpcOutput.send_to_client`;
* lower-level RBC, field-open, and exponent-open operations.

The effect scheduler runs local instructions in bounded slices so non-MPC work does not block the async runtime indefinitely.

## Builtins and standard library

`VirtualMachine::new()` registers two broad groups:

| Group            | Examples                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Standard library | arrays/lists, objects, closures, local storage, `print`, `type`, `slice`, `contains`, `assert`, `ClientStore.*`, `MpcOutput.send_to_client` |
| MPC builtins     | `Share.*`, `Mpc.*`, `Bytes.*`, `Crypto.*`, `Field.*`, `Rbc.*`, `Avss.*`                                                                     |

`print` writes through the configured VM output sink. `LocalStorage.*` builtins require a local-storage backend to be configured on the VM builder.

## Bytecode and manifests

`.stflb` bytecode is defined by `CompiledBinary` in `stoffel-vm-types`. The current format uses magic bytes `STFL` and format version `9`.

A compiled binary carries:

* a scalar constant pool;
* compiled function records;
* function type metadata;
* client input/output schema metadata;
* MPC backend and curve selections;
* preprocessing demand estimates.

The preprocessing manifest helps local/network MPC runtimes prepare material such as triples, random shares, PRandBits, and PRandInts. A `dynamic` flag indicates that runtime demand may exceed the static estimate.

## Lower-level embedding

Direct VM construction is useful for VM tests and bytecode tooling, but most application integrations should use `stoffel build` plus `Stoffel::load_file(...)` from the Rust SDK.

```rust theme={null}
use std::collections::HashMap;

use stoffel_vm::core_types::Value;
use stoffel_vm::core_vm::VirtualMachine;
use stoffel_vm::functions::VMFunction;
use stoffel_vm::instructions::Instruction;

fn main() -> Result<(), String> {
    let mut vm = VirtualMachine::new();

    let hello = VMFunction::new(
        "hello".to_string(),
        vec![],
        vec![],
        None,
        2,
        vec![
            Instruction::LDI(0, Value::String("Hello from Stoffel VM".to_string())),
            Instruction::PUSHARG(0),
            Instruction::CALL("print".to_string()),
            Instruction::LDI(1, Value::Unit),
            Instruction::RET(1),
        ],
        HashMap::new(),
    );

    vm.try_register_function(hello)?;
    vm.execute("hello")?;
    Ok(())
}
```

## See also

* [Virtual Machine Overview](./overview)
* [Instructions and Types](./instructions)
* [Built-in Functions](./builtins)
* [Rust SDK Overview](../rust-sdk/overview)
