5

I am developing a virtual machine for personal use (open source of course!)

In researching and looking at the source code of other virtual machines, especially those of scripting languages; I noted that all the VM archs I perused used a load/store architecture but I've never seen a VM arch use register/memory architecture.

Is there a performance advantage in using load/store arch vs register/memory arch and addressing modes?

4
  • Different instruction set architectures (ISA) differ widely in how many registers there are and how they use the registers. By making assumptions about this in your VM, you are making it unnecessary hard (up to impossible) to implement the VM on an ISA that doesn't match your assumptions. Oct 14, 2018 at 6:50
  • @BartvanIngenSchenau I see, so you suggest I implement the ISA as load/store arch?
    – Nergal
    Oct 14, 2018 at 7:05
  • What I say is that if you model your bytecode after the ARM register set (and usage), you may make it unnecessary hard to implement your VM on an x86 PC. The load/store architecture makes the fewest assumptions about registers in the underlying hardware, so it can be applied on the largest range of hardware architectures. Oct 14, 2018 at 7:23
  • The Lua 5.0 compiler actually uses a register-based VM. According to their paper, they claim that Lua is the first language with widespread adoption to use a register based VM. Aug 28, 2022 at 10:36

2 Answers 2

11

There are a variety of VMs, including VMs that use “registers”. However, the point of the VM is that it abstracts over physical machines so the VM will likely not limit the number of registers! Such registers are then essentially equivalent to local variables.

  • If the VM uses an interpreted execution model, both stack-based VMs and register-based VMs will typically access an array.
  • If the VM uses a compiled execution model, both stack based and register based VMs will allocate physical registers (if the compilation target uses registers), and spill the remainder to memory. Optimal register allocation is a hard problem either way.

Stack based VMs are slightly easier to implement and have significant historical precedents to the point that there is a whole class of languages which are stack based (concatenative languages). If the VM is implemented in a high level language there can be some practical difficulties if stack entries can have different sizes (e.g. byte vs. word), and because stack entries have unknown types. E.g. in JVM instructions are typed and the VM can statically verify during bytecode loading that the bytecode will not corrupt the stack.

Register based VMs can have a slightly easier time with optimizations if the intermediate language is given in Static Single Assignment form (SSA). Each logical register is assigned at exactly one instruction and is read-only afterwards. This makes it easy to analyse data flows. Of course this prevents a naive mapping from logical to physical registers, but that is no disadvantage compared to stack based VMs. However, most interpreters or JIT compilers are not very concerned with optimizability.

The most well known register based VM is LLVM, which uses SSA. This VM is mostly used as an abstract instruction set (intermediate representation) in ahead-of-time compilers such as clang or rustc.

Register based VMs also occasionally pop up for dynamic language's runtimes, but are more niche here. Parrot and MoarVM (both connected to the Perl6 language) come to mind.

2
  • thank you for your reply @amon I made a register-based VM except I made it more "traditional" by giving it a static number of globally available registers and I made the ISA design "register-memory" with addressing modes, etc.
    – Nergal
    Oct 14, 2018 at 16:41
  • The Dalvik VM (default execution engine on Android until a couple of years ago) is also register-based. Oct 15, 2018 at 7:04
6

The idea is that the code for your virtual machine is not interpreted, but compiled at runtime into machine code for the real machine.

A register/memory approach makes it harder to translate into native code. You have two places where data can be stored: In memory or in a register, whereas the load / store architecture only has memory. The two places make it harder to analyze and translate into good native code.

PS. JIT compilation gives you massive speed gains. Register-based VM might give a little boost when interpreting, but it's not really worth it because JIT is the goal. And how do you implement the VM registers in the interpreter? Most likely you end up storing them in memory anyway.

1
  • ahhh so the load/store architecture is just to make it easier to JIT? No real performance boost with the bytecode?
    – Nergal
    Oct 13, 2018 at 23:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.