Memory Architecture
Instructions
The Stack Machine
uses JavaScript functions to simulate machine instructions.
Beginning with Machine10
the machine classes generate and store the instructions in an array code[]
which is considered immutable once filled.
To simplify visual inspection of code[],
the instructions are implemented through methods of the machine classes.
Methods for simple instructions such as Add directly use memory as an argument,
i.e., they are instruction functions.
Other methods such as Branch require a memory address, etc., as arguments and return
the actual instruction functions.
All of the instruction functions manipulate memory in some way.
There are two interpreters which call the instruction functions:
-
The stack machine uses one JavaScript array
memoryfor frames and the value stack. -
The garbage-collected stack machine uses
memoryfor global information followed by the value stack, and individually allocated arrays for the frames. These arrays have a property.idwith a unique value for tracing;.idis initialized frommemory.id.
To enable single step execution the interpreters return memory
with a property .continue indicating if the program is suspended (true)
or has terminated (false).
Example 6/10: Arithmetic Expressions
memory |
use |
|---|---|
0 ... |
values of global variables |
| size ... | value stack |
Add (memory) // stack: ... a b -> ... a+b
Divide (memory) // stack: ... a b -> ... a/b
Input (dflt) // stack: ... -> ... input
Load (addr) // stack: ... -> ... memory[addr]
Minus (memory) // stack: ... a -> ... -a
Multiply (memory) // stack: ... a b -> ... a*b
Pop (memory) // stack: ... val -> ...
Push (result) // stack: ... -> ... result
Puts (memory) // stack: ... val -> ... | puts(val)
Store (a) // stack: ... val -> ... val | memory[a]: val
Subtract (memory) // stack: ... a b -> ... a-b
Example 6/11: Control Structures
memory |
use |
|---|---|
.pc register |
next address in code to execute |
0 ... |
values of global variables |
| size ... | value stack |
Six.Machine11 extends Six.Machine10
Branch (a) // stack: ... -> ... | pc: a
Bzero (a) // stack: ... bool -> ... | pc: !bool? a
Eq (memory) // stack: ... a b -> ... a == b
Ge (memory) // stack: ... a b -> ... a >= b
Gt (memory) // stack: ... a b -> ... a > b
Le (memory) // stack: ... a b -> ... a <= b
Lt (memory) // stack: ... a b -> ... a < b
Ne (memory) // stack: ... a b -> ... a != b
Print (n) // stack: ... n*val -> ...
Example 7/04: Functions
memory |
use |
|---|---|
.pc register |
next address in code to execute |
0 ... |
values of global variables |
| frame | |
+0 |
result value of function call |
+1 |
return address in code for function call |
| ... | stack |
Seven.Machine04 extends Six.Machine11
// stack: ... -> ... old-pc | pc: addr
Call (addr)
// stack: ... old-pc -> ,,, 0 old-pc
Entry (memory)
// stack: ... old-pc -> ... | pc: old-pc
Return (memory)
// stack: ... x old-pc result -> ... result old-pc result
ReturnValue (memory)
Example 7/06: Local Variables
memory |
use |
|---|---|
.pc register |
next address in code to execute |
.fp register |
base address of current frame in memory |
0 ... |
values of global variables |
| frame | |
+0 ... |
argument values for the parameters |
+parms |
return address in code for function call |
+parms+1 |
dynamic link, i.e., address in memory of previous frame |
+parms+2 |
result value of function call |
+parms+3 ... |
values of local variables |
| ... | stack |
Seven.Machine06 extends Seven.Machine04
// stack: ... arguments old-pc
// -> ... arguments old-pc old-fp result locals
Entry (parms, size)
// stack: ... arguments old-pc old-fp result locals
// -> ... result old-pc
Exit (parms)
// stack: ... -> ... frame[addr]
LoadFP (addr)
// stack: ... val -> ... val | frame[addr]: val
StoreFP (addr)
Example 7/13: Nested Functions
memory |
use |
|---|---|
.pc register |
next address in code to execute |
.fp register |
base address of current frame in memory |
.dp register |
base address of current display in memory |
0 ... |
values of global variables |
| frame | |
+0 ... |
values of parameter arguments |
+parms |
return address in code for function call |
+parms+1 |
address in memory of previous frame |
+parms+2 |
address in memory of previous display |
+parms+3 |
result value of function call — memory.dp points here |
+parms+3+1 ... |
base addresses of visible frames |
+parms+3+depth |
base address of this frame — at depth |
+parms+4+depth ... |
values of local variables |
| ... | stack |
Seven.Machine13 extends Seven.Machine06
// stack: ... arguments old-pc
// -> ... arguments old-pc old-fp old-dp result display locals
Entry (parms, depth, size)
// stack: ... arguments old-pc old-fp old-dp result display locals
// -> ... result old-pc
Exit (memory)
// stack: ... -> ... frame[depth][addr]
LoadDP (addr, depth)
// stack: ... val -> ... val | frame[depth][addr]: val
StoreDP (addr, depth)
Example 8/01: Global first-order Functions
Same memory layout as Local Variables above.
// stack: ... addr -> ... old-pc | pc: addr`
CallValue (memory)
// stack: ... x-len n*val -> ... n*val x-len`
Rotate (n, len = 1)
Example 8/08: Functions as Argument Values
Same memory layout as Nested Functions above.
This machine requires two slots to represent a function value:
display pointer followed by function start address.
This does not affect the
Call,
CallValue, and
Return instructions.
// stack: ... arguments dp old-pc
// -> ... arguments old-pc old-fp old-dp result display locals`
Entry (args, depth, vars)
// stack: ... arguments old-pc old-fp old-dp result display locals
// -> ... result old-pc`
Exit (args)
// stack: ... -> ... dp`
PushDP (memory)
Example 8/14: Nested first-order Functions
This machine manages frames dynamically: frames are separate arrays subject to JavaScript's garbage collection.
This machine requires two slots to represent a function value:
frame pointer followed by function start address.
This does not affect the
Call,
CallValue, and
Return instructions.
memory |
use |
|---|---|
.pc register |
next address in code to execute |
.fp register |
null or Array of current frame |
0 ... |
values of global variables |
| ... | stack |
frame |
use |
0 |
return address in code for function call |
1 |
null or Array of previous frame |
1+1 ... |
arrays of visible frames |
1+depth |
Array of this frame (at depth) |
2+depth |
result value of function call |
3+depth |
extra slot, exactly if result value is function value |
| ... | argument values |
... frame size-1 |
local variable values |
// stack: ... arguments fp old-pc
// -> ... | frame: old-pc old-fp display result arguments locals`
Entry (args, depth, result, vars)
// stack: ... | frame: old-pc old-fp display result ...
// -> ... result old-pc | fp: old-fp | frame unchanged`
Exit (depth, result)
// stack: ... -> ... frame[depth][addr]`
LoadGC (addr, depth)
// stack: ... -> ... fp`
PushFP (memory)
// stack: ... val -> ... val | frame[depth][addr]: val`
StoreGC (addr, depth)
Example 11/10: Compiling Arithmetic Expressions
This mix-in for code generation extends Six.Machine10 with a Power instruction
to support exponentiation.
Power (memory) // stack: ... a b -> ... a**b
Example 11/11: Compiling a Little Language
This mix-in for code generation extends Six.Machine11 with a Bnzero instruction
to optimize code generation for while loops.
It requires Eleven.Symbols.
Bnzero (a) // stack: ... bool -> ... | pc: bool? a
Example 11/12: Compiling a Typed Little Language
These mix-ins for code generation extend Six.Machine11 with instructions
to support code generation for Boolean and string values and conversions.
IfTrue (a) // stack: ... bool -> ... bool | pc: bool? a
IfFalse (a) // stack: ... bool -> ... bool | pc: !bool? a
Not (memory) // stack: ... a -> ... !a
Concat (memory) // stack: ... a b -> ... a+b
Len (memory) // stack: ... a -> ... a.length
InputString ('prompt', 'default') // stack: ... -> ... string
Cast ('to', 'from') // stack: ... a -> ... cast a