A bit of an appeal to authority here I know, but Eric Lippert felt stack machines were easier to generate code for:
A stack machine is a very simple way to describe a complex computation; by being able to write code for such a simple machine, it lowers the cost of making a compiler. And not only is it easier to write compilers and jitters that target simple stack machines, it is easier to write other code analysis tools as well.
https://learn.microsoft.com/en-us/archive/blogs/ericlippert/...
Both are simple if you’re willing to give up performance (map VM registers to memory for the first case, don’t use more than R registers for the second case), but that means giving away a lot of performance. So, you’ll essentially be working on two different optimizers, with each only affecting some of your target CPUs.
If, on the other hand, your VM has zero registers, you only have to write the code for running efficiently on machines with more than zero registers. That’s conceptually easier, and (loosely) means every improvement you make will improve things on all target CPUs.
Also note that single static assignment as used in LLVM (not really a VM) somewhat uses the opposite approach: it assumes there are infinitely many registers, and then adds code to map that down to whatever is available on the target hardware.
Going back to the question: I think stack-based CPUs lost because all their instructions contend on access to the stack, making it hard for the hardware to pipeline instructions and/or execute them out of order.
But it's normally agreed to be less efficient. See Y Shi's 2005 paper Virtual Machine Showdown:
> We found that a register architecture requires an average of 47% fewer executed VM instructions, and that the re- sulting register code is 25% larger than the correpsonding stack code. The increased cost of fetching more VM code due to larger code size involves only 1.07% extra real ma- chine loads per VM instruction eliminated. On a Pentium 4 machine, the register machine required 32.3% less time to execute standard benchmarks if dispatch is performed using a C switch statement. Even if more efficient threaded dis- patch is available (which requires labels as first class values), the reduction in running time is still around 26.5% for the register architecture.
But if you're working in Forth, everything is stack ops anyway, you wouldn't benefit much from that kind of analysis, and really a stack-based CPU is going to give you the most speed because the most common operations boil down to a single instruction per.
Chuck Moore doesn't mind this because he writes literally everything in Forth. Most of us who work in other languages would mind the performance hit.
But IIRC so is JVM (Java), CIL (C#), and Python's bytecode, so why did op single out Forth and omit those?..