-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This wiki contains documentation for KineticVM's architecture and assembly language.
The codebase itself will be documented using rustdoc
(see the README for further details).
KineticVM has a rather unusual memory model with the following features:
- KineticVM's memory is divided into fixed-sized arrays called "objects." These objects can be arbitrarily reordered by the virtual machine during garbage collection.
- Two objects are allocated during VM startup: one holds the register file, and one holds the program's code. The program can use system registers to dynamically allocate new objects.
- Every value in KineticVM is either a 16-bit signed integer or a pointer to an object. These two types are distinguished to allow for precise (non-conservative) garbage collection. Types are checked dynamically, at runtime.
Since KineticVM is a transport-triggered architecture, the only instruction available is "move."
Thus any KineticVM assembly file consists of a series of moves; instructions can also be given labels for use in control flow. The move instruction has two operands (a source and a destination), and is written as SOURCE -> DESTINATION
. These operands can be of one of four types, which are documented below.
Constants such as 123
and -5
can be used as the source for a move instruction. So, for instance, 123 -> %OUT
moves the number 123
into the system register OUT
, thus printing it to the screen.
User-defined registers such as count
or return_addr
can be used as the source or destination for a move instruction.
The KineticVM assembler will allocate a VM register for every unique identifier used in the program;
thus the virtual machine (much like LLVM) has no limit on the number of registers.
So, for instance, consider the code:
5 -> my_var
my_var -> %OUT
The KineticVM assembler will create a register for the identifier my_var
. When running the program, the VM will store 5
in this register, and then print the register's contents to the screen. User-defined registers can also hold pointers to objects in memory; such objects are allocated and modified using system registers.
System registers (such as %OUT
) can be used as the source or destination of a move instruction. All operations (including simple ones like addition) are accomplished by reading and writing these system registers. Their names are prefixed with a %
to distinguish them from ordinary user-defined registers.
The address corresponding to an instruction label can be used as the source of a move instruction. Consider the following code:
:line_two -> test
line_two:
test -> %OUT
The first line moves the address of the label line_two
(which, in this case, is 1
) into a user-defined register, and then prints that value. If we had instead written:
0 -> test
:line_two -> test
line_two:
test -> %OUT
Then the address of line_two
would be 2
, so the value 2
would be printed. Control flow is generally achieved by moving label addresses into the system register %PC
, which holds the address of the next instruction to execute. More simply: the instruction :line_two -> %PC
jumps to the label line_two
.
This instruction has two operands: a source and a destination.
The source can be an immediate constant (such as $1
), a user-defined register (such as count
), a system register (such as %SUM
), or the address of an instruction label (such as :loop_start
).