The VM engine is the VM interpreter that executes the VM code. It is essential for an interpretive system.
Vmgen supports two methods of VM instruction dispatch: threaded code (fast, but gcc-specific), and switch dispatch (slow, but portable across C compilers); you can use conditional compilation (‘defined(__GNUC__)’) to choose between these methods, and our example does so.
For both methods, the VM engine is contained in a C-level function. Vmgen generates most of the contents of the function for you (name-vm.i), but you have to define this function, and macros and variables used in the engine, and initialize the variables. In our example the engine function also includes name-labels.i (see VM instruction table).
In addition to executing the code, the VM engine can optionally also print out a trace of the executed instructions, their arguments and results. For superinstructions it prints the trace as if only component instructions were executed; this allows to introduce new superinstructions while keeping the traces comparable to old ones (important for regression tests).
It costs significant performance to check in each instruction whether to print tracing code, so we recommend producing two copies of the engine: one for fast execution, and one for tracing. See the rules for engine.o and engine-debug.o in vmgen-ex/Makefile for an example.
The following macros and variables are used in name-vm.i:
LABEL(
inst_name)
switch
label (the ‘:’ is provided by Vmgen). For switch
dispatch this should expand to ‘case label:’; for
threaded-code dispatch this should just expand to ‘label:’.
In either case label is usually the inst_name with some
prefix or suffix to avoid naming conflicts.
LABEL2(
inst_name)
NAME(
inst_name_string)
DEF_CA
NEXT_P0 NEXT_P1 NEXT_P2
The simplest variant is if ‘NEXT_P2’ does everything and the other macros do nothing. Then also related macros like ‘IP’, ‘SET_IP’, ‘IP’, ‘INC_IP’ and ‘IPTOS’ are very straightforward to define. For switch dispatch this code consists just of a jump to the dispatch code (‘goto next_inst;’ in our example); for direct threaded code it consists of something like ‘({cfa=*ip++; goto *cfa;})’.
Pulling code (usually the ‘cfa=*ip++;’) up into ‘NEXT_P1’ usually does not cause problems, but pulling things up into ‘NEXT_P0’ usually requires changing the other macros (and, at least for Gforth on Alpha, it does not buy much, because the compiler often manages to schedule the relevant stuff up by itself). An even more extreme variant is to pull code up even further, into, e.g., NEXT_P1 of the previous VM instruction (prefetching, useful on PowerPCs).
INC_IP(
n)
IP
by n.
SET_IP(
target)
IP
to target.
vm_
A2
B(a,b)
Cell
in our example) and type-prefix types
used with that stack (in both directions). For the type-prefix type,
you use the type-prefix (not the C type string) as type name (e.g.,
‘vm_Cell2i’, not ‘vm_Cell2Cell’). In addition, you have to
define a vm_X2X macro for the stack's basic type X
(used in superinstructions).
The stack basic type for the predefined ‘inst-stream’ is ‘Cell’. If you want a stack with the same item size, making its basic type ‘Cell’ usually reduces the number of macros you have to define.
Here our examples differ a lot: vmgen-ex uses casts in these macros, whereas vmgen-ex2 uses union-field selection (or assignment to union fields). Note that casting floats into integers and vice versa changes the bit pattern (and you do not want that). In this case your options are to use a (temporary) union, or to take the address of the value, cast the pointer, and dereference that (not always possible, and sometimes expensive).
vm_two
A2
B(a1,a2,b)
vm_
B2two
A(b,a1,a2)
a1
, a2
) and a
variable b
of a type that takes two stack items. This does not
occur in our small examples, but you can look at Gforth for examples
(see vm_twoCell2d
in engine/forth.h).
NEXT_P*
.
IMM_ARG(access,value)
TOS
IF_
stackpointerTOS(
expr)
SUPER_END
vm_count_block(IP)
for
profiling.
SUPER_CONTINUE
MAYBE_UNUSED
__attribute__((unused))
for gcc-2.7 and
higher. It suppresses the warnings about unused variables in the code
for superinstructions. You need to define this only if you are using
superinstructions.
VM_DEBUG
vm_debug
vm_out
printarg_
type(
value)
type-prefix
definition
(e.g., ‘printarg_i’); in superinstructions it is currently the
basic type of the stack.