1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +02:00

Started documenting the compiler.

* doc/guile-vm.texi:  Documented the compiler (node `The Compiler').
  Removed a number of things that might have been relevant to Guile-VM 0.0.

* module/system/il/compile.scm (optimize):  Commented out the case
  using `<ghil-inst?>'.

* src/vm_engine.c (vm_run)[objects_handle]:  New variable.
  Before leaving the function, release OBJECTS_HANDLE.

* src/vm_engine.h (CACHE_PROGRAM):  Use `scm_vector_writable_elements'
  instead of `scm_vector_elements';  don't release the handle right away.

* src/vm_loader.c (load-program):  New commented out piece of code
  dealing with simple vectors.

* src/vm_system.c (object-ref):  Added the type of OBJNUM.

git-archimport-id: lcourtes@laas.fr--2005-mobile/guile-vm--mobile--0.6--patch-3
This commit is contained in:
Ludovic Courtes 2005-06-25 06:57:20 +00:00 committed by Ludovic Courtès
parent 0b5f0e49a8
commit a52b2d3d55
6 changed files with 251 additions and 161 deletions

View file

@ -15,6 +15,16 @@
Instruction
@end macro
@c For Scheme procedure definitions.
@macro scmproc{}
Scheme Procedure
@end macro
@c Scheme records.
@macro scmrec{}
Record
@end macro
@ifinfo
@dircategory Scheme Programming
@direntry
@ -90,8 +100,8 @@ However, be warned that important parts still correspond to version
@menu
* Introduction::
* Variable Management::
* Program Execution::
* Instruction Set::
* The Compiler::
@detailmenu
--- The Detailed Node Listing ---
@ -103,6 +113,14 @@ Instruction Set
* Subprogram Control Instructions::
* Data Control Instructions::
The Compiler
* Overview::
* The Language Front-Ends::
* GHIL::
* GLIL::
* The Assembler::
@end detailmenu
@end menu
@ -156,9 +174,13 @@ Most instructions deal with the accumulator (ac). The VM stores all
results from functions in ac, instead of pushing them into the stack.
I'm not sure whether this is a good thing or not.
@node Variable Management, Program Execution, Introduction, Top
@node Variable Management, Instruction Set, Introduction, Top
@chapter Variable Management
FIXME: This chapter needs to be reviewed so that it matches reality.
A more up-to-date description of the mechanisms described in this
section is given in @ref{Instruction Set}.
A program may have access to local variables, external variables, and
top-level variables.
@ -301,38 +323,6 @@ object directly.
[ We'll also need dynamic scope addressing to support Emacs Lisp? ]
@unnumberedsubsec At a Glance
Guile VM has a set of instructions for each instruction family. `%load'
is, for example, a family to load an object from memory and set the
accumulator (ac). There are four basic `%load' instructions:
@example
%loadl - Local addressing
%loade - External addressing
%loadt - Top-level addressing
%loadi - Immediate addressing
@end example
A possible program code may look like this:
@example
%loadl (0 . 1) ; ac = local[0][1]
%loade (2 . 3) ; ac = external[2][3]
%loadt (foo . #<undefined>) ; ac = #<undefined>
%loadi "hello" ; ac = "hello"
@end example
One instruction that uses real addressing is `%jump', which changes the
value of the program counter:
@example
%jump 0x80234ab8 ; pc = 0x80234ab8
@end example
@node Program Execution, Instruction Set, Variable Management, Top
@chapter Program Execution
Overall procedure:
@ -346,120 +336,8 @@ Overall procedure:
@item When all programs terminated, the VM returns the final value and stops.
@end enumerate
@section Environment
Local variable:
@example
(let ((a 1) (b 2) (c 3)) (+ a b c)) ->
%pushi 1 ; a
%pushi 2 ; b
%pushi 3 ; c
%bind 3 ; create local bindings
%pushl (0 . 0) ; local variable a
%pushl (0 . 1) ; local variable b
%pushl (0 . 2) ; local variable c
add 3 ; ac = a + b + c
%unbind ; remove local bindings
@end example
External variable:
@example
(define foo (let ((n 0)) (lambda () n)))
%pushi 0 ; n
%bind 1 ; create local bindings
%export [0] ; make it an external variable
%make-program #<bytecode xxx> ; create a program in this environment
%unbind ; remove local bindings
%savet (foo . #<undefined>) ; save the program in foo
(foo) ->
%loadt (foo . #<program xxx>) ; program has an external link
%call 0 ; change the current external link
%loade (0 . 0) ; external variable n
%return ; recover the external link
@end example
Top-level variable:
@example
foo ->
%loadt (foo . #<program xxx>) ; top-level variable foo
@end example
@section Flow control
@example
(if #t 1 0) ->
%loadi #t
%br-if-not L1
%loadi 1
%jump L2
L1: %loadi 0
L2:
@end example
@section Function call
Builtin function:
@example
(1+ 2) ->
%loadi 2 ; ac = 2
1+ ; one argument
(+ 1 2) ->
%pushi 1 ; 1 -> stack
%loadi 2 ; ac = 2
add2 ; two argument
(+ 1 2 3) ->
%pushi 1 ; 1 -> stack
%pushi 2 ; 2 -> stack
%pushi 3 ; 3 -> stack
add 3 ; many argument
@end example
External function:
@example
(version) ->
%func0 (version . #<primitive-procedure version>) ; no argument
(display "hello") ->
%loadi "hello"
%func1 (display . #<primitive-procedure display>) ; one argument
(open-file "file" "w") ->
%pushi "file"
%loadi "w"
%func2 (open-file . #<primitive-procedure open-file>) ; two arguments
(equal 1 2 3)
%pushi 1
%pushi 2
%pushi 3
%loadi 3 ; the number of arguments
%func (equal . #<primitive-procedure equal>) ; many arguments
@end example
@section Subprogram call
@node Instruction Set, , Program Execution, Top
@node Instruction Set, The Compiler, Variable Management, Top
@chapter Instruction Set
The Guile VM instruction set is roughly divided two groups: system
@ -843,6 +721,203 @@ Push @code{#t} onto the stack.
@item num-eq2
@end itemize
@node The Compiler, , Instruction Set, Top
@chapter The Compiler
This section describes Guile-VM's compiler and the compilation process
to produce bytecode executable by the VM itself (@pxref{Instruction
Set}).
@menu
* Overview::
* The Language Front-Ends::
* GHIL::
* GLIL::
* The Assembler::
@end menu
@node Overview, The Language Front-Ends, The Compiler, The Compiler
@section Overview
Compilation in Guile-VM is a three-stage process:
@enumerate
@item the source programming language (e.g. R5RS Scheme) is read and
translated into GHIL, @dfn{Guile's High-Level Intermediate Language};
@item GHIL code is then translated into a lower-level intermediate
language call GLIL, @dfn{Guile's Low-Level Intermediate Language};
@item finally, GLIL is @dfn{assembled} into the VM's assembly language
(@pxref{Instruction Set}) and bytecode.
@end enumerate
The use of two separate intermediate languages eases the
implementation of front-ends since the gap between high-level
languages like Scheme and GHIL is relatively small.
From an end-user viewpoint, compiling a Guile program into bytecode
can be done either by using the @command{guilec} command-line tool, or
by using the @code{compile-file} procedure exported by the
@code{(system base compile)} module.
@node The Language Front-Ends, GHIL, Overview, The Compiler
@section The Language Front-Ends
Guile-VM comes with a number of @dfn{language front-ends}, that is,
code that can read a given high-level programming language like R5RS
Scheme, and translate it into a lower-level representation suitable to
the compiler.
Each language front-end provides a @dfn{specification} and a
@dfn{translator} to GHIL. Both of them come in the @code{language}
module hierarchy. As an example, the front-end for Scheme is located
in the @code{(language scheme spec)} and @code{(language scheme
translate)} modules. Language front-ends can then be retrieved using
the @code{lookup-language} procedure of the @code{(system base
language)} module.
@deftp @scmrec{} <language> name title version reader printer read-file expander translator evaluator environment
Denotes a language front-end specification a various methods used by
the compiler to handle source written in that language. Of particular
interest is the @code{translator} slot (@pxref{GHIL}).
@end deftp
@deffn @scmproc{} lookup-language symbol
Look for a language front-end named @var{symbol} and return the
@code{<language>} record describing it if found. If @var{symbol}
doesn't denote a language front-end, an error is raised. Note that
this procedure assumes that language @var{symbol} exists if there
exist a @code{(language @var{symbol} spec)} module.
@end deffn
@node GHIL, GLIL, The Language Front-Ends, The Compiler
@section Guile's High-Level Intermediate Language
GHIL has constructs almost equivalent to those found in Scheme.
However, unlike Scheme, it is meant to be read only by the compiler
itself. Therefore, a sequence of GHIL code is only a sequence of GHIL
@emph{objects} (records), as opposed to symbols, each of which
represents a particular language feature. These records are all
defined in the @code{(system il ghil)} module and are named
@code{<ghil-*>}.
Each GHIL record has at least two fields: one containing the
environment (Guile module) in which it is considered, and one
containing its location [FIXME: currently seems to be unused]. Below
is a list of the main GHIL object types and their fields:
@example
;; Objects
(<ghil-void> env loc)
(<ghil-quote> env loc obj)
(<ghil-quasiquote> env loc exp)
(<ghil-unquote> env loc exp)
(<ghil-unquote-splicing> env loc exp)
;; Variables
(<ghil-ref> env loc var)
(<ghil-set> env loc var val)
(<ghil-define> env loc var val)
;; Controls
(<ghil-if> env loc test then else)
(<ghil-and> env loc exps)
(<ghil-or> env loc exps)
(<ghil-begin> env loc exps)
(<ghil-bind> env loc vars vals body)
(<ghil-lambda> env loc vars rest body)
(<ghil-call> env loc proc args)
(<ghil-inline> env loc inline args)
@end example
As can be seen from this examples, the constructs in GHIL are pretty
close to the fundamental primitives of Scheme.
It is the role of front-end language translators (@pxref{The Language
Front-Ends}) to produce a sequence of GHIL objects from the
human-readable, source programming language.
[FIXME: Describe more.]
@node GLIL, The Assembler, GHIL, The Compiler
@section Guile's Low-Level Intermediate Language
A GHIL instruction sequence can be compiled into GLIL using the
@code{compile} procedure exported by the @code{(system il compile)}
module. During this translation process, various optimizations may
also be performed.
The module @code{(system il glil)} defines record types representing
various low-level abstractions. Compared to GHIL, the flow control
primitives in GLIL are much more low-level: only @code{<glil-label>},
@code{<glil-branch>} and @code{<glil-call>} are available, no
@code{lambda}, @code{if}, etc.
@deffn @scmproc{} compile ghil environment . opts
Compile @var{ghil}, a GHIL instruction sequence, within
environment/module @var{environment}, and return the resulting GLIL
instruction sequence. The option list @var{opts} may be either the
empty list or a list containing the @code{:O} keyword in which case
@code{compile} will first go through an optimization stage of
@var{ghil}.
@end deffn
@deffn @scmproc{} pprint-glil glil . port
Print @var{glil}, a GLIL sequence instructions, in a human-readable
form. If @var{port} is passed, it will be used as the output port.
@end deffn
Let's consider the following Scheme expression:
@example
(lambda (x) (+ x 1))
@end example
The corresponding (unoptimized) GLIL code, as shown by
@code{pprint-glil}, looks like this:
@example
(@@asm (0 0 0 0)
(@@asm (1 0 0 0) ;; expect one arg.
(@@bind (x argument 0)) ;; debugging info
(module-ref #f +) ;; lookup `+'
(argument-ref 0) ;; push the argument onto
;; the stack
(const 1) ;; push `1'
(tail-call 2) ;; call `+', with 2 args,
;; using the same stack frame
(@@source 15 33)) ;; additional debugging info
(return 0))
@end example
This is not unlike the VM's assembly language described in
@ref{Instruction Set}.
@node The Assembler, , GLIL, The Compiler
@section The Assembler
The final compilation step consists in converting the GLIL instruction
sequence into VM bytecode. This is what the @code{assemble} procedure
defined in the @code{(system vm assemble)} module is for. It relies
on the @code{code->bytes} procedure of the @code{(system vm conv)}
module to convert instructions (represented as lists whose @code{car}
is a symbol naming the instruction, e.g. @code{object-ref},
@pxref{Instruction Set}) into binary code, or @dfn{bytecode}.
Bytecode itself is represented using SRFI-4 byte vectors,
@inforef{SRFI-4, SRFI-4 homogeneous numeric vectors, guile}.
@deffn @scmproc{} assemble glil environment . opts
Return a binary representation of @var{glil} (bytecode), either in the
form of an SRFI-4 @code{u8vector} or a @code{<bytespec>} object.
[FIXME: Why is that?]
@end deffn
@c *********************************************************************
@c @node Concept Index, Command Index, Related Information, Top
@c @unnumbered Concept Index

View file

@ -53,8 +53,9 @@
(($ <ghil-lambda> env vars rest body)
(<ghil-lambda> env vars rest (optimize body)))
(($ <ghil-inst> inst args)
(<ghil-inst> inst (map optimize args)))
;; FIXME: <ghil-inst> does not exist. -- Ludo'.
; (($ <ghil-inst> inst args)
; (<ghil-inst> inst (map optimize args)))
(($ <ghil-call> env proc args)
(match proc

View file

@ -58,6 +58,7 @@ vm_run (SCM vm, SCM program, SCM args)
struct scm_program *bp = NULL; /* program base pointer */
SCM external = SCM_EOL; /* external environment */
SCM *objects = NULL; /* constant objects */
scm_t_array_handle objects_handle; /* handle of the OBJECTS array */
size_t object_count; /* length of OBJECTS */
SCM *stack_base = vp->stack_base; /* stack base address */
SCM *stack_limit = vp->stack_limit; /* stack limit address */
@ -178,6 +179,9 @@ vm_run (SCM vm, SCM program, SCM args)
vm_error:
SYNC_ALL ();
if (objects)
scm_array_handle_release (&objects_handle);
vp->last_frame = vm_heapify_frames (vm);
scm_ithrow (sym_vm_error, SCM_LIST3 (sym_vm_run, err_msg, err_args), 1);
}

View file

@ -134,16 +134,20 @@
/* Get a local copy of the program's "object table" (i.e. the vector of
external bindings that are referenced by the program), initialized by
`load-program'. */
#define CACHE_PROGRAM() \
{ \
ssize_t _vincr; \
scm_t_array_handle _vhandle; \
\
bp = SCM_PROGRAM_DATA (program); \
/* Was: objects = SCM_VELTS (bp->objs); */ \
objects = scm_vector_elements (bp->objs, &_vhandle, \
&object_count, &_vincr); \
scm_array_handle_release (&_vhandle); \
/* XXX: We could instead use the "simple vector macros", thus not having to
call `scm_vector_writable_elements ()' and the likes. */
#define CACHE_PROGRAM() \
{ \
ssize_t _vincr; \
\
bp = SCM_PROGRAM_DATA (program); \
/* Was: objects = SCM_VELTS (bp->objs); */ \
\
if (objects) \
scm_array_handle_release (&objects_handle); \
\
objects = scm_vector_writable_elements (bp->objs, &objects_handle, \
&object_count, &_vincr); \
}
#define SYNC_BEFORE_GC() \

View file

@ -132,6 +132,12 @@ VM_DEFINE_LOADER (load_program, "load-program")
/* init object table */
if (scm_is_vector (x))
{
#if 0
if (scm_is_simple_vector (x))
printf ("is_simple_vector!\n");
else
printf ("NOT is_simple_vector\n");
#endif
p->objs = x;
POP (x);
}

View file

@ -208,7 +208,7 @@ VM_DEFINE_INSTRUCTION (list_break, "list-break", 0, 0, 0)
VM_DEFINE_INSTRUCTION (object_ref, "object-ref", 1, 0, 1)
{
register objnum = FETCH ();
register unsigned objnum = FETCH ();
CHECK_OBJECT (objnum);
PUSH (OBJECT_REF (objnum));
NEXT;