[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22. "The Compiler"

.setq compiler section-page .setq compiler-chapter chapter-number


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.1 "The Basic Operations of the Compiler"

The purpose of the Lisp compiler is to convert Lisp functions into programs in the Lisp Machine's instruction set, so that they will run more quickly and take up less storage. Compiled functions are represented in Lisp by FEFs (Function Entry Frames), which contain machine code as well as various other information. The printed representation of a FEF is
 
#<DTP-FEF-POINTER address name>

If you want to understand the output of the compiler, refer to (understanding-compiled-code).

There are three ways to invoke the compiler from the Lisp Machine. First, you may have an interpreted function in the Lisp environment which you would like to compile. The function compile is used to do this. Second, you may have code in an editor buffer which you would like to compile. The Zwei editor has commands to read code into Lisp and compile it. Third, you may have a program (a group of function definitions and other forms) written in a file on the file system. The compiler can translate this file into a QFASL file. Loading in the QFASL file is almost the same as reading in the source file; the difference is that the functions defined in the file will be defined as compiled functions instead of interpreted functions. The qc-file function is used for translating source files into QFASL files.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.2 "How to Invoke the Compiler"

Function: compile function-spec &optional definition
If definition is supplied, it should be a lambda-expression. Otherwise function-spec (this is usually a symbol, but see (function-spec) for details) should be defined as an interpreted function and its definition will be used as the lambda-expression to be compiled. The compiler converts the lambda-expression into a FEF, saves the lambda-expression as the :previous-expr-definition and :previous-definition properties of function-spec if it is a symbol, and changes function-spec's definition to be the FEF. (See fdefine, (fdefine-fun)). (Actually, if function-spec is not defined as a lambda-expression, and function-spec is a symbol, compile will try to find a lambda-expression in the :previous-expr-definition property of function-spec and use that instead.)

Function: uncompile symbol
If symbol is not defined as an interpreted function and it has a :previous-expr-definition property, then uncompile will restore the function cell from the value of the property. (Otherwise, uncompile does nothing and returns "Not compiled".) This "undoes" the effect of compile. See also undefun, (undefun-fun).

Function: qc-file filename &optional output-file load-flag in-core-flag package functions-defined file-local-declarations dont-set-default-p read-then-process-flag
This function takes a formidable number of arguments, but normally only one argument is supplied. The file filename is given to the compiler, and the output of the compiler is written to a file whose name is filename except with a file type of "QFASL". The input format for files to the compiler is described on (compiler-input-section). Macro definitions, subst definitions, and special declarations created during the compilation are undone when the compilation is finished.

The optional arguments allow certain modifications to the standard procedure. output-file lets you change where the output is written. package lets you specify in what package the source file is to be read. Normally the system knows, or asks interactively, and you need not supply this argument. load-flag and in-core-flag are incomprehensible; you don't want to use them. functions-defined and file-local-declarations are for compiling multiple files as if they were one. dont-set-default-p suppresses the changing of the default file name to filename that normally occurs.

Normally, a form is read from the file and processed and then another form is read and processed, and so on. But if read-then-process-flag is non-nil, the whole source file is read before any of it is processed. This is not done by default; it has the problem that compile-time reader-macros defined in the file will not work properly.

Function: qc-file-load filename &optional output-file load-flag in-core-flag package functions-defined file-local-declarations dont-set-default-p read-then-process-flag
qc-file-load compiles a file and then loads in the resulting QFASL file.

See also the disassemble function ((disassemble-fun)), which lists the instructions of a compiled function in symbolic form.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.3 "Input to the Compiler"

.setq compiler-input-section section-page

The purpose of qc-file is to take a file and produce a translated version which does the same thing as the original except that the functions are compiled. qc-file reads through the input file, processing the forms in it one by one. For each form, suitable binary output is sent to the QFASL file so that when the QFASL file is loaded the effect of that source form will be reproduced. The differences between source files and QFASL files are that QFASL files are in a compressed binary form which reads much faster (but cannot be edited), and that function definitions in QFASL files have been translated from Lisp forms to FEFs.

So, if the source contains a (defun ...) form at top level, then when the QFASL file is loaded, the function will be defined as a compiled function. If the source file contains a form which is not of a type known specially to the compiler, then that form (encoded in QFASL format) will be output "directly" into the QFASL file, so that when the QFASL file is loaded that form will be evaluated. Thus, if the source file contains (setq x 3), then the compiler will put in the QFASL file instructions to set x to 3 at load time (that is, when the QFASL file is loaded into the Lisp environment). It happens that QFASL files have a specific way to setq a symbol. For a more general form, the QFASL file would contain instructions to recreate the list structure of a form and then call eval on it.

Sometimes we want to put things in the file that are not merely meant to be translated into QFASL form. One such occasion is top level macro definitions; the macros must actually get defined within the compiler in order for the compiler to be able to expand them at compile time. So when a macro form is seen, it should (sometimes) be evaluated at compile time, and should (sometimes) be put into the QFASL file.

Another thing we sometimes want to put in a file is compiler declarations. These are forms which should be evaluated at compile time to tell the compiler something. They should not be put into the QFASL file, unless they are useful for working incrementally on the functions in the file, compiling them one by one from the editor.

Therefore, a facility exists to allow the user to tell the compiler just what to do with a form. One might want a form to be:

Put into the QFASL file (compiled, of course), or not.
Evaluated within the compiler, or not.
Evaluated if the file is read directly into Lisp, or not.

Two forms are recognized by the compiler to allow this. The less general, old-fashioned one is declare; the completely general one is eval-when.

An eval-when form looks like
 
(eval-when times-list
	   form1
	   form2
           ...)
The times-list may contain one or more of the symbols load, compile, or eval. If load is present, the forms are written into the QFASL file to be evaluated when the QFASL file is loaded (except that defun forms will put the compiled definition into the QFASL file instead). If compile is present, the forms are evaluated in the compiler. If eval is present, the forms are evaluated when read into Lisp; this is because eval-when is defined as a special form in Lisp. (The compiler ignores eval in the times-list.) For example,
 
(eval-when (compile eval) (macro foo (x) (cadr x)))
would define foo as a macro in the compiler and when the file is read in interpreted, but not when the QFASL file is fasloaded.

For the rest of this section, we will use lists such as are given to eval-when, e.g. (load eval), (load compile), etc. to describe when forms are evaluated.

A declare form looks like (declare form1 form2 ...). declare is defined in Lisp as a special form which does nothing; so the forms within a declare are not evaluated at eval time. The compiler does the following upon finding form within a declare: if form is a call to either special or unspecial, form is treated as (load compile); otherwise it is treated as (compile).

If a form is not enclosed in an eval-when nor a declare, then the times at which it will be evaluated depend on the form. The following table summarizes at what times evaluation will take place for any given form seen at top level by the compiler.

(eval-when times-list form1 ...)
times-list

(declare (special ...)) or (declare (unspecial ...))
(load compile)

(declare anything-else)
(compile)

(special ...) or (unspecial ...)
(load compile eval)

(macro ...) or (defmacro ...) or (defsubst ...)
(load compile eval)

(comment ...)
Ignored at all times.

(compiler-let ((var val) ...) body...)
Processes the body in its normal fashion, but at (compile eval) time, the indicated variable bindings are in effect. These variables will typically affect the operation of the compiler or of macros. See (compiler-let-discussion).

(local-declare (decl decl ...) body...)
Processes the body in its normal fashion, with the indicated declarations added to the front of the list which is the value of local-declarations.

(defflavor ...) or (defstruct ...)
(load compile eval)

(defun ...) or (defmethod ...) or (defselect ...)
(load eval), but at load time what is processed is not this form itself, but the result of compiling it.

anything-else
(load eval)

Sometimes a macro wants to return more than one form for the compiler top level to see (and to be evaluated). The following facility is provided for such macros. If a form
 
(progn (quote compile) form1 form2 ...)
is seen at the compiler top level, all of the forms are processed as if they had been at compiler top level. (Of course, in the interpreter they will all be evaluated, and the (quote compile) will harmlessly evaluate to the symbol compile and be ignored.) See (progn-quote-compile-discussion) for additional discussion of this.

Special Form: eval-when (time...) body...
When seen by the interpreter, if one of the times is the symbol eval then the body forms are evaluated; otherwise eval-when does nothing.

But when seen by the compiler, this special form does the special things described above.

Special Form: declare declaration...
declare does nothing, and returns the symbol declare.

But when seen by the compiler, this special form does the special things described above.

There is also a different use of declare, used in conjuction with the arglist function (see (arglist-fun)).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.4 "Compiler Declarations"

This section describes functions meant to be called during compilation, and variables meant to be set or bound during compilation, by using declare or local-declare.

Special Form: local-declare (declaration...) body...
A local-declare form looks like
 
(local-declare (decl1 decl2 ...)
   form1
   form2
   ...)
Each decl is consed onto the list local-declarations while the forms are being evaluated (in the interpreter) or compiled (in the compiler). There are two uses for this. First, it can be used to pass information from outer macros to inner macros. Secondly, the compiler will specially interpret certain decls as local declarations, which only apply to the compilations of the forms. It understands the following forms:

(special var1 var2 ...)
The variables var1, var2, etc. will be treated as special variables during the compilation of the forms.

(unspecial var1 var2 ...)
The variables var1, var2, etc. will be treated as local variables during the compilation of the forms.

(arglist . arglist)
Putting this local declaration around a defun saves arglist as the argument list of the function, to be used instead of its lambda-list if anyone asks what its arguments are. This is purely documentation.

(return-list . values)
Putting this local declaration around a defun saves values as the return values list of the function, to be used if anyone asks what values it returns. This is purely documentation.

(def name . definition)
name will be defined for the compiler during the compilation of the forms. The compiler uses this to keep track of macros and open-codable functions (defsubsts) defined in the file being compiled. Note that the cddr of this item is a function.

Special Form: special variable...
Declares each variable to be "special" for the compiler.

Special Form: unspecial variable...
Removes any "special" declarations of the variables for the compiler.

The next three declarations are primarily for Maclisp compatibility.

Special Form: *expr symbol...
Declares each symbol to be the name of a function. In addition it prevents these functions from appearing in the list of functions referenced but not defined printed at the end of the compilation.

Special Form: *lexpr symbol...
Declares each symbol to be the name of a function. In addition it prevents these functions from appearing in the list of functions referenced but not defined printed at the end of the compilation.

Special Form: *fexpr symbol...
Declares each symbol to be the name of a special form. In addition it prevents these names from appearing in the list of functions referenced but not defined printed at the end of the compilation.

There are some advertised variables whose compile-time values affect the operation of the compiler. The user may set these variables by including in his file forms such as
 
(declare (setq open-code-map-switch t))

Variable: run-in-maclisp-switch
If this variable is non-nil, the compiler will try to warn the user about any constructs which will not work in Maclisp. By no means will all Lisp Machine system functions not built in to Maclisp be cause for warnings; only those which could not be written by the user in Maclisp (for example, make-array, value-cell-location, etc.). Also, lambda-list keywords such as &optional and initialized prog variables will be mentioned. This switch also inhibits the warnings for obsolete Maclisp functions. The default value of this variable is nil.

Variable: obsolete-function-warning-switch
If this variable is non-nil, the compiler will try to warn the user whenever an "obsolete" Maclisp-compatibility function such as maknam or samepnamep is used. The default value is t.

Variable: allow-variables-in-function-position-switch
If this variable is non-nil, the compiler allows the use of the name of a variable in function position to mean that the variable's value should be funcall'd. This is for compatibility with old Maclisp programs. The default value of this variable is nil.

Variable: open-code-map-switch
If this variable is non-nil, the compiler will attempt to produce inline code for the mapping functions (mapc, mapcar, etc., but not mapatoms) if the function being mapped is an anonymous lambda-expression. This allows that function to reference the local variables of the enclosing function without the need for special declarations. The generated code is also more efficient. The default value is t.

Variable: all-special-switch
If this variable is non-nil, the compiler regards all variables as special, regardless of how they were declared. This provides compatibility with the interpreter at the cost of efficiency. The default is nil.

Variable: inhibit-style-warnings-switch
If this variable is non-nil, all compiler style-checking is turned off. Style checking is used to issue obsolete function warnings and won't-run-in-Maclisp warnings, and other sorts of warnings. The default value is nil. See also the inhibit-style-warnings macro, which acts on one level only of an expression.

Macro: compiler-let ((variable value)...) body...
Syntactically identical to let, compiler-let allows compiler switches to be bound locally at compile time, during the processing of the body forms.
 
Example:
(compiler-let ((open-code-map-switch nil))
          (map (function (lambda (x) ...)) foo))
will prevent the compiler from open-coding the map. When interpreted, compiler-let is equivalent to let. This is so that global switches which affect the behavior of macro expanders can be bound locally.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.5 Controlling Compiler Warnings

By controlling the compile-time values of the variables run-in-maclisp-switch, obsolete-function-warning-switch, and inhibit-style-warning-switch (explained above), you can enable or disable some of the warning messages of the compiler. The following special form is also useful:

Macro: inhibit-style-warnings form
Prevents the compiler from performing style-checking on the top level of form. Style-checking will still be done on the arguments of form. Both obsolete function warnings and won't-run-in-Maclisp warnings are done by means of the style-checking mechanism, so, for example,
 
(setq bar (inhibit-style-warnings (value-cell-location foo)))
will not warn that value-cell-location will not work in Maclisp, but
 
(inhibit-style-warnings (setq bar (value-cell-location foo)))
will warn, since inhibit-style-warnings applies only to the top level of the form inside it (in this case, to the setq).

Sometimes functions take argument that they deliberately do not use. Normally the compiler warns you if your program binds a variable that it never references. In order to disable this warning for variables that you know you are not going to use, there are two things you can do. The first thing is to name the variables ignore or ignored. The compiler will not complain if a variable by one of these names is not used. Furthermore, by special dispensation, it is all right to have more than one variable in a lambda-list that has one of these names. The other thing you can do is simply use the variable, for effect (ignoring its value), at the front of the function. Example:
 
(defun the-function (list fraz-name fraz-size)
  fraz-size       ; This argument is not used.
  ...)
This has the advantage that arglist (see (arglist-fun)) will return a more meaningful argument list for the function, rather than returning something with ignores in it. .vindex ignore

The following function is useful for requesting compiler warnings in certain esoteric cases. Normally, the compiler notices whenever any function x uses (calls) any other function y; it makes notes of all these uses, and then warns you at the end of the compilation if the function y got called but was neither defined nor declared (by *expr, see (*expr-fun)). This usually does what you want, but sometimes there is no way the compiler can tell that a certain function is being used. Suppose that instead of x's containing any forms that call y, x simply stores y away in a data structure somewhere, and someplace else in the program that data structure is accessed and funcall is done on it. There is no way that the compiler can see that this is going to happen, and so it can't notice the function usage, and so it can't create a warning message. In order to make such warnings happen, you can explicitly call the following function at compile-time.

Function: compiler:function-referenced what by
what is a symbol that is being used as a function. by may be any function spec. compiler:function-referenced must be called at compile-time while a compilation is in progress. It tells the compiler that the function what is referenced by by. When the compilation is finished, if the function what has not been defined, the compiler will issue a warning to the effect that by referred to the function what, which was never defined.

Special Form: compiler:make-obsolete function reason
This special form declares a function to be obsolete; code that calls it will get a compiler warning, under the control of obsolete-function-warning-switch. This is used by the compiler to mark as obsolete some Maclisp functions which exist in Zetalisp but should not be used in new programs. It can also be useful when maintaining a large system, as a reminder that a function has become obsolete and usage of it should be phased out. An example of an obsolete-function declaration is:
 
(compiler:make-obsolete create-mumblefrotz
	"use MUMBLIFY with the :FROTZ option instead")


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.6 "Compiler Source-Level Optimizers"

The compiler stores optimizers for source code on property lists so as to make it easy for the user to add them. An optimizer can be used to transform code into an equivalent but more efficient form (for example, (eq obj nil) is transformed into (null obj), which can be compiled better). An optimizer can also be used to tell the compiler how to compile a special form. For example, in the interpreter do is a special form, implemented by a function which takes quoted arguments and calls eval. In the compiler, do is expanded in a macro-like way by an optimizer, into equivalent Lisp code using prog, cond, and go, which the compiler understands.

The compiler finds the optimizers to apply to a form by looking for the compiler:optimizers property of the symbol which is the car of the form. The value of this property should be a list of optimizers, each of which must be a function of one argument. The compiler tries each optimizer in turn, passing the form to be optimized as the argument. An optimizer which returns the original form unchanged (eq to the argument) has "done nothing", and the next optimizer is tried. If the optimizer returns anything else, it has "done something", and the whole process starts over again. Only after all the optimizers have been tried and have done nothing is an ordinary macro definition processed. This is so that the macro definitions, which will be seen by the interpreter, can be overridden for the compiler by optimizers.

Optimizers should not be used to define new language features, because they only take effect in the compiler; the interpreter (that is, the evaluator) doesn't know about optimizers. So an optimizer should not change the effect of a form; it should produce another form that does the same thing, possibly faster or with less memory or something. That is why they are called optimizers. If you want to actually change the form to do something else, you should be using macros.

Special Form: compiler:add-optimizer function optimizer optimized-into...
Puts optimizer on function's optimizers list if it isn't there already. optimizer is the name of an optimization function, and function is the name of the function calls which are to be processed. Neither is evaluated.

(compiler:add-optimizer function optimizer optimize-into-1 optimize-into-2...) also remembers optimize-into-1, etc., as names of functions which may be called in place of function as a result of the optimization.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.7 "Files that Maclisp Must Compile"

Certain programs are intended to be run both in Maclisp and in Zetalisp. Their source files need some special conventions. For example, all special declarations must be enclosed in declares, so that the Maclisp compiler will see them. The main issue is that many functions and special forms of Zetalisp do not exist in Maclisp. It is suggested that you turn on run-in-maclisp-switch in such files, which will warn you about a lot of problems that your program may have if you try to run it in Maclisp.

The macro-character combination "#Q" causes the object that follows it to be visible only when compiling for Zetalisp. The combination "#M" causes the following object to be visible only when compiling for Maclisp. These work both on subexpressions of the objects in the file, and at top level in the file. To conditionalize top-level objects, however, it is better to put the macros if-for-lispm and if-for-maclisp around them. (You can only put these around a single object.) The if-for-lispm macro turns off run-in-maclisp-switch within its object, preventing spurious warnings from the compiler. The #Q macro-character cannot do this, since it can be used to conditionalize any S-expression, not just a top-level form.

To allow a file to detect what environment it is being compiled in, the following macros are provided:

Macro: if-for-lispm form
If (if-for-lispm form) is seen at the top level of the compiler, form is passed to the compiler top level if the output of the compiler is a QFASL file intended for Zetalisp. If the Zetalisp interpreter sees this it will evaluate form (the macro expands into form).

Macro: if-for-maclisp form
If (if-for-maclisp form) is seen at the top level of the compiler, form is passed to the compiler top level if the output of the compiler is a FASL file intended for Maclisp (e.g. if the compiler is COMPLR). If the Zetalisp interpreter sees this it will ignore it (the macro expands into nil).

Macro: if-for-maclisp-else-lispm maclisp-form lispm-form
If (if-for-maclisp-else-lispm form1 form2) is seen at the top level of the compiler, form1 is passed to the compiler top level if the output of the compiler is a FASL file intended for Maclisp; otherwise form2 is passed to the compiler top level.

Macro: if-in-lispm form
In Zetalisp, (if-in-lispm form) causes form to be evaluated; in Maclisp, form is ignored.

Macro: if-in-maclisp form
In Maclisp, (if-in-maclisp form) causes form to be evaluated; in Zetalisp, form is ignored.

When you have two definitions of one function, one conditionalized for one machine and one for the other, put them next to each other in the source file with the second "(defun" indented by one space, and the editor will put both function definitions on the screen when you ask to edit that function.

In order to make sure that those macros are defined when reading the file into the Maclisp compiler, you must make the file start with a prelude, which should look like:
 
(declare (cond ((not (status feature lispm))
                (load '|AI: LISPM2; CONDIT|))))
This will do nothing when you compile the program on the Lisp Machine. If you compile it with the Maclisp compiler, it will load in definitions of the above macros, so that they will be available to your program. The form (status feature lispm) is generally useful in other ways; it evaluates to t when evaluated on the Lisp machine and to nil when evaluated in Maclisp.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

22.8 Putting Data in QFASL Files

.setq fasdump section-page

It is possible to make a QFASL file containing data, rather than a compiled program. This can be useful to speed up loading of a data structure into the machine, as compared with reading in printed representations. Also, certain data structures such as arrays do not have a convenient printed representation as text, but can be saved in QFASL files. For example, the system stores fonts this way. Each font is in a QFASL file (on the LMFONT directory) which contains the data structures for that font. When the file is loaded, the symbol which is the name of the font gets set to the array which represents the font. Putting data into a QFASL file is often referred to as "fasdumping the data".

In compiled programs, the constants are saved in the QFASL file in this way. The compiler optimizes by making constants which are equal become eq when the file is loaded. This does not happen when you make a data file yourself; identity of objects is preserved. Note that when a QFASL file is loaded, objects that were eq when the file was written are still eq; this does not normally happen with text files.

The following types of objects can be represented in QFASL files: Symbols (but uninterned symbols will be interned when the file is loaded), numbers of all kinds, lists, strings, arrays of all kinds, instances, and FEFs.

When an instance is fasdumped (put into a QFASL file), it is sent a :fasd-form message, which must return a Lisp form which, when evaluated, will recreate the equivalent of that instance. This is because instances are often part of a large data structure, and simply fasdumping all of the instance variables and making a new instance with those same values is unlikely to work. Instances remain eq; the :fasd-form message is only sent the first time a particular instance is encountered during writing of a QFASL file. If the instance does not accept the :fasd-form message, it cannot be fasdumped.

Function: compiler:fasd-symbol-value filename symbol
Writes a QFASL file named filename which contains the value of symbol. When the file is loaded, symbol will be setq'ed to the same value. filename is parsed with the same defaults that load and qc-file use. The file type defaults to "qfasl".

Function: compiler:fasd-font name
Writes the font named name into a QFASL file with the appropriate name (on the LMFONT directory).

Function: compiler:fasd-file-symbols-properties filename symbols properties dump-values-p dump-functions-p new-symbol-function
This is a way to dump a complex data structure into a QFASL file. The values, the function definitions, and some of the properties of certain symbols are put into the QFASL file in such a way that when the file is loaded the symbols will be setqed, fdefined, and putproped appropriately. The user can control what happens to symbols discovered in the data structures being fasdumped.

filename is the name of the file to be written. It is parsed with the same defaults that load and qc-file use. The file type defaults to "qfasl".

symbols is a list of symbols to be processed. properties is a list of properties which are to be fasdumped if they are found on the symbols. dump-values-p and dump-functions-p control whether the values and function definitions are also dumped.

new-symbol-function is called whenever a new symbol is found in the structure being dumped. It can do nothing, or it can add the symbol to the list to be processed by calling compiler:fasd-symbol-push. The value returned by new-symbol-function is ignored.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Brad Parker on June, 13 2006 using texi2html