1
0
Fork 0
cl-sites/ecl.common-lisp.dev/static/manual/The-compiler.html
2024-12-24 19:15:49 +01:00

406 lines
17 KiB
HTML

<!DOCTYPE html>
<html>
<!-- Created by GNU Texinfo 7.0.3, https://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>The compiler (ECL Manual)</title>
<meta name="description" content="The compiler (ECL Manual)">
<meta name="keywords" content="The compiler (ECL Manual)">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content="makeinfo">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="index.html" rel="start" title="Top">
<link href="Indexes.html" rel="index" title="Indexes">
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
<link href="Developer_0027s-guide.html" rel="up" title="Developer's guide">
<link href="Porting-ECL.html" rel="next" title="Porting ECL">
<link href="The-interpreter.html#The-interpreter" rel="prev" title="The interpreter">
<style type="text/css">
<!--
/* colors */
div.example {margin-left: 3.2em}
span.r {font-family: initial; font-weight: normal; font-style: normal}
ul.mark-bullet {list-style-type: disc}
@media (prefers-color-scheme: dark) {
/* dark theme */
html { color: seashell;
background: #1A1A1A; }
body { background: #1A1A1A; }
th { border-bottom: 2px solid lightgray; }
h1, h2, h3, h4, h5 { background-image: linear-gradient(to left, #202020, #3A3A3A); }
code, var, code a { color: darkorange;
background: #2A2A2A; }
a { color: seashell; }
pre { background: #2A2A2A;
color: seashell;
/* mark longer code block with stripe on the left */
border-left: 5px solid darkorange;
padding-left: 10px; }
pre.screen { background: #2A2A2A;
border: 1px solid lightgray; }
pre.programlisting { background: #2A2A2A;
border-left: 1px solid lightgray;
border-top: 1px solid lightgray; }
/* we need a light background in order for the images to be readable */
img { background: white }
}
@media (prefers-color-scheme: light) {
/* light theme */
html { background: white }
body { background: white }
th { border-bottom: 2px solid gray; }
h1, h2, h3, h4, h5 { background: lightgray; }
code, var, code a { color: darkred;
background: whitesmoke; }
a { color: #000; }
pre { background: whitesmoke;
color: black;
/* mark longer code block with stripe on the left */
border-left: 5px solid darkred;
padding-left: 10px; }
pre.screen { background: #EEE;
border: 1px solid black; }
pre.programlisting { background: #EEEEEE;
border-left: 1px solid black;
border-top: 1px solid black; }
}
body {
margin: 1em 125px 0 10%;
line-height: 1.5em;
padding: 0 2em 1em 2em;
font: 13px Verdana,Arial, sans-serif
}
ul, dd, dl, dt { margin-top: 0; margin-bottom: 0; }
p, code, td, dl, dt {
line-height: 1.5em;
}
table {
font: inherit;
border-collapse: collapse;
}
th, td {
vertical-align: top;
}
h1, h2, h3 { padding-left: 15px; }
h4, h5 { padding-left: 5px; }
code, pre {
font-size: 1em;
font-family: monospace;
}
var {
font-size: 1em;
}
/* links inside code appear the same as the code itself */
code a {
font-weight: normal;
text-decoration: none;
}
/* but get an underline when hovering */
code a:hover {
text-decoration: underline;
}
/* ordinary links appear in bold */
a { font-weight: bold; }
pre.verbatim {
margin: 0 0 0 0;
}
pre {
overflow: auto;
}
pre.screen {
font-weight: bold;
padding: 0.5em;
}
pre.programlisting {
padding: 0.5em;
}
div p { padding: 0 2em }
li p { padding: 0; margin: 0 }
hr { display: none; }
div.funcsynopsis p {
text-indent: -2em;
}
div.variablelist {
padding: 0 2em;
}
.type, .funcsynopsis, .symbol {
font-family: monospace;
}
.type, .symbol, .replaceable {
white-space: nowrap;
}
-->
</style>
</head>
<body lang="en">
<div class="section-level-extent" id="The-compiler">
<div class="nav-panel">
<p>
Next: <a href="Porting-ECL.html" accesskey="n" rel="next">Porting ECL</a>, Previous: <a href="The-interpreter.html#The-interpreter" accesskey="p" rel="prev">The interpreter</a>, Up: <a href="Developer_0027s-guide.html" accesskey="u" rel="up">Developer&rsquo;s guide</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Indexes.html" title="Index" rel="index">Index</a>]</p>
</div>
<h3 class="section" id="The-compiler-1">4.7 The compiler</h3>
<ul class="mini-toc">
<li><a href="The-compiler.html#The-compiler-translates-to-C" accesskey="1">The compiler translates to C</a></li>
<li><a href="The-compiler.html#The-compiler-mimics-human-C-programmer" accesskey="2">The compiler mimics human C programmer</a></li>
<li><a href="The-compiler.html#Implementation-of-Compiled-Closures" accesskey="3">Implementation of Compiled Closures</a></li>
<li><a href="The-compiler.html#Use-of-Declarations-to-Improve-Efficiency" accesskey="4">Use of Declarations to Improve Efficiency</a></li>
<li><a href="The-compiler.html#Inspecting-generated-C-code" accesskey="5">Inspecting generated C code</a></li>
</ul>
<div class="subsection-level-extent" id="The-compiler-translates-to-C">
<h4 class="subsection">4.7.1 The compiler translates to C</h4>
<p>The ECL compiler is essentially a translator from Common-Lisp to C. Given a Lisp source file, the compiler first generates three intermediate files:
</p>
<ul class="itemize mark-bullet">
<li>a C-file which consists of the C version of the Lisp program
</li><li>an H-file which consists of declarations referenced in the C-file
</li><li>a Data-file which consists of Lisp data to be used at load time
</li></ul>
<p>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 <em class="dfn">Fasl-file</em>. The generated Fasl-file can be loaded into the ECL system by the Common-Lisp function <code class="code">load</code>. By default, the three intermediate files are deleted after the compilation, but, if asked, the compiler leaves them.
</p>
<p>The merits of the use of C as the intermediate language are:
</p>
<ul class="itemize mark-bullet">
<li>The ECL compiler is highly portable.
</li><li>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.
</li><li>Hardware-dependent optimizations such as register allocations are done by the C compiler.
</li></ul>
<p>The demerits are:
</p>
<ul class="itemize mark-bullet">
<li>At those sites where no C compiler is available, the users cannot compile their Lisp programs.
</li><li>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.
</li></ul>
</div>
<div class="subsection-level-extent" id="The-compiler-mimics-human-C-programmer">
<h4 class="subsection">4.7.2 The compiler mimics human C programmer</h4>
<p>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:
</p>
<div class="example lisp">
<pre class="lisp-preformatted">(defvar *delta* 2)
(defun add1 (x) (+ *delta* x))
</pre></div>
<p>The compiler generates the following intermediate C code.
</p>
<div class="example">
<pre class="verbatim">/* function definition for ADD1 */
/* optimize speed 3, debug 0, space 0, safety 2 */
static cl_object L1add1(cl_object v1x)
{
cl_object env0 = ECL_NIL;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
ecl_cs_check(cl_env_copy,value0);
{
TTL:
value0 = ecl_plus(ecl_symbol_value(VV[0]),v1x);
cl_env_copy-&gt;nvalues = 1;
return value0;
}
}
/* initialization of this module */
ECL_DLLEXPORT void init_fas_CODE(cl_object flag)
{
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
cl_object *VVtemp;
if (flag != OBJNULL){
Cblock = flag;
#ifndef ECL_DYNAMIC_VV
flag-&gt;cblock.data = VV;
#endif
flag-&gt;cblock.data_size = VM;
flag-&gt;cblock.temp_data_size = VMtemp;
flag-&gt;cblock.data_text = compiler_data_text;
flag-&gt;cblock.cfuns_size = compiler_cfuns_size;
flag-&gt;cblock.cfuns = compiler_cfuns;
flag-&gt;cblock.source = make_constant_base_string(&quot;test.lisp&quot;);
return;}
#ifdef ECL_DYNAMIC_VV
VV = Cblock-&gt;cblock.data;
#endif
Cblock-&gt;cblock.data_text = (const cl_object *)&quot;@EcLtAg:init_fas_CODE@&quot;;
VVtemp = Cblock-&gt;cblock.temp_data;
ECL_DEFINE_SETF_FUNCTIONS
si_Xmake_special(VV[0]);
if (ecl_boundp(cl_env_copy,VV[0])) { goto L2; }
cl_set(VV[0],ecl_make_fixnum(2));
L2:;
ecl_cmp_defun(VV[2]); /* ADD1 */
}
</pre></div>
<p>The C function <code class="code">L1add1</code> implements the Lisp function <code class="code">add1</code>. This relation is established by <code class="code">ecl_cmp_defun</code> in the initialization function <code class="code">init_fas_CODE</code>, which is invoked at load time. There, the vector <code class="code">VV</code> consists of Lisp objects; <code class="code">VV[0]</code> and <code class="code">VV[1]</code> in this example hold the Lisp symbols <code class="code">*delta*</code> and <code class="code">add1</code>, while <code class="code">VV[2]</code> holds the function object for <code class="code">add1</code>, which is created during initialization of the module. <code class="code">VM</code> in the definition of <code class="code">L1add1</code> is a C macro declared in the corresponding H-file. The actual value of <code class="code">VM</code> is the number of value stack locations used by this module, i.e., 3 in this example. Thus the following macro definition is found in the H-file.
</p>
<div class="example">
<pre class="example-preformatted">#define VM 3
</pre></div>
</div>
<div class="subsection-level-extent" id="Implementation-of-Compiled-Closures">
<h4 class="subsection">4.7.3 Implementation of Compiled Closures</h4>
<p>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.
</p>
<p>Ordinarily, lexical variables in a compiled function <code class="code">f</code> 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 &quot;environment list&quot;, which is local to <code class="code">f</code>. In addition, a local variable is created which points to the lexical variable&rsquo;s location (within the environment list), so that the variable may be accessed through an indirection rather than by list traversal.
</p>
<p>The environment list is a pushdown list: It is empty when <code class="code">f</code> 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 <code class="code">f</code>, 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 <code class="code">f</code>, the compiled code for the closure is coupled with the environment list at that moment to form the compiled closure.
</p>
<p>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.
</p>
<p>Let us see an example. Suppose the following function has been compiled.
</p>
<div class="example lisp">
<pre class="lisp-preformatted">(defun foo (x)
(let ((a #'(lambda () (incf x)))
(y x))
(values a #'(lambda () (incf x y)))))
</pre></div>
<p><code class="code">foo</code> returns two compiled closures. The first closure increments <var class="var">x</var> by one, whereas the second closure increments <var class="var">x</var> by the initial value of <var class="var">x</var>. Both closures return the incremented value of <var class="var">x</var>.
</p>
<div class="example">
<pre class="example-preformatted">&gt;(multiple-value-setq (f g) (foo 10))
#&lt;compiled-closure nil&gt;
&gt;(funcall f)
11
&gt;(funcall g)
21
&gt;
</pre></div>
<p>After this, the two compiled closures look like:
</p>
<pre class="verbatim"> second closure y: x:
|-------|------| |-------|------| |------|------|
| ** | --|-----&gt;| 10 | --|------&gt;| 21 | nil |
|-------|------| |-------|------| |------|------|
^
first closure |
|-------|------| |
| * | --|----------|
|-------|------|
* : address of the compiled code for #'(lambda () (incf x))
** : address of the compiled code for #'(lambda () (incf x y))
</pre>
</div>
<div class="subsection-level-extent" id="Use-of-Declarations-to-Improve-Efficiency">
<h4 class="subsection">4.7.4 Use of Declarations to Improve Efficiency</h4>
<p>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,
</p>
<div class="example lisp">
<pre class="lisp-preformatted">(eval-when (:compile-toplevel)
(proclaim '(ftype (function (fixnum fixnum) fixnum) tak))
(proclaim '(optimize (speed 3) (debug 0) (safety 0))))
(defun tak (x y)
(declare (fixnum x y))
(if (not (&lt; y x))
y
(tak (tak (1- x) y)
(tak (1- y) x))))
</pre></div>
<p>The compiler generates the following C code (Note that the tail-recursive call of <code class="code">tak</code> was replaced by iteration):
</p><div class="example">
<pre class="verbatim">/* function definition for TAK */
/* optimize speed 3, debug 0, space 0, safety 0 */
static cl_object L1tak(cl_object v1x, cl_object v2y)
{
cl_object env0 = ECL_NIL;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
cl_fixnum v3x;
cl_fixnum v4y;
v3x = ecl_fixnum(v1x);
v4y = ecl_fixnum(v2y);
TTL:
if ((v4y)&lt;(v3x)) { goto L1; }
value0 = ecl_make_fixnum(v4y);
cl_env_copy-&gt;nvalues = 1;
return value0;
L1:;
{
cl_fixnum v5;
{
cl_fixnum v6;
v6 = (v3x)-1;
v5 = ecl_fixnum(L1tak(ecl_make_fixnum(v6), ecl_make_fixnum(v4y)));
}
{
cl_fixnum v6;
v6 = (v4y)-1;
v4y = ecl_fixnum(L1tak(ecl_make_fixnum(v6), ecl_make_fixnum(v3x)));
}
v3x = v5;
}
goto TTL;
}
</pre></div>
</div>
<div class="subsection-level-extent" id="Inspecting-generated-C-code">
<h4 class="subsection">4.7.5 Inspecting generated C code</h4>
<p>Common-Lisp defines a function disassemble, which is supposed to disassemble a compiled function and to display the assembler code. According to <cite class="cite">Common-Lisp: The Language</cite>,
</p>
<blockquote class="quotation">
<p>This is primary useful for debugging the compiler, ..\\
</p></blockquote>
<p>This is, however, <em class="emph">useless</em> 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.
</p>
<div class="example">
<pre class="verbatim">&gt; (defun add1 (x) (1+ x))
ADD1
&gt; (disassemble *)
/* function definition for ADD1 */
/* optimize speed 3, debug 0, space 0, safety 2 */
static cl_object L1add1(cl_object v1x)
{
cl_object env0 = ECL_NIL;
const cl_env_ptr cl_env_copy = ecl_process_env();
cl_object value0;
ecl_cs_check(cl_env_copy,value0);
{
TTL:
value0 = ecl_one_plus(v1x);
cl_env_copy-&gt;nvalues = 1;
return value0;
}
}
</pre></div>
</div>
</div>
<hr>
<div class="nav-panel">
<p>
Next: <a href="Porting-ECL.html" accesskey="n" rel="next">Porting ECL</a>, Previous: <a href="The-interpreter.html#The-interpreter" accesskey="p" rel="prev">The interpreter</a>, Up: <a href="Developer_0027s-guide.html" accesskey="u" rel="up">Developer&rsquo;s guide</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Indexes.html" title="Index" rel="index">Index</a>]</p>
</div>
</body>
</html>