For those that criticize it and find faults with it -- I'm sure the author would consider pull requests. Or you could provide your own fork with all the improvements that you believe are necessary.
Cosplaying Captain Obvious neither.
* In interpreted environments, registers are stored in memory anyways, so the advantage of simulating them isn't as great
* It is easier to generate code for stack machines, because you don't need to run register allocation
* There's a tradeoff in instruction complexity versus number of instructions between stack and register machines
Wonder which is a better source represntation for a JIT thou.
[1] https://www.usenix.org/legacy/events/vee05/full_papers/p153-...
...unless the interpreter maps VM registers directly onto machine registers. With some VMs it's possible, and then you can get very good performance.
If you are interested here is a toy vm I wrote myself: https://github.com/byo3rn/ire
PS: I suck at documentation.
For [1], search page for "vdbe" (virtual database engine). Hopefully you find interesting things.
I intend to write up my findings, but haven’t gotten around to it yet. I do have slides for a talk for this project though: https://speakerdeck.com/ddfreyne/simulating-a-cpu-with-ruby
case JMP: {
ip = program[ip + 1] - 1;
break;
}My VM was very simple, and I've not touched it for a while, but the whole thought of how to handle branching was what made it more interesting than other similar toy systems.
The most cool thing I like about it is the opcodes and registers are extremely general purpose. So, to do a branch, you do `mov IP, label`, or even a "push.mv" instruction which when used against IP is basically the same as the usual "call" instruction, but can also be used with data registers to save a register to the stack and then set it to a value.
I've found the hardest thing about making a VM isn't making a VM, but rather making the infrastructure around it (assembler, debugger, compilers, etc)
I wrote something a little like this once too - there was a register stack and call, jump, branch were all implemented by pushing or popping the register stack.
The book teaches you to build:
1) A CPU from basic electronics elements
2) An assembler to generate machine code
3) A bytecode VM that can be simulated and an assembler generator from the bytecode
4) A basic programming language that generates bytecode
5) An operating system using that language.
I'm midway through building the Assembler and VM myself :-).
I think nowadays it is kind of a minimum requirement to have the intermediate code JIT-compiled (or at least compiled).
I'm also missing a garbage collector, although that is not necessarily part of a VM (but often is). See NaCl for a counterexample. By the way, a project that I'd like to see is an efficient garbage collector implemented inside the VM, instead of as being part of the VM.
There's libz80 (https://github.com/ggambetta/libz80) which is (AFAIK) quite complete and correct but just a library, and the 8086 one (https://github.com/ggambetta/emulator-backed-remakes) which is incomplete and buggy but serves a much more interesting purpose :)
Practical? Not in the least. But, it was a good weekend's worth of fun.
Every CS student in the world has written a toy VM just like this one.
Skip the articles beneath your skill level and move along.
My objection, though, is not what's above/below me, it's that given the title, I was expecting a deeper article only to find one that has appeared numerous times. It was frustration I was expressing more than condescension. But you're quite right in using the term "ungenerous." I appreciate the gentle reproof.
It's much easier to teach someone who knows how to build a basic register machine how to set up the stack for a call to a C function than it is to teach someone who doesn't know how a register machine works how to build one.
Also: there are reasons you might want to build a simple VM that has no access to system services and no notion of an exception. BPF is a good example of such a virtual machine.
If there's something frustrating about this article to me, it's that it lacks a motivating example. Why would you want to build this VM? What are you compiling down to it from?
Would appreciate if you did an advanced version of this writeup :)
https://netspring.wordpress.com/2015/05/10/toy-virtual-machi...
Are you maybe getting your signals crossed between the kind of VM this article is talking about (in the p-code sense of a VM) and virtualization systems?
> A process VM, sometimes called an application virtual machine, or Managed Runtime Environment (MRE), runs as a normal application inside a host OS and supports a single process. ... Process VMs are implemented using an interpreter; performance comparable to compiled programming languages is achieved by the use of just-in-time compilation.
It points to several examples of process VMs. One is Parrot. Quoting from http://en.wikipedia.org/wiki/Parrot_virtual_machine :
> Parrot is a register-based process virtual machine designed to run dynamic languages efficiently. It is possible to compile Parrot assembly language and PIR (an intermediate language) to Parrot bytecode and execute it.
(I quoted that one over Java and Python virtual machines because it uses the phase "assembly language" in the context of the VM.)