Bytecode Compilation

Quartz includes a stack-based bytecode virtual machine for faster execution. This document describes the bytecode format, instruction set, and how to work with compiled code.

Overview

The bytecode VM provides:

Using Bytecode Mode

Compile and Run

# Compile to bytecode and run immediately
quartz -c script.qz

Ahead-of-Time Compilation

# Compile to .qzb file
quartz -b script.qz

# This creates script.qzb

# Run the compiled bytecode
quartz script.qzb

Disassemble Bytecode

# View bytecode instructions
python3 tools/qzb_disasm.py script.qzb

File Format (.qzb)

The .qzb file format:

┌─────────────────────────────────┐
│ Magic: "QZB\0" (4 bytes)        │
├─────────────────────────────────┤
│ Version: u16                     │
├─────────────────────────────────┤
│ Flags: u32                       │
├─────────────────────────────────┤
│ Constant Pool                    │
│  ├─ Count: u32                   │
│  └─ Constants: Value[]           │
├─────────────────────────────────┤
│ String Table                     │
│  ├─ Count: u32                   │
│  └─ Strings: String[]            │
├─────────────────────────────────┤
│ Function Table                   │
│  ├─ Count: u32                   │
│  └─ Functions: FuncDef[]         │
├─────────────────────────────────┤
│ Bytecode                         │
│  ├─ Size: u32                    │
│  └─ Instructions: u8[]           │
└─────────────────────────────────┘

VM Architecture

Stack-Based Execution

The VM uses an operand stack for computations:

let x = 1 + 2

Execution:

LOAD_CONST 1    # Stack: [1]
LOAD_CONST 2    # Stack: [1, 2]
ADD             # Stack: [3]
STORE_LOCAL 0   # Stack: [], x = 3

Call Frame Stack

Function calls use a separate call frame stack:

┌───────────────────┐
│ Frame 2: inner()  │ ← Current
│  - Locals: [...]  │
│  - Return addr    │
├───────────────────┤
│ Frame 1: outer()  │
│  - Locals: [...]  │
│  - Return addr    │
├───────────────────┤
│ Frame 0: main     │
│  - Globals: [...] │
└───────────────────┘

Instruction Set

Stack Operations

Opcode Args Description
`LOAD_CONST`indexPush constant from pool
`LOAD_LOCAL`indexPush local variable
`STORE_LOCAL`indexPop and store to local
`LOAD_GLOBAL`indexPush global variable
`STORE_GLOBAL`indexPop and store to global
`POP`Discard top of stack
`DUP`Duplicate top of stack

Arithmetic Operations

Opcode Description Stack Effect
`ADD`Addition[a, b] → [a+b]
`SUB`Subtraction[a, b] → [a-b]
`MUL`Multiplication[a, b] → [a*b]
`DIV`Division[a, b] → [a/b]
`MOD`Modulo[a, b] → [a%b]
`NEG`Negate[a] → [-a]

Comparison Operations

Opcode Description Stack Effect
`EQ`Equal[a, b] → [a==b]
`NE`Not equal[a, b] → [a!=b]
`LT`Less than[a, b] → [a
`LE`Less or equal[a, b] → [a<=b]
`GT`Greater than[a, b] → [a>b]
`GE`Greater or equal[a, b] → [a>=b]

Logical Operations

Opcode Description Stack Effect
`AND`Logical AND[a, b] → [a&&b]
`OR`Logical OR[a, b] → [a||b]
`NOT`Logical NOT[a] → [!a]

Control Flow

Opcode Args Description
`JUMP`offsetUnconditional jump
`JUMP_IF_FALSE`offsetJump if top is false
`JUMP_IF_TRUE`offsetJump if top is true
`LOOP`offsetJump backward (for loops)

Function Operations

Opcode Args Description
`CALL`argcCall function with argc args
`CALL_NATIVE`index, argcCall native function
`RETURN`Return from function
`MAKE_CLOSURE`indexCreate closure

Object Operations

Opcode Args Description
`MAKE_ARRAY`sizeCreate array from stack
`MAKE_DICT`sizeCreate dict from stack
`GET_INDEX`Array/dict index access
`SET_INDEX`Array/dict index assignment
`GET_PROP`indexObject property access
`SET_PROP`indexObject property assignment
`NEW_INSTANCE`indexCreate class instance

Example: Compilation

Source Code

fn factorial(n) {
if (n <= 1) {
return 1
}
return n * factorial(n - 1)
}

io.println(factorial(5))

Compiled Bytecode

; Function: factorial
; Locals: [n]
0000: LOAD_LOCAL 0          ; load n
0002: LOAD_CONST 0          ; load 1
0004: LE                    ; n <= 1
0005: JUMP_IF_FALSE 0011    ; skip if false
0008: LOAD_CONST 0          ; load 1
000A: RETURN                ; return 1
000B: LOAD_LOCAL 0          ; load n
000D: LOAD_LOCAL 0          ; load n
000F: LOAD_CONST 0          ; load 1
0011: SUB                   ; n - 1
0012: CALL 1                ; factorial(n-1)
0014: MUL                   ; n * factorial(n-1)
0015: RETURN                ; return result

; Main
0016: LOAD_CONST 1          ; load 5
0018: CALL 1                ; factorial(5)
001A: CALL_NATIVE 0, 1      ; io.println
001D: POP
001E: HALT

Optimizations

Constant Folding

let x = 2 + 3 * 4  // Compiled as: let x = 14

Dead Code Elimination

fn example() {
return 42
io.println("unreachable")  // Removed
}

Inline Caching

Property lookups are cached for repeated access patterns.

Peephole Optimization

; Before
LOAD_CONST 0
POP

; After (removed entirely)

Performance Comparison

Benchmark Interpreter Bytecode VM Speedup
Fibonacci(30)450ms180ms2.5x
Tight loop 1M120ms35ms3.4x
Array operations85ms42ms2.0x
String concat95ms78ms1.2x

💡 When to Use Bytecode Mode

Development Tools

Disassembler

python3 tools/qzb_disasm.py script.qzb

Output:

=== Quartz Bytecode Disassembly ===
File: script.qzb
Version: 1
Constant Pool: 5 entries
0: (int) 1
1: (int) 5
...

Functions: 1
factorial(1 args, 1 locals)

Code:
0000: LOAD_LOCAL 0
0002: LOAD_CONST 0
...

Inspector

python3 tools/qzb_inspect.py script.qzb

Shows file structure, metadata, and statistics.

Bytecode Verifier

python3 tools/verify_bytecode.py script.qzb

Validates bytecode for correctness.

See Also: Architecture Overview →