- 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.
This commit is contained in:
jjgarcia 2001-10-03 16:30:15 +00:00
parent adb9011d60
commit 5f0dbbf1d1
13 changed files with 252 additions and 540 deletions

View file

@ -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:
=====

View file

@ -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},

View file

@ -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},

View file

@ -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

View file

@ -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);

View file

@ -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)
{

View file

@ -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();

View file

@ -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;

View file

@ -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

View file

@ -25,6 +25,7 @@
<tr><td><a href="ecls.html"><b>User's guide</b></a></td></tr>
<tr><td><a href="benchmark.html"><b>Benchmarks</b></a></td></tr>
<tr><td><a href="download.html"><b>Distribution</b></a></td></tr>
<tr><td><a href="download.html"><b>Mailing list</b></a></td></tr>
<tr><td><a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/ecls/ecls"><b>Browse CVS</b></a></td></tr>
</table>
<td width="7"></td>

View file

@ -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
#<interpreted-function @var{name}>
#<interpreted-function @code{address}>
@end example
@noindent Compiled functions (including compiled macro-expansion functions)
are printed in the following formats.
@example
#<compiled-function @var{name}>
@end example
or
@example
#<compiled-closure nil>
@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

View file

@ -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);

View file

@ -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, ...));