{Begin SubSec Block Compiling} {Title Block Compiling} {Text {Tag BlockCompiler} {Tag BlockCompiling} {index *BEGIN* block compiling} {note should put in some warnings about TCOMPLing and BCOMPLing and BRECOMPILEing the same file. Apparently, you can wind up with garbage if you don't do one thing consistantly} Block compiling provides a way of compiling several functions into a single block. Function calls between the component functions of the block are very fast. Thus, compiling a block consisting of just a single recursive function may be yield great savings if the function calls itself many times, e.g., {fn EQUAL}, {fn COPY}, and {fn COUNT} are block compiled in Interlisp-10. The output of a block compilation is a single, usually large, function. Calls from within the block to functions outside of the block look like regular function calls, except that they are usually linked (see {PageRef Tag LinkedFunctionCalls}). A block can be entered via several different functions, called {index *PRIMARY* entries (to a block)}entries.{foot Actually the block is entered the same as every other function, i.e., at the top. However, the entry functions call the main block with their name as one of its arguments, and the block dispatches on the name, and jumps to the portion of the block corresponding to that entry point. The effect is thus the same as though there were several different entry points. }{comment endfootnote} These must be specified when the block is compiled. For example, the error block has three entries, {lisp ERRORX}, {lisp INTERRUPT}, and {lisp FAULT1}. Similarly, the compiler block has nine entries. Note: In Interlisp-D, block compiling is handled somewhat differently; block compiling provides a mechanism for hiding function names internal to a block, but it does not provide a performance improvement. Block compiling in Interlisp-D works by automatically renaming the block functions with special names, and calling these functions with the normal function-calling mechanisms. Specifically, a function {arg FN} is renamed to {lisp \{arg BLOCK-NAME}/{arg FN}}. For example, function {lisp FOO} in block {lisp BAR} is renamed to "{lisp \BAR/FOO}". Note that it is possible with this scheme to break functions internal to a block. {Begin SubSec RETFNS} {Title RETFNS} {Text Another savings in block compilation arises from omitting most of the information on the stack about internal calls between functions in the block. However, if a function's name must be visible on the stack, e.g., if the function is to be returned from {fn RETFROM}, {fn RETTO}, {fn RETEVAL}, etc., it must be included on the list {index *PRIMARY* RETFNS Var}{var RETFNS}. }{End SubSec RETFNS} {Begin SubSec BLKAPPLYFNS} {Title BLKAPPLYFNS} {Text Normally, a call to {fn APPLY} from inside a block would be the same as a call to any other function outside of the block. If the first argument to {fn APPLY} turned out to be one of the entries to the block, the block would have to be reentered. {index *PRIMARY* BLKAPPLYFNS Var}{var BLKAPPLYFNS} enables a program to compute the name of a function in the block to be called next, without the overhead of leaving the block and reentering it. This is done by including on the list {var BLKAPPLYFNS} those functions which will be called in this fashion, and by using {index BLKAPPLY FN}{fn BLKAPPLY} in place of {index APPLY FN}{fn APPLY}, and {index BLKAPPLY* FN}{fn BLKAPPLY*} in place of {index APPLY* FN}{fn APPLY*}. If {fn BLKAPPLY} or {fn BLKAPPLY*} is given a function not on {var BLKAPPLYFNS}, the effect is the same as a call to {fn APPLY} or {fn APPLY*} and no error is generated. Note however, that {var BLKAPPLYFNS} must be set at {it compile} time, not run time, and furthermore, that all functions on {var BLKAPPLYFNS} must be in the block, or an error is generated (at compile time), {lisp NOT ON BLKFNS}.{index NOT ON BLKFNS Error} }{End SubSec BLKAPPLYFNS} {Begin SubSec BLKLIBRARY} {Title BLKLIBRARY} {Text Compiling a function open via a macro provides a way of eliminating a function call. For block compiling, the same effect can be achieved by including the function in the block. A further advantage is that the code for this function will appear only once in the block, whereas when a function is compiled open, its code appears at each place where it is called. The block library{index block library} feature provides a convenient way of including functions in a block. It is just a convenience since the user can always achieve the same effect by specifying the function(s) in question as one of the block functions, provided it has an {lisp EXPR} definition at compile time. The block library feature simply eliminates the burden of supplying this definition. To use the block library feature, place the names of the functions of interest on the list {index *PRIMARY* BLKLIBRARY Var}{var BLKLIBRARY}, and their {index EXPR Prop}{prop EXPR} definitions on the property list of the functions under the property {index *PRIMARY* BLKLIBRARYDEF Prop}{prop BLKLIBRARYDEF}. When the block compiler compiles a form, it first checks to see if the function being called is one of the block functions. If not, and the function is on {var BLKLIBRARY}, its definition is obtained from the property value of {prop BLKLIBRARYDEF}, and it is automatically included as part of the block. The functions {fn ASSOC}, {fn EQUAL}, {fn GETPROP}, {fn LAST}, {fn LENGTH}, {fn LISPXWATCH}, {fn MEMB}, {fn MEMBER}, {fn NCONC1}, {fn NLEFT}, {fn NTH}, {fn /RPLNODE}, and {fn TAILP} already have {index BLKLIBRARYDEF Prop}{prop BLKLIBRARYDEF} properties. }{End SubSec BLKLIBRARY} {Begin SubSec Block Declarations} {Title Block Declarations} {Text {Tag BlockDeclarations} {index *BEGIN* block declarations} {index *PRIMARY* block declarations} Block compiling a file frequently involves giving the compiler a lot of information about the nature and structure of the compilation, e.g., block functions, entries, specvars, linking, etc. To help with this, there is the {filecom BLOCKS} file package command{index BLOCKS FileCom} ({PageRef FileCom BLOCKS}), which has the form: {lispcode (BLOCKS {arg BLOCK{sub 1}} {arg BLOCK{sub 2}} {ellipsis} {arg BLOCK{sub N}})} where each {arg BLOCK{sub i}} is a block declaration. The {index BLOCKS FileCom}{filecom BLOCKS} command outputs a {index DECLARE: filecom}{filecom DECLARE:} expression, which is noticed by {fn BCOMPL} and {fn BRECOMPILE}. {index BCOMPL FN}{fn BCOMPL} and {index BRECOMPILE FN}{fn BRECOMPILE} are sensitive to these declarations and take the appropriate action. Note: Masterscope includes a facility for checking the block declarations of a file or files for various anomalous conditions, e.g. functions in block declarations which aren't on the file(s), functions in {lisp ENTRIES} not in the block, variables that may not need to be {lisp SPECVARS} because they are not used freely below the places they are bound, etc. See {PageRef Tag MASTERSCOPE} The form of a block declaration is: {lispcode ({arg BLKNAME} {arg BLKFN{sub 1}} {ellipsis} {arg BLKFN{sub M}} ({arg VAR{sub 1}} . {arg VALUE{sub 1}}) {ellipsis} ({arg VAR{sub N}} . {arg VALUE{sub N}}))} {arg BLKNAME} is the name of a block. {lisp {arg BLKFN{sub 1}} {ellipsis} {arg BLKFN{sub M}}} are the functions in the block and correspond to {arg BLKFNS} in the call to {index BLOCKCOMPILE FN}{fn BLOCKCOMPILE}. The ({arg VAR{sub i}} . {arg VALUE{sub i}}) expressions indicate the settings for variables affecting the compilation of that block. If {arg VALUE{sub i}} is atomic, then {arg VAR{sub i}} is set to {arg VALUE{sub i}} (e.g. {lisp (LINKFNS . T)}), otherwise {arg VAR{sub i}} is set to the {fn UNION} of {arg VALUE{sub i}} and the current value of the variable {arg VAR{sub i}}. Also, expressions of the form {lisp ({arg VAR} * {arg FORM})} will cause {arg FORM} to be evaluated and the resulting list used as described above (e.g. {lisp (GLOBALVARS * MYGLOBALVARS)}). As an example, one of the block definitions for the editor is shown below. The block name is {lisp EDITBLOCK}, it includes a number of functions ({lisp EDITL0}, {lisp EDITL1}, {ellipsis} {lisp EDITH}), and it sets the variables {lisp ENTRIES}, {lisp SPECVARS}, {lisp RETFNS}, {lisp GLOBALVARS}, {lisp BLKAPPLYFNS}, {lisp BLKLIBRARY}, and {lisp NOLINKFNS}: {lispcode (EDITBLOCK EDITL0 EDITL1 UNDOEDITL EDITCOM EDITCOMA EDITCOML EDITMAC EDITCOMS EDIT]UNDO UNDOEDITCOM UNDOEDITCOM1 EDITSMASH EDITNCONC EDIT1F EDIT2F EDITNTH BPNT BPNT0 BPNT1 RI RO LI LO BI BO EDITDEFAULT ## EDUP EDIT* EDOR EDRPT EDLOC EDLOCL EDIT: EDITMBD EDITXTR EDITELT EDITCONT EDITSW EDITMV EDITTO EDITBELOW EDITRAN TAILP EDITSAVE EDITH (ENTRIES EDITL0 ## UNDOEDITL) (SPECVARS L COM LCFLG #1 #2 #3 LISPXBUFS **COMMENT**FLG PRETTYFLG UNDOLST UNDOLST1) (RETFNS EDITL0) (GLOBALVARS EDITCOMSA EDITCOMSL EDITOPS HISTORYCOMS EDITRACEFN) (BLKAPPLYFNS RI RO LI LO BI BO EDIT: EDITMBD EDITMV EDITXTR) (BLKLIBRARY LENGTH NTH LAST) (NOLINKFNS EDITRACEFN))} Whenever {fn BCOMPL} or {fn BRECOMPILE} encounter a block declaration, they rebind {index RETFNS Var}{var RETFNS}, {index SPECVARS Var}{var SPECVARS}, {index GLOBALVARS Var}{var GLOBALVARS}, {index BLKLIBRARY Var}{var BLKLIBRARY}, {index NOLINKFNS Var}{var NOLINKFNS}, {index LINKFNS Var}{var LINKFNS}, and {var DONTCOMPILEFNS}{index DONTCOMPILEFNS Var} to their top level values, bind {index BLKAPPLYFNS Var}{var BLKAPPLYFNS} and {index ENTRIES Var}{var ENTRIES} to {lisp NIL}, and bind {var BLKNAME}{index BLKNAME Var} to the first element of the declaration. They then scan the rest of the declaration, setting these variables as described above. When the declaration is exhausted, the block compiler is called and given {var BLKNAME}, the list of block functions, and {var ENTRIES}. If a function appears in a block declaration, but is not defined in one of the files, then if it has an in-core definition, this definition is used and a message printed {index NOT ON FILE, COMPILING IN CORE DEFINITION Error}{lisp NOT ON FILE, COMPILING IN CORE DEFINITION}. Otherwise, the message {index NOT COMPILEABLE Error}{lisp NOT COMPILEABLE}, is printed and the block declaration processed as though the function were not on it, i.e. calls to the function will be compiled as external function calls. Note that since all compiler variables are rebound for each block declaration, the declaration only has to set those variables it wants {it changed}. Furthermore, setting a variable in one declaration has no effect on the variable's value for another declaration. After finishing all blocks, {index BCOMPL FN}{fn BCOMPL} and {index BRECOMPILE FN}{fn BRECOMPILE} treat any functions in the file that did not appear in a block declaration in the same way as do {index TCOMPL FN}{fn TCOMPL} and {index RECOMPILE FN}{fn RECOMPILE}. If the user wishes a function compiled separately as well as in a block, or if he wishes to compile some functions (not blockcompile), with some compiler variables changed, he can use a special pseudo-block declaration of the form {lispcode (NIL {arg BLKFN{sub 1}} {ellipsis} {arg BLKFN{sub M}} ({arg VAR{sub 1}} . {arg VALUE{sub 1}}) {ellipsis} ({arg VAR{sub N}} . {arg VALUE{sub N}}))} {index NIL (in Block Declarations)} which means that {arg BLKFN{sub 1}} {ellipsis} {arg BLKFN{sub M}} should be compiled after first setting {arg VAR{sub 1}} {ellipsis} {arg VAR{sub N}} as described above. For example, {lispcode (NIL CGETD FNTYP ARGLIST NARGS NCONC1 GENSYM (LINKFNS . T))} appearing as a "block declaration" will cause the six indicated functions to be compiled while {index LINKFNS Var}{var LINKFNS}={lisp T} so that all of their calls will be linked (except for those functions on {index NOLINKFNS Var}{var NOLINKFNS}). {index *END* block declarations} }{End SubSec Block Declarations} {Begin SubSec Block Compiling Functions} {Title Block Compiling Functions} {Text {index *BEGIN* block compiling functions} {index *BEGIN* compiler functions} There are three user level functions for block compiling, {fn BLOCKCOMPILE}, {fn BCOMPL}, and {fn BRECOMPILE}, corresponding to {fn COMPILE}, {fn TCOMPL}, and {fn RECOMPILE}. All of them ultimately call the same low level functions in the compiler, i.e., there is no "block compiler" per se. Instead, when block compiling, a flag is set to enable special treatment for {index SPECVARS Var}{var SPECVARS}, {index RETFNS Var}{var RETFNS}, {index BLKAPPLYFNS Var}{var BLKAPPLYFNS}, and for determining whether or not to link a function call. Note that all of the remarks on macros, globalvars, compiler messages, etc., all apply equally for block compiling. Using block declarations, the user can intermix in a single file functions compiled normally, functions compiled normally with linked calls, and block compiled functions. {FnDef {FnName BLOCKCOMPILE} {FnArgs BLKNAME BLKFNS ENTRIES FLG} {Text {arg BLKNAME} is the name of a block, {arg BLKFNS} is a list of the functions comprising the block, and {arg ENTRIES} a list of entries to the block.{index entries (to a block)} Each of the entries must also be on {arg BLKFNS} or an error is generated, {lisp NOT ON BLKFNS}.{index NOT ON BLKFNS Error} If only one entry is specified, the block name can also be one of the {arg BLKFNS}, e.g., {lisp (BLOCKCOMPILE 'FOO '(FOO FIE FUM) '(FOO))}. However, if more than one entry is specified, an error will be generated, {lisp CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME}.{index CAN'T BE BOTH AN ENTRY AND THE BLOCK NAME Error} If {arg ENTRIES} is {lisp NIL}, {lisp (LIST {arg BLKNAME})} is used, e.g., {lisp (BLOCKCOMPILE 'COUNT '(COUNT COUNT1))} If {arg BLKFNS} is {lisp NIL}, {lisp (LIST {arg BLKNAME})} is used, e.g., {lisp (BLOCKCOMPILE 'EQUAL)} {fn BLOCKCOMPILE} asks the standard compiler questions and then begins compiling. As with {fn COMPILE}, if the compiled code is being written to a file, the file is closed unless {arg FLG}={lisp T}. The value of {fn BLOCKCOMPILE} is a list of the {index entries (to a block)}entries, or if {arg ENTRIES}={lisp NIL}, the value is {arg BLKNAME}. The output of a call to {fn BLOCKCOMPILE} is one function definition for {arg BLKNAME}, plus definitions for each of the functions on {arg ENTRIES} if any. These entry functions are very short functions which immediately call {arg BLKNAME}. }} {index *BEGIN* compiling files} {FnDef {FnName BCOMPL} {FnArgs FILES CFILE {anonarg} {anonarg}} {Text {arg FILES} is a list of symbolic files (if atomic, {lisp (LIST {arg FILES})} is used). {fn BCOMPL} differs from {index TCOMPL FN}{fn TCOMPL} in that it compiles all of the files at once, instead of one at a time, in order to permit one block to contain functions in several files. (If you have several files to be {fn BCOMPL}ed {it separately}, you must make several calls to {fn BCOMPL}.) Output is to {arg CFILE} if given, otherwise to a file whose name is {lisp (CAR {arg FILES})} suffixed with {index DCOM (as suffix to file name)}{lisp DCOM}. For example, {lisp (BCOMPL '(EDIT WEDIT))} produces one file, {lisp EDIT.DCOM}. {fn BCOMPL} asks the standard compiler questions, except for "{lisp OUTPUT FILE:}", then processes each file exactly the same as {fn TCOMPL} ({PageRef Fn TCOMPL}). {fn BCOMPL} next processes the block declarations as described above. Finally, it compiles those functions not mentioned in one of the block declarations, and then writes out all other expressions. If {it any} of the files have property {prop FILETYPE} with value {lisp CLISP}, or a list containing {lisp CLISP}, then {index DWIMIFYCOMPFLG Var}{var DWIMIFYCOMPFLG} is rebound to {lisp T} for {it all} of the files. See {PageRef Tag CompilingCLISP}. The value of {fn BCOMPL} is the output file (the new compiled file). If the compilation is aborted due to an error or control-D, all files are closed and the (partially complete) output file is deleted. Note that it is permissible to {index TCOMPL FN}{fn TCOMPL} files set up for {fn BCOMPL}; the block declarations will simply have no effect. Similarly, you can {index BCOMPL FN}{fn BCOMPL} a file that does not contain any block declarations and the result will be the same as having {fn TCOMPL}ed it. }} {Begin Note} Date: 6 DEC 1979 2107-PST From: TEITELMAN i fixed errortypelst so that if a file wont open error occurs when no version was specified, it will try the next higher version. this will fix the problem of having a compilatin going in a higher fork, doing an exec and trying to compile something and getting a file wont open error on bcompl.scratch. i also fixed bcompl so that it does not save undo informaton when it is actually compiling (since it is compiling from the file anyway). this should save some space when compiling large files with lots of clisp. {End Note} {FnDef {FnName BRECOMPILE} {FnArgs FILES CFILE FNS {anonarg}} {Text {fn BRECOMPILE} plays the same role for {fn BCOMPL} that {fn RECOMPILE} plays for {fn TCOMPL}. Its purpose is to allow the user to update a compiled file without requiring an entire {fn BCOMPL}. {arg FILES} is a list of symbolic files (if atomic, {lisp (LIST {arg FILES})} is used). {arg CFILE} is the compiled file produced by {fn BCOMPL} or a previous {fn BRECOMPILE} that contains compiled definitions that may be copied. The interpretation of {arg FNS} is the same as with {fn RECOMPILE}. {fn BRECOMPILE} asks the standard compiler questions except for "{lisp OUTPUT FILE:}". As with {fn BCOMPL}, output automatically goes to {lisp {arg FILE}.DCOM}, where {arg FILE} is the first file in {arg FILES}. {fn BRECOMPILE} processes each file the same as {fn RECOMPILE} ({PageRef Fn RECOMPILE}), then processes each block declaration. If {it any} of the functions in the block are to be recompiled, the entire block must be (is) recompiled. Otherwise, the block is copied from {arg CFILE} as with {fn RECOMPILE}. For pseudo-block declarations of the form {lisp (NIL {arg FN1} {ellipsis})}, all variable assignments are made, but only those functions indicated by {arg FNS} are recompiled. After completing the block declarations, {fn BRECOMPILE} processes all functions that do not appear in a block declaration, recompiling those dictated by {arg FNS}, and copying the compiled definitions of the remaining from {arg CFILE}. Finally, {fn BRECOMPILE} writes onto the output file the "other expressions" collected in the initial scan of {arg FILES}. The value of {fn BRECOMPILE} is the output file (the new compiled file). If the compilation is aborted due to an error or control-D, all files are closed and the (partially complete) output file is deleted. If {arg CFILE}={lisp NIL}, the old version of {lisp {arg FILE}.DCOM} is used, as with {fn RECOMPILE}. In addition, if {arg FNS} and {arg CFILE} are both {lisp NIL}, {arg FNS} is set to the value of {index RECOMPILEDEFAULT Var}{var RECOMPILEDEFAULT}, initially {lisp EXPRS}. {note apparently, it doesn't check that CFILE has anything to do with FILE. warning??} }} {index *END* block compiling functions} }{End SubSec Block Compiling Functions} {index *END* block compiling} }{End SubSec Block Compiling}