24.1 The Compiler

The compiler is a program that may make code run faster by translating programs into an implementation-dependent form that can be executed more efficiently by the computer. Most of the time you can write programs without worrying about the compiler; compiling a file of code should produce an equivalent but more efficient program. When doing more esoteric things, you may need to think carefully about what happens at “compile time” and what happens at “load time.” Then the eval-when construct becomes particularly useful.

Most declarations are not used by the Common Lisp interpreter; they may be used to give advice to the compiler. The compiler may attempt to check your advice and warn you if it is inconsistent.

Unlike most other Lisp dialects, Common Lisp recognizes special declarations in interpreted code as well as compiled code.

The internal workings of a compiler will of course be highly implementation-dependent. The following functions provide a standard interface to the compiler, however.

[Function] compile name &optional definition

If definition is supplied, it should be a lambda-expression, the interpreted function to be compiled. If it is not supplied, then name should be a symbol with a definition that is a lambda-expression; that definition is compiled and the resulting compiled code is put back into the symbol as its function definition.

name may be any function-name (a symbol or a list whose car is setf—see section 7.1). One may write (compile ’(setf cadr)) to compile the setf expansion function for cadr.

If the optional definition argument is supplied, it may be either a lambda-expression (which is coerced to a function) or a function to be compiled; if no definition is supplied, the symbol-function of the symbol is extracted and compiled. It is permissible for the symbol to have a macro definition rather than a function definition; both macros and functions may be compiled.

It is an error if the function to be compiled was defined interpretively in a non-null lexical environment. (An implementation is free to extend the behavior of compile to compile such functions properly, but portable programs may not depend on this capability.) The consequences of calling compile on a function that is already compiled are unspecified.

The definition is compiled and a compiled-function object produced. If name is a non-nil symbol, then the compiled-function object is installed as the global function definition of the symbol and the symbol is returned. If name is nil, then the compiled-function object itself is returned. For example:


(defun foo ...)  foo        ;A function definition
(compile ’foo)  foo ;Compile it
;Now foo runs faster (maybe)
(compile nil
         ’(lambda (a b c) (- (* b b) (* 4 a c))))
    a compiled function of three arguments that computes b2 − 4ac

X3J13 voted in June 1989 to specify that compile returns two additional values indicating whether the compiler issued any diagnostics (see section 24.1.1).


X3J13 voted in March 1989 to add two new keyword arguments :verbose and :print to compile-file by analogy with load. The new function definition is as follows.

[Function] compile-file input-pathname &key :output-file :verbose :print

The input-pathname must be a valid file specifier, such as a pathname. The defaults for input-filename are taken from the variable *default-pathname-defaults*. The file should be a Lisp source file; its contents are compiled and written as a binary object file.

The :verbose argument (which defaults to the value of *compile-verbose*), if true, permits compile-file to print a message in the form of a comment to *standard-output* indicating what file is being compiled and other useful information.

The :print argument (which defaults to the value of *compile-print*), if true, causes information about top-level forms in the file being compiled to be printed to *standard-output*. Exactly what is printed is implementation-dependent; nevertheless something will be printed.


X3J13 voted in March 1988 to specify exactly which streams may be used as pathnames (see section 23.1.6).

X3J13 voted in June 1989 to clarify that supplying a wild pathname as the input-pathname argument to compile-file has implementation-dependent consequences; compile-file might signal an error, for example, or might compile all files that match the wild pathname.

X3J13 voted in June 1989 to require compile-file to accept logical pathnames (see section 23.1.5).

The :output-file argument may be used to specify an output pathname; it defaults in a manner appropriate to the implementation’s file system conventions.

X3J13 voted in June 1989 to specify that compile-file returns three values: the truename of the output file (or nil if the file could not be created) and two values indicating whether the compiler issued any diagnostics (see section 24.1.1).

X3J13 voted in October 1988 to specify that compile-file, like load, rebinds *package* to its current value. If some form in the file changes the value of *package*, the old value will be restored when compilation is completed.

X3J13 voted in June 1989 to specify restrictions on conforming programs to ensure consistent handling of symbols and packages.

In order to guarantee that compiled files can be loaded correctly, the user must ensure that the packages referenced in the file are defined consistently at compile and load time. Conforming Common Lisp programs must satisfy the following requirements.

If any of these conditions do not hold, the package in which load looks for the affected symbols is unspecified. Implementations are permitted to signal an error or otherwise define this behavior.

These requirements are merely an explicit statement of the status quo, namely that users cannot depend on any particular behavior if the package environment at load time is inconsistent with what existed at compile time.

X3J13 voted in March 1989 to specify that compile-file must bind *readtable* to its current value at the time compile-file is called; the dynamic extent of the binding should encompass all of the file-loading activity. This allows a portable program to include forms such as

(in-package "FOO")

(eval-when (:execute :load-toplevel :compile-toplevel)
  (setq *readtable* foo:my-readtable))

without performing a net global side effect on the loading environment. Such statements allow the remainder of such a file to be read either as interpreted code or by compile-file in a syntax determined by an alternative readtable.

X3J13 voted in June 1989 to require that compile-file bind two new variables *compile-file-pathname* and *compile-file-truename*; the dynamic extent of the bindings should encompass all of the file-compiling activity.

[Variable] *compile-verbose*

This variable provides the default for the :verbose argument to compile-file. Its initial value is implementation-dependent.


[Variable] *compile-print*

This variable provides the default for the :print argument to compile-file. Its initial value is implementation-dependent.


[Variable] *compile-file-pathname*

X3J13 voted in June 1989 to introduce *compile-file-pathname*; it is initially nil but compile-file binds it to a pathname that represents the file name given as the first argument to compile-file merged with the defaults (see merge-pathname).


[Variable] *compile-file-truename*

Variable is initially nil but compile-file binds it to the “true name” of the pathname of the file being compiled. See truename.


[Специальный оператор] load-time-value form [read-only-p]

This is a mechanism for delaying evaluation of a form until it can be done in the run-time environment.

If a load-time-value expression is seen by compile-file, the compiler performs its normal semantic processing (such as macro expansion and translation into machine code) on the form, but arranges for the execution of the form to occur at load time in a null lexical environment, with the result of this evaluation then being treated as an immediate quantity (that is, as if originally quoted) at run time. It is guaranteed that the evaluation of the form will take place only once when the file is loaded, but the order of evaluation with respect to the execution of top-level forms in the file is unspecified.

If a load-time-value expression appears within a function compiled with compile, the form is evaluated at compile time in a null lexical environment. The result of this compile-time evaluation is treated as an immediate quantity in the compiled code.

In interpreted code, form is evaluated (by eval) in a null lexical environment and one value is returned. Implementations that implicitly compile (or partially compile) expressions passed to eval may evaluate the form only once, at the time this compilation is performed. This is intentionally similar to the freedom that implementations are given for the time of expanding macros in interpreted code.

If the same (as determined by eq) list (load-time-value form) is evaluated or compiled more than once, it is unspecified whether the form is evaluated only once or is evaluated more than once. This can happen both when an expression being evaluated or compiled shares substructure and when the same expression is passed to eval or to compile multiple times. Since a load-time-value expression may be referenced in more than one place and may be evaluated multiple times by the interpreter, it is unspecified whether each execution returns a “fresh” object or returns the same object as some other execution. Users must use caution when destructively modifying the resulting object.

If two lists (load-time-value form) are equal but not eq, their values always come from distinct evaluations of form. Coalescing of these forms is not permitted.

The optional read-only-p argument designates whether the result may be considered a read-only constant. If nil (the default), the result must be considered ordinary, modifiable data. If t, the result is a read-only quantity that may, as appropriate, be copied into read-only space and may, as appropriate, be shared with other programs. The read-only-p argument is not evaluated and only the literal symbols t and nil are permitted.

This new feature addresses the same set of needs as the now-defunct #, reader syntax but in a cleaner and more general manner. Note that #, syntax was reliably useful only inside quoted structure (though this was not explicitly mentioned in the first edition), whereas a load-time-value form must appear outside quoted structure in a for-evaluation position.

See make-load-form.


[Function] disassemble name-or-compiled-function

The argument should be a function object, a lambda-expression, or a symbol with a function definition. If the relevant function is not a compiled function, it is first compiled. In any case, the compiled code is then “reverse-assembled” and printed out in a symbolic format. This is primarily useful for debugging the compiler, but also often of use to the novice who wishes to understand the workings of compiled code. ________________________________________________

Заметка для реализации: Implementors are encouraged to make the output readable, preferably with helpful comments.

___________________________________________________________________________________________________________

When disassemble compiles a function, it never installs the resulting compiled-function object in the symbol-function of a symbol.

name may be any function-name (a symbol or a list whose car is setf—see section 7.1). Thus one may write (disassemble ’(setf cadr)) to disassemble the setf expansion function for cadr.


[Function] function-lambda-expression fn

This function allows the source code for a defined function to be recovered. (The committee noted that the first edition provided no portable way to recover a lambda-expression once it had been compiled or evaluated to produce a function.)

This function takes one argument, which must be a function, and returns three values.

The first value is the defining lambda-expression for the function, or nil if that information is not available. The lambda-expression may have been preprocessed in some ways but should nevertheless be of a form suitable as an argument to the function compile or for use in the function special operator.

The second value is nil if the function was definitely produced by closing a lambda-expression in the null lexical environment; it is some non-nil value if the function might have been closed in some non-null lexical environment.

The third value is the “name” of the function; this is nil if the name is not available or if the function had no name. The name is intended for debugging purposes only and may be any Lisp object (not necessarily one that would be valid for use as a name in a defun or function special operator, for example). _________________________________________________________________

Заметка для реализации: An implementation is always free to return the values nil, t, nil from this function but is encouraged to make more useful information available as appropriate. For example, it may not be desirable for files of compiled code to retain the source lambda-expressions for use after the file is loaded, but it is probably desirable for functions produced by “in-core” calls to eval, compile, or defun to retain the defining lambda-expression for debugging purposes. The function function-lambda-expression makes this information, if retained, accessible in a standard and portable manner.

___________________________________________________________________________________________________________

[Макрос] with-compilation-unit ({option-name option-value}*) {form}*

with-compilation-unit executes the body forms as an implicit progn. Within the dynamic context of this form, warnings deferred by the compiler until “the end of compilation” will be deferred until the end of the outermost call to with-compilation-unit. The results are the same as those of the last of the forms (or nil if there is no form).

Each option-name is an unevaluated keyword; each option-value is evaluated. The set of keywords permitted may be extended by the implementation, but the only standard option keyword is :override; the default value for this option is nil. If with-compilation-unit forms are nested dynamically, only the outermost such call has any effect unless the :override value of an inner call is true.

The function compile-file should provide the effect of

(with-compilation-unit (:override nil) ...)

around its code.

Any implementation-dependent extensions to this behavior may be provided only as the result of an explicit programmer request by use of an implementation-dependent keyword. It is forbidden for an implementation to attach additional meaning to a conforming use of this macro.

Note that not all compiler warnings are deferred. In some implementations, it may be that none are deferred. This macro only creates an interface to the capability where it exists, it does not require the creation of the capability. An implementation that does not defer any compiler warnings may correctly implement this macro as an expansion into a simple progn.


24.1.1 Compiler Diagnostics

compile and compile-file may output warning messages; any such messages should go to the stream that is the value of *error-output*.

First, note that error and warning conditions may be signaled either by the compiler itself or by code being processed by the compiler (for example, arbitrary errors may occur during compile-time macro expansion or processing of eval-when forms). Considering only those conditions signaled by the compiler (as opposed to during compilation):

Both compile and compile-file are permitted (but not required) to establish a handler for conditions of type error. Such a handler might, for example, issue a warning and restart compilation from some implementation-dependent point in order to let the compilation proceed without manual intervention.

The functions compile and compile-file each return three values. See the definitions of these functions for descriptions of the first value. The second value is nil if no compiler diagnostics were issued, and true otherwise. The third value is nil if no compiler diagnostics other than style warnings were issued; a non-nil value indicates that there were “serious” compiler diagnostics issued or that other conditions of type error or warning (but not style-warning) were signaled during compilation.

24.1.2 Compiled Functions

Certain requirements are imposed on the functions produced by the compilation process.

If a function is of type compiled-function, then all macro calls appearing lexically within the function have already been expanded and will not be expanded again when the function is called. The process of compilation effectively turns every macrolet or symbol-macrolet construct into a progn (or a locally) with all instances of the local macros in the body fully expanded.

If a function is of type compiled-function, then all load-time-value forms appearing lexically within the function have already been pre-evaluated and will not be evaluated again when the function is called.

Implementations are free to classify every function as a compiled-function provided that all functions satisfy the preceding requirements. Conversely, it is permissible for a function that is not a compiled-function to satisfy the preceding requirements.

If one or more functions are defined in a file that is compiled with compile-file and the compiled file is subsequently loaded by the function load, the resulting loaded function definitions must be of type compiled-function.

The function compile must produce an object of type compiled-function as the value that is either returned or stored into the symbol-function of a symbol argument.

Note that none of these restrictions addresses questions of the compilation technology or target instruction set. For example, a compiled function does not necessarily consist of native machine instructions. These requirements merely specify the behavior of the type system with respect to certain actions taken by compile, compile-file, and load.

24.1.3 Compilation Environment

Following information must be available at compile time for correct compilation and what need not be available until run time.

The following information must be present in the compile-time environment for a program to be compiled correctly. This information need not also be present in the run-time environment.

The compiler may incorporate the following kinds of information into the code it produces, if the information is present in the compile-time environment and is referenced within the code being compiled; however, the compiler is not required to do so. When compile-time and run-time definitions differ, it is unspecified which will prevail within the compiled code (unless some other behavior is explicitly specified below). It is also permissible for an implementation to signal an error at run time on detecting such a discrepancy. In all cases, the absence of the information at compile time is not an error, but its presence may enable the compiler to generate more efficient code.

The compiler must not make any additional assumptions about consistency between the compile-time and run-time environments. In particular, the compiler may not assume that functions that are defined in the compile-time environment will retain either the same definition or the same signature at run time, except as described above. Similarly, the compiler may not signal an error if it sees a call to a function that is not defined at compile time, since that function may be provided at run time.

X3J13 voted in January 1989 to specify the compile-time side effects of processing various macro forms.

Calls to defining macros such as defmacro or defvar appearing within a file being processed by compile-file normally have compile-time side effects that affect how subsequent forms in the same file are compiled. A convenient model for explaining how these side effects happen is that each defining macro expands into one or more eval-when forms and that compile-time side effects are caused by calls occurring in the body of an (eval-when (:compile-toplevel) ...) form.

The affected defining macros and their specific side effects are as follows. In each case, it is identified what a user must do to ensure that a program is conforming, and what a compiler must do in order to correctly process a conforming program.

Compile-time side effects may cause information about a definition to be stored in a different manner from information about definitions processed either interpretively or by loading a compiled file. In particular, the information stored by a defining macro at compile time may or may not be available to the interpreter (either during or after compilation) or during subsequent calls to compile or compile-file. For example, the following code is not portable because it assumes that the compiler stores the macro definition of foo where it is available to the interpreter.

(defmacro foo (x) ‘(car ,x))

(eval-when (:execute :compile-toplevel :load-toplevel)
  (print (foo ’(a b c))))     ;Wrong

The goal may be accomplished portably by including the macro definition within the eval-when form:

(eval-when (eval compile load)
  (defmacro foo (x) ‘(car ,x))
  (print (foo ’(a b c))))     ;Right

X3J13 voted in June 1989 to specify the compile-time side effects of processing various CLOS-related macro forms. Top-level calls to the CLOS defining macros have the following compile-time side effects; any other compile-time behavior is explicitly left unspecified.

24.1.4 Similarity of Constants

Following paragraphs specifies what objects can be in compiled constants and what relationship there must be between a constant passed to the compiler and the one that is established by compiling it and then loading its file.

The key is a definition of an equivalence relationship called “similarity as constants” between Lisp objects. Code passed through the file compiler and then loaded must behave as though quoted constants in it are similar in this sense to quoted constants in the corresponding source code. An object may be used as a quoted constant processed by compile-file if and only if the compiler can guarantee that the resulting constant established by loading the compiled file is “similar as a constant” to the original. Specific requirements are spelled out below.

Some types of objects, such as streams, are not supported in constants processed by the file compiler. Such objects may not portably appear as constants in code processed with compile-file. Conforming implementations are required to handle such objects either by having the compiler or loader reconstruct an equivalent copy of the object in some implementation-specific manner or by having the compiler signal an error.

Of the types supported in constants, some are treated as aggregate objects. For these types, being similar as constants is defined recursively. We say that an object of such a type has certain “basic attributes”; to be similar as a constant to another object, the values of the corresponding attributes of the two objects must also be similar as constants.

A definition of this recursive form has problems with any circular or infinitely recursive object such as a list that is an element of itself. We use the idea of depth-limited comparison and say that two objects are similar as constants if they are similar at all finite levels. This idea is implicit in the definitions below, and it applies in all the places where attributes of two objects are required to be similar as constants. The question of handling circular constants is the subject of a separate vote by X3J13 (see below).

The following terms are used throughout this section. The term constant refers to a quoted or self-evaluating constant, not a named constant defined by defconstant. The term source code is used to refer to the objects constructed when compile-file calls read (or the equivalent) and to additional objects constructed by macro expansion during file compilation. The term compiled code is used to refer to objects constructed by load.

Two objects are similar as a constant if and only if they are both of one of the types listed below and satisfy the additional requirements listed for that type.

X3J13 voted in March 1989 to specify the circumstances under which constants may be coalesced in compiled code.

Suppose A and B are two objects used as quoted constants in the source code, and that A and B are the corresponding objects in the compiled code. If A and B are eql but A and B were not eql, then we say that A and B have been coalesced by the compiler.

An implementation is permitted to coalesce constants appearing in code to be compiled if and only if they are similar as constants, except that objects of type symbol, package, structure, or standard-object obey their own rules and may not be coalesced by a separate mechanism. ____________________________

Обоснование: Objects of type symbol and package cannot be coalesced because the fact that they are named, interned objects means they are already as coalesced as it is useful for them to be. Uninterned symbols could perhaps be coalesced, but that was thought to be more dangerous than useful. Structures and objects could be coalesced if a “similar as a constant” predicate were defined for them; it would be a generic function. However, at present there is no such predicate. Currently make-load-form provides a protocol by which compile-file and load work together to construct an object in the compiled code that is equivalent to the object in the source code; a different mechanism would have to be added to permit coalescing.

___________________________________________________________________________________________________________

Note that coalescing is possible only because it is forbidden to destructively modify constants (see quote).

Objects containing circular or infinitely recursive references may legitimately appear as constants to be compiled. The compiler is required to preserve eql-ness of substructures within a file compiled by compile-file.