From: Guillermo J. Rozas Date: Fri, 29 Mar 1991 01:27:27 +0000 (+0000) Subject: Add JMiller's comments and make some edits incorporating some X-Git-Tag: 20090517-FFI~10799 X-Git-Url: https://birchwood-abbey.net/git?a=commitdiff_plain;h=d64ba32799d2748d41cfcd7e22a728c6167600c7;p=mit-scheme.git Add JMiller's comments and make some edits incorporating some suggestions by JMiller, MarkF, and Arthur. --- diff --git a/v7/src/compiler/documentation/porting.guide b/v7/src/compiler/documentation/porting.guide index 8f7663283..bafb8ab56 100644 --- a/v7/src/compiler/documentation/porting.guide +++ b/v7/src/compiler/documentation/porting.guide @@ -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. 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. 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. * 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)))) ) -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. * 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. 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. - + * 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?] - +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. - + * 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. 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 + +but + jsr fixed-routine + + -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. + * (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) @@ -1640,68 +1721,38 @@ will fail. Thus a standard header generates the following code: = 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. - * (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. + * (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 - -but - jsr fixed-routine - - - -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. 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 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 + 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. 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 "") (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 "") ; writes a .lap file. + (compiler:disassemble ) ; 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. 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. 7. Bibliography