Add JMiller's comments and make some edits incorporating some
authorGuillermo J. Rozas <edu/mit/csail/zurich/gjr>
Fri, 29 Mar 1991 01:27:27 +0000 (01:27 +0000)
committerGuillermo J. Rozas <edu/mit/csail/zurich/gjr>
Fri, 29 Mar 1991 01:27:27 +0000 (01:27 +0000)
suggestions by JMiller, MarkF, and Arthur.

v7/src/compiler/documentation/porting.guide

index 8f7663283882a2de4212b69e9ce885dbe50e0258..bafb8ab56faacc105fb2f407884a216fb0c134dc 100644 (file)
@@ -1,6 +1,6 @@
 Emacs: Please use -*- Text -*- mode.  Thank you.
 
-$Header: /Users/cph/tmp/foo/mit-scheme/mit-scheme/v7/src/compiler/documentation/porting.guide,v 1.18 1991/03/06 03:46:47 jinx Exp $
+$Header: /Users/cph/tmp/foo/mit-scheme/mit-scheme/v7/src/compiler/documentation/porting.guide,v 1.19 1991/03/29 01:27:27 jinx Exp $
 
 Copyright (c) 1991 Massachusetts Institute of Technology
 
@@ -43,11 +43,11 @@ Text tagged by ==> is intended primarily for the compiler developers.
 
 Good luck!
 
-[*Markf: A section outlining a procedure to use for actually doing
+ [*Markf: A section outlining a procedure to use for actually doing
 the port (what should be done and when, how to debug ...) would be
 useful]
 
-[*Markf: a discussion (or at least a mention) of the stuff in
+ [*Markf: A discussion (or at least a mention) of the stuff in
 base/debug.scm would be useful] 
 
                Acknowledgments
@@ -120,9 +120,10 @@ its pass structure.
 \f
        0.1.  Liar's package structure
 
-[*Artur: What is a package and what are the basic commands for moving
+ [*Artur: What is a package and what are the basic commands for moving
 between packages?  Give a brief introduction to the structure of .pkg
-files (forward pointer).  At least tell where to find this information.]
+files (forward pointer).  At least tell where to find this
+information.]
 
 The package structure of the compiler reflects the pass structure and
 is specified in compiler/machines/port/comp.pkg, where port is the
@@ -299,7 +300,7 @@ other words, segmented address spaces with segments necessarily
 smaller than the Scheme runtime heap will make Liar very hard or
 inefficient to port.
 
-[*Markf: Insert short description of the assumptions in what follows:]
+ [*Markf: Insert short description of the assumptions in what follows:]
 - Liar assumes that code and data can coexist in the same address
 space.  In other words, a true Harvard architecture, with separate
 code and data spaces, would be hard to support without relatively
@@ -450,9 +451,9 @@ expressions untouched, or to be simplified in different ways,
 depending on the availability of memory operands or richer addressing
 modes.  Since these rules vary from port to port, the final RTL
 differs for the different ports.
-[*Markf: note also that the simplification is constrained by
-the kinds of RTL expressions that the LAP rules for a particular port
-will accept.]
+ [*Markf: Note also that the simplification is constrained by the
+kinds of RTL expressions that the LAP rules for a particular port will
+accept.]
 
 - The open coding of Scheme primitives is port-dependent.  On some
 machines, for example, there is no instruction to multiply integers,
@@ -553,8 +554,15 @@ writer:
 compiler should compile each top-level lambda expression independently
 or compile the whole input program (or file) as a block.  It is
 usually set to true, but must be set to false for cross-compilation.
-The cross-compiler does this automatically.
-[*Markf: Why does cross-compilation set it this way?]
+The cross-compiler does this automatically.  (Foot)
+(Foot) The reason for this is that the gc offset words in compiled
+entry points may differ for the source and target machines, and thus
+the source machine's garbage collector may be confused by target
+machine's compiled entry points.  This is circumvented by having the
+cross compiler generate a single compiled code block object,
+manipulated and dumped as a vector object (instead of as an entry
+point).  The final entry points are generated by
+cross-compile-bin-file-end running interpreted on the target machine.
 
 * compiler:open-code-primitives? This switch controls whether Liar
 will open code (inline code) MIT Scheme primitives.  It is usually set
@@ -620,7 +628,7 @@ the originals that would make updating your port easier.
 \f
        4.1 Compiler building files:
 
-[*Arthur: Make separate entries for comp.con and comp.ldr in the list
+ [*Arthur: Make separate entries for comp.con and comp.ldr in the list
 of files under.]
 
 * comp.pkg:
@@ -772,12 +780,11 @@ should probably be copied verbatim.
 microcode/cmpint-md.h, and is explained in microcode/cmpint.txt .
 ==> We should probably rename one or the other to be alike.
 
-The following parameters specify the format of closures containing
-multiple entry points to the front-end of the compiler.  These
-closures are described in some detail in microcode/cmpint.txt and in
-more detail in the section that explains the rules used to generate
-such objects.
-[*Arthur: What is a closure?]
+The following parameters specify to the front-end the format of
+closures containing multiple entry points.  Closures are described in
+some detail in microcode/cmpint.txt and in section 5.3.3.  Very
+briefly, a closure is a procedure object that contains a code pointer
+and a set of free variable values or locations.
        
 - closure-object-first-offset: This procedure takes a single argument,
 the number of entry points in a closure object, and computes the
@@ -807,7 +814,7 @@ of them.  It computes the number of bytes that must be added to the
 entry point's address to result in the entry point's environment
 pointer.  If entry points are always aligned on long-word boundaries,
 this number should always be zero, otherwise it should be the distance
-to the zeroth entry point.
+to the first (lowest addressed) entry point.
 
 The remaining code in machin.scm describes the register set of the
 architecture and defines the register conventions imposed by the port.
@@ -931,8 +938,9 @@ inherent register types (typically GENERAL and FLOAT).
 - REGISTER-TYPES-COMPATIBLE? is a boolean procedure that decides
 whether two registers can hold the same range of values.
 
-- REGISTER-REFERENCE maps RTL register numbers into pieces of assembly
-language used to refer to those registers.
+- REGISTER-REFERENCE maps RTL register numbers into register
+references, ie. pieces of assembly language used to refer to those
+registers.
 
 - REGISTER->REGISTER-TRANSFER issues code to copy the contents of one
 RTL register into another.
@@ -1141,7 +1149,7 @@ of VARIABLE-WIDTH as the keyword for this syntax.
 \f
 * inerly.scm:
        This file provides alternative expanders for the port-specific
-syntax.  This alternative expanders are used when the assembly
+syntax.  These alternative expanders are used when the assembly
 language that appears in the LAPGEN rules is assembled (early) at
 compiler pre-processing time.  That is, the procedures defined in this
 file are only used if COMPILER:ENABLE-EXPANSION-DECLARATIONS? is set
@@ -1296,9 +1304,11 @@ For example,
                      (number? divisor)
                      (zero? (remainder number divisor))))
       <rule body>)
-will match (MULTIPLE 14 7) and (MULTIPLE 36 4), but not (MULTIPLE 2)
-(MULTIPLE 14 2 3), (MULTIPLE FOO 3), or (HELLO 14 7).
-Note that rules need not have qualifiers
+
+will match (MULTIPLE 14 7) and (MULTIPLE 36 4), but will not match
+(MULTIPLE FOO 3), (MULTIPLE 37 4), (MULTIPLE 2), (MULTIPLE 14 2 3),
+nor (HELLO 14 7).
+Note that rules need not have qualifiers.
 
 * <rule body> is an arbitrary Lisp expression whose value is the
 translation determined by the rule.  It will typically use the
@@ -1339,9 +1349,9 @@ firing the corresponding body.
 
 The bodies are defined in terms of the WORD syntax defined in
 insmac.scm, and the ``commas'' used with the pattern variables in the
-rule bodies are a consequence of the WORD syntax.
-[*Arthur: Refer to backquote syntax for more information?  Forward
-pointer to 5.3.1.]
+rule bodies are a consequence of the WORD syntax.  The meaning of the
+commas is identical to the meaning of the commas in a ``backquote''
+Scheme expression, and is briefly described in section 5.3.1.
 \f
        5.2 Rule variable syntax.
 
@@ -1454,18 +1464,25 @@ data.
 
 The register allocator manipulates RTL registers, but there are also
 wrappers for the common cases that return and manipulate register
-references.  If you have carefully chosen your RTL register numbers
-for machine registers to match the hardware numbering, and your
-assembly language does not distinguish between references to a
-register and other fields, you can ignore register references and use
-the RTL register numbers directly.  This is a common situation with
-load-store architectures.
+references.  Register references are fragments of assembly lanuage
+that refer to the registers.  For example, on the MC68k, register d3
+is represented as RTL register number 3, and a register reference for
+it would be (d 3).
+
+If you have carefully chosen your RTL register numbers for machine
+registers to match the hardware numbering, and your assembly language
+does not distinguish between references to a register and other
+fields, you can ignore register references and use the RTL register
+numbers directly.  This is a common situation with load-store
+architectures.
 
 The interface to the register allocator is defined in
 compiler/back/lapgn2.scm.  Not all ports use all of the procedures
 defined there.  Often a smaller subset is sufficient depending on
 whether there are general addressing modes, etc.  A list of
 the most frequently used follows:
+ [*JMiller: I'd like a picture showing RTL reg., hardware reg., map,
+etc.]
 
 * LOAD-ALIAS-REGISTER! expects an RTL register and a register type and
 returns a machine register of the specified type that is an alias for
@@ -1480,22 +1497,21 @@ only alias for the RTL register and should be written with the new
 contents of the RTL register.  ALLOCATE-ALIAS-REGISTER! is used to
 generate aliases for target RTL registers.  REFERENCE-TARGET-ALIAS!
 performs the same action but returns a register reference instead of
-an RTL register number.
-[*Arthur: Include forward reference to CLEAR-REGISTERS!]
+an RTL register number.  See CLEAR-REGISTERS! below.
 
 * STANDARD-REGISTER-REFERENCE expects an RTL register, a register
 type, and a boolean.  It will return a reference for an alias of the
 specified register containing the current value of the RTL register.
 This reference will be of the specified type if the boolean is false,
 or sometimes of other types if the boolean is true.  In other words,
-the boolean argument determined whether other types are acceptable,
+the boolean argument determines whether other types are acceptable,
 although not desirable.  The register type may be false, specifying
 that there really is no preference for the type, and any reference is
 valid.  Note that STANDARD-REGISTER-REFERENCE should be used only for
 source pseudo-registers (i.e. those that already contain data), and may
 return a memory reference for those machines with general addressing
 modes if there is no preferred type or alternates are acceptable.
-
+\f
 * MOVE-TO-ALIAS-REGISTER! expects a source RTL register, a register
 type, and a target RTL register.  It returns a new alias for the
 target of the specified type containing a copy of the current contents
@@ -1505,20 +1521,22 @@ alias for target.
 
 * MOVE-TO-TEMPORARY-REGISTER! expects a source RTL register and a
 register type and returns an appropriate register containing a copy of
-the source.  The register is intended for temporary use, and
-MOVE-TO-TEMPORARY-REGISTER! attempts to reuse an existing alias for
-the source RTL register.
-[*Markf: What does temporary mean?]
-\f
+the source.  The register is intended for temporary use, that is, use
+only within the code generated by the expansion of the current RTL
+instruction.  The register becomes automatically available for
+subsequent RTL instructions.  MOVE-TO-TEMPORARY-REGISTER! attempts to
+use an existing alias for the source RTL register if it is not the
+last remaining alias or the value of the source is not needed
+later.
+
 * REUSE-PSEUDO-REGISTER-ALIAS! expects an RTL register, a register
-type, and two continuations.  It attempts to find a reusable alias for
+type, and two procedures.  It attempts to find a reusable alias for
 the RTL register of the specified type, and invokes the first
-continuation passing it the alias if it succeeds, or the second
-continuation with no arguments if it fails.  MOVE-TO-ALIAS-REGISTER!
+procedure passing it the alias if it succeeds, or the second
+procedure with no arguments if it fails.  MOVE-TO-ALIAS-REGISTER!
 and MOVE-TO-TEMPORARY-REGISTER! are written in terms of
 REUSE-PSEUDO-REGISTER-ALIAS! but occasionally neither meets the
 requirements.
-[*Markf: continuations? really?]
 
 * NEED-REGISTER! expects an RTL machine register and informs the
 register allocator that the rule in use requires that register so it
@@ -1531,10 +1549,10 @@ but you may occasionally need to invoke it explicitly.
 
 * LOAD-MACHINE-REGISTER! expects an RTL register and an RTL machine
 register and generates code that copies the current value of the RTL
-register to the machine register.  It is used to pass arguments on
+register to the machine register.  It is used to pass arguments in
 registers to out-of-line code, typically in the compiled code runtime
 library.
-[*Markf: Explain the register map.]
+ [*Markf: Explain the register map.]
 
 * ADD-PSEUDO-REGISTER-ALIAS! expects an RTL pseudo-register and an
 available machine register (no longer an alias), and makes the
@@ -1551,7 +1569,7 @@ register have been saved if necessary.
 contents held in aliases into the memory homes if needed.
 This procedure returns an assembly language code fragment, and is
 typically used before invoking out-of-line code.
-
+\f
 * DELETE-DEAD-REGISTERS! informs the register allocator that RTL
 pseudo registers whose contents will not be needed after the RTL rule
 being translated can be eliminated from the register map and their
@@ -1573,66 +1591,129 @@ while on a load-store architecture we might define
       (delete-dead-registers!)
       (allocate-alias-register! rtl-reg 'GENERAL))
 
-This example brings up the most important rule to be followed when
-writing RTL assignment rules: Any rule that writes an RTL
-pseudo-register MUST invoke DELETE-DEAD-REGISTERS! after allocating
-aliases for the necessary sources but before allocating an alias for
-the target. Rules frequently expand into the following pattern:
+- VERY IMPORTANT: -
+This example brings up the cardinal rule of RTL assignment rules: Any rule
+that writes an RTL pseudo-register MUST invoke DELETE-DEAD-REGISTERS!
+after allocating aliases for the necessary sources but before
+allocating an alias for the target.  Rules frequently expand into the
+following pattern:
     (let* ((r1 (standard-source source1))
           (r2 (standard-source source2))
           (rt (standard-target target)))
       (LAP ...))
 
-Note that MOVE-TO-ALIAS-REGISTER! already invokes
-DELETE-DEAD-REGISTERS! because it simultaneously allocates an alias
-for a source and for a target.
+Note that LET would not work in the above example since Scheme does not
+specify the order of argument evaluation, and Liar chooses arbitrary
+orders.
+
+MOVE-TO-ALIAS-REGISTER! invokes DELETE-DEAD-REGISTERS! because it
+simultaneously allocates an alias for a source and for a target.
 \f
        5.3.3 Invocation rules, etc.
 
 The meaning and intent of most statement rules in an existing port is
-readily apparent.  There are some rules, however, whose meaning is not
-so obvious.  Most of these are written assuming some understanding of
-some of the concepts in the implementation.  The more arcane rules
-have to do with procedures and the representation of numbers.
+readily apparent.  The more arcane rules have to do with procedures
+and the representation of numbers.  What follows is a description of
+some of the more obscure rules related to procedures and some of the
+implementation concepts required to understand them.
+
+In the invocation rules, FRAME-SIZE is the number of arguments passed
+in the call (often plus one), and there is often more than one rule
+with the same keyword, typically to handle the common cases (small
+FRAME-SIZE) more efficiently.
+
+Various of the rules specify the number of arguments that the
+resulting procedure will accept.  The range is described in terms of
+two parameters, MIN and MAX:
+ - MIN is always positive and it is one greater than the smallest
+number of arguments allowed.
+ - MAX may be positive or negative.  If positive, it is one greater
+than the largest number of arguments allowed.  If negative, it
+indicates that the procedure will accept an unbounded number of
+arguments, and the absolute value of MAX minus (MIN + 1) is the number
+of positional optional parameters.  Either way, the absolute value of
+MAX is the size of the procedure's call frame counting the procedure
+itself.
+ These two values are encoded into the format word of the resulting
+procedures so that dynamic APPLY can check the number of arguments
+passed and reformat the stack appropriately.
+ Non-positive MINs are used to indicate that the compiled entry point
+is not a procedure, but a return address, a compiled expression, or a
+pointer to an internal label.
+
+The CONS-CLOSURE rules will dynamically create some new instructions
+in the runtime heap, and these instructions must be visible to
+the processor's instruction cache.  On machines where the programmer
+is given no control over the caches, this may be impossible.
+
+On machines where the control is minimal or flushing is expensive
+(i.e., there is a single instruction or operating-system call to flush
+the complete caches or synchronize both caches), the following
+solution can be used to amortize the cost:
+
+The CONS-CLOSURE rules can generate code to allocate a closure from a
+pre-allocated pool and invoke an out-of-line routine to refill the
+pool when it is empty.  The routine allocates more space from the
+heap, initializes the instructions, and synchronizes the caches.
+
+Since the real entry points are not known until the closure objects
+are created, instead of using absolute jumps to the real entry
+points, the pre-allocated closures can contain jumps to a fixed
+routine that will extract the real entry point from the word pointed
+at by the return address and invoke it.  In other words, the code
+inserted in the closure objects will not be
+       jsr real-entry-point
+       <storage for first free variable>
+but
+       jsr fixed-routine
+       <storage for real-entry-point>
+       <storage for first free variable>
 
-The following is a description of some of the more obscure rules
-related to procedures:
+and the fixed-routine will do something like
+       load    0(return-address),rtemp
+       jmp     0(rtemp)
 
+The 68040 version of the Motorola 68000 family port uses this trick
+because the 68040 cache is typically configured in copyback mode, and
+synchronizing the caches involves a supervisor call.
+\f
 * (INVOCATION:UUO-LINK (? frame-size) (? continuation) (? name))
   This rule is used to invoke a procedure named by a free variable.
 It is the rule used to generate a branch to an execute cache as
 described in microcode/cmpint.txt.  The rule should allocate a new
 execute cache in the compiled code block by using FREE-UUO-LINK-LABEL,
 and should then branch to the instruction portion of the execute
-cache.
+cache.  FRAME-SIZE is the number of arguments passed in the call, plus
+one.
 
 * (INVOCATION-PREFIX:MOVE-FRAME-UP (? frame-size) (? address))
-  These rules are used to shift call frames on the stack to maintain
+  This rule is used to shift call frames on the stack to maintain
 proper tail recursion.  ADDRESS specifies where to start pushing the
-frame.  It should be a pointer into the used portion of the stack, i.e.
-point to a higher address.
+frame.  It should be a pointer into the used portion of the stack,
+i.e.  point to a higher address.
 
 * (INVOCATION-PREFIX:DYNAMIC-LINK (? frame-size) (? address-1) (? address-2))
-  These rules are similar to the INVOCATION-PREFIX:MOVE-FRAME-UP
-rules, but are used when the destination of the frame is not known at
-compile time.  The destination depends on the continuation in effect
-at the time of the call, and the section of the stack that contains
-enclosing environment frames for the called procedure.  Two addresses
-are specified and the one that is closest to the current stack pointer
+  This rule is similar to the INVOCATION-PREFIX:MOVE-FRAME-UP rule,
+but is used when the destination of the frame is not known at compile
+time.  The destination depends on the continuation in effect at the
+time of the call, and the section of the stack that contains enclosing
+environment frames for the called procedure.  Two addresses are
+specified and the one that is closest to the current stack pointer
 should be used, that is the numerically lower of the two addresses.
-==> These dynamic-link instructions need not exist in the RTL.  They
-could be expanded into comparisons and uses of
-INVOCATION-PREFIX:MOVE-FRAME-UP with computed values.
+==> This rule need not need not exist in the RTL.  It could be
+expanded into comparisons and uses of INVOCATION-PREFIX:MOVE-FRAME-UP
+with computed values.
 
 * (OPEN-PROCEDURE-HEADER (? label-name))
-  These rules are used to generate the entry code to procedures and
-continuations (return addresses).  On entry to procedures and
-continuations, a gc/interrupt check is performed, and the appropriate
-routine in the runtime library is invoked if necessary.  This check is
-performed by comparing the memory Free pointer to the compiled code's
-version of the MemTop pointer.  The low-level interrupt handlers
-change the MemTop pointer to guarantee that the following comparison
-will fail.  Thus a standard header generates the following code:
+  This rule (and its siblings) is used to generate the entry code to
+procedures and continuations (return addresses).  On entry to
+procedures and continuations, a gc/interrupt check is performed, and
+the appropriate routine in the runtime library is invoked if
+necessary.  This check is performed by comparing the memory Free
+pointer to the compiled code's version of the MemTop pointer.  The
+low-level interrupt handlers change the MemTop pointer to guarantee
+that such comparisons will fail in the future.  A standard header
+generates the following code:
     (LABEL gc-label)
        <code to invoke the runtime library>
        <format and gc words for the entry point>
@@ -1640,68 +1721,38 @@ will fail.  Thus a standard header generates the following code:
        <branch to gc-label if Free >= MemTop>
 
 Each of the individual headers is somewhat idiosyncratic, but the
-idiosyncratic code is machine independent.
+idiosyncracies are captured in the machine-independent runtime
+library.
+
+Note that procedures that expect dynamic links must guarantee that the
+dynamic link is preserved around the execution of the interrupt
+handler.  This is accomplished by invoking an alternate entry point in
+the runtime library and passing along the contents of the dynamic link
+register.
 
-Procedures that expect dynamic links must guarantee that the dynamic
-link is preserved around the execution of the interrupt handler.  This
-is accomplished by invoking an alternate entry point in the runtime
-library and passing along the contents of the dynamic link register.
-\f
 * (CLOSURE-HEADER (? label-name) (? nentries) (? entry))
   NENTRIES is the number of entry points that the closure object has,
 and ENTRY is the zero-based index for this entry point.  Closure
-headers also perform gc/interrupt tests, but they must also
-reconstruct the canonical closure object from the ``return address''
-and push the resulting object on the Scheme stack.  When backing out
-for interrupts, they may have to adjust the canonical closure object
-to be the real closure object if these two are different.  You should
-read the section on closures in microcode/cmpint.txt for a more
-complete explanation.
-
+headers also perform gc/interrupt tests, but they may also have to
+reconstruct the distinguished (canonical) closure object from a
+closure with multiple entry points from the ``return address'' and
+push the resulting object on the Scheme stack.  When backing out for
+interrupts, they may have to adjust the canonical closure object to be
+the real closure object if these two are different.  You should read
+the section on closures in microcode/cmpint.txt for a more complete
+explanation.
+\f
 * (ASSIGN (REGISTER (? target))
          (CONS-CLOSURE (ENTRY:PROCEDURE (? procedure-label))
                        (? min) (? max) (? size)))
   This rule issues the code to create a closure object whose real
-entry point is PROCEDURE-LABEL, that will accept between MIN-1 and
-MAX-1 (both inclusive) arguments, and that will have storage for SIZE
-free variables.  The free variable storage need not be initialized
-since it will be by subsequent RTL instructions.  The entry point of
-the resulting closure object should be written to RTL register TARGET.
-The format of closure objects is described in microcode/cmpint.txt.
-[*Arthur: From where did the "-1"s come?]
-
-Note that CONS-CLOSURE will dynamically create some new instructions
-on the runtime heap, and that these instructions must be visible to
-the processor's instruction cache.  This may make this rule impossible
-to translate on machines where the programmer is given no control over
-the caches.  
-
-On machines where the control is minimal or flushing is expensive
-(i.e., there is a single instruction or operating-system call to flush
-the complete caches or synchronize both caches), a solution is
-possible:
-
-This rule can generate code to invoke an out-of-line routine.  The
-routine can manage a large pool of pre-allocated closures, and
-flush/synchronize the caches only when the pool is exhausted and more
-are allocated from the heap.
-
-Since the real entry points are not known until the closures objects
-are initialized, instead of using absolute jumps to the real entry
-points, the pre-allocated closures can contain jumps to a fixed
-routine that will extract the real entry point from the word pointed
-at by the return address and invoke it.  In other words, the code
-inserted in the closure objects will not be
-       jsr real-entry-point
-       <storage for first free variable>
-but
-       jsr fixed-routine
-       <storage for real-entry-point>
-       <storage for first free variable>
-
-and the fixed-routine will do something like
-       load    0(return-address),rtemp
-       jmp     0(rtemp)
+entry point is PROCEDURE-LABEL, that will accept a number of arguments
+specified by MIN and MAX, and that will have storage for SIZE free
+variables.  The free variable storage need not be initialized since it
+will be written immediately by subsequent RTL instructions.  The entry
+point of the resulting closure object should be written to RTL
+register TARGET.  The format of closure objects is described in
+microcode/cmpint.txt.
 
 * (ASSIGN (REGISTER (? target))
          (CONS-MULTICLOSURE (? nentries) (? size) (? entries)))
@@ -1709,9 +1760,7 @@ and the fixed-routine will do something like
 allocate a closure object with NENTRIES entry points.  SIZE is the
 number of words allocated for free variables, and ENTRIES is a vector
 of entry-point descriptors.  Each descriptor is a list containing a
-label, a minimum (plus one) number of arguments, and a maximum (plus
-one) number of arguments.  Obviously the same problems with the
-processor's instruction cache occur, and the solutions are analogous.
+label, a min, and a max as in the rule above.
 \f
 The file compiler/machines/port/rules3.scm contains most of these
 procedure-related rules.  It also contains three procedures that
@@ -1722,11 +1771,10 @@ top-level code of a compilation unit.
 * (GENERATE/QUOTATION-HEADER env-label free-label n-sections)
   This procedure generates the header for the top-level expression
 given to COMPILE-SCODE, and generates its entry code.  This code
-initializes the executing compiled code block.  The initialization
-consists of storing the environment with respect to which the
-expression is evaluated into the environment slot of the compiled code
-block (labeled by ENV-LABEL), and invoking the linker to link in the
-executing compiled code block.
+initializes the compiled code block being executed.  The
+initialization consists of stashing the evaluation environment in the
+compiled code block at the location labeled by ENV-LABEL, and invoking
+the linker to fix the free references in the compiled code block.
 
 The linker (a runtime library utility) expects three arguments:
   The address of the first word of the compiled code block, labeled
@@ -1737,16 +1785,16 @@ compiled code block, labeled by FREE-LABEL.
 
 * (GENERATE/REMOTE-LINK label env-offset free-offset n-sections)
   This procedure is similar to generate/quotation-header but is used
-to generate the code that initializes and links in a different
-compiled code block, a pointer to which is stored in the constants
-section of the executing compiled code block.
-  LABEL is a label into the constants block where the remote compiled
-code block is stored,
-  ENV-OFFSET is the offset in the remote compiled code block where the
-environment of execution should be stored,
-  FREE-OFFSET is the offset of the first linker section in the remote
+to generate code that initializes and links not the executing compiled
+code block, but a different compiled code block, pointed at by a
+location in the currently executing compiled code block.
+  LABEL is a label into current block where the pointer to the code
+block to be linked is stored,
+  ENV-OFFSET is the offset in the other code block where the
+environment of evaluation should be stored,
+  FREE-OFFSET is the offset of the first linker section in the other
 compiled code block, and
-  N-SECTIONS is the number of linker sections in the remote compiled
+  N-SECTIONS is the number of linker sections in the other compiled
 code block.
 
 * (GENERATE/CONSTANTS-BLOCK consts reads writes execs)
@@ -1767,19 +1815,19 @@ is the layout and size of the storage allocated for each execute cache
 by a SCHEME-OBJECT directive, and the order in which they are issued
 determines the layout in the constants block.  
 
-This machine-dependent expansion is performed by the TRANSMOGRIFLY
+The machine-dependent expansion is performed by the TRANSMOGRIFLY
 procedure.  TRANSMOGRIFLY expects a list of lists whose first elements
 are the names of free variables and the rest of the elements are the
-number of arguments (plus 1) passed in the call.  It returns a list of
-pairs of objects and labels.  It should expand each <NAME,ARITY> pair
-into EXECUTE-CACHE-SIZE pairs, one of which should contain NAME as the
-object, another of which should contain ARITY as the object, and the
-rest of which (if any) can contain anything.  The pairs containing
-NAME and ARITY, and any additional ones, must be ordered to make the
-EXTRACT_EXECUTE_CACHE_ARITY, EXTRACT_EXECUTE_CACHE_SYMBOL macros from
-microcode/cmpint-port.h work correctly.  Note that the arity MUST NOT
-be overwritten when the execute cache is initialized to contain
-instructions.
+frame sizes (number of arguments plus one) passed in the call.  It
+returns a list of pairs of objects and labels.  It should expand each
+<NAME,ARITY> pair into EXECUTE-CACHE-SIZE pairs, one of which should
+contain NAME as the object, another of which should contain ARITY as
+the object, and the rest of which (if any) can contain anything.  The
+pairs containing NAME and ARITY, and any additional ones, must be
+ordered to make the EXTRACT_EXECUTE_CACHE_ARITY,
+EXTRACT_EXECUTE_CACHE_SYMBOL macros from microcode/cmpint-port.h work
+correctly.  The arity MUST NOT be overwritten when the execute cache
+is initialized to contain instructions.
 \f
        5.3.4 Fixnum rules.
 
@@ -1869,7 +1917,7 @@ SCHEME-TO-INTERFACE, typically directly accessible through a dedicated
 processor register.  SCHEME-TO-INTERFACE expects at least one and up
 to five arguments.  The first argument is the index of the runtime
 library service to invoke, and the rest are the parameters to the
-service routine.  These arguments are passed on fixed locations,
+service routine.  These arguments are passed in fixed locations,
 typically registers.  Runtime library utilities return their values
 (if any) in the compiler's value register.  The following is a typical
 example of such an invocation where INVOKE-INTERFACE expects the index
@@ -1942,8 +1990,8 @@ that are stored in hardware registers.  These hardware registers may
 be targeted explicitly by the programmer or implicitly by the
 hardware.  Conditional branch instructions determine whether to branch
 or not depending on the contents of the condition registers at the
-the branch instruction is executed.  These condition registers may be
-named explicitly by the instructions, or assumed implicitly.
+time the branch instruction is executed.  These condition registers
+may be named explicitly by the instructions, or assumed implicitly.
 
 * compare-and-branch instructions.  The instruction set includes
 instructions that compare two values (or a value against 0) and branch
@@ -2098,11 +2146,11 @@ lists the cmpint-port.h and cmpaux-port.m4 files.  You can emulate the
 version for any other compiler port.  It is especially important that
 the microcode sources be compiled with HAS_COMPILER_SUPPORT defined.
 
-- Remove (or save elsewhere) all the .o files and scheme, the linked
-scheme microcode.
+- Remove (or save elsewhere) all the .o files, scheme.touch, and
+scheme, the linked scheme microcode.
 
-- Do ``make scheme'' or ``make xmakefile;make -f xmakefile scheme'' to
-generate a new linked microcode.
+- Do ``make xmakefile;make -f xmakefile scheme'' to generate a new
+linked microcode.
 
 Once you have a new linked microcode, you need to regenerate the
 runtime system image files even if you have not changed the length of
@@ -2134,7 +2182,6 @@ again after executing
   (begin
      (cd "<runtime directory pathname>")
      (load "runtim.sf"))
-[*Arthur: Is this still necessary?]
        
        6.2 Building an interpreted compiler
 
@@ -2223,13 +2270,22 @@ A good idea at the beginning is to turn COMPILER:GENERATE-RTL-FILES?
 and COMPILER:GENERATE-LAP-FILES? on and compare them for plausibility.
 If you have ported the disassembler as well, you should try
 disassembling some files and comparing them to the input LAP.  They
-won't be identical, but they should be similar.
+won't be identical, but they should be similar.  The disassembler can
+be invoked as follows:
+
+    (compiler:write-lap-file "<pathname of .com file>") ; writes a .lap file.
+    (compiler:disassemble <compiled entry point>) ; writes on the screen.
+
+Note that both COMPILER:GENERATE-LAP-FILES? and
+COMPILER:WRITE-LAP-FILE write .lap files, so you may want to rename
+one of them.
 
 Various runtime system files also make good tests.  In particular, you
-may want to try list.scm, vector.scm, and arith.scm.  Note that to
-test procedures from arith.scm, you must execute
+may want to try list.scm, vector.scm, and arith.scm.  You can try them
+by loading them, and invoking procedures defined in them, but you must
+execute
     (initialize-microcode-dependencies!)
-after loading the file.
+after loading arith.com and before invoking procedures defined there.
 \f
        6.4 Compiling the compiler
 
@@ -2264,7 +2320,7 @@ Once you have the cross-compiler, you can use CROSS-COMPILE-BIN-FILE
 to generate .moc files.  The .moc files can be translated to .psb
 files on the Vax.  These .psb files can in turn be translated to .moc
 files on the Sparc, and you can generate the final .com files by using
-CROSS-COMPILE-BIN-FILE-END define in compiler/base/crsend.  Note that
+CROSS-COMPILE-BIN-FILE-END defined in compiler/base/crsend.  Note that
 compiler/base/crsend can be loaded on a plain runtime system (i.e.
 without SF or a compiler).  You will probably find the following
 idioms useful:
@@ -2383,40 +2439,42 @@ and other forms of unexpected failure.  In particular, hardware traps
 the re-compilation process are a good clue that there is a problem
 somewhere.
 
-The worst bugs to track are interrupt related or garbage-collection
-related.  They will often make the compiler crash at seemingly random
-points, and are very hard to reproduce.  A common source of this kind
-of bug is a problem in the rules for procedure headers.  Make sure
-that the rules for the various kinds of procedure headers generate the
-desired code, and that the desired code operates correctly.  You can
-test this explicitly by using an assembly-language debugger (e.g. gdb,
-adb) to set breakpoints at the entry points of various kinds of
-procedures.  When the breakpoints are reached, you can bump the Free
-pointer to a value larger than MemTop, so that the interrupt branch
-will be taken.  If the code continues to execute correctly, you are
-probably safe.  You should especially check procedures that expect
-dynamic links for these must be saved and restored correctly.
-Closures should also be tested carefully, since they need to be
-reentered correctly, and the closure object on the stack may have to
-be bumped.
+The worst bugs to track are interrupt related, and garbage-collection
+related.  They will often make the compiled code crash at seemingly
+random points, and are very hard to reproduce.  A common source of
+this kind of bug is a problem in the rules for procedure headers.
+Make sure that the rules for the various kinds of procedure headers
+generate the desired code, and that the desired code operates
+correctly.  You can test this explicitly by using an assembly-language
+debugger (e.g. gdb, adb) to set breakpoints at the entry points of
+various kinds of procedures.  When the breakpoints are reached, you
+can bump the Free pointer to a value larger than MemTop, so that the
+interrupt branch will be taken.  If the code continues to execute
+correctly, you are probably safe.  You should especially check
+procedures that expect dynamic links for these must be saved and
+restored correctly.  Closures should also be tested carefully, since
+they need to be reentered correctly, and the closure object on the
+stack may have to be bumped.  
+ [*JMiller: Show examples from stable ports and how to use gdb/adb to
+debug it.]
 
 Register allocation bugs also manifest themselves in unexpected ways.
 If you forget to use NEED-REGISTER! on a register used by a LAPGEN
 rule, or if you allocate registers for the sources and target of a
-rule in the wrong order, you may not notice for a long time, but some
-poor program will hit it.  If this happens, you will be lucky if you
-can find and disassemble a relatively small procedure that does not
-operate properly, but typically the only notice you will get is when
-Scheme crashes in an unrelated place.  Fortunately, this type of bug
-is reproducible.  In order to find the incorrectly compiled code, you
-can use binary search on the sources by mixing interpreted and
-compiled binaries.  When loading the compiler, .bin files will be used
-for those files for which the corresponding .com file does not exist.
-Thus you can move .com files in and out of the appropriate
-directories, reload, and test again.  Once you determine the procedure
-in which the bug occurs, re-compiling the module and examining the
-resulting RTL and LAP programs should lead to identification of the
-bug.
+rule in the wrong order (remember the cardinal rule!), you may not
+notice for a long time, but some poor program will hit it.  If this
+happens, you will be lucky if you can find and disassemble a
+relatively small procedure that does not operate properly, but
+typically the only notice you will get is when Scheme crashes in an
+unrelated place.  Fortunately, this type of bug is reproducible.  In
+order to find the incorrectly compiled code, you can use binary search
+on the sources by mixing interpreted and compiled binaries.  When
+loading the compiler, .bin files will be used for those files for
+which the corresponding .com file does not exist.  Thus you can move
+.com files in and out of the appropriate directories, reload, and test
+again.  Once you determine the procedure in which the bug occurs,
+re-compiling the module and examining the resulting RTL and LAP
+programs should lead to identification of the bug.
 \f
                7. Bibliography