texinfo: Port Compiler from the old documentation

This commit is contained in:
Tomek Kurcz 2017-09-06 14:14:29 +02:00
parent 24e3407010
commit b985ef658f
2 changed files with 196 additions and 1 deletions

View file

@ -0,0 +1,194 @@
@node The compiler
@section The compiler
@subsection The compiler translates to C
The ECL compiler is essentially a translator from Common-Lisp to C. Given a Lisp source file, the compiler first generates three intermediate files:
@itemize
@item a C-file which consists of the C version of the Lisp program
@item an H-file which consists of declarations referenced in the C-file
@item a Data-file which consists of Lisp data to be used at load time
@end itemize
The ECL compiler then invokes the C compiler to compile the C-file into an object file. Finally, the contents of the Data-file is appended to the object file to make a @dfn{Fasl-file}. The generated Fasl-file can be loaded into the ECL system by the Common-Lisp function @code{load}. By default, the three intermediate files are deleted after the compilation, but, if asked, the compiler leaves them.
The merits of the use of C as the intermediate language are:
@itemize
@item The ECL compiler is highly portable.
@item Cross compilation is possible, because the contents of the intermediate files are common to all versions of ECL. For example, one can compile his or her Lisp program by the ECL compiler on a Sun, bring the intermediate files to DOS, compile the C-file with the gcc compiler under DOS, and then append the Data-file to the object file. This procedure generates the Fasl-file for the ECL system on DOS. This kind of cross compilation makes it easier to port ECL.
@item Hardware-dependent optimizations such as register allocations are done by the C compiler.
@end itemize
The demerits are:
@itemize
@item At those sites where no C compiler is available, the users cannot compile their Lisp programs.
@item The compilation time is long. 70% to 80% of the compilation time is used by the C compiler. The ECL compiler is therefore slower than compiler generating machine code directly.
@end itemize
@subsection The compiler mimics human C programmer
The format of the intermediate C code generated by the ECL compiler is the same as the hand-coded C code of the ECL source programs. For example, supposing that the Lisp source file contains the following function definition:
@verbatim
(defvar *delta* 2)
(defun add1 (x) (+ *delta* x))
@end verbatim
The compiler generates the following intermediate C code.
@verbatim
/* function definition for ADD1 */
static cl_object L1(cl_object V1)
{ VT2 VLEX2 CLSR2
cl_object value0;
value0=number_plus(symbol_value(VV[0]),V1); NVALUES=1;
return value0;
}
/* initialization of this module */
void init_CODE(cl_object flag)
{ VT1 CLSR1
cl_object value0;
if (!FIXNUMP(flag)){
Cblock=flag;
#ifndef ECL_DYNAMIC_VV
flag->cblock.data = VV;
#endif
flag->cblock.self_destruct=0;
flag->cblock.data_size = VM;
flag->cblock.data_text = compiler_data_text;
flag->cblock.data_text_size = compiler_data_text_size;
return;}
#ifdef ECL_DYNAMIC_VV
VV = Cblock->cblock.data;
#endif
T0= MAKE_FIXNUM(2);
si_Xmake_special(VV[0])
if(SYM_VAL(T0)!=OBJNULL) cl_setq(VV[0],T0);
cl_def_c_function(VV[1],(void*)L1,1);
}
@end verbatim
The C function L1 implements the Lisp function add1. This relation is established by @code{cl_def_c_function} in the initialization function @code{init_CODE}, which is invoked at load time. There, the vector @code{VV} consists of Lisp objects; @code{VV[0]} and @code{VV[1]} in this example hold the Lisp symbols @code{*delta*} and @code{add1}. @code{VM} in the definition of @code{L1} is a C macro declared in the corresponding H-file. The actual value of @code{VM} is the number of value stack locations used by this module, i.e., 2 in this example. Thus the following macro definition is found in the H-file.
@verbatim
#define VM 2
@end verbatim
@subsection Implementation of Compiled Closures
The ECL compiler takes two passes before it invokes the C compiler. The major role of the first pass is to detect function closures and to detect, for each function closure, those lexical objects (i.e., lexical variable, local function definitions, tags, and block-names) to be enclosed within the closure. This check must be done before the C code generation in the second pass, because lexical objects to be enclosed in function closures are treated in a different way from those not enclosed.
Ordinarily, lexical variables in a compiled function @var{f} are allocated on the C stack. However, if a lexical variable is to be enclosed in function closures, it is allocated on a list, called the "environment list", which is local to @var{f}. In addition, a local variable is created which points to the lexical variable's location (within the environment list), so that the variable may be accessed through an indirection rather than by list traversal.
The environment list is a pushdown list: It is empty when @var{f} is called. An element is pushed on the environment list when a variable to be enclosed in closures is bound, and is popped when the binding is no more in effect. That is, at any moment during execution of @var{f}, the environment list contains those lexical variables whose binding is still in effect and which should be enclosed in closures. When a compiled closure is created during execution of @var{f}, the compiled code for the closure is coupled with the environment list at that moment to form the compiled closure.
Later, when the compiled closure is invoked, a pointer is set up to each lexical variable in the environment list, so that each object may be referenced through a memory indirection.
Let us see an example. Suppose the following function has been compiled.
@verbatim
(defun foo (x)
(let ((a #'(lambda () (incf x)))
(y x))
(values a #'(lambda () (incf x y)))))
@end verbatim
@code{foo} returns two compiled closures. The first closure increments @var{x} by one, whereas the second closure increments @var{x} by the initial value of @var{x}. Both closures return the incremented value of @var{x}.
@verbatim
>(multiple-value-setq (f g) (foo 10))
#<compiled-closure nil>
>(funcall f)
11
>(funcall g)
21
>
@end verbatim
After this, the two compiled closures look like:
@verbatim
second closure y: x:
|-------|------| |-------|------| |------|------|
| ** | --|----->| 10 | --|------>| 21 | nil |
|-------|------| |-------|------| |------|------|
^
first closure |
|-------|------| |
| * | --|----------|
|-------|------|
* : address of the compiled code for #'(lambda () (incf x))
** : address of the compiled code for #'(lambda () (incf x y))
@end verbatim
@subsection Use of Declarations to Improve Efficiency
Declarations, especially type and function declarations, increase the efficiency of the compiled code. For example, for the following Lisp source file, with two Common-Lisp declarations added,
@verbatim
(eval-when (compile)
(proclaim '(function tak (fixnum fixnum fixnum) fixnum))
(defun tak (x y z)
(declare (fixnum x y z))
(if (not (< y x))
z
(tak (tak (1- x) y z)
(tak (1- y) z x)
(tak (1- z) x y))))
@end verbatim
The compiler generates the following C code:
@verbatim
/* local entry for function TAK */
static int LI1(register int V1,register int V2,register int V3)
{ VT3 VLEX3 CLSR3
TTL:
if (V2 < V1) {
goto L2;}
return(V3);
L2:
{ int V5;
V5 = LI1((V1)-1,V2,V3);
{ int V6;
V6 = LI1((V2)-1,V3,V1);
V3 = LI1((V3)-1,V1,V2);
V2 = V6;
V1 = V5;}}
goto TTL;
;;; Note: Tail-recursive call of TAK was replaced by iteration.
}
@end verbatim
@subsection Inspecting generated C code
Common-Lisp defines a function disassemble, which is supposed to disassemble a compiled function and to display the assembler code. According to @cite{Common-Lisp: The Language},
@quotation
This is primary useful for debugging the compiler, ..\\
@end quotation
This is, however, @emph{useless} in our case, because we are not concerned with assembly language. Rather, we are interested in the C code generated by the ECL compiler. Thus the disassemble function in ECL accepts not-yet-compiled functions only and displays the translated C code.
@verbatim
> (defun add1 (x) (1+ x))
ADD1
> (disassemble *)
;;; Compiling (DEFUN ADD1 ...).
;;; Emitting code for ADD1.
/* function definition for ADD1 */
static L1(int narg, object V1)
{ VT3 VLEX3 CLSR3
TTL:
VALUES(0) = one_plus((V1));
RETURN(1);
}
@end verbatim

View file

@ -10,7 +10,7 @@
* Manipulating Lisp objects::
* Environment implementation::
@c * The interpreter::
@c * The compiler::
* The compiler::
@c * Porting ECL::
* Removed features::
@c * Experimental features::
@ -22,4 +22,5 @@
@c @include developer-guide/dpp.txi
@include developer-guide/objects.txi
@include developer-guide/environment.txi
@include developer-guide/compiler.txi
@include developer-guide/removed.txi