diff options
author | Ludovic Courtes <ludovic.courtes@laas.fr> | 2005-06-25 06:57:20 +0000 |
---|---|---|
committer | Ludovic Courtès <ludo@gnu.org> | 2008-04-25 19:09:30 +0200 |
commit | a52b2d3d5547564c10cfce9ae9993ee0a9ae698e (patch) | |
tree | 627a219a1f1d8d501113ec389a311bd67a47a053 | |
parent | 0b5f0e49a8b172544e2a7cb564c01c2ec439aaec (diff) | |
download | guile-a52b2d3d5547564c10cfce9ae9993ee0a9ae698e.tar.gz |
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
-rw-r--r-- | doc/guile-vm.texi | 371 | ||||
-rw-r--r-- | module/system/il/compile.scm | 5 | ||||
-rw-r--r-- | src/vm_engine.c | 4 | ||||
-rw-r--r-- | src/vm_engine.h | 24 | ||||
-rw-r--r-- | src/vm_loader.c | 6 | ||||
-rw-r--r-- | src/vm_system.c | 2 |
6 files changed, 251 insertions, 161 deletions
diff --git a/doc/guile-vm.texi b/doc/guile-vm.texi index 44213beb3..a0b7eacdd 100644 --- a/doc/guile-vm.texi +++ b/doc/guile-vm.texi @@ -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 diff --git a/module/system/il/compile.scm b/module/system/il/compile.scm index 4e6cb19a3..eab25d600 100644 --- a/module/system/il/compile.scm +++ b/module/system/il/compile.scm @@ -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 diff --git a/src/vm_engine.c b/src/vm_engine.c index 8c93c1de0..aa45971bf 100644 --- a/src/vm_engine.c +++ b/src/vm_engine.c @@ -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); } diff --git a/src/vm_engine.h b/src/vm_engine.h index ac12caabe..02b719375 100644 --- a/src/vm_engine.h +++ b/src/vm_engine.h @@ -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() \ diff --git a/src/vm_loader.c b/src/vm_loader.c index 59aae86a2..e8eaec122 100644 --- a/src/vm_loader.c +++ b/src/vm_loader.c @@ -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); } diff --git a/src/vm_system.c b/src/vm_system.c index 5eb125b88..e220b613c 100644 --- a/src/vm_system.c +++ b/src/vm_system.c @@ -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; |