1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-12 06:41:13 +02:00

small vm.texi updates

* doc/ref/vm.texi (A Virtual Machine for Guile):
  (VM Concepts, Variables and the VM, Branch Instructions): Small
  updates.
This commit is contained in:
Andy Wingo 2010-03-14 23:28:26 +01:00
parent 7e08f8a6c1
commit f11871d6c5

View file

@ -7,9 +7,9 @@
@node A Virtual Machine for Guile @node A Virtual Machine for Guile
@section A Virtual Machine for Guile @section A Virtual Machine for Guile
Guile has both an interpreter and a compiler. To a user, the Guile has both an interpreter and a compiler. To a user, the difference
difference is largely transparent---interpreted and compiled is transparent---interpreted and compiled procedures can call each other
procedures can call each other as they please. as they please.
The difference is that the compiler creates and interprets bytecode The difference is that the compiler creates and interprets bytecode
for a custom virtual machine, instead of interpreting the for a custom virtual machine, instead of interpreting the
@ -33,21 +33,19 @@ machine.
@subsection Why a VM? @subsection Why a VM?
@cindex interpreter @cindex interpreter
@cindex evaluator For a long time, Guile only had an interpreter. Guile's interpreter
For a long time, Guile only had an interpreter, called the operated directly on the S-expression representation of Scheme source
@dfn{evaluator}. Guile's evaluator operates directly on the code.
S-expression representation of Scheme source code.
But while the evaluator is highly optimized and hand-tuned, and But while the interpreter was highly optimized and hand-tuned, it still
contains some extensive speed trickery (@pxref{Memoization}), it still
performs many needless computations during the course of evaluating an performs many needless computations during the course of evaluating an
expression. For example, application of a function to arguments expression. For example, application of a function to arguments
needlessly conses up the arguments in a list. Evaluation of an needlessly consed up the arguments in a list. Evaluation of an
expression always has to figure out what the car of the expression is expression always had to figure out what the car of the expression is --
-- a procedure, a memoized form, or something else. All values have to a procedure, a memoized form, or something else. All values have to be
be allocated on the heap. Et cetera. allocated on the heap. Et cetera.
The solution to this problem is to compile the higher-level language, The solution to this problem was to compile the higher-level language,
Scheme, into a lower-level language for which all of the checks and Scheme, into a lower-level language for which all of the checks and
dispatching have already been done---the code is instead stripped to dispatching have already been done---the code is instead stripped to
the bare minimum needed to ``do the job''. the bare minimum needed to ``do the job''.
@ -71,7 +69,21 @@ for Guile (@code{cons}, @code{struct-ref}, etc.).
So this is what Guile does. The rest of this section describes that VM So this is what Guile does. The rest of this section describes that VM
that Guile implements, and the compiled procedures that run on it. that Guile implements, and the compiled procedures that run on it.
Note that this decision to implement a bytecode compiler does not Before moving on, though, we should note that though we spoke of the
interpreter in the past tense, Guile still has an interpreter. The
difference is that before, it was Guile's main evaluator, and so was
implemented in highly optimized C; now, it is actually implemented in
Scheme, and compiled down to VM bytecode, just like any other program.
(There is still a C interpreter around, used to bootstrap the compiler,
but it is not normally used at runtime.)
The upside of implementing the interpreter in Scheme is that we preserve
tail calls and multiple-value handling between interpreted and compiled
code. The downside is that the interpreter in Guile 2.0 is slower than
the interpreter in 1.8. We hope the that the compiler's speed makes up
for the loss!
Also note that this decision to implement a bytecode compiler does not
preclude native compilation. We can compile from bytecode to native preclude native compilation. We can compile from bytecode to native
code at runtime, or even do ahead of time compilation. More code at runtime, or even do ahead of time compilation. More
possibilities are discussed in @ref{Extending the Compiler}. possibilities are discussed in @ref{Extending the Compiler}.
@ -79,12 +91,9 @@ possibilities are discussed in @ref{Extending the Compiler}.
@node VM Concepts @node VM Concepts
@subsection VM Concepts @subsection VM Concepts
A virtual machine (VM) is a Scheme object. Users may create virtual Compiled code is run by a virtual machine (VM). Each thread has its own
machines using the standard procedures described later in this manual, VM. When a compiled procedure is run, Guile looks up the virtual machine
but that is usually unnecessary, as Guile ensures that there is one for the current thread and executes the procedure using that VM.
virtual machine per thread. When a VM-compiled procedure is run, Guile
looks up the virtual machine for the current thread and executes the
procedure using that VM.
Guile's virtual machine is a stack machine---that is, it has few Guile's virtual machine is a stack machine---that is, it has few
registers, and the instructions defined in the VM operate by pushing registers, and the instructions defined in the VM operate by pushing
@ -113,12 +122,6 @@ the ``program counter'' (pc). This set of registers is pretty typical
for stack machines; their exact meanings in the context of Guile's VM for stack machines; their exact meanings in the context of Guile's VM
are described in the next section. are described in the next section.
A virtual machine executes by loading a compiled procedure, and
executing the object code associated with that procedure. Of course,
that procedure may call other procedures, tail-call others, ad
infinitum---indeed, within a guile whose modules have all been
compiled to object code, one might never leave the virtual machine.
@c wingo: The following is true, but I don't know in what context to @c wingo: The following is true, but I don't know in what context to
@c describe it. A documentation FIXME. @c describe it. A documentation FIXME.
@ -241,8 +244,8 @@ prove statements about functions. It is especially good at describing
scope relations, and it is for that reason that we mention it here. scope relations, and it is for that reason that we mention it here.
Guile allocates all variables on the stack. When a lexically enclosed Guile allocates all variables on the stack. When a lexically enclosed
procedure with free variables---a @dfn{closure}---is created, it procedure with free variables---a @dfn{closure}---is created, it copies
copies those variables its free variable vector. References to free those variables into its free variable vector. References to free
variables are then redirected through the free variable vector. variables are then redirected through the free variable vector.
If a variable is ever @code{set!}, however, it will need to be If a variable is ever @code{set!}, however, it will need to be
@ -551,8 +554,8 @@ All the conditional branch instructions described below work in the
same way: same way:
@itemize @itemize
@item They pop off the Scheme object located on the stack and use it as @item They pop off Scheme object(s) located on the stack for use in the
the branch condition; branch condition
@item If the condition is true, then the instruction pointer is @item If the condition is true, then the instruction pointer is
increased by the offset passed as an argument to the branch increased by the offset passed as an argument to the branch
instruction; instruction;
@ -560,22 +563,20 @@ instruction;
the one to which the instruction pointer points). the one to which the instruction pointer points).
@end itemize @end itemize
Note that the offset passed to the instruction is encoded on two 8-bit Note that the offset passed to the instruction is encoded as three 8-bit
integers which are then combined by the VM as one 16-bit integer. Note integers, in big-endian order, effectively giving Guile a 24-bit
also that jump targets in Guile are aligned on 8-byte boundaries, and relative address space.
that the offset refers to the @var{n}th 8-byte boundary, effectively
giving Guile a 19-bit relative address space.
@deffn Instruction br offset @deffn Instruction br offset
Jump to @var{offset}. Jump to @var{offset}. No values are popped.
@end deffn @end deffn
@deffn Instruction br-if offset @deffn Instruction br-if offset
Jump to @var{offset} if the condition on the stack is not false. Jump to @var{offset} if the object on the stack is not false.
@end deffn @end deffn
@deffn Instruction br-if-not offset @deffn Instruction br-if-not offset
Jump to @var{offset} if the condition on the stack is false. Jump to @var{offset} if the object on the stack is false.
@end deffn @end deffn
@deffn Instruction br-if-eq offset @deffn Instruction br-if-eq offset