DEV Community

Void
Void

Posted on

How I Migrated My Language (Arc) from a Tree-Walker to a Bytecode VM in 7 Days

If you told me a year ago, when I first started learning how to code, that I would be writing a custom bytecode virtual machine, I would have laughed you out of the room.

But after spending about 1.5 months building my programming language, Arc, as a standard tree-walking interpreter, I hit a wall. Tree-walkers are elegant, but they are notoriously slow because they constantly traverse the Abstract Syntax Tree (AST) during execution.
I wanted speed. I wanted to understand how the big leagues (like Lua or Python) actually execute code. So, I decided to rip out the heart of Arc and replace it with a stack-based bytecode VM.

It was a wild 3-day sprint. Here is how it went down.

The Timeline

Day 1: The Valley of Despair

  • Status: Very tough day. Code is completely half-baked. Most things are broken.
  • The Reality: I underestimated bytecode. Turning an AST into a linear stream of instructions (opcodes) and operands is a massive mental shift. I spent hours wrestling with stack offsets and trying to emit correct bytecode. By the end of the day, the codebase was a graveyard of compiler errors. I went to bed questioning my life choices.

Day 2: It Works... But It's Slower?

  • Status: Basic support is finally there, but the VM is incredibly slow.
  • The Reality: The VM finally successfully executed its first program! The excitement lasted for exactly five seconds until I ran my benchmarks. The bytecode VM was slower at EVERYTHING... except for a recursive Fibonacci function. Why? Because the tree-walker had massive overhead for function calls, while my VM handled call frames a bit better. But for loops, math, and variable assignments? The VM was dragging its feet.

Day 3: The Optimization Breakthrough

  • Status: Optimizations kicked in. Matches the tree-walker, and smashes Fibonacci.
  • The Reality: Day 2's slowness was because of naive decoding loops. On Day 3, I tightened the main loop, used a flat array for the stack, and streamlined instruction decoding.
  • The Result: The bytecode VM is now neck-and-neck with the old tree-walking approach on basic scripts, and it absolutely annihilated the recursive fib(30) benchmark, running almost 500ms faster!

Day 4-7: Coming soon...

What is Next?

The core architecture is finally handling the heavy lifting, but the sprint is not over yet.
For the second half of the week, the focus shifts entirely to stability, refinement, and edge cases. Up next is tracking down some elusive memory leaks, hunting down edge-case bugs that crash the stack, and polishing the codebase so it is ready for prime time. Stay tuned for the next update.

Repository: https://github.com/VxidDev/Arc

Top comments (1)

Collapse
 
merbayerp profile image
Mustafa ERBAY

I love that you included the “it works, but it’s slower” stage. 🤣
A lot of people assume a bytecode VM automatically means better performance, but the implementation details matter just as much as the architecture. Also, going from learning programming a year ago to building a custom VM is an impressive journey.