I've got subleq+eforth (https://github.com/howerj/muxleq) running in JS
which is dead simple to do. No input but I could output
ASCII mapping values to an array.
On one hand, all these mini interpreters and compilers are cool. I have a soft spot for extensible systems. On the other hand, all these things are a huge security problem. When every subsystem and data format is carrying around its own Turing complete bytecode and JIT, they all need to be secure and bug free for the system to be secure and bug free. And that far more code surface to keep clean.
Busicom 141 PF calculator (1971). This was a product built on the Intel 4004 processor. It was not programmed using Intel 4004 machine langauge directly, but using a more powerful machine language for which the 4004 ran an intepreter included in the image.
These little VMs in applications are everywhere. Apple Mach-O binaries have built in opcodes for binding and rebasing symbols interpreted by (numerous) little VMs in dyld:
Their use is less common now since the introduction of the mach-o load command LC_DYLD_CHAINED_FIXUPS, but these opcodes still have to be supported for older binaries. Also, some popular compilers including Zig still emit these opcodes for LC_DYLD_INFO and LC_DYLD_INFO_ONLY.
I was told by an engineer at Microsoft that Excel's formula interpreter is essentially a kind of bytecode-based stack machine. This came up in the context of a bug I found (while working on a project with Microsoft) that revealed that not only was there a small floating-point bug in some calculations, but (improbably, to me) that Excel preserved this inaccuracy across architectures for decades. So the bytecode interpreter made sense. That said, I've never seen this implementation myself, so it may still be rumor.
Naughty Dog's Uncharted games for PS3 used bytecode VMs for various graphic tasks - essentially they implemented shaders running on SPUs using their custom bytecoded VM, with compiler written in Scheme.
A medium-spicy take of mine is that a bytecode VM in a GPU kernel is not as bad of an idea as one might think, and in some cases it can actually be the most reasonable solution. Some fun examples:
1. As mentioned in the post above, the Dolphin emulator famously implements the entire Gamecube/Wii GPU pipeline in a single gigantic ubershader, and this is useful because it avoids shader compilation stalls [1].
2. Blender's Cycles renderer implements its shading graph eval system as a bytecode VM in a GPU kernel [2]. IIRC early versions of Vray GPU did something similar. There are better ways of course, but a VM gets you surprisingly far as a general approach.
3. Finally, a lot of ML frameworks (Tensorflow, PyTorch, etc) by default use the GPU relatively suboptimally (especially without kernel fusion and such). Tensor frameworks can extract a lot more perf out of GPUs using a VM-in-a-giant-kernel approach [3].
If you think abstractly about how a GPU SM actually works (using CUDA terminology here), all threads in a warp must execute in lockstep and the cost of execution divergence across threads in a warp is that you effectively run serially, losing the parallel advantage of the SM. This penalty gets magnified enormously if you are doing memory reads after wherever the execution divergence happens, since you now have multiple slow memory stalls in serial instead of one big memory read at once for all threads. If you're clever about implementing a bytecode VM, you can load as much state as you need upfront into shared memory, and then if your bytecode VM is just looping through executing a bunch of opcodes in a huge switch statement, then at least as far as the SM is concerned, there's no execution divergence! All threads look like they're doing the same thing at the same time; even if within the VM what is happening a lot is just no-ops, at the SM level you're not dealing with serialized memory stalls and serial scheduling and such.
Is it the _best_ most optimal approach imaginable? Almost certainly not! But can it be a _surprisingly good_ and possibly even reasonable approach for some problem domains and specific constraints? Yeah absolutely!
I guess all (or most) of these could be replaced by a RISC-V VM, with the necessary domain extensions. A RISC-V VM already competes with WASM for many applications.
35 comments
[ 4.8 ms ] story [ 51.7 ms ] threadThere is a tiny Java Bytecode VM in an insanely large list of places, you can find some of them here:
https://github.com/crocs-muni/javacard-curated-list https://en.wikipedia.org/wiki/Java_Card
I guess that is why you say re.Compile.
https://jxself.org/compiling-the-trap.shtml
I've got subleq+eforth (https://github.com/howerj/muxleq) running in JS which is dead simple to do. No input but I could output ASCII mapping values to an array.
https://esolangs.org/wiki/Subleq
So, yes. yt-dlp runs propietary Youtube JS code defying the original purpose.
- ACPI configuration for power management and platform stuff [1]
- Bitcoin transactions [2]
- TrueType fonts [3]
[1] https://wiki.osdev.org/AML
[2] https://en.bitcoin.it/wiki/Script
[3] https://learn.microsoft.com/en-us/typography/opentype/spec/t...
[1] https://docs.oracle.com/cd/E19957-01/802-3239-10/sbusandfc.h...
LLMs enter the chat
[1] https://github.com/fabiensanglard/Another-World-Bytecode-Int...
Fun fact, for the console port of Dragon Age: Origins the scripts were cross compiled to cpp.
[1] https://formats.kaitai.io/python_pickle/
https://github.com/apple-oss-distributions/dyld/blob/e9da5ae...
https://github.com/apple-oss-distributions/dyld/blob/e9da5ae...
Their use is less common now since the introduction of the mach-o load command LC_DYLD_CHAINED_FIXUPS, but these opcodes still have to be supported for older binaries. Also, some popular compilers including Zig still emit these opcodes for LC_DYLD_INFO and LC_DYLD_INFO_ONLY.
Quake 1 had QuakeC: [1] https://en.wikipedia.org/wiki/QuakeC [2] Hello world in QuakeC - https://www.leonrische.me/pages/quakec_bytecode_hello_world....
Quake 2 moved to native binaries.
Quake 3 had a new VM that enabled compiling regular C using LCC: [1] https://fabiensanglard.net/quake3/qvm.php [2] Spec - https://www.icculus.org/~phaethon/q3mc/q3vm_specs.html
Lesser known- games using Havok Physics may have used Havok's MOPP (a bytecode and interpreter for partitioning and searching the geometry).
https://github.com/niftools/nifxml/wiki/Havok-MOPP-Data-form...
I plan to eventually use it for things like automatic spam filtering as well.
1. As mentioned in the post above, the Dolphin emulator famously implements the entire Gamecube/Wii GPU pipeline in a single gigantic ubershader, and this is useful because it avoids shader compilation stalls [1].
2. Blender's Cycles renderer implements its shading graph eval system as a bytecode VM in a GPU kernel [2]. IIRC early versions of Vray GPU did something similar. There are better ways of course, but a VM gets you surprisingly far as a general approach.
3. Finally, a lot of ML frameworks (Tensorflow, PyTorch, etc) by default use the GPU relatively suboptimally (especially without kernel fusion and such). Tensor frameworks can extract a lot more perf out of GPUs using a VM-in-a-giant-kernel approach [3].
If you think abstractly about how a GPU SM actually works (using CUDA terminology here), all threads in a warp must execute in lockstep and the cost of execution divergence across threads in a warp is that you effectively run serially, losing the parallel advantage of the SM. This penalty gets magnified enormously if you are doing memory reads after wherever the execution divergence happens, since you now have multiple slow memory stalls in serial instead of one big memory read at once for all threads. If you're clever about implementing a bytecode VM, you can load as much state as you need upfront into shared memory, and then if your bytecode VM is just looping through executing a bunch of opcodes in a huge switch statement, then at least as far as the SM is concerned, there's no execution divergence! All threads look like they're doing the same thing at the same time; even if within the VM what is happening a lot is just no-ops, at the SM level you're not dealing with serialized memory stalls and serial scheduling and such.
Is it the _best_ most optimal approach imaginable? Almost certainly not! But can it be a _surprisingly good_ and possibly even reasonable approach for some problem domains and specific constraints? Yeah absolutely!
[1] https://dolphin-emu.org/blog/2017/07/30/ubershaders/ [2] https://www.youtube.com/watch?v=etGMk9wYwNs&t=1882s [3] https://hazyresearch.stanford.edu/blog/2025-09-28-tp-llama-m...
https://en.wikipedia.org/wiki/SWEET16