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

TypeDescriptionSize
I64Signed 64-bit integer8 bytes
I32Signed 32-bit integer4 bytes
I16Signed 16-bit integer2 bytes
I8Signed 8-bit integer1 byte
U64Unsigned 64-bit integer8 bytes
U32Unsigned 32-bit integer4 bytes
U16Unsigned 16-bit integer2 bytes
U8Unsigned 8-bit integer1 byte
FloatFixed-point number (stored as I64)8 bytes
BoolBoolean value1 byte
StringUTF-8 stringVariable
UnitNil/void value0 bytes

Complex Types

TypeDescription
ObjectKey-value map (reference)
ArrayIndexed collection (reference)
ClosureFunction with captured environment
ForeignRust object exposed via FFI
ShareSecret-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
InstructionOperationDescription
ADDdest = src1 + src2Addition
SUBdest = src1 - src2Subtraction
MULdest = src1 * src2Multiplication
DIVdest = src1 / src2Integer division
MODdest = src1 % src2Modulo

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

InstructionOperationDescription
ANDdest = src1 & src2Bitwise AND
ORdest = src1 \| src2Bitwise OR
XORdest = src1 ^ src2Bitwise XOR
NOTdest = ~srcBitwise NOT (unary)
SHLdest = src << amountShift left
SHRdest = src >> amountShift 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:

  • -1 if reg1 < reg2
  • 0 if reg1 == reg2
  • 1 if reg1 > reg2

Control Flow

JMP - Unconditional Jump

Jump to a labeled instruction.

JMP label

Conditional Jumps

Jump based on the compare flag (set by CMP):

InstructionConditionDescription
JMPEQflag == 0Jump if equal
JMPNEQflag != 0Jump if not equal
JMPLTflag == -1Jump if less than
JMPGTflag == 1Jump 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 PUSHARG before 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:

OpcodeHexInstruction
00x00LD
10x01LDI
20x02MOV
30x03ADD
40x04SUB
50x05MUL
60x06DIV
70x07MOD
80x08AND
90x09OR
100x0AXOR
110x0BNOT
120x0CSHL
130x0DSHR
140x0EJMP
150x0FJMPEQ
160x10JMPNEQ
170x11CALL
180x12RET
190x13PUSHARG
200x14CMP
210x15JMPLT
220x16JMPGT

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 triples
  • open_share() reconstructs values

Client Input

The ClientStore provides access to client inputs:

  • take_share(party_id, index) retrieves secret shares
  • get_number_clients() returns participant count

Next Steps