[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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] | [ ? ] |
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.
See also the disassemble function ((disassemble-fun)), which lists the instructions of a compiled function in symbolic form.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 ...) |
(eval-when (compile eval) (macro foo (x) (cadr x))) |
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 ...)
(declare (special ...)) or (declare (unspecial ...))
(declare anything-else)
(special ...) or (unspecial ...)
(macro ...) or (defmacro ...) or (defsubst ...)
(comment ...)
(compiler-let ((var val) ...) body...)
(local-declare (decl decl ...) body...)
(defflavor ...) or (defstruct ...)
(defun ...) or (defmethod ...) or (defselect ...)
anything-else
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 ...) |
But when seen by the compiler, this special form does the special things described above.
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] | [ ? ] |
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.
(local-declare (decl1 decl2 ...) form1 form2 ...) |
(special var1 var2 ...)
(unspecial var1 var2 ...)
(arglist . arglist)
(return-list . values)
(def name . definition)
The next three declarations are primarily for Maclisp compatibility.
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)) |
Example: (compiler-let ((open-code-map-switch nil)) (map (function (lambda (x) ...)) foo)) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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:
(setq bar (inhibit-style-warnings (value-cell-location foo))) |
(inhibit-style-warnings (setq bar (value-cell-location foo))) |
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. ...) |
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.
(compiler:make-obsolete create-mumblefrotz "use MUMBLIFY with the :FROTZ option instead") |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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.
(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] | [ ? ] |
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:
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|)))) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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.
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] | [ ? ] |