37.2. The virtual machine

The bytecode can be thought of as being interpreted by a virtual processor. The engine which interprets the bytecode (the implementation of the virtual machine) is either a C function (interpret_bytecode in eval.d), or a just-in-time compiler which translates a function's bytecode into hardware CPU instructions the first time said function is called, see Section 3.2.1.1, “Just-In-Time Native Compilation”.

The virtual machine is a stack machine with two stacks:

STACK
a stack for CLISP objects and frames (Lisp stack).
SP
a stack for other data and pointers (Program stack).

This two-stack architecture permits to save an unlimited number of CLISP objects on the STACK (needed for handling of Common Lisp multiple values), without consing[3]. Also, in a world with a compacting no-ambiguous-roots garbage collector, STACK must only hold CLISP objects, and SP can hold all the other data belonging to a frame, which would not fit into STACK without tagging/untagging overhead.

The scope of STACK and SP is only valid for a given function invocation. Whereas the amount of STACK space needed for executing a function (excluding other function calls) is unlimited, the amount of SP space needed is known a priori, at compile time. When a function is called, no relation is specified between the caller's STACK and the callee's STACK, and between the caller's SP and the callee's SP. The bytecode is designed so that outgoing arguments on the caller's STACK can be shared by the caller's incoming arguments area (on the callee's STACK), but a virtual machine implementation may also copy outgoing arguments to incoming arguments instead of sharing them.

The virtual machine has a special data structure, values, containing the top of stack, specially adapted to Common Lisp multiple values:

mv_count
an unsigned integer.
value1
the primary value, a CLISP object. If mv_count = 0, this is NIL.
mv_space
all values except the first one, an array of CLISP objects.

The contents of values is short-lived. It does not survive a function call, not even a garbage-collection.

The interpretation of some bytecode instructions depends on a constant, jmpbufsize. This is a CPU-dependent number, the value of SYSTEM::*JMPBUF-SIZE*. In C, it is defined as ceiling(sizeof(jmp_buf),sizeof(void*)).


These notes document CLISP version 2.49.93+Last modified: 2018-02-19