Reading — step 1 of 3
The Bytecode Representation
Chunks of Bytecode
We now shift from the tree-walk interpreter to building a bytecode virtual machine. The first step: designing the bytecode format itself.
Why Bytecode?
Tree-walk interpreters are slow because they traverse heap-allocated AST nodes. A bytecode VM is faster because:
- Bytecode is a flat array of bytes — excellent cache locality
- No pointer chasing — instructions are sequential
- The dispatch loop is tight and branch-predictor friendly
- We can apply optimizations during compilation
CPython, Lua, Ruby (since 1.9), and Erlang all use bytecode VMs. V8 also compiles to bytecode (Ignition) before optionally JIT-compiling hot functions.
The Chunk
A chunk is our unit of bytecode. It contains:
Chunk {
code: byte[] // the bytecode instructions
constants: Value[] // constant pool (literals)
lines: int[] // line number for each byte (for errors)
}
The code array holds opcodes (one byte each) and their operands. The constants array holds literal values referenced by index. The lines array maps each byte offset to a source line number for error reporting.
Opcodes
Each instruction starts with a one-byte opcode:
OP_CONSTANT 0x00 // push a constant (followed by 1-byte index)
OP_RETURN 0x01 // return from function
OP_NEGATE 0x02 // negate top of stack
OP_ADD 0x03 // pop two, push sum
OP_SUBTRACT 0x04
OP_MULTIPLY 0x05
OP_DIVIDE 0x06
The Constant Pool
Numbers and strings are too large to embed directly in the bytecode stream. Instead, we store them in the constant pool and reference them by index:
chunk.constants = [42.0, "hello"]
chunk.code = [OP_CONSTANT, 0, OP_CONSTANT, 1, OP_ADD, OP_RETURN]
OP_CONSTANT 0 means "push constants[0] (42.0) onto the stack."
Disassembly
A disassembler converts raw bytes back into human-readable form — essential for debugging:
0000 1 OP_CONSTANT 0 '42'
0002 | OP_CONSTANT 1 '3.14'
0004 | OP_ADD
0005 | OP_RETURN
This shows: byte offset, line number, opcode name, and operand details.
Your Task
Build the Chunk data structure, implement adding constants and emitting opcodes, and write a disassembler that prints bytecode in human-readable form.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…