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) —
thisbinding - R(1)..R(arity) — function arguments
- R(arity+1).. — local variables and temporaries
Instruction Set
Loading
| Opcode | Format | Description |
|---|---|---|
LOADK | iABx | R(A) = K(Bx) — load from constant pool |
LOADI | iAsBx | R(A) = sBx — load small integer |
LOADNULL | iA | R(A) = null |
LOADTRUE | iA | R(A) = true |
LOADFALSE | iA | R(A) = false |
MOVE | iABC | R(A) = R(B) — register copy |
Arithmetic
| Opcode | Format | Description |
|---|---|---|
ADD | iABC | R(A) = R(B) + R(C) |
SUB | iABC | R(A) = R(B) - R(C) |
MUL | iABC | R(A) = R(B) * R(C) |
DIV | iABC | R(A) = R(B) / R(C) |
MOD | iABC | R(A) = R(B) % R(C) |
POW | iABC | R(A) = R(B) ^ R(C) |
NEG | iABC | R(A) = -R(B) |
INC | iABC | R(A) = R(B) + 1 |
DEC | iABC | R(A) = R(B) - 1 |
Comparison
| Opcode | Format | Description |
|---|---|---|
EQ | iABC | R(A) = R(B) == R(C) |
NEQ | iABC | R(A) = R(B) != R(C) |
LT | iABC | R(A) = R(B) < R(C) |
LE | iABC | R(A) = R(B) <= R(C) |
GT | iABC | R(A) = R(B) > R(C) |
GE | iABC | R(A) = R(B) >= R(C) |
Property Access
| Opcode | Format | Description |
|---|---|---|
GETFIELD | iABC | R(A) = R(B)[K(C)] — named property |
SETFIELD | iABC | R(A)[K(B)] = R(C) — set named property |
GETINDEX | iABC | R(A) = R(B)[R(C)] — computed property |
SETINDEX | iABC | R(A)[R(B)] = R(C) — set computed property |
Variable Resolution
| Opcode | Format | Description |
|---|---|---|
GETNAME | iABx | Unresolved variable (compiler placeholder) |
GETINTRINSIC | iABx | Global intrinsic / built-in |
GETENV | iABx | Module environment variable |
GETUP | iABC | R(A) = UpFrame(B).slots[C] — closure upvalue |
SETUP | iABC | UpFrame(A).slots[B] = R(C) — set closure upvalue |
Control Flow
| Opcode | Format | Description |
|---|---|---|
JMP | isJ | Unconditional jump |
JMPTRUE | iAsBx | Jump if R(A) is true |
JMPFALSE | iAsBx | Jump if R(A) is false |
JMPNULL | iAsBx | Jump if R(A) is null |
Function Calls
| Opcode | Format | Description |
|---|---|---|
CALL | iABC | Call R(A) with B args starting at R(A+1), C=keep result |
RETURN | iA | Return R(A) |
RETNIL | — | Return null |
CLOSURE | iABx | Create closure from function pool entry Bx |
Object / Array
| Opcode | Format | Description |
|---|---|---|
NEWOBJECT | iA | R(A) = {} |
NEWARRAY | iABC | R(A) = array(B) |
PUSH | iABC | Push 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.