From 5f0dbbf1d19b414e58fd112f764949de748c569d Mon Sep 17 00:00:00 2001 From: jjgarcia Date: Wed, 3 Oct 2001 16:30:15 +0000 Subject: [PATCH] - Remove function_entry_table. - The value of *package* is correctly set and restored while loading compiled code. This way, 'ecls -eval "(print *package*)"' produces the expected result #<"COMMON-LISP-USER" package> - COMPILE-FILE now outputs three values. - The value of si::*keep-definitions* determines whether the interpreter keeps the source of defined functions, for later use with COMPILE and DISASSEMBLE. For instance, > (set si::*keep-definitions* t) > (defun foo (x) (1+ x)) > (compile 'foo) > (foo 2) 3 > (compile 'foo) ;;; Error .... These definitions are lost once the function is compiled, hence the second error message. --- src/CHANGELOG | 28 +++ src/c/all_functions.d | 4 +- src/c/all_symbols.d | 1 + src/c/cfun.d | 139 ++----------- src/c/compiler.d | 4 + src/c/error.d | 45 ---- src/c/init.d | 3 - src/c/read.d | 6 +- src/cmp/cmpmain.lsp | 77 +++---- src/doc/head | 1 + src/doc/user.txi | 472 +++++++++++++++--------------------------- src/h/external.h | 7 - src/h/lisp_external.h | 5 +- 13 files changed, 252 insertions(+), 540 deletions(-) diff --git a/src/CHANGELOG b/src/CHANGELOG index 392769319..c163e4f5c 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -796,6 +796,34 @@ ECLS 0.4 - Implemented WITH-STANDARD-IO-SYNTAX. +ECLS 0.5 +======== + +* System design and portability: + + - Remove function_entry_table. + +* Visible changes and ANSI compatibility: + + - The value of *package* is correctly set and restored while loading + compiled code. This way, 'ecls -eval "(print *package*)"' produces + the expected result #<"COMMON-LISP-USER" package> + + - COMPILE-FILE now outputs three values. + + - The value of si::*keep-definitions* determines whether the + interpreter keeps the source of defined functions, for later use + with COMPILE and DISASSEMBLE. For instance, + > (set si::*keep-definitions* t) + > (defun foo (x) (1+ x)) + > (compile 'foo) + > (foo 2) + 3 + > (compile 'foo) + ;;; Error .... + These definitions are lost once the function is compiled, hence + the second error message. + TODO: ===== diff --git a/src/c/all_functions.d b/src/c/all_functions.d index b9a9468ca..2eeea5a84 100644 --- a/src/c/all_functions.d +++ b/src/c/all_functions.d @@ -92,6 +92,7 @@ const struct function_info all_functions[] = { {"COMPILED-FUNCTION-NAME", siLcompiled_function_name, si}, {"COMPILED-FUNCTION-BLOCK", siLcompiled_function_block, si}, + {"COMPILED-FUNCTION-SOURCE", siLcompiled_function_source, si}, /* character.d */ @@ -144,9 +145,6 @@ const struct function_info all_functions[] = { /* error.c */ -#if defined(FRAME_CHAIN) && !defined(RUNTIME) - {"BT", siLbacktrace, si}, -#endif {"ERROR", clLerror, cl}, {"CERROR", clLcerror, cl}, diff --git a/src/c/all_symbols.d b/src/c/all_symbols.d index c3c6f28c6..4861ce1bd 100644 --- a/src/c/all_symbols.d +++ b/src/c/all_symbols.d @@ -39,6 +39,7 @@ const struct symbol_info all_symbols[] = { /* compiler.c */ {&clSlambda_block, "LAMBDA-BLOCK", CL_ORDINARY}, +{&siVkeep_definitions, "*KEEP-DEFINITIONS*", SI_SPECIAL}, /* conditional.c */ {&clSotherwise, "OTHERWISE", CL_ORDINARY}, diff --git a/src/c/cfun.d b/src/c/cfun.d index ac7302359..1f8505ada 100644 --- a/src/c/cfun.d +++ b/src/c/cfun.d @@ -21,8 +21,6 @@ cl_object @'defun', @'defmacro'; #endif PDE -static void record_fun_entry (cl_object sym, void *addr); - cl_object make_cfun(cl_object (*self)(), cl_object name, cl_object cblock) { @@ -57,9 +55,6 @@ MF(cl_object sym, cl_object (*self)(), cl_object block) if (sym->symbol.isform && sym->symbol.mflag) sym->symbol.isform = FALSE; clear_compiler_properties(sym); -#ifndef RUNTIME - record_fun_entry(sym, self); -#endif #ifdef PDE record_source_pathname(sym, @'defun'); #endif PDE @@ -81,9 +76,6 @@ MM(cl_object sym, cl_object (*self)(), cl_object block) if (sym->symbol.isform && sym->symbol.mflag) sym->symbol.isform = FALSE; clear_compiler_properties(sym); -#ifndef RUNTIME - record_fun_entry(sym, self); -#endif #ifdef PDE record_source_pathname(sym, @'defmacro'); #endif PDE @@ -103,9 +95,6 @@ make_function(char *s, cl_object (*f)()) x = make_ordinary(s); SYM_FUN(x) = make_cfun(f, x, NULL); x->symbol.mflag = FALSE; -#ifndef RUNTIME - record_fun_entry(x, f); -#endif return(x); } @@ -117,9 +106,6 @@ make_si_function(char *s, cl_object (*f)()) x = make_si_ordinary(s); SYM_FUN(x) = make_cfun(f, x, NULL); x->symbol.mflag = FALSE; -#ifndef RUNTIME - record_fun_entry(x, f); -#endif return(x); } @@ -139,6 +125,27 @@ make_si_function(char *s, cl_object (*f)()) @(return output) @) +@(defun si::compiled_function_source (fun) + cl_object output; +@ + switch(type_of(fun)) { + case t_bytecodes: + if (!Null(fun->bytecodes.lex)) + output = Cnil; + else { + output = fun->bytecodes.data[fun->bytecodes.size-1]; + if (!CONSP(output)) output = Cnil; + } + break; + case t_cfun: + case t_cclosure: + output = Cnil; break; + default: + FEerror("~S is not a compiled-function.", 1, fun); + } + @(return output) +@) + @(defun si::compiled_function_block (fun) cl_object output; @ @@ -152,107 +159,3 @@ make_si_function(char *s, cl_object (*f)()) } @(return output) @) - - -#ifndef RUNTIME - -#define FUN_TABLE_INC 256 -void **function_entry_table; -int function_entries_max; -int function_entries; - -/*---------------------------------------------------------------------- - * fun_entry_search -- - * function_entry_table is an array containing alternated addr, sym values - * sorted in increasing addr value. - * Result: - * the index of the largest addr which is smaller than key - * -2 if no such addr is present - *---------------------------------------------------------------------- - */ -static int -fun_entry_search(char *key) -{ - void **table = function_entry_table; - int len = function_entries; - int low = 0; - int high = len; - int mid, probe; - char *entry; - if (len == 0) - return(-2); - while (TRUE) { - mid = (low + high) / 2; - probe = mid * 2; - entry = (char *)table[probe]; - if (entry == key) - return(probe); - if (entry < key) { - if (mid + 1 == len || (char*)table[probe+2] > key) - return(probe); - else - low = mid; - } else { - if (probe == 0) - return(-2); - else - high = mid; - } - } -} - -/* - *---------------------------------------------------------------------- - * record_fun_entry -- - * records the code start of function bound to symbol, so that - * one can determine which function is executing - * - *---------------------------------------------------------------------- - */ -static void -record_fun_entry(cl_object sym, void *addr) -{ - cl_object def; - register int i, end; - - end = 2*function_entries; - def = SYM_FUN(sym); - if (def != OBJNULL && type_of(def) == t_cfun) { - /* clear previous definition */ - void *prevaddr = (void *)def->cfun.entry; - i = fun_entry_search(prevaddr); - if (i >= 0 && function_entry_table[i] == prevaddr) { - function_entries--; - end -= 2; - memmove(&function_entry_table[i], &function_entry_table[i+2], - sizeof(void *) * (end - i)); - } - } - i = fun_entry_search(addr); - if (i < 0 || function_entry_table[i] != (char*)addr) { - if (2*function_entries_max == end) { - function_entries_max += FUN_TABLE_INC; - function_entry_table = realloc(function_entry_table, - 2 * function_entries_max * sizeof(void *)); - } - i += 2; - memmove(&function_entry_table[i+2], &function_entry_table[i], - sizeof(void *) * (end - i)); - function_entries++; - } - function_entry_table[i++] = (char *)addr; - function_entry_table[i++] = (char *)sym; -} - -cl_object -get_function_entry(void *addr) -{ - int i; - i = fun_entry_search(addr); - if (i >= 0) - return((cl_object)function_entry_table[i+1]); - else - return(OBJNULL); -} - -#endif RUNTIME diff --git a/src/c/compiler.d b/src/c/compiler.d index c0cb7d692..b13e5b500 100644 --- a/src/c/compiler.d +++ b/src/c/compiler.d @@ -24,6 +24,7 @@ cl_object @'defun'; cl_object @'compile', @'load', @'eval', @'progn', @'warn', @'typep', @'otherwise'; cl_object @':execute', @':compile-toplevel', @':load-toplevel'; cl_object @'si::*inhibit-macro-special*'; +cl_object @'si::*keep-definitions*'; cl_object @'&optional'; cl_object @'&rest'; @@ -2124,6 +2125,9 @@ make_lambda(cl_object name, cl_object lambda) { compile_body(body); asm_op(OP_HALT); + if (!Null(SYM_VAL(@'si::*keep-definitions*'))) + asm1(lambda); + c_env = old_c_env; return asm_end(handle, Cnil); diff --git a/src/c/error.d b/src/c/error.d index ea0605fcd..015c9dd8e 100644 --- a/src/c/error.d +++ b/src/c/error.d @@ -327,51 +327,6 @@ not_a_variable(cl_object obj) rest)); @) -#if defined(FRAME_CHAIN) && !defined(RUNTIME) -static char * -get_current_frame(void) -{ - char *frame; - GET_CURRENT_FRAME(frame); - return frame; -} - -@(defun si::backtrace () - char *this_frame, *next_frame, *next_pc; - bool first = TRUE; - cl_object sym; - jmp_buf buf; -@ - /* ensure flushing of register caches */ - if (ecls_setjmp(buf) == 0) ecls_longjmp(buf, 1); - - this_frame = get_current_frame(); - while (TRUE) { - next_frame = FRAME_CHAIN(this_frame); - next_pc = FRAME_SAVED_PC(this_frame); -#ifdef DOWN_STACK - if (next_frame == 0 || next_frame > (char *)cs_org) break; -#else - if (next_frame < (char *)cs_org) break; -#endif - sym = (cl_object)get_function_entry(next_pc); - if (sym) { - if (!first) - printf(" < "); - else - first = FALSE; - princ(sym, Cnil); - } -/* - else - printf("FP: 0x%x, PC: 0x%x\n", next_frame, next_pc); -*/ - this_frame = next_frame; - } - @(return) -@) -#endif - void init_error(void) { diff --git a/src/c/init.d b/src/c/init.d index 34c2b965b..537d22681 100644 --- a/src/c/init.d +++ b/src/c/init.d @@ -23,9 +23,6 @@ extern void init_CLOS(); void init_lisp(void) { -#ifndef RUNTIME - function_entry_table = (void *)malloc(2 * function_entries_max * sizeof(void *)); -#endif init_symbol(); init_package(); diff --git a/src/c/read.d b/src/c/read.d index 5f84b8f30..22e0a4452 100644 --- a/src/c/read.d +++ b/src/c/read.d @@ -2318,9 +2318,7 @@ read_VV(cl_object block, void *entry) entry_point_ptr entry_point = entry; cl_object *VV; int len; -#ifdef PDE bds_ptr old_bds_top = bds_top; -#endif if (block == NULL) block = alloc_object(t_codeblock); @@ -2341,7 +2339,7 @@ read_VV(cl_object block, void *entry) old_backq_level = backq_level; old_package = SYM_VAL(@'*package*'); - SYM_VAL(@'*package*') = lisp_package; + bds_bind(@'*package*', lisp_package); setup_standard_READ(); @@ -2377,9 +2375,7 @@ read_VV(cl_object block, void *entry) close_stream(in, 0); read_VV_block = OBJNULL; -#ifdef PDE bds_unwind(old_bds_top); -#endif READtable = old_READtable; READdefault_float_format = old_READdefault_float_format; diff --git a/src/cmp/cmpmain.lsp b/src/cmp/cmpmain.lsp index 16e3e251c..45d04a1de 100644 --- a/src/cmp/cmpmain.lsp +++ b/src/cmp/cmpmain.lsp @@ -172,7 +172,7 @@ main(int argc, char **argv) Cannot compile ~a." (namestring input-pathname)) (setq *error-p* t) - (return-from compile-file (values))) + (return-from compile-file (values nil t t))) (setq *error-p* nil *compiler-in-use* t) @@ -181,7 +181,7 @@ Cannot compile ~a." (format t "~&;;; The source file ~a is not found.~%" (namestring input-pathname)) (setq *error-p* t) - (return-from compile-file (values))) + (return-from compile-file (values nil t t))) (when *compile-verbose* (format t "~&;;; Compiling ~a." @@ -277,7 +277,7 @@ Cannot compile ~a." #+dlopen (if system-p o-pathname so-pathname) #-dlopen - o-pathname) + (values o-pathname nil nil)) (progn (when (probe-file c-pathname) (delete-file c-pathname)) @@ -286,7 +286,7 @@ Cannot compile ~a." (when (probe-file o-pathname) (delete-file o-pathname)) (format t "~&;;; No FASL generated.~%") (setq *error-p* t) - (values)) + (values nil t t)) )) ) @@ -327,29 +327,9 @@ Cannot compile ~a." `(defun ,name ,@(cdr def)) `(set 'GAZONK #',def)))) ((and (fboundp name) - (consp (setq def (symbol-function name)))) - (cond ((and (eq (car def) 'LAMBDA-BLOCK) - (consp (cdr def)) (consp (cddr def))) - (if (eq (cadr def) name) - (setq form `(defun ,name ,@(cddr def))) - (setq form `(defun ,name ,(caddr def) - (block ,(cadr def) ,@(cdddr def)))))) - ((eq (car def) 'LAMBDA) - (setq form `(defun ,name ,@(cdr def)))) - ((and (eq (car def) 'LAMBDA-CLOSURE) - (consp (cdr def)) (null (cadr def)) - (consp (cddr def)) (null (caddr def)) - (consp (cdddr def)) (null (cadddr def))) - (setq form `(defun ,name ,@(cddddr def)))) - ((and (eq (car def) 'LAMBDA-BLOCK-CLOSURE) - (consp (cdr def)) (null (cadr def)) - (consp (cddr def)) (null (caddr def)) - (consp (cdddr def)) (null (cadddr def)) - (consp (cddddr def))) - (setq form `(defun ,name - (block ,(car (cddddr def)) - ,@(cdr (cddddr def)))))) - (t (error "I cannot compile such ~Ss, sorry." (car def))))) + (setq def (symbol-function name)) + (setq form (si::compiled-function-source def))) + (setq form `(defun ,name ,@form))) (t (error "No lambda expression is assigned to the symbol ~s." name))) (dotimes (n 1000 @@ -420,28 +400,31 @@ Cannot compile ~a." &aux def disassembled-form (*compiler-in-use* *compiler-in-use*) (*print-pretty* nil)) - (when *compiler-in-use* - (format t "~&;;; The compiler was called recursively.~ - ~%Cannot disassemble ~a." thing) - (setq *error-p* t) - (return-from disassemble)) - (setq *error-p* nil - *compiler-in-use* t) - - (cond ((null thing)) - ((symbolp thing) - (setq def (symbol-function thing)) - (when (macro-function thing) - (setq def (cdr def))) - (if (and (consp def) - (eq (car def) 'LAMBDA-BLOCK) - (consp (cdr def))) - (setq disassembled-form `(defun ,thing ,@(cddr def))) - (error "The function object ~s cannot be disassembled." def))) - ((and (consp thing) (eq (car thing) 'LAMBDA)) - (setq disassembled-form `(defun gazonk ,@(cdr thing)))) + (cond ((null thing)) + ((symbolp thing) + (setq def (symbol-function thing)) + (when (macro-function thing) + (setq def (cdr def))) + (return-from disassemble (disassemble def))) + ((functionp thing) + (if (setq def (si::compiled-function-source thing)) + (setq disassembled-form + `(defun ,(or (si::compiled-function-name thing) + GAZONK) + ,@def)) + (error "The function definition for ~S was lost." thing))) + ((and (consp thing) (eq (car thing) 'LAMBDA)) + (setq disassembled-form `(defun gazonk ,@(cdr thing)))) (t (setq disassembled-form thing))) + (when *compiler-in-use* + (format t "~&;;; The compiler was called recursively.~ + ~%Cannot disassemble ~a." thing) + (setq *error-p* t) + (return-from disassemble)) + (setq *error-p* nil + *compiler-in-use* t) + (let* ((null-stream (make-broadcast-stream)) (*compiler-output1* null-stream) (*compiler-output2* (if h-file diff --git a/src/doc/head b/src/doc/head index 6ead8721f..8bff7f1da 100644 --- a/src/doc/head +++ b/src/doc/head @@ -25,6 +25,7 @@ User's guide Benchmarks Distribution + Mailing list Browse CVS diff --git a/src/doc/user.txi b/src/doc/user.txi index 598240cd0..4f647d53b 100644 --- a/src/doc/user.txi +++ b/src/doc/user.txi @@ -136,13 +136,8 @@ Copyright @copyright{} 2000, Juan Jose Garcia Ripoll @ecls{} is an implementation of @clisp{} originally designed for being @emph{embeddable} into C based applications. - This document describes the @ecls{} implementation and how it differs from -@bibcite{ANSI} and @bibcite{Steele:84}. In general, as work in @ecls{} is -completed section by section, we will drop compatibility with -@bibcite{Steele:84} and the corresponding chapter will be updated to document -@emph{only} the differences with @bibcite{ANSI}. - +@bibcite{ANSI} and @bibcite{Steele:84}. This manual also documents some implementation facilities, such as the multithread facility of @ecls{} or the foreign function interface. And finally this manual is the main source of information to understand @@ -1182,56 +1177,31 @@ There are no implementation-dependent features for structures. @node Functions, Unreadable data objects, Structures, Standards @section Functions -@table @code -@item (lambda @var{lambda-list} . body) -A lambda-expression with null lexical environment and with no implicit block -around it. This type of function typically appears when @code{`(lambda -@var{lambda-list} . body)} is evaluated. - -@item (lambda-block @var{block-name lambda-list} . body) -A lambda-expression with null lexical environment but with an implicit block -around it. This type of function typically appears when @code{(defun -@var{function-name lambda-list} . body)} is evaluated. In this case, -@var{block-name} is identical to @var{function-name} . - -@item (lambda-closure @var{env1 env2 env3 lambda-list} . body) - -A lambda-expression with lexical environments but with no implicit block around -it. This type of function typically appears when @code{#`(lambda -@var{lambda-list} . body)} (or, equivalently, @code{(function (lambda -@var{lambda-list} . body))}) is evaluated. @var{env1}, @var{env2}, and -@var{env3} represent the variable bindings, the local function/macro -definitions, and the tag/block-name establishments, respectively, at the time -the closure was created. - -@item (lambda-block-closure @var{env1 env2 env3 block-name lambda-list} . body) - -A lambda-expression with lexical environments and with an implicit block around -it. Local functions and local macros are represented in this -format. @var{env1}, @var{env2}, and @var{env3} represent the variable bindings, -the local function/macro bindings, and the tag/block-name establishments, -respectively, at the time the local function/macro was created by @code{flet}, -@code{labels}, or @code{macrolet}. The @var{block-name} is identical to the -local function/macro name. -@end table - -Compiled functions (including compiled macro-expansion functions) are printed -in the following formats. - +All functions in @ecls{} are either compiled into bytecodes to be interpreted, +or they are translated into C and then compiled using a native C compiler. +Interpreted functions are printed using the formats +@example +# +# +@end example +@noindent Compiled functions (including compiled macro-expansion functions) +are printed in the following formats. @example # -@end example -or -@example # @end example -Incidentally, the value of @code{(symbol-function @var{special-form-name})} is -a list, -@example -(special . @var{address}) -@end example -@noindent if @var{special-form-name} names a special form. +The output of @code{(symbol-function @var{fun})} is a list, is either a +function object if @code{'fun} is has a function definition, +@code{(macro . function-object)} if @code{'fun} is a macro, and @code{'special} +if @code{'fun} is a special form. + +@ecls{} usually drops the source code of a function unless the global +variable @var{si:*keep-definitions*} was true when the function was +translated into bytecodes. Therefore, if you wish to use +@code{#'compile} and @code{#'disassemble} on defined functions, you +should issue @code{(setq si:*keep-definitions* t)} at the beginning of +your session. @clisp{} constants related to functions have the following values in @ecls{}. @@ -2539,17 +2509,52 @@ If @var{package} is @nil{}, then all packages are searched. @node The interpreter, The compiler, Program development, Top @chapter The Interpreter +Former versions of @ecls{}, as well as many other lisps, used linked lists to +represent code. As of version 0.3 a bytecodes compiler and a bytecodes +interpreter were developed to circumvent the limitations of linked lists. + When you enter code at the lisp prompt, or when you load a source file, @ecls{} begins a process known as minimal compilation. Barely this process consists on parsing each form, macroexpanding it and translating it into an intermediate language made of @emph{bytecodes}. -@menu -* The bytecodes:: -@end menu +The bytecodes compiler is implemented in @file{src/c/compiler.d}. The main +entry point is the lisp function @code{SI::MAKE-LAMBDA}, which takes a +name for the function and the body of the lambda lists, and produces a +lisp object that can be invoked. For instance, -@node The bytecodes, , The interpreter, The interpreter -@section The intermediate language +@example +> (defvar fun (si::make-lambda 'f '((x) (1+ x)))) +*FUN* +> (funcall fun 2) +3 +@end example + +@ecls{} can only execute bytecodes. When a list is passed to @code{EVAL} it +must be first compiled to bytecodes and, if the process succeeds, then the +resulting bytecodes are passed to the interpreter. Similarly, every time a +function object is created, such as in @code{DEFUN} or @code{DEFMACRO}, the +bytecodes compiler processes the lambda form to produce a suitable bytecodes +object. + +The fact that @ecls{} performs this eager compilation means that changes on +a macro are not immediately seen in code which was already compiled. This has +subtle implications. Take the following code: +@example +> (defmacro f (a b) `(+ ,a ,b)) +F +> (defun g (x y) (f x y)) +G +> (g 1 2) +3 +> (defmacro f (a b) `(- ,a ,b)) +F +> (g 1 2) +3 +@end example + +@noindent The last statement always outputs @code{3} while in former +implementations based on processing of lambda lists it would produce @code{-1}. @node The compiler, Declarations, The interpreter, Top @@ -4331,106 +4336,85 @@ that case @code{suspend} will return the values specified by @code{resume}. * @ecls{} and @clisp{}:: * @ecls{} is written in C and Lisp:: * Porting @ecls{}:: +* Extending @ecls{}:: * The @ecls{} Compiler:: * The C language interface:: -* @ecls{} size:: -* Gabriel benchmark:: @end menu @node @ecls{} and @clisp{}, @ecls{} is written in C and Lisp, Implementation details, Implementation details @section @ecls{} is a full @clisp{} system. -@ecls{} is a full implementation of the @clisp{} language described in the book - -@display -@cltl{}. -by Guy L. Steele Jr. et al. -Digital Press, 1984 -@end display - -@noindent @ecls{} supports all @clisp{} functions, macros, and special forms defined in -it, and all @clisp{} variables and constants are defined in @ecls{} exactly as -described in the @cltl{}. +@ecls{} is an implementation of the @clisp{} which aims to comply with +the @ansi{} language described in the X3J13 specification. @ecls{} +supports most @clisp{} functions, macros, and special forms defined in +that reference, and all @clisp{} variables and constants should be in +@ecls{} exactly as described in the @ansi{}. Any deviation should be +noticed to the maintainers. @node @ecls{} is written in C and Lisp, Porting @ecls{}, @ecls{} and @clisp{}, Implementation details @section @ecls{} is written in C and Lisp -The kernel of @ecls{} is written in C, including: - +@noindent The kernel of @ecls{} is written in C, including: @itemize @item memory management and garbage collection -@item the evaluator (or interpreter) -@item @clisp{} special forms +@item the bytecodes compiler and interpreter @end itemize -The @ecls{} compiler is entirely written in @clisp{}. - -Each @clisp{} function or macro is written either in C or in Lisp. - -@example - in C: - 418 @clisp{} functions - 11 @clisp{} macros - - in Lisp: - 133 @clisp{} functions - 59 @clisp{} macros -@end example - -The size of the source code is: - -@example - C code 705 Kbytes - @clisp{} functions and macros written in Lisp - 173 Kbytes - The compiler 264 Kbytes - --------------------------- - total 1142 Kbytes -@end example - -Three routines in the kernel are partly written in assembly language: - +The kernel understands all special forms from @clisp{}, and it supplies +enough functions to create and manipulate all lisp objects. @ecls{} can +start up using just the C core, as it contains enough functions to +manipulate all lisp objects. However, in order to support the most of +the @clisp{} language, @ecls{} also needs lisp implementations of @itemize -@item bignum multiplication -@item bignum division -@item function call +@item macros, +@item CLOS, and +@item the translator to C. @end itemize - -@noindent The total size of assembly code is 20 to 30 lines, depending on the -version of @ecls{}. C version of these routines are however also available. +@noindent These items are stored as lisp source, that can be either interpreter +(as during the bootstrap process) or translated to C and later compiled +(to build the final program). -@node Porting @ecls{}, The @ecls{} Compiler, @ecls{} is written in C and Lisp, Implementation details +@node Porting @ecls{}, Extending @ecls{}, @ecls{} is written in C and Lisp, Implementation details @section Porting @ecls{} To port @ecls{} to a new architecture, the following steps are required: @enumerate -@item Compile all source C code with a GCC compiler. +@item Ensure that the GNU Multiprecision library supports this machine. -@item Compile the Lisp libraries and the Lisp compiler, -supplied already translated into C, except for one file in the compiler -which contains machine dependencies. +@item Ensure that the Boehm-Weiser garbage collector is supported by that +architecture. Alternatively, port ECLS's own garbage collector +@file{src/c/alloc.d} and @file{src/c/gbc.d} to that platform. -@item Link the binaries and create an executable. +@item Fix @file{src/configure.in} and @file{src/h/machines.h} so that they +both supply flags for the new host machine. -@item Use the executable to compile the machine dependent compiler file +@item Fix the machine dependent code in @file{src/c/}. The most critical +parts are in the @file{unix*.d} files. -@item Build the full image +@item Compile as in any other platform. + +@item Run the tests and compare to the results of other platforms. @end enumerate +@node Extending @ecls{}, The @ecls{} Compiler, Porting @ecls{}, Implementation details +@section Extending @ecls{} + +If you want to extend, fix or simply customize @ecls{} for your own needs, +you should understand how the implementation works. + @menu * Objects Representation:: -* The heap:: * @ecls{} stacks:: * Procedure Call Conventions:: -* The interpreter guts:: -* The Invocation History Stack:: +* The lexical environment:: +* The interpreter stack:: @end menu -@node Objects Representation, The heap, Porting @ecls{}, Porting @ecls{} +@node Objects Representation, @ecls{} stacks, Extending @ecls{}, Extending @ecls{} @subsection Objects Representation @ecls{} supports immediate data for representing fixnum's and @@ -4449,11 +4433,6 @@ Fixnum and characters are represented as follows: |------------|------|--| @end example -A third kind of immediate data is the @code{locative}, which constains -an indirect pointer to another object. @code{locative} are useful for -the efficient representation of logical variables when implementing -unifcation or Prolog. - Other @ecls{} objects are represented as (a pointer to) a cell that is allocated on the heap. Each cell consists of several words (1 word = 32 bit) whose first half word is in the format common to all data types: @@ -4482,76 +4461,7 @@ may be represented by its fixnum value, and a character object may be represented by its character code. -@node The heap, @ecls{} stacks, Objects Representation, Porting @ecls{} -@subsection The heap - -The whole heap of @ecls{} is divided into pages (1 page = 2048 -bytes). Each page falls in one of the following classes: - -@itemize -@item pages that contain @emph{cells} consisting of the same number of words -@item pages that contain @emph{blocks} of variable size -@item pages that contain @emph{relocatable blocks}: i.e. blocks of variable size -which can be moved in memory, such as array elements. -@end itemize - -Free cells (i.e., those cells that are not used any more) consisting -of the same number of words are linked together to form a free list. -When a new cell is requested, the first cell in the free list (if it -is not empty) is used and is removed from the list. If the free list -is empty, then the garbage collector is invoked to collect unused -cells. If the new free list is too short after the garbage -collection, then new pages are allocated dynamically. Free blocks are -also linked together in the order of the size so that, when a block is -being allocated on the heap, the smallest free area that is large -enough to hold the block datum will be used. Cell pages are never -compactified. Once a page is allocated for cells with @emph{n} words, -the page is used for cells with @emph{n} words only, even after all the -cells in the page become garbage. The same rule holds for block -pages. In contrast, relocatable pages are sometimes compactified. -That is, each relocatable datum may be moved to another place. - -The actual configuration of the @ecls{} heap is: - -@example -lower address higher address - +-----------------------------+----------+---------------------+ - | cell pages and block pages | hole | relocatable pages | - +-----------------------------+----------+---------------------+ -@end example - -There is a ``hole'' between the area for cell/block pages -and the area for relocatable pages. New pages are allocated in the -hole for cell/block pages, whereas new relocatable pages are -allocated by expanding the heap to the higher address, i.e., to the -right in this figure. When the hole becomes empty, the area for -relocatable pages are shifted to the right to reserve a certain number -of pages as the hole. During this process, the relocatable data in -the relocatable pages are compactified. No free list is maintained -for relocatable data. - -Symbol print names and string bodies are usually allocated -in relocatable pages. However, when the @ecls{} system is created, i.e., -when the object module of @ecls{} is created, such relocatable data are -moved towards the area for cell/block pages and then the pages for -relocatable data are marked ``static''. The garbage collector never -tries to sweep static pages. Thus, within the object module of @ecls{}, -the heap looks like: - -@example -lower address higher address - +--------------------------------------+ - | cell/block pages and static pages | - +--------------------------------------+ -@end example - -Notice that the hole is not included in the object module; -it is allocated only when the @ecls{} system is started. This saves -secondary storage a little bit. The maximum size of the hole is about -100 pages (= 200 Kbytes). - - -@node @ecls{} stacks, Procedure Call Conventions, The heap, Porting @ecls{} +@node @ecls{} stacks, Procedure Call Conventions, Objects Representation, Extending @ecls{} @subsection @ecls{} stacks @ecls{} uses the following stacks: @@ -4563,110 +4473,111 @@ consisting of catch, block, tagbody frames @item Bind Stack for shallow binding of dynamic variables -@item Invocation History Stack -maintaining information for debugging +@item Interpreter Stack +acts as a Forth data stack, keeping intermediate arguments to +interpreted functions, plus a history of called functions. @item C Control Stack -used for: -@itemize -@item arguments/values passing -@item typed lexical variables -@item temporary values -@item function invocation -@end itemize +used for arguments/values passing, typed lexical variables, +temporary values, and function invocation. @end table -@node Procedure Call Conventions, The interpreter guts, @ecls{} stacks, Porting @ecls{} +@node Procedure Call Conventions, The lexical environment, @ecls{} stacks, Extending @ecls{} @subsection Procedure Call Conventions @ecls{} employs standard C calling conventions to achieve efficiency and interoperability with other languages. Each Lisp function is implemented as a C function whcih takes as many argument as the Lisp original plus one additional integer argument -which holds the number of actual arguments. The function returns an -integer which is the number of multiple Lisp values produced. The actual vales -themselves are placed in a global (per thread) array (@code{VALUES}). +which holds the number of actual arguments. The function sets @code{NValues} +to the number of Lisp values produced, it returns the first one and the +remaining ones are kept in a global (per thread) array (@code{VALUES}). To show the argument/value passing mechanism, here we list the actual code for the @clisp{} function @code{cons}. @example -Lcons(int narg, object car, object cdr) +clLcons(int narg, object car, object cdr) @{ object x; check_arg(2); x = alloc_object(t_cons); CAR(x) = car; CDR(x) = cdr; - VALUES(0) = x; - RETURN(1); + NValues = 1; + return x; @} @end example -@ecls{} adopts the convention that the name of a function that -implements a @clisp{} function begins with @code{L}, followed by the -name of the @clisp{} function. (Strictly speaking, `@code{-}' and -`@code{*}' in the @clisp{} function name are replaced by `@code{_}' and -`@code{A}', respectively, to obey the syntax of C.) +@ecls{} adopts the convention that the name of a function that implements a +@clisp{} function begins with a short package name (@code{cl} for COMMON-LISP, +@code{si} for SYSTEM, etc), followed by @code{L}, and followed by the name of +the @clisp{} function. (Strictly speaking, `@code{-}' and `@code{*}' in the +@clisp{} function name are replaced by `@code{_}' and `@code{A}', respectively, +to obey the syntax of C.) -@code{check_arg(2)} in the code of @code{Lcons} checks that exactly two +@code{check_arg(2)} in the code of @code{clLcons} checks that exactly two arguments are supplied to @code{cons}. That is, it checks that @code{narg} is 2, and otherwise, it causes an error. @code{allocate_object(t_cons)} allocates a cons cell in the heap and returns the pointer to the cell. After the @code{CAR} and the @code{CDR} fields of the cell are set, the cell pointer is -put in the @code{VALUES} array. The integer returned by the function (1 in this -case) represents the number of values of the function. +returned directly. The number assigned to NValues set by the function (1 in +this case) represents the number of values of the function. +In general, if one is to play with the C kernel of @ecls{} there is no need to +know about all these conventions. There is a preprocessor that takes care of +the details, by using a lispy representation of the statements that output +values, and of the function definitions. For instance, the actual source code +for @code{clLcons} in @file{src/c/lists.d} -@node The interpreter guts, The Invocation History Stack, Procedure Call Conventions, Porting @ecls{} -@subsection The interpreter +@example +@@(defun cons (car cdr) +@@ + @@(return CONS(car, cdr)) +@@) +@end example -The @ecls{} interpreter uses three A-lists (Association lists) to +@node The lexical environment, The interpreter stack, Procedure Call Conventions, Extending @ecls{} +@subsection The lexical environment + +The @ecls{} interpreter uses two A-lists (Association lists) to represent lexical environments. @itemize @item One for variable bindings -@item One for local function/macro definitions -@item One for tag/block bindings +@item One for local function/macro/tag/block bindings @end itemize -When a function closure is created, the current three A-lists are +When a function closure is created, the current two A-lists are saved in the closure along with the lambda expression. Later, when the closure is invoked, the saved A-lists are used to recover the lexical environment. -@node The Invocation History Stack, , The interpreter guts, Porting @ecls{} -@subsection The Invocation History Stack +@node The interpreter stack, , The lexical environment, Extending @ecls{} +@subsection The interpreter stack -The invocation history stack consists of two kinds of elements. Each element -may be either a pair of a Lisp form and a pointer to lexical environment: +The bytecodes interpreter uses a stack of its own to save and restore values +from intermediate calculations. This Forth-like data stack is also used in +other parts of the C kernel for various purposes, such as saving compiled code, +keeping arguments to FORMAT, etc. +However, one of the most important roles of the Interpreter Stack is to keep a +log of the functions which are called during the execution of bytecodes. For +each function invoked, the interpreter keeps three lisp objects on the stack: @example -+----------+------------------------+ -| form | environment-pointer | -+----------+------------------------+ ++----------+------------------------------------------------+ +| function | lexical environment | index to previous record | ++----------+---------------------+--------------------------+ @end example -@noindent or a pair of a function name and a pointer to the value stack: +The first item is the object which is funcalled. It can be a bytecodes object, +a compiled function or a generic function. In the last two cases the lexical +environment is just NIL. In the first case, the second item on the stack is +the lexical environment on which the code is executed. Each of these records +are popped out of the stack after function invocation. -@example -+-----------------+-----------------+ -| function-name | stack-pointer | -+-----------------+-----------------+ -@end example - -The former is pushed on the invocation history stack when an -interpreted code is evaluated. The @emph{form} is the interpreted code -itself and the @emph{environment-pointer} points to a three elements -array which holds the three elements that represent the lexical -environment. The latter is pushed when a compiled function is -invoked. The @emph{function-name} is the name of the called function -and the @emph{stack-pointer} points to the position of the first -argument to the function. For both kinds, the element on the -invocation history stack is popped at the end of the evaluation. - -Let us see how the invocation history stack is used for debugging. +Let us see how these invocation records are used for debugging. @example >(defun fact (x) ;;; Wrong definition of the @@ -4740,7 +4651,7 @@ Block names: FACT. ;;; The block FACT is established. @end example -@node The @ecls{} Compiler, The C language interface, Porting @ecls{}, Implementation details +@node The @ecls{} Compiler, The C language interface, Extending @ecls{}, Implementation details @section The @ecls{} Compiler The @ecls{} compiler is essentially a translator from @clisp{} to C. Given @@ -4826,8 +4737,8 @@ init_code(char *start,int size,object data) static L1(int narg, object V1) @{ check_arg(1); - VALUES(0)=one_plus(V1); - RETURN(1); + NValues=1; + return one_plus(V1); @} @end example @@ -5003,7 +4914,7 @@ TTL: @end example -@node The C language interface, @ecls{} size, The @ecls{} Compiler, Implementation details +@node The C language interface, , The @ecls{} Compiler, Implementation details @section The C language interface There are several mechanism to integrate C code within @ecls{}. @@ -5030,63 +4941,6 @@ of the specified C function from @ecls{}. (defentry tak (int int int) (int "tak")) @end lisp -@node @ecls{} size, Gabriel benchmark, The C language interface, Implementation details -@section @ecls{} size - -The size of the object module of the whole @ecls{} system (including the -Compiler) is -@example - @ecls{}/SUN 2.04 Mbytes -@end example - -Since all system initialization (such as loading the database of the @ecls{} -compiler) has been done when the object module is created, the object module -size roughly corresponds to the initial size of the @ecls{} process when a -@ecls{} session is started, minus the initial size of the hole in the heap -(about 200 Kbytes). - - -@node Gabriel benchmark, , @ecls{} size, Implementation details -@section Gabriel benchmark - -The following table shows the results of Richard Gabriel's Lisp benchmark tests -in @ecls{}. The results with five other public domain @clisp{} implementation -are also listed for comparison. Each number represents the CPU time (in -seconds) for the compiled program. The code for the benchmark is taken from: - -@display -@emph{Performance and Evaluation of Lisp Systems} -by Richard P. Gabriel -Computer Systems Ser. Research Reports -MIT Press, 1985 -@end display - -For the details of the benchmark tests, refer to the book above. - -@multitable {Benchmark} {Sun ELC} -@item Benchmark @tab Sun ELC -@item Test @tab -@item Boyer @tab 2.067 -@item Browse @tab 3.750 -@item Ctak @tab 0.967 -@item Dderiv @tab 1.717 -@item Deriv @tab 1.200 -@item Destru @tab 0.350 -@item Div2 @tab 2.467 -@item Fft @tab 2.317 -@item Fprint @tab 0.167 -@item Fread @tab 0.133 -@item Puzzle @tab 1.833 -@item Stak @tab 0.000 -@item Tak @tab 0.200 -@item Takl @tab 0.267 -@item Takr @tab 0.233 -@item Tprint @tab 0.117 -@item Traverse @tab 6.783 -@item Triang @tab 10.067 -@end multitable - - @node Everything, Bibliography, Implementation details, Top @chapter Everything diff --git a/src/h/external.h b/src/h/external.h index a42c97fbc..2daa94d48 100644 --- a/src/h/external.h +++ b/src/h/external.h @@ -132,19 +132,12 @@ extern void init_catch(void); /* cfun.c */ -#ifndef RUNTIME -#define FUN_TABLE_SIZE 256 -extern void **function_entry_table; -extern int function_entries_max; -extern int function_entries; -#endif extern cl_object make_cfun(cl_object (*self)(), cl_object name, cl_object block); extern cl_object make_cclosure(cl_object (*self)(), cl_object env, cl_object block); extern void MF(cl_object sym, cl_object (*self)(), cl_object block); extern void MM(cl_object sym, cl_object (*self)(), cl_object block); extern cl_object make_function(char *s, cl_object (*f)()); extern cl_object make_si_function(char *s, cl_object (*f)()); -extern cl_object get_function_entry(void *); extern void init_cfun(void); diff --git a/src/h/lisp_external.h b/src/h/lisp_external.h index 80d47f7dd..798ae23e0 100644 --- a/src/h/lisp_external.h +++ b/src/h/lisp_external.h @@ -87,6 +87,7 @@ extern cl_object siSdefmacro; #endif extern cl_object siLcompiled_function_name _ARGS((int narg, cl_object fun)); extern cl_object siLcompiled_function_block _ARGS((int narg, cl_object fun)); +extern cl_object siLcompiled_function_source _ARGS((int narg, cl_object fun)); /* character.c */ @@ -153,6 +154,7 @@ extern cl_object Kexecute; extern cl_object Kcompile_toplevel; extern cl_object Kload_toplevel; extern cl_object clSotherwise; +extern cl_object siVkeep_definitions; extern cl_object siLprocess_declarations _ARGS((int narg, cl_object body, ...)); extern cl_object siLprocess_lambda_list _ARGS((int narg, cl_object lambda)); extern cl_object siLmake_lambda _ARGS((int narg, cl_object name, cl_object body)); @@ -183,9 +185,6 @@ extern cl_object Kformat_control, Kformat_arguments; extern cl_object siSuniversal_error_handler; extern cl_object siSterminal_interrupt; -#if defined(FRAME_CHAIN) && !defined(RUNTIME) -extern cl_object siLbacktrace _ARGS((int narg)); -#endif extern cl_object clLerror _ARGS((int narg, cl_object eformat, ...)); extern cl_object clLcerror _ARGS((int narg, cl_object cformat, cl_object eformat, ...));