--- title: "Register VM" description: "Register-based virtual machine (Mach)" --- ## 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 | 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: ```c 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. ### Constant Pool Index Overflow Named property instructions (`LOAD_FIELD`, `STORE_FIELD`, `DELETE`) use the iABC format where the constant pool key index occupies an 8-bit field (max 255). When a function references more than 256 unique property names, the serializer automatically falls back to a two-instruction sequence: 1. `LOADK tmp, key_index` — load the key string into a temporary register (iABx, 16-bit index) 2. `LOAD_DYNAMIC` / `STORE_DYNAMIC` / `DELETEINDEX` — use the register-based variant This is transparent to the mcode compiler and streamline optimizer.