Commit d595f98a authored by Daniel Kochmański's avatar Daniel Kochmański
Browse files

Merge branch 'fix-429-compile-closure' into 'develop'

Fix #429

Closes #429

See merge request embeddable-common-lisp/ecl!110
parents 4a20aa14 b56c9c12
......@@ -45,9 +45,11 @@
(cond ((functionp definition)
(multiple-value-bind (form lexenv) (function-lambda-expression definition)
(when form
(if lexenv
(setf definition (si:eval-with-env form lexenv))
(setf definition (si:eval-with-env form nil nil nil t)))))
(cond ((eq lexenv t)
(warn "COMPILE can not compile C closures")
(return-from bc-compile (values definition t nil)))
(lexenv (setf definition (si:eval-with-env form lexenv)))
(t (setf definition (si:eval-with-env form nil nil nil t))))))
(when name (setf (fdefinition name) definition))
(return-from bc-compile (values (or name definition) nil nil)))
((not (null definition))
......@@ -66,9 +68,11 @@
(multiple-value-bind (form lexenv)
(function-lambda-expression (fdefinition name))
(when form
(if lexenv
(setf definition (si:eval-with-env form lexenv))
(setf definition (si:eval-with-env form nil nil nil t)))))
(cond ((eq lexenv t)
(warn "The bytecodes compiler can not compile C closures")
(return-from bc-compile (values definition t nil)))
(lexenv (setf definition (si:eval-with-env form lexenv)))
(t (setf definition (si:eval-with-env form nil nil nil t))))))
(when (null definition)
(warn "We have lost the original function definition for ~s." name)
(return-from bc-compile (values name t nil)))
......
......@@ -114,7 +114,7 @@ ecl_rem_setf_definition(cl_object sym)
int type;
@
if (Null(cl_functionp(def)))
FEinvalid_function(def);
FEinvalid_function(def);
pack = ecl_symbol_package(sym);
if (pack != ECL_NIL
&& pack->pack.locked
......
......@@ -371,7 +371,7 @@ asm_op2c(cl_env_ptr env, int code, cl_object o) {
* SI:UNWIND-PROTECT-BOUNDARY
* (:declare declaration-arguments*)
* macro-record = (function-name FUNCTION [| function-object]) |
* (macro-name si::macro macro-function)
* (macro-name si::macro macro-function) |
* SI:FUNCTION-BOUNDARY |
* SI:UNWIND-PROTECT-BOUNDARY
*
......@@ -453,18 +453,15 @@ static void
c_register_symbol_macro(cl_env_ptr env, cl_object name, cl_object exp_fun)
{
const cl_compiler_ptr c_env = env->c_env;
c_env->variables = CONS(cl_list(3, name, @'si::symbol-macro', exp_fun),
c_env->variables);
c_env->variables = CONS(cl_list(3, name, @'si::symbol-macro', exp_fun), c_env->variables);
}
/* UNUSED
static void
c_register_macro(cl_env_ptr env, cl_object name, cl_object exp_fun)
{
const cl_compiler_ptr c_env = env->c_env;
c_env->macros = CONS(cl_list(3, name, @'si::macro', exp_fun), c_env->macros);
}
*/
static void
c_register_macro(cl_env_ptr env, cl_object name, cl_object exp_fun)
{
const cl_compiler_ptr c_env = env->c_env;
c_env->macros = CONS(cl_list(3, name, @'si::macro', exp_fun), c_env->macros);
}
static void
c_register_var(cl_env_ptr env, cl_object var, bool special, bool bound)
......@@ -486,7 +483,7 @@ c_register_boundary(cl_env_ptr env, cl_object type)
}
static void
guess_environment(cl_env_ptr env, cl_object interpreter_env)
guess_compiler_environment(cl_env_ptr env, cl_object interpreter_env)
{
if (!LISTP(interpreter_env))
return;
......@@ -501,14 +498,23 @@ guess_environment(cl_env_ptr env, cl_object interpreter_env)
{
cl_object record = ECL_CONS_CAR(interpreter_env);
if (!LISTP(record)) {
c_register_function(env, record);
if (ecl_t_of(record) == t_bclosure)
record = record->bclosure.code;
c_register_function(env, record->bytecodes.name);
} else {
cl_object record0 = ECL_CONS_CAR(record);
cl_object record1 = ECL_CONS_CDR(record);
if (ECL_SYMBOLP(record0)) {
c_register_var(env, record0, FALSE, TRUE);
if (record0 == @'si::macro')
c_register_macro(env, ECL_CONS_CDR(record1), ECL_CONS_CAR(record1));
else if (record0 == @'si::symbol-macro')
c_register_symbol_macro(env, ECL_CONS_CDR(record1), ECL_CONS_CAR(record1));
else
c_register_var(env, record0, FALSE, TRUE);
} else if (record1 == ecl_make_fixnum(0)) {
c_register_tags(env, ECL_NIL);
/* We have lost the information, which tag corresponds to
the lex-env record. If we are compiling a closure over a
tag, we will get an error later on. */
} else {
c_register_block(env, record1);
}
......@@ -931,11 +937,11 @@ c_call(cl_env_ptr env, cl_object args, int flags) {
flags = FLAG_VALUES;
} else if (ECL_SYMBOLP(name) &&
((flags & FLAG_GLOBAL) || Null(c_tag_ref(env, name, @':function'))))
{
asm_op2(env, OP_CALLG, nargs);
asm_c(env, name);
flags = FLAG_VALUES;
} else {
{
asm_op2(env, OP_CALLG, nargs);
asm_c(env, name);
flags = FLAG_VALUES;
} else {
/* Fixme!! We can optimize the case of global functions! */
asm_function(env, name, (flags & FLAG_GLOBAL) | FLAG_REG0);
asm_op2(env, OP_CALL, nargs);
......@@ -1384,6 +1390,30 @@ c_function(cl_env_ptr env, cl_object args, int flags) {
return asm_function(env, function, flags);
}
static cl_object
create_macro_lexenv(cl_compiler_ptr c_env)
{
/* Creates a new lexenv out of the macros in the current compiler
* environment */
cl_object lexenv = ECL_NIL;
cl_object records;
for (records = c_env->macros; !Null(records); records = ECL_CONS_CDR(records)) {
cl_object record = ECL_CONS_CAR(records);
if (ECL_ATOM(record))
continue;
if (CADR(record) == @'si::macro')
lexenv = CONS(CONS(@'si::macro', CONS(CADDR(record), CAR(record))), lexenv);
}
for (records = c_env->variables; !Null(records); records = ECL_CONS_CDR(records)) {
cl_object record = ECL_CONS_CAR(records);
if (ECL_ATOM(record))
continue;
if (CADR(record) == @'si::symbol-macro')
lexenv = CONS(CONS(@'si::symbol-macro', CONS(CADDR(record), CAR(record))), lexenv);
}
return lexenv;
}
static int /* XXX: here we look for function in cmpenv */
asm_function(cl_env_ptr env, cl_object function, int flags) {
if (!Null(si_valid_function_name_p(function))) {
......@@ -1412,10 +1442,26 @@ asm_function(cl_env_ptr env, cl_object function, int flags) {
}
const cl_compiler_ptr c_env = env->c_env;
asm_op2c(env,
(Null(c_env->variables) && Null(c_env->macros)) ? OP_QUOTE : OP_CLOSE,
ecl_make_lambda(env, name, body));
cl_object lambda = ecl_make_lambda(env, name, body);
cl_object macro_lexenv = create_macro_lexenv(c_env);
if (Null(macro_lexenv)) {
if (Null(c_env->variables)) {
/* No closure */
asm_op2c(env, OP_QUOTE, lambda);
} else {
/* Close only around functions and variables */
asm_op2c(env, OP_CLOSE, lambda);
}
} else {
lambda = ecl_close_around(lambda, macro_lexenv);
if (Null(c_env->variables)) {
/* Close only around macros */
asm_op2c(env, OP_QUOTE, lambda);
} else {
/* Close around macros, functions and variables */
asm_op2c(env, OP_CLOSE, lambda);
}
}
return FLAG_REG0;
}
ERROR:
......@@ -2430,7 +2476,8 @@ compile_with_load_time_forms(cl_env_ptr env, cl_object form, int flags)
*/
if (c_env->load_time_forms != ECL_NIL) {
cl_index *bytecodes = save_bytecodes(env, handle, current_pc(env));
/* Make sure the forms are compiled in the right order */
/* reverse the load time forms list to make sure the forms are
* compiled in the right order */
cl_object p, forms_list = cl_nreverse(c_env->load_time_forms);
c_env->load_time_forms = ECL_NIL;
p = forms_list;
......@@ -3101,14 +3148,17 @@ si_make_lambda(cl_object name, cl_object rest)
}
old_c_env = the_env->c_env;
c_new_env(the_env, &new_c_env, compiler_env, 0);
guess_environment(the_env, interpreter_env);
new_c_env.lex_env = env;
guess_compiler_environment(the_env, interpreter_env);
if (compiler_env_p == ECL_NIL) {
new_c_env.lex_env = env;
} else {
new_c_env.lex_env = ECL_NIL;
}
new_c_env.stepping = stepping != ECL_NIL;
ECL_UNWIND_PROTECT_BEGIN(the_env) {
if (Null(execute)) {
cl_index handle = asm_begin(the_env);
new_c_env.mode = FLAG_LOAD;
/*cl_print(1,form);*/
compile_with_load_time_forms(the_env, form, FLAG_VALUES);
asm_op(the_env, OP_EXIT);
the_env->values[0] = asm_end(the_env, handle, form);
......
......@@ -429,7 +429,7 @@ disassemble(cl_object bytecodes, cl_opcode *vector) {
Returns from the block whose record in the lexical environment
occuppies the n-th position.
*/
case OP_RETURN: string = "RETFROM";
case OP_RETURN: string = "RETFROM\t";
GET_OPARG(n, vector);
goto OPARG;
......
......@@ -164,6 +164,20 @@ ecl_stack_frame_close(cl_object f)
}
/* ------------------------------ LEXICAL ENV. ------------------------------ */
/*
* A lexical environment is a list of pairs, each one containing
* either a variable definition, a tagbody or block tag, or a local
* function or macro definition.
*
* lex_env ---> ( { record }* )
* record = variable | function | block_tag | tagbody_tag | macro
*
* variable = ( var_name[symbol] . value )
* function = function[bytecodes]
* block_tag = ( tag[fixnum] . block_name[symbol] )
* tagbody_tag = ( tag[fixnum] . 0 )
* macro = ( { si::macro | si::symbol-macro } macro_function[bytecodes] . macro_name )
*/
#define bind_var(env, var, val) CONS(CONS(var, val), (env))
#define bind_function(env, name, fun) CONS(fun, (env))
......@@ -206,16 +220,28 @@ _ecl_bclosure_dispatch_vararg(cl_narg narg, ...)
return output;
}
static cl_object
close_around(cl_object fun, cl_object lex) {
cl_object
ecl_close_around(cl_object fun, cl_object lex) {
cl_object v;
if (Null(lex)) return fun;
if (ecl_t_of(fun) != t_bytecodes)
FEerror("Internal error: close_around should be called on t_bytecodes.", 0);
v = ecl_alloc_object(t_bclosure);
v->bclosure.code = fun;
v->bclosure.lex = lex;
v->bclosure.entry = _ecl_bclosure_dispatch_vararg;
switch (ecl_t_of(fun)) {
case t_bytecodes:
v = ecl_alloc_object(t_bclosure);
v->bclosure.code = fun;
v->bclosure.lex = lex;
v->bclosure.entry = _ecl_bclosure_dispatch_vararg;
break;
case t_bclosure:
v = ecl_alloc_object(t_bclosure);
v->bclosure.code = fun->bclosure.code;
/* Put the predefined macros in fun->bclosure.lex at the end of
the lexenv so that lexenv indices are still valid */
v->bclosure.lex = ecl_append(lex, fun->bclosure.lex);
v->bclosure.entry = fun->bclosure.entry;
break;
default:
FEerror("Internal error: ecl_close_around should be called on t_bytecodes or t_bclosure.", 0);
}
return v;
}
......@@ -671,7 +697,7 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes)
do {
cl_object f;
GET_DATA(f, vector, data);
f = close_around(f, old_lex);
f = ecl_close_around(f, old_lex);
lex_env = bind_function(lex_env, f->bytecodes.name, f);
} while (--nfun);
THREAD_NEXT;
......@@ -702,7 +728,7 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes)
{
cl_object l = lex_env;
do {
ECL_RPLACA(l, close_around(ECL_CONS_CAR(l), lex_env));
ECL_RPLACA(l, ecl_close_around(ECL_CONS_CAR(l), lex_env));
l = ECL_CONS_CDR(l);
} while (--nfun);
}
......@@ -730,14 +756,13 @@ ecl_interpret(cl_object frame, cl_object env, cl_object bytecodes)
THREAD_NEXT;
}
/* OP_CLOSE name{symbol}
Extracts the function associated to a symbol. The function
may be defined in the global environment or in the local
environment. This last value takes precedence.
/* OP_CLOSE name{symbol}
Creates a closure around the current lexical environment for
the function associated to the given symbol.
*/
CASE(OP_CLOSE); {
GET_DATA(reg0, vector, data);
reg0 = close_around(reg0, lex_env);
reg0 = ecl_close_around(reg0, lex_env);
THREAD_NEXT;
}
/* OP_GO n{arg}, tag-ndx{arg}
......
......@@ -251,6 +251,7 @@
;; explicitely the bytecodes compiler with an environment, no
;; stepping, compiler-env-p = t and execute = nil, so that the
;; form does not get executed.
;; FIXME: Why is execute t then?
(si::eval-with-env method-lambda env nil t t)))
(values call-next-method-p
next-method-p-p
......
......@@ -25,6 +25,54 @@ that are susceptible to be changed by PROCLAIM."
(defun cmp-env-copy (&optional (env *cmp-env*))
(cons (car env) (cdr env)))
(defun set-closure-env (definition lexenv &optional (env *cmp-env*))
"Set up an environment for compilation of closures: Register closed
over macros in the compiler environment and enclose the definition of
the closure in let/flet forms for variables/functions it closes over."
(loop for record in lexenv
do (cond ((not (listp record))
(multiple-value-bind (record-def record-lexenv)
(function-lambda-expression record)
(cond ((eql (car record-def) 'LAMBDA)
(setf record-def (cdr record-def)))
((eql (car record-def) 'EXT:LAMBDA-BLOCK)
(setf record-def (cddr record-def)))
(t
(error "~&;;; Error: Not a valid lambda expression: ~s." record-def)))
;; allow for closures which close over closures.
;; (first record-def) is the lambda list, (rest
;; record-def) the definition of the local function
;; in record
(setf (rest record-def)
(list (set-closure-env (if (= (length record-def) 2)
(second record-def)
`(progn ,@(rest record-def)))
record-lexenv env)))
(setf definition
`(flet ((,(compiled-function-name record)
,@record-def))
,definition))))
((and (listp record) (symbolp (car record)))
(cond ((eq (car record) 'si::macro)
(cmp-env-register-macro (cddr record) (cadr record) env))
((eq (car record) 'si::symbol-macro)
(cmp-env-register-symbol-macro-function (cddr record) (cadr record) env))
(t
(setf definition
`(let ((,(car record) ',(cdr record)))
,definition)))
))
;; ((and (integerp (cdr record)) (= (cdr record) 0))
;; Tags: We have lost the information, which tag
;; corresponds to the lex-env record. If we are
;; compiling a closure over a tag, we will get an
;; error later on.
;; )
;; (t
;; Blocks: Not yet implemented
)
finally (return definition)))
(defmacro cmp-env-variables (&optional (env '*cmp-env*))
`(car ,env))
......@@ -102,6 +150,11 @@ that are susceptible to be changed by PROCLAIM."
(cmp-env-variables env))
env)
(defun cmp-env-register-symbol-macro-function (name function &optional (env *cmp-env*))
(push (list name 'si::symbol-macro function)
(cmp-env-variables env))
env)
(defun cmp-env-register-block (blk &optional (env *cmp-env*))
(push (list :block (blk-name blk) blk)
(cmp-env-variables env))
......
......@@ -45,8 +45,8 @@
(cmp-env-register-function fun new-env)
(push (cons fun (cdr def)) defs)))
;; Now we compile the functions, either in an empty environment
;; in which there are no new functions
;; Now we compile the functions, either in the current environment
;; or in an empty environment in which there are no new functions
(let ((*cmp-env* (cmp-env-copy (if (eq origin 'FLET) *cmp-env* new-env))))
(dolist (def (nreverse defs))
(let ((fun (first def)))
......
......@@ -734,6 +734,7 @@ compiled successfully, returns the pathname of the compiled file"
#+dlopen
(defun compile (name &optional (def nil supplied-p)
&aux form data-pathname
(lexenv nil)
(*suppress-compiler-messages* (or *suppress-compiler-messages*
(not *compile-verbose*)))
(*compiler-in-use* *compiler-in-use*)
......@@ -761,7 +762,11 @@ after compilation."
(when (functionp def)
(unless (function-lambda-expression def)
(return-from compile def))
(setf def (function-lambda-expression def)))
(multiple-value-setq (def lexenv)
(function-lambda-expression def))
(when (eq lexenv t)
(warn "COMPILE can not compile C closures")
(return-from compile (values def t nil))))
(setq form (if name
`(setf (fdefinition ',name) #',def)
`(set 'GAZONK #',def))))
......@@ -777,17 +782,20 @@ after compilation."
(t
(setq form `(setf (fdefinition ',name) #',form))))
(let*((*load-time-values* 'values) ;; Only the value is kept
(tmp-names (safe-mkstemp (format nil "TMP:ECL~3,'0x" (incf *gazonk-counter*))))
(data-pathname (first tmp-names))
(c-pathname (compile-file-pathname data-pathname :type :c))
(h-pathname (compile-file-pathname data-pathname :type :h))
(o-pathname (compile-file-pathname data-pathname :type :object))
(so-pathname (compile-file-pathname data-pathname))
(init-name (compute-init-name so-pathname :kind :fasl))
(compiler-conditions nil))
(let* ((*load-time-values* 'values) ;; Only the value is kept
(tmp-names (safe-mkstemp (format nil "TMP:ECL~3,'0x" (incf *gazonk-counter*))))
(data-pathname (first tmp-names))
(c-pathname (compile-file-pathname data-pathname :type :c))
(h-pathname (compile-file-pathname data-pathname :type :h))
(o-pathname (compile-file-pathname data-pathname :type :object))
(so-pathname (compile-file-pathname data-pathname))
(init-name (compute-init-name so-pathname :kind :fasl))
(compiler-conditions nil)
(*permanent-data* t) ; needed for literal objects in closures
(*cmp-env-root* *cmp-env-root*))
(with-compiler-env (compiler-conditions)
(setf form (set-closure-env form lexenv *cmp-env-root*))
(print-compiler-info)
(data-init)
(t1expr form)
......
@node The interpreter
@section The interpreter
@menu
* ECL stacks::
* Procedure Call Conventions::
* The lexical environment::
* The interpreter stack::
@end menu
@node ECL stacks
@subsection ECL stacks
ECL uses the following stacks:
@multitable @columnfractions .3 .7
......@@ -14,6 +22,7 @@ ECL uses the following stacks:
@tab used for arguments/values passing, typed lexical variables, temporary values, and function invocation.
@end multitable
@node Procedure Call Conventions
@subsection Procedure Call Conventions
ECL employs standard C calling conventions to achieve efficiency and
interoperability with other languages. Each Lisp function is
......@@ -69,20 +78,22 @@ instance, the actual source code for @code{cl_cons} in
@)
@end verbatim
@node The lexical environment
@subsection The lexical environment
The ECL interpreter uses two A-lists (Association lists) to represent
lexical environments.
@itemize
@item One for variable bindings
@item One for local function/macro/tag/block bindings
@end itemize
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
The ECL interpreter uses a list containing local functions and macros,
variables, tags and blocks to represent the lexical environment. When
a function closure is created, the current lexical environment is
saved in the closure along with the lambda expression. Later, when the
closure is invoked, this list is used to recover the lexical
environment.
Note that this list is different from what the Common Lisp standard
calls a lexical environment, which is the content of a
@code{&environment} parameter to @code{defmacro}. For the differences
between this two environments see the comments in
@code{src/c/compiler.d} and @code{src/c/interpreter.d}.
@node The interpreter stack
@subsection The interpreter stack
The bytecodes interpreter uses a stack of its own to save and restore
......
......@@ -756,7 +756,9 @@ make_lambda function.
@cppindex cl_eval
@deftypefun cl_object si_safe_eval (cl_object form, cl_object env, ...)
@code{si_safe_eval} evaluates @code{form} in the lexical environment
@code{si_safe_eval} evaluates @code{form} in the lexical
environment@footnote{Note that @code{env} must be a lexical
environment as used in the interpreter, @xref{The lexical environment}}
@code{env}, which can be @var{ECL_NIL}. Before evaluating it, the
expression form must be bytecompiled.
......@@ -769,7 +771,7 @@ compatibility with previous versions.
Equivalent of @code{si_safe_eval} (macro define).
@end table
@subheading Exmaple
@subheading Example
@exindex @code{cl_safe_eval}
@example
si_object form = c_string_to_object("(print 1)");
......
......@@ -228,6 +228,7 @@ extern cl_object si_constant_form_value _ECL_ARGS((cl_narg narg, cl_object form,
extern cl_object _ecl_bytecodes_dispatch_vararg(cl_narg narg, ...);
extern cl_object _ecl_bclosure_dispatch_vararg(cl_narg narg, ...);
extern cl_object ecl_close_around(cl_object fun, cl_object env);
/* ffi/backtrace.d */
......
......@@ -387,23 +387,6 @@ extern ECL_API ecl_frame_ptr _ecl_frs_push(register cl_env_ptr, register cl_obje
__ecl_env->nvalues = 3; return __aux1; \
} while (0)
/*****************************
* LEXICAL ENVIRONMENT STACK
*****************************/
/*
* A lexical environment is a list of pairs, each one containing either
* a variable definition, a tagbody or block tag, or a local function
* definition.
*
* lex_env ---> ( { record }* )
* record = variable | function | block_tag | tagbody_tag
*
* variable = ( var_name[symbol] . value )
* function = ( function[bytecodes] . fun_name[symbol] )
* block_tag = ( tag[fixnum] . block_name[symbol] )
* tagbody_tag = ( tag[fixnum] . 0 )
*/
/*************
* LISP STACK
*************/
......
......@@ -348,37 +348,37 @@ environment can be used to bytecompile the functions in MACROLET
or SYMBOL-MACRO forms, and also to evaluate other forms."
(declare (si::c-local))
(flet ((local-var-error-function (name)
#'(lambda (whole env)
(declare (ignore whole env))
(error
"In a MACROLET function you tried to access a local variable, ~A,
#'(lambda (whole env)
(declare (ignore whole env))
(error
"In a MACROLET function you tried to access a local variable, ~A,
from the function in which it appears." name)))
(local-fun-error-function (name)
#'(lambda (whole env)
(declare (ignore whole env))
(error
"In a MACROLET function you tried to access a local function, ~A,
#'(lambda (whole env)
(declare (ignore whole env))
(error
"In a MACROLET function you tried to access a local function, ~A,
from the function in which it appears." name))))
(cons (do ((env (car old-env) (cdr env))
(variables '()))
((endp env) (nreverse variables))
(let ((i (car env)))
(if (consp i)
(let ((name (first i)))
(if (not (keywordp name))
(push (if (second i)
i
(list name 'si::symbol-macro (local-var-error-function name)))
variables))))))
(let ((name (first i)))
(if (not (keywordp name))
(push (if (second i)
i
(list name 'si::symbol-macro (local-var-error-function name)))
variables))))))
(do ((env (cdr old-env) (cdr env))
(macros '()))
((endp env) (nreverse macros))
(let ((i (car env)))
(if (consp i)
(push (if (eq (second i) 'SI::MACRO)
i
(list