There seems to be a consensus that register bytecodes are superior to stack bytecodes. I don’t quite know how to cite “common knowledge”, but doing a google search for “Python register VM” or “stack vs register vm” supports the fact that many people believe this. There was a comment on this blog to this effect as well.
Anyway, regardless of whether it truly is something that everyone believes or not, I thought I’d add my two cents. Pyston uses a register bytecode for Python, and I wouldn’t say it’s as great as people claim.
Lifetime management for refcounting
Why? One of the commonly-cited reasons that register bytecodes are better is that they don’t need explicit push/pop instructions. I’m not quite sure I agree that you don’t need push instructions — you still need an equivalent “load immediate into register”. But the more interesting one (at least for this blog post) is pop.
The problem is that in a reference-counted VM, we need to explicitly kill registers. While the Python community has made great strides to support deferred destruction, there is still code (especially legacy code) that relies on immediate destruction. In Pyston, we’ve found that it’s not good enough to just decref a register the next time it is set: we need to decref a register the last time it is used. This means that we had to add explicit “kill flags” to our instructions that say which registers should be killed as a result of the instruction. In certain cases we need to add explicit “kill instructions” whose only purpose is to kill a register.
In the end it’s certainly manageable. But because we use a register bytecode, we need to add explicit lifetime management, whereas in a stack bytecode you get that for free.
I don’t think it’s a huge deal either way, because I don’t think interpretation overhead is the main factor in Python performance, and a JIT can smooth over the differences anyway. But the lifetime-management aspect was a surprise to me and I thought I’d mention it.