Register VM

Overview

The Mach VM is a register-based virtual machine using 32-bit instructions. It is modeled after Lua’s register VM — operands are register indices rather than stack positions, reducing instruction count and improving performance.

Instruction Formats

All instructions are 32 bits wide. Four encoding formats are used:

iABC — Three-Register

[op: 8][A: 8][B: 8][C: 8]

Used for operations on three registers: R(A) = R(B) op R(C).

iABx — Register + Constant

[op: 8][A: 8][Bx: 16]

Used for loading constants: R(A) = K(Bx).

iAsBx — Register + Signed Offset

[op: 8][A: 8][sBx: 16]

Used for conditional jumps: if R(A) then jump by sBx.

isJ — Signed Jump

[op: 8][sJ: 24]

Used for unconditional jumps with a 24-bit signed offset.

Registers

Each function frame has a fixed number of register slots, determined at compile time. Registers hold:

  • R(0)this binding
  • R(1)..R(arity) — function arguments
  • R(arity+1).. — local variables and temporaries

Instruction Set

Loading

OpcodeFormatDescription
LOADKiABxR(A) = K(Bx) — load from constant pool
LOADIiAsBxR(A) = sBx — load small integer
LOADNULLiAR(A) = null
LOADTRUEiAR(A) = true
LOADFALSEiAR(A) = false
MOVEiABCR(A) = R(B) — register copy

Arithmetic

OpcodeFormatDescription
ADDiABCR(A) = R(B) + R(C)
SUBiABCR(A) = R(B) - R(C)
MULiABCR(A) = R(B) * R(C)
DIViABCR(A) = R(B) / R(C)
MODiABCR(A) = R(B) % R(C)
POWiABCR(A) = R(B) ^ R(C)
NEGiABCR(A) = -R(B)
INCiABCR(A) = R(B) + 1
DECiABCR(A) = R(B) - 1

Comparison

OpcodeFormatDescription
EQiABCR(A) = R(B) == R(C)
NEQiABCR(A) = R(B) != R(C)
LTiABCR(A) = R(B) < R(C)
LEiABCR(A) = R(B) <= R(C)
GTiABCR(A) = R(B) > R(C)
GEiABCR(A) = R(B) >= R(C)

Property Access

OpcodeFormatDescription
GETFIELDiABCR(A) = R(B)[K(C)] — named property
SETFIELDiABCR(A)[K(B)] = R(C) — set named property
GETINDEXiABCR(A) = R(B)[R(C)] — computed property
SETINDEXiABCR(A)[R(B)] = R(C) — set computed property

Variable Resolution

OpcodeFormatDescription
GETNAMEiABxUnresolved variable (compiler placeholder)
GETINTRINSICiABxGlobal intrinsic / built-in
GETENViABxModule environment variable
GETUPiABCR(A) = UpFrame(B).slots[C] — closure upvalue
SETUPiABCUpFrame(A).slots[B] = R(C) — set closure upvalue

Control Flow

OpcodeFormatDescription
JMPisJUnconditional jump
JMPTRUEiAsBxJump if R(A) is true
JMPFALSEiAsBxJump if R(A) is false
JMPNULLiAsBxJump if R(A) is null

Function Calls

OpcodeFormatDescription
CALLiABCCall R(A) with B args starting at R(A+1), C=keep result
RETURNiAReturn R(A)
RETNILReturn null
CLOSUREiABxCreate closure from function pool entry Bx

Object / Array

OpcodeFormatDescription
NEWOBJECTiAR(A) = {}
NEWARRAYiABCR(A) = array(B)
PUSHiABCPush R(B) to array R(A)

JSCodeRegister

The compiled output for a function:

struct JSCodeRegister {
  uint16_t arity;           // argument count
  uint16_t nr_slots;        // total register count
  uint32_t cpool_count;     // constant pool size
  JSValue *cpool;           // constant pool
  uint32_t instr_count;     // instruction count
  MachInstr32 *instructions; // 32-bit instruction array
  uint32_t func_count;      // nested function count
  JSCodeRegister **functions; // nested function table
  JSValue name;             // function name
  uint16_t disruption_pc;   // exception handler offset
};

The constant pool holds all non-immediate values referenced by LOADK instructions: strings, large numbers, and other constants.