based on Build a Lua Interpreter in Rust by Wubing Zheng
Operations can manipulate in three ways:
- push a value on top of the stack, incrementing stack size
- pop a value off the stack
- set the value of an item on the stack at a specific index
Bytecode instruction as specified in luajit:
A single bytecode instruction is 32 bit wide and has an 8 bit opcode field and
several operand fields of 8 or 16 bit. Instructions come in one of two formats:
+---+---+---+---+
| B | C | A | OP|
| D | A | OP|
+---+---+---+---+
When assigning local variables they are stored on the stack and their name is appended to a locals
list.
When a name is referenced, first go through locals
in reverse. If the name can be found, its index points to the
stack index where the value is stored. The VM can then use the MOVE(dst, src) bytecode to copy that value to the top
of the stack.
- the
print
function is loaded onto the stack with theGetGlobal(dst,src)
. It finds the name of the global atsrc
in theconstants
and loads it onto the stack atdst
. - the next opcode is
LoadConst(dst,src)
which loads"hello world"
from theconstants
list todst
on the stack - the opcode
Call(func_index, ...)
which executes the function that is stored on the stack atfunc_index
. The function takes a single argument and expects it atfunc_index+1
on the stack.
Aside from loading functions and their arguments onto the stack to be executed, local variables are also stored on the
stack. This is what makes them execute faster than accessing global variables.
Therefore the next free index on the stack is equivalent to locals.len()
.