;;; -*- Mode:gate; Fonts:(HL12 HL12I HL12B CPTFONTB HL12BI HL12B HL12I ) -*- =Node: Compiler Source-Level Optimizers =Text: 3COMPILER 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, 2(eq 1obj* nil)* is transformed into 2(null 1obj*)*, 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 2do* is a special form, implemented by a function which takes quoted arguments and calls 2eval*. In the compiler, 2do* is expanded in a macro-like way by an optimizer into equivalent Lisp code using 2prog*, 2cond*, and 2go*, which the compiler understands. The compiler finds the optimizers to apply to a form by looking for the 2compiler:optimizers* property of the symbol that 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 that returns the original form unchanged (2eq* 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. 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. In principle, the code ought to compile just as correctly if the optimizer is eliminated. 3compiler:add-optimizer* 1function* 1optimizer* 1optimized-into...* 1Macro* Puts 1optimizer* on 1function*'s optimizers list if it isn't there already. 1optimizer* is the name of an optimization function, and 1function* is the name of the function calls which are to be processed. Neither is evaluated. 2(compiler:add-optimizer 1function* 1optimizer* 1optimize-into-1* 1optimize-into-2...*)* also remembers 1optimize-into-1*, etc., as names of functions which may be called in place of 1function* as a result of the optimization. Then 2who-calls* of 1function* will also mention callers of 1optimize-into-1*, etc. 3compiler:defoptimizer* 1function* 1optimizer-name* 1(optimizes-into...)* 1lambda-list* 1body...Macro* Defines an optimizer and installs it. Equivalent to 3(progn* 3 (defun 1optimizer-name* 1lambda-list** 3 1body*...)* 3 (compiler:add-optimizer 1function* 1optimizer-name** 3 1optimizes-into*...)) compiler:defcompiler-synonym* 1function* 1for-function* 1Macro* Makes 1function* a synonym for 1for-function* in code being compiled. Example: 3(compiler:defcompiler-synonym plus +)* is how the compiler is told how to compile 2plus*. =Node: Maclisp Compatibility =Text: 3MACLISP COMPATIBILITY* Certain programs are intended to be run both in Maclisp and in Zetalisp. Their source files need some special conventions. For example, all 2special* declarations must be enclosed in 2declare*'s, 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 2run-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 2#+lispm* causes the object that follows it to be visible only when compiling for Zetalisp. The combination 2#+maclisp* 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 2if-for-lispm* and 2if-for-maclisp* around them. The 2if-for-lispm* macro turns off 2run-in-maclisp-switch* within its object, preventing spurious warnings from the compiler. The 2#+lispm* reader construct does not dare do this, since it can be used to conditionalize any object, not just a expression that will be evaluated. To allow a file to detect what environment it is being compiled in, the following macros are provided: 3if-for-lispm* 1form* 1Macro* If 2(if-for-lispm 1form*)* is seen at the top level of the compiler, 1form* 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 evaluates 1form* (the macro expands into 1form*). 3if-for-maclisp* 1form* 1Macro* If 2(if-for-maclisp 1form*)* is seen at the top level of the compiler, 1form* 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 ignores this form entirely (the macro expands into 2nil*). 3if-for-maclisp-else-lispm* 1maclisp-form* 1lispm-form* 1Macro* If 2(if-for-maclisp-else-lispm 1form1* 1form2*)* is seen at the top level of the compiler, 1form1* is passed to the compiler top level if the output of the compiler is a FASL file intended for Maclisp; otherwise 1form2* is passed to the compiler top level. 3if-in-lispm* 1form* 1Macro* In Zetalisp, 2(if-in-lispm 1form*)* causes 1form* to be evaluated; in Maclisp, 1form* is ignored. 3if-in-maclisp* 1form* 1Macro* In Maclisp, 2(if-in-maclisp 1form*)* causes 1form* to be evaluated; in Zetalisp, 1form* is ignored. 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: 3(eval-when (compile)* 3 (cond ((not (status feature lispm))* 3 (load '|PS:CONDIT.LISP|))))* 3 ;; Or other suitable filename* This does nothing when you compile the program on the Lisp Machine. If you compile it with the Maclisp compiler, it loads in definitions of the above macros, so that they will be available to your program. The form 2(status feature lispm)* is generally useful in other ways; it evaluates to 2t* when evaluated on the Lisp Machine and to 2nil* when evaluated in Maclisp. There are some advertised variables whose compile-time values affect the operation of the compiler. Mostly these are for Maclisp compatibility features. You can set these variables by including in his file forms such as 3(eval-when (compile) (setq open-code-map-switch t))* However, these variables seem not to be needed very often. 3run-in-maclisp-switch* 1Variable* If this variable is non-2nil*, the compiler tries to warn the user about any constructs that will not work in Maclisp. By no means all Lisp Machine system functions not built in to Maclisp cause warnings; only those that could not be written by the user in Maclisp (for example, 2make-array*, 2value-cell-location*, etc.). Also, lambda-list keywords such as 2&optional* and initialized 2prog* variables are be mentioned. This switch also inhibits the warnings for obsolete Maclisp functions. The default value of this variable is 2nil*. 3obsolete-function-warning-switch* 1Variable* If this variable is non-2nil*, the compiler tries to warn the user whenever an obsolete Maclisp-compatibility function such as 2maknam* or 2samepnamep* is used. The default value is 2t*. 3allow-variables-in-function-position-switch* 1Variable* If this variable is non-2nil*, the compiler allows the use of the name of a variable in function position to mean that the variable's value should be 2funcall*'ed. This is for compatibility with old Maclisp programs. The default value of this variable is 2nil*. 3open-code-map-switch* 1Variable* If this variable is non-2nil*, the compiler attempts to produce inline code for the mapping functions (2mapc*, 2mapcar*, etc., but not 2mapatoms*) if the function being mapped is an anonymous lambda-expression. The generated code is faster but larger. The default value is 2t*. If you want to turn off open coding of these functions, It is preferable to use 2(declare* 2(notinline mapc mapcar ...))*. 3inhibit-style-warnings-switch* 1Variable* If this variable is non-2nil*, all compiler style-checking is turned off. Style checking is used to issue obsolete function warnings, won't-run-in-Maclisp warnings, and other sorts of warnings. The default value is 2nil*. See also the 2inhibit-style-warnings* macro, which acts on one level only of an expression. 3compiler-let* 1((variable* 1value)...)* 1body...* 1Macro* Allows local rebinding of global switches that affect either compilation or the behavior of user-written macros. Its syntax is like that of 2let*, and in the interpreter it is identical to 2let*. When encountered in compiled code, the variables are bound around the compilation of 1body* rather than around the execution at a later time of the compiled code for 1body*. For example, Example: 3(compiler-let ((open-code-map-switch nil))* 3 (mapc (function (lambda (x) ...)) foo))* prevents the compiler from open-coding the 2mapc*. The same results can be obtained more cleanly using 2declare*. User-written macros can examine the declarations using 2getdecl*. The next three functions are primarily for Maclisp compatibility. In Maclisp, they are declarations, used within a 2declare* at top level in the file. 3*expr* 1symbol...* 1Special Form* Declares each 1symbol* 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. 3*lexpr* 1symbol...* 1Special Form* Declares each 1symbol* 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. 3*fexpr* 1symbol...* 1Special Form* Declares each 1symbol* 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. =Node: Putting Data in QFASL Files =Text: 3PUTTING DATA IN QFASL FILES* 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 2SYS: FONTS;* directory) that contains the data structures for that font. When the file is loaded, the symbol that is the name of the font gets set to the array that represents the font. Putting data into a QFASL file is often referred to as ``1fasdumping* the data''. In compiled programs, the constants are saved in the QFASL file in this way. The compiler optimizes by making constants that are 2equal* become 2eq* 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 2eq* when the file was written are still 2eq*; this does not normally happen with text files. The following types of objects can be represented in QFASL files: Symbols (uninterned or uninterned), numbers of all kinds, lists, strings, arrays of all kinds, named structures, instances, and FEFs. 3:fasd-form* 1Operation on instances* When an instance is fasdumped (put into a QFASL file), it is sent a 2:fasd-form* message, which must return a Lisp form that, 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 2eq*; the 2: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 2:fasd-form* message, it cannot be fasdumped. Loading a QFASL file in which a named structure has been fasdumped creates a new named structure with components identical to those of the one that was dumped. Then the 2:fasd-fixup* operation is invoked, which gives the new structure the opportunity to correct its contents if they are not supposed to be just the same as what was dumped. The meaning of a QFASL file is greatly affected by the package used for loading it. Therefore, the file itself says which package to use. In 2dump-forms-to-file*, you can specify the package to use by including a 2:package* attribute in the 1attribute-list* argument. For example, if that argument is the list 2(:package "SI")* then the file is dumped and loaded in the 2si* package. If the package is not specified in this way, 2user* is used. The other fasdumping functions always use 2user*. 3dump-forms-to-file* 1filename* 1forms-list* &optional 1attribute-list* Writes a QFASL file named 1filename* which contains, in effect, the forms in 1forms-list*. That is to say, when the file is loaded, its effect will be the same as evaluating those forms. Example: 3(dump-forms-to-file "foo" '((setq x 1) (setq y 2)))* 3(load "foo")* 3x => 1* 3y => 2* 1attribute-list* is the file attribute list to store in the QFASL file. It is a list of alternating keywords and values, and corresponds to the 2-*-* line of a source file. The most useful keyword in this context is 2:package*, whose value in the attribute list specifies the package to be used both in dumping the forms and in loading the file. If no 2:package* keyword is present, the file will be loaded in whatever package is current at the time. 3compiler:fasd-symbol-value* 1filename* 1symbol* Writes a QFASL file named 1filename* which contains the value of 1symbol*. When the file is loaded, 1symbol* will be 2setq*'ed to the same value. 1filename* is parsed and defaulted with the default pathname defaults. The file type defaults to 2:qfasl*. 3compiler:fasd-font* 1name* Writes the font named 1name* into a QFASL file with the appropriate name (on the 2SYS:* 2FONTS;* directory). 3compiler:fasd-file-symbols-properties* 1filename* 1symbols* 1properties* 1dump-values-p* 1dump-functions-p* 1new-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 2setq*ed, 2fdefine*d, and 2putprop*ped appropriately. The user can control what happens to symbols discovered in the data structures being fasdumped. 1filename* is the name of the file to be written. It is defaulted with the default pathname defaults. The file type defaults to 2"QFASL"*. 1symbols* is a list of symbols to be processed. 1properties* is a list of properties which are to be fasdumped if they are found on the symbols. 1dump-values-p* and 1dump-functions-p* control whether the values and function definitions are also dumped. 1new-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 2compiler:fasd-symbol-push*. The value returned by 1new-symbol-function* is ignored. =Node: Analyzing QFASL Files =Text: 3ANALYZING QFASL FILES* QFASL files are composed of 16-bit nibbles. The first two nibbles in the file contain fixed values, which are there so the system can tell a proper QFASL file. The next nibble is the beginning of the first 1group*. A group starts with a nibble that specifies an operation. It may be followed by other nibbles that are arguments. Most of the groups in a QFASL file are there to construct objects when the file is loaded. These objects are recorded in the 1fasl-table*. Each time an object is constructed, it is assigned the next sequential index in the fasl-table. The indices are used by other groups later in the file, to refer back to objects already constructed. To prevent the fasl-table from becoming too large, the QFASL file can be divided into 1whacks*. The fasl-table is cleared out at the beginning of each whack. The other groups in the QFASL file perform operations such as evaluating a list previously constructed or storing an object into a symbol's function cell or value cell. If you are having trouble with a QFASL file and want to find out exactly what it does when it is loaded, you can use UNFASL to find out. 3si:unfasl-print* 1input-file-name* Prints on 2*standard-output** a description of the contents of the QFASL file 1input-file-name*. 3si:unfasl-file* 1input-file-name* &optional 1output-file-name* Writes a description of the contents of the QFASL file 1input-file-name* into the output file. The output file type defaults to 2:unfasl* and the rest of the pathname defaults from 1input-file-name*.