Instructions and Types
StoffelVM is a register-based virtual machine optimized for Multi-Party Computation. This page documents the complete instruction set and type system.
Architecture Overview
- Register-based: Operations use numbered registers rather than a stack
- Dual register sets: Separate registers for clear (public) and secret values
- 32 registers: Registers 0-15 for clear values, 16-31 for secret values
- Compare flag: Single flag for conditional jumps
Value Types
Primitive Types
| Type | Description | Size |
|---|---|---|
I64 | Signed 64-bit integer | 8 bytes |
I32 | Signed 32-bit integer | 4 bytes |
I16 | Signed 16-bit integer | 2 bytes |
I8 | Signed 8-bit integer | 1 byte |
U64 | Unsigned 64-bit integer | 8 bytes |
U32 | Unsigned 32-bit integer | 4 bytes |
U16 | Unsigned 16-bit integer | 2 bytes |
U8 | Unsigned 8-bit integer | 1 byte |
Float | Fixed-point number (stored as I64) | 8 bytes |
Bool | Boolean value | 1 byte |
String | UTF-8 string | Variable |
Unit | Nil/void value | 0 bytes |
Complex Types
| Type | Description |
|---|---|
Object | Key-value map (reference) |
Array | Indexed collection (reference) |
Closure | Function with captured environment |
Foreign | Rust object exposed via FFI |
Share | Secret-shared value for MPC |
Share Types (MPC)
Secret-shared values carry a type tag:
ShareType:
Int(i64), I32, I16, I8
U8, U16, U32, U64
Float, Bool
Instruction Set
Memory Operations
LD - Load from Stack
Load a value from the stack into a register.
LD dest_reg, stack_offset
dest_reg: Target register (0-31)stack_offset: Offset from stack base
LDI - Load Immediate
Load a constant value into a register.
LDI dest_reg, constant_index
dest_reg: Target register (0-31)constant_index: Index into constant pool
MOV - Move
Copy value from one register to another.
MOV dest_reg, src_reg
dest_reg: Target register (0-31)src_reg: Source register (0-31)
PUSHARG - Push Argument
Push a register value onto the argument stack for function calls.
PUSHARG src_reg
src_reg: Register containing the argument
Arithmetic Operations
All arithmetic operations follow the pattern:
OP dest_reg, src1_reg, src2_reg
| Instruction | Operation | Description |
|---|---|---|
ADD | dest = src1 + src2 | Addition |
SUB | dest = src1 - src2 | Subtraction |
MUL | dest = src1 * src2 | Multiplication |
DIV | dest = src1 / src2 | Integer division |
MOD | dest = src1 % src2 | Modulo |
Example:
LDI R0, 10 # R0 = 10
LDI R1, 3 # R1 = 3
ADD R2, R0, R1 # R2 = 13
SUB R3, R0, R1 # R3 = 7
MUL R4, R0, R1 # R4 = 30
DIV R5, R0, R1 # R5 = 3
MOD R6, R0, R1 # R6 = 1
Bitwise Operations
| Instruction | Operation | Description |
|---|---|---|
AND | dest = src1 & src2 | Bitwise AND |
OR | dest = src1 \| src2 | Bitwise OR |
XOR | dest = src1 ^ src2 | Bitwise XOR |
NOT | dest = ~src | Bitwise NOT (unary) |
SHL | dest = src << amount | Shift left |
SHR | dest = src >> amount | Shift right |
Note: NOT only takes two operands: NOT dest_reg, src_reg
Example:
LDI R0, 5 # R0 = 0101 (binary)
LDI R1, 3 # R1 = 0011 (binary)
AND R2, R0, R1 # R2 = 0001 = 1
OR R3, R0, R1 # R3 = 0111 = 7
XOR R4, R0, R1 # R4 = 0110 = 6
NOT R5, R0 # R5 = ~5
LDI R6, 1
SHL R7, R0, R6 # R7 = 10
SHR R8, R0, R6 # R8 = 2
Comparison Operation
CMP - Compare
Compare two registers and set the compare flag.
CMP reg1, reg2
Sets compare flag:
-1ifreg1 < reg20ifreg1 == reg21ifreg1 > reg2
Control Flow
JMP - Unconditional Jump
Jump to a labeled instruction.
JMP label
Conditional Jumps
Jump based on the compare flag (set by CMP):
| Instruction | Condition | Description |
|---|---|---|
JMPEQ | flag == 0 | Jump if equal |
JMPNEQ | flag != 0 | Jump if not equal |
JMPLT | flag == -1 | Jump if less than |
JMPGT | flag == 1 | Jump if greater than |
Example:
LDI R0, 10
LDI R1, 20
CMP R0, R1 # flag = -1 (10 < 20)
JMPLT less # Jumps because flag == -1
JMP end
less:
LDI R2, 1 # This executes
JMP end
end:
RET R2
Function Operations
CALL - Call Function
Call a function by name or index.
CALL function_name
- Arguments must be pushed with
PUSHARGbefore calling - Creates new activation record
- Jumps to function entry point
RET - Return
Return from function with a value.
RET src_reg
src_reg: Register containing return value- Pops activation record
- Returns control to caller
Example:
# Function: add(a, b) -> a + b
add:
LD R0, 0 # Load first argument
LD R1, 1 # Load second argument
ADD R2, R0, R1 # Add them
RET R2 # Return result
# Calling code:
main:
LDI R0, 10
PUSHARG R0 # Push first argument
LDI R1, 20
PUSHARG R1 # Push second argument
CALL add # Call function
# Result is now in return register
Opcode Encoding
Instructions are encoded as bytes with the following opcodes:
| Opcode | Hex | Instruction |
|---|---|---|
| 0 | 0x00 | LD |
| 1 | 0x01 | LDI |
| 2 | 0x02 | MOV |
| 3 | 0x03 | ADD |
| 4 | 0x04 | SUB |
| 5 | 0x05 | MUL |
| 6 | 0x06 | DIV |
| 7 | 0x07 | MOD |
| 8 | 0x08 | AND |
| 9 | 0x09 | OR |
| 10 | 0x0A | XOR |
| 11 | 0x0B | NOT |
| 12 | 0x0C | SHL |
| 13 | 0x0D | SHR |
| 14 | 0x0E | JMP |
| 15 | 0x0F | JMPEQ |
| 16 | 0x10 | JMPNEQ |
| 17 | 0x11 | CALL |
| 18 | 0x12 | RET |
| 19 | 0x13 | PUSHARG |
| 20 | 0x14 | CMP |
| 21 | 0x15 | JMPLT |
| 22 | 0x16 | JMPGT |
Bytecode Format
Compiled programs use the .stfb or .stfbin extension:
Header:
[4 bytes] Magic: "STFL"
[2 bytes] Version: 1
Constants:
[4 bytes] Count
[variable] Constant values (type + data)
Functions:
[4 bytes] Count
For each function:
[variable] Name (length-prefixed string)
[4 bytes] Register count
[4 bytes] Parameter count
[variable] Parameter names
[4 bytes] Upvalue count
[variable] Upvalue names
[variable] Parent function name (optional)
[4 bytes] Label count
[variable] Labels (name + instruction index)
[4 bytes] Instruction count
[variable] Instructions
Execution Model
Activation Records
Each function call creates an activation record containing:
- Function name
- Local variables (HashMap)
- Registers (SmallVec, optimized for 16)
- Instructions
- Upvalues (for closures)
- Argument stack
- Compare flag
- Instruction pointer
Register Allocation
- Registers 0-15: Clear (public) values
- Registers 16-31: Secret (MPC) values
- Compiler performs graph-coloring register allocation
- Spills to stack when registers exhausted
Constant Pool
Constants are stored in a separate pool and referenced by index:
- Reduces instruction size
- Enables deduplication
- Supports all value types
MPC Integration
For MPC operations, the VM integrates with the HoneyBadger protocol:
Secret Operations
When operating on Share values:
- Arithmetic uses secure MPC protocols
multiply_share()uses Beaver triplesopen_share()reconstructs values
Client Input
The ClientStore provides access to client inputs:
take_share(party_id, index)retrieves secret sharesget_number_clients()returns participant count
Next Steps
- Implementation Details: VM architecture deep dive
- Built-in Functions: Standard library functions
- Using the VM: Running programs