Stacks and virtual machines


The von Neumann architecture

Recall the architecture we have been working with:
-----------------
| ------------- |
| | PC        | |     ----------
| |-----------| |     |        |
| | Registers |===BUS== Memory |
| |-----------| |     |        |
| | ALU       | |     ----------
| ------------- |
-----------------
Components: This style of computer is known as the von Neumann model, in honor of the man who initially developed it, John von Neumann, 1903-1957. This model is also known as the stored program model of computation.

All memory accesses must be done sequentially under control of the CPU. The speed of the CPU, memory, and bus dictate the maximum rate at which the CPU can process instructions.

The main purpose of executing a program is to change the state of the system (values in memory and registers) until some final state is reached. This is done by assigning and re-assigning values to registers and memory locations.

This re-assignment introduces temporal dependencies between instructions. One instruction can't be executed until a previous dependent instruction has finished---a register or memory location can be updated (overwritten) only when the value of that register or memory location is no longer needed, or is no longer valid within the context of the program.

Modern CPUs and compilers are able to extract some amount of instruction-level parallelism from instructions which are not temporally dependent, but still the overall degree of temporal dependence is high. This is a result of the kind of architecture the program is run on, the von Neumann architecture.

Stacks

Functions (including recursive functions) can be computed on the von Neumann architecture by introduction of a stack, which accumulates a stack frame when any function is entered. The stack frame is the state of the outer function at the moment the inner function is called.

It turns out that the stack is also powerful enough to be able to replace the set of general purpose registers in a CPU. Consider the line of calculators manufactured by HP: these all use stack processing, often referred to as ``reverse Polish notation,'' or RPN.

Stack computations

To compute on a stack machine (or calculator), arguments to an operator are pushed on the stack, then the operator is applied to the arguments. The result of the operation is left on the top of the stack. Here is an example:

The expression we wish to calculate is: 10 + 20.

The RPN expression is: 10 20 +.

The stack instructions are:

        pushc 10    ; push constant value 10
        pushc 20    ; push 20
        add         ; add the result
(NOTE: draw a stack and trace the execution of these instructions) Consider two other expressions: (((a+b)+c)+d) [left-associative addition]

and: (a+(b+(c+d))) [right-associative addition]

The instructions for the first expression are:

        push a    ; push value in memory location a
        push b
        add
        push c
        add
        push d
        add
while the instructions for the second expression are:
        push a
        push b
        push c
        push d
        add
        add
        add
The effect of right associativity is to require a larger stack.

Some other useful stack instructions are:

        pop x     ; pop top element from stack, place in memory at location x
        mul
        cmp       ; compare top 2 elements, set flag for <, =, >
        br l      ; branch unconditionally to l (location or label)
        be l
        bl l
        bg l

Stack computers

Stack computers are not easy to find. HP (I think) used to manufacture a stack CPU for general purpose computers, but all modern computers use a von Neumann, or derivative, architecture.

Stack ``machines'' are widely used, though, but not in hardware form. A stack computer can easily be emulated in software (a machine emulator is usually called a virtual machine, or VM). The programming language Forth is based on the notion of stack computation. The PostScript document language is a functional language which ``runs'' on a stack VM, which itself is often stored in ROM and run on a RISC CPU inside a laser printer.

Virtual machines

So why is it useful to use a VM? Why don't you just use whichever real machine (real CPU) is most convenient to use?

A VM is used most often when you would like to implement one or another programming language. If the programming language is very machine-specific, then there is little benefit to using a VM. On the other hand, if you would like your language to run on many different kinds of computer architectures (PC, Mac, Unix), then it may be easiest to develop a VM, and to port that VM to each different architecture. Once a program is compiled to the VM, it runs on each architecture that runs the VM. If a new architecture is developed, once the VM is ported to it, all programs for the VM will run on that architecture.

Portability is not the only benefit to using a VM. Others are:

Registers in a VM

Stack architectures are popular in VMs. We saw already that with only a stack and memory you can perform all general-purpose computation. As it turns out, the stack is all that is necessary to do function calls and recursion.

Recall the reason why CPUs have registers in the first place: to have as much high-speed memory as close to the CPU as possible. It's not that programming with registers is easier than without; it's that it makes your program run faster to use registers whenever possible.

But does a set of registers in a VM (an emulated CPU, a CPU running in software) help a program running on that VM any faster?

In a real CPU, the philosophy is that more registers are better, but the upper bound is limited by the amount of space available on the chip.

In a VM, there is no such limitation, so the number of potential registers is virtually unbound. The primary consideration here, though, is where these registers are located. Since a register in a VM is simply a variable, it is located somewhere in RAM. Therefore, the registers in a VM perform no better than ``normal'' memory locations in the VM.

In fact, it can be argued that the VM registers contend with the other ``normal'' memory locations for space in the real CPU's caches and registers. The more registers there are, the more contention there will be. Therefore, it may actually be beneficial to have no registers.