The pymdl book
31. Making it run faster
The environment manual's optimization tools reorganize compiled MDL for
the PDP-10: they combine routines, replace mediated calls with direct calls,
and move data into pure storage. pymdl runs the archived MDL tools,
including GLUE, PDUMP, SUBRFY, PURITY, and CLEAN, against the code
and storage represented by its emulator.
Read this chapter to understand those transformations and the archived files they produced. Their historical speed benefits are not host performance guarantees; a change must be measured on pymdl itself. For compilation to Python, start with the native compiler in chapter 34.
Chapter 6. Making It Run Faster
Once you have a working program, you will probably want it to run fast. The most obvious way of doing this is to compile it. MDL provides other ways to speed up code, chiefly by eliminating mediated subroutine calls, and by reducing the size of garbage-collected space.
Mediated subroutine calls (or 'MCALLs') are the standard method of function calling in MDL. They provide a great deal of information and control during program development and debugging, but the overhead of an MCALL is superfluous in debugged production programs. Consequently, several methods exist for removing this overhead.
A subtle impediment to increased speed in a production program is the amount of time devoted to garbage collection. As this is proportional to the size of the garbage collected space, it is advantageous to make that space as small as possible. One way to do this is to purify as many of the static data structures in the MDL as possible.
One by-product of the procedures mentioned above is that much of the resulting code and structure becomes pure and therefore shareable between many MDL processes.
6.1. GLUE
A facility exists to allow separately compiled and assembled RSUBRs to be
'glued' together. This makes calls between RSUBRs in the group much
faster, as MCALLs are replaced by PUSHJs. The many instructions of an
MCALL are replaced by the single PUSHJ, but the mediation provided by
MCALL is lost: no FRAME is produced. GLUEing is accomplished by the
concatenation of the code and reference VECTORs of the RSUBRs being
GLUEd, which gives them a common 'frame of reference.'
Additionally, GLUE is interfaced with the compiler such that:
- The
RSUBRs can be run unGLUEd for convenient tracing and debugging. After debugging, they can beGLUEd together and run much faster. - An individual
FUNCTIONcan be recompiled without the overhead of recompiling everythingGLUEd to itsRSUBR. After the recompilation, the entire set can be reGLUEd.
6.1.1. How to Glue
"GLUE" is a PACKAGE and it may be obtained by doing
<USE "GLUE">
The call to glue a group of RSUBRs and/or RSUBR-ENTRYs is:
<GROUP-GLUE group-name:atom
substitute:boolean
script:channel
package:string-or-list
survivors:list
victims:list>
where:
group-name is an ATOM as returned by GROUP-LOAD, and it is the only
required argument.
substitute is a flag: if it is true, the current RSUBRs and RSUBR-ENTRYs
will be fixed so that they may still run in the current MDL. This is
expensive but necessary if PRINTTYPEs or interrupt handlers are among the
RSUBRs in the group. If the flag is FALSE or not supplied, the group must
be GROUP-DUMPed and reloaded before use.
script, if supplied and a CHANNEL, is used by GROUP-GLUE to print out its
progress through its task. Otherwise, GROUP-GLUE works silently.
package, if provided and non-FALSE, implies PACKAGE mode will be used.
This argument should be a STRING specifying the PACKAGE that is being
glued. In PACKAGE mode only the ENTRYs of that PACKAGE will be
preserved and all RSUBR-ENTRYs associated with internal functions will be
removed. This option can also be used by setting the ATOM PKG to the
name of the PACKAGE. package may also be a LIST of PACKAGE names, in
which case the ENTRYs of all the PACKAGEs listed will be preserved.
survivors, if provided, indicates that SURVIVOR mode will be used. This
argument should be a list of those RSUBR-ENTRYs to be preserved. All other
RSUBR-ENTRYs will be flushed. This option overrides PACKAGE mode. This
option can also be used by setting the ATOM SURV to the LIST of
RSUBR-ENTRYs being preserved.
victims allows 'survivors' to be specified by default: that is, it is a
LIST of those functions which should not survive after GLUE has run.
This is sometimes more convenient to specify than explicit survivors.
There are two advantages to removing unneeded RSUBR-ENTRYs. The group is
made smaller by the absence of the RSUBR-ENTRYs. Also the code for the
group is reduced, as the code for handling MCALLs to those RSUBR-ENTRYs
is removed. In general only the ENTRYs need to be kept for a PACKAGE.
This can be done by specifying the PACKAGE using PACKAGE mode.
SURVIVOR mode should be used if the user wishes to explicitly state which
RSUBR-ENTRYs are to be kept.
Measured (
tests/test_mudbug.py, the round trip of 6.1 through 6.3 and 4.2 with the era's programs).GLUEhere is MIT'sELMER(muddle/elmer.ubt149, 1981). A real 1981NBIN,MUDBUG;FINDAT, isGROUP-LOADed with.GLUEset so the compiler's glue bits attach (6.2);<GROUP-GLUE FINDAT>answers"DONE"and the group'sFINDATOMis oneRSUBRof more than a thousand words, the whole package concatenated;<PDUMP FINDAT>answers"DUMPED"and writesfindat.fbin, which begins'<PCODE "FINDAT">, withfindat.sav55andfindat.fix55onMUDTMP; the maintainer demon adds them to the pure-mapping databases --ADDING FILE FINDAT SAV55-- and<SAV-BLOCK "FINDAT" "MUDSAV;SAV FILE">in a fresh interpreter hands back the glued code word for word. A secondPDUMPinto the same library walkspdump.mud's uniquifier to its next candidate,1FINDA-- the digit the manual's1MAIL SAV53carries, and the six-character truncation of ITS names -- which is the only way that loop has ever been put under a test deliberately. The databases the test dumps into are built empty rather than copied from an era library, because what is being checked is the round trip and not a mutation of an era artifact.
6.1.2. GLUE as a Program
In addition to the "GLUE" PACKAGE, there is a program in which GLUE and
PDUMP (see section 6.3) are preloaded. It will prompt for each of the
usual arguments to GROUP-GLUE, permitting the user to conveniently GLUE
(and PDUMP) several PACKAGEs in one session.
pymdl's own. That program's driver survives as
muddle/gluer.18and is here as theGLUERtool; theGLUEandPDUMPit preloads are the same packages.
6.2. Glue Bits
GLUE is able to perform its transformations on compiled or assembled code
with the aid of a data structure produced during assembly. This structure is
called the 'GLUE Bits'. It is an association placed on the RSUBR by this
FORM:
<AND <ASSIGNED? GLUE>
.GLUE
<PUT rsubr GLUE glue-bits:uvector>>
Thus if .GLUE is non-FALSE the association will be available to programs
wishing to use it.
Internally, the GLUE bits consist of two bits for each word of code in the
CODE element of the RSUBR, followed by words specifying calling
information. For each INTERNAL-ENTRY in the code, there is a word giving
the number of arguments it takes and the offset of the INTERNAL-ENTRY in
the CODE UVECTOR. The two bits for individual instructions are
interpreted with the index field of the instruction as follows:
- bits 0 implies the instruction is uninteresting;
- Index field (
M) and bits 1 implies the instruction is a reference to the code itself (a jump, perhaps); - Index field (
R) and bits 1 implies a reference to an impure slot of theRVECTOR(the compiler does not generate such references); - Index field (
R) and bits 2 implies the instruction is anMCALL; - Index field (
R) and bits 3 implies the instruction is a reference to a pure slot of theRVECTOR.
See section 7 for more details on the format of MDL Assembly code.
pymdl's own. The
FORMabove is what anNBINcarries after eachRSUBR(33.11), andGLUEthe atom is aROOTatom interned before the glue package loads, so that the package's reads, the loader's<PUT rsubr GLUE bits>and a user's<SET GLUE T>all resolve to one atom. The assembler of chapter 35 writes glue bits for what it assembles, and the compiler of chapter 34 for what it compiles;tools/era_tape_diff.pyreads them to tell a glued sibling call from a standaloneMCALLwhen it compares our words with the tape's (38.3).
6.3. PDUMP
MDL provides a mechanism for sharing compiled programs among several MDL
processes, and for dynamically moving the compiled code in and out of the
virtual address space as space is needed in the interpreter. This mechanism
is described in detail in section 4.2. This section describes how to convert
a compiled program into a sharable version, known as an FBIN (fast-binary)
version of the program.
First load the group-purifier. Next, GROUP-LOAD your group (or groups).
<GROUP-LOAD binary-file:string>
which returns the group-name of the group. This (and any other groups to be dumped together) is then passed to the pure-dumper:
<PDUMP group-name1:atom group-name2:atom ...>
This creates several files, only one of which you need be concerned with:
sname;group-name1 FBIN
If given more than one group-name, PDUMP will create one FBIN file for
each group, but only a single FIXUP and a single SAV file containing the
fixups and code for all of the groups named. The FIXUP and SAV files are
put on the "MUDTMP" directory and eventually are inserted in the pure code
library, as described in section 4.2.
Alternative methods of PDUMPing are to specify that as an option to the
program GLUE (see section 6.1.2), or to use its preloaded PDUMP directly
after exiting its READER with ^S.
A warning about combining GLUE and PDUMP: if you attempt to PDUMP
several groups that have been GLUEd together, you will lose. This is
because the references to the 'group-RSUBR' will fall on the wrong OBLISTs.
PDUMP also produces a structure analogous to the GLUE bits (see section
6.2) produced by the compiler, but containing only information about the
RVECTOR of the RSUBR, for the use of PURIFY (see section 6.5).
pymdl's own.
PDUMPismuddle/pdump.22, and the demon of 4.2.1 that takes itsSAVnnandFIXnnfiles offMUDTMPinto the databases isndem.py: the era demon survives only as MIDAS assembly (mudsys/ndem.46, "MUDDLE SAV-FIXUP MAINTAINER"), and this is its cycle reimplemented from that source against the database formats -- the delete pass with its free-area coalescing, the planning pass placing 'files' first-fit in 1024-word or 256-word blocks, the update pass, the salvage pass -- validated by reading the result back with the era's ownDBMAIN(chapter 30).PDUMPchecks its names against the siteSAVdatabase the way the manual's 4.2 implies andpdump.muddoes:FINDATtaken means1FINDA. The era'sSAVandFIXUPfiles pymdl reads are the 1983 tape's; the ones it writes are its own, in the same format.
6.4. SUBRFY
SUBRification is a way of getting rid of many of the MCALLs which could not
be practically removed using GLUE. If a FUNCTION is called by many
separate groups, it is difficult to GLUE it to all the groups or to GLUE
all the groups together.
What is really needed is to be able to allow something to be called with
PUSHJ from separate groups without forcing it to be part of those groups.
This is indeed the case with PUSHJ entries to MDL SUBRs (in the
interpreter). A user can make his RSUBRs look like SUBRs in this
respect.
SUBRFY takes a group, which must be in NBIN format. It purifies the
RSUBRs and RSUBR-ENTRYs in the group and changes them so that they can be
called with PUSHJ. It also produces a file, known as the 'preload' file,
which can be used by the compiler to generate PUSHJs to the functions in
the SUBRified group.
SUBRFY should be loaded before loading the group to be processed. The
reason for this is that it guarantees that GLUE bits stay around. To load
SUBRFY:
<USE "SUBRFY">
You should then GROUP-LOAD the group. Your group should be GLUEd already,
since SUBRFY does not GLUE the group together.
SUBRFY can then be called in the following manner:
<SUBRFY group:atom
file-name:string
output:channel>
where:
group is the name of the group.
file-name is the name of the file in which SUBRFY should put the information
for the compiler. This defaults to the name of the input file with second
name "PRELOD".
output is an optional argument which specifies a CHANNEL on which to print
information about SUBRFY's progress. The default is not to print anything.
The file produced by SUBRFY should be FLOADed for compilations where
functions in the SUBRified group are called. This can be done by FLOADing
it in the 'Things to do' part of a COMBAT plan.
Like purification, SUBRification changes the MDL. The only way to preserve
the SUBRified group is to SAVE the MDL. Before SAVEing the MDL the
"SUBRFY" PACKAGE should be removed. This can be done by doing a
<KILL-SUBRFY>
followed by a
<GC 0 T>
SUBRFYing a group implies that the group is not going to change at all
frequently, if ever. A new SUBRFYed SAVE file may be created at any time,
and elements of the group may be recompiled. However, if the calling
sequences of any of the functions in that group change, you invalidate any
functions compiled using the 'preload' file for that group. In short, think
twice before tying yourself down with SUBRFY.
Found along the way. The surviving
SUBRFYsource is not the program MIT ran. Run end to end here over a 1983 libraryNBIN--GROUP-LOAD,SUBRFY, the group rewired to theSUBRcalling tier and purified, the preload fileGROUP-DUMPed -- the source reportsDONEand the transformed code then fails to execute, because its glue pass "skips one glue code per internal entry", compensating for a code-removal pass the era itself had retired, and the desynchronised glue relocates the wrong words. Remove that one<SET GLUE-BITS <REST .GLUE-BITS>>from a copy and the identical flow is behaviour-preserving: the rewired code executes on the emulator and answers exactly what it answered before, for every case. The era's own compiledSUBRFY(muddle/subrfy.nbin) does not desynchronise -- itsUNRELATE, fed six words and alternating bits, answers(101 2 103 4 105 6)for every internal-entry list, where the source's answer shifts with the list -- so MIT's binary was built from a revision that did not survive. This is not theFRAMEScase of chapter 29, where the binary carried the same fault and compilation made it harmless; here aRESTcompiles to aREST. pymdl runs the era source as it is; the test that demonstrates the one step patches a copy in a temporary directory and says what it would take, not what is done.
6.5. Purification
A facility exists to permit the purification of MDL objects. Purified objects can be shared between MDL processes and also are not examined by the garbage collector. What follows is a description of how this facility can be used.
The purification facility in MDL is most useful in the creation of
subsystems. Non-purified RVECTORs of RSUBRs and tables used by
subsystems are kept in garbage collected space. This means that these
objects, which will never become garbage, are examined at each garbage
collection, slowing down the garbage collection process. Also, if two people
are using the same subsystem, they cannot share the tables and RSUBRs kept
in garbage collected space. By using purification these two problems can be
alleviated.
To purify most objects the user can call the PURIFY SUBR. The object
will be purified, and all references to that object in the MDL core image
will be changed to point to the new pure object. This simple method cannot
be used in the case of RSUBRs. Purification of RSUBRs is a several-step
process beginning with compilation.
6.5.1. Purifying RSUBRs
Once your FBIN or NBIN is ready you can actually do purification. To do
this first
<USE "PURITY">
This PACKAGE contains the routines needed to purify RSUBRs. Then
GROUP-LOAD the files you wish to have purified. Once this is done type
<GROUP-PURIFY group:atom output:channel>
This will purify and link all RSUBRs and RSUBR-ENTRYs in the group and
will also attempt to purify any RSUBRs or RSUBR-ENTRYs called by the
group. Giving the optional channel will cause GROUP-PURIFY to print
information concerning the progress of the purification.
GROUP-PURIFY will only purify RSUBRs and RSUBR-ENTRYs. In order to
purify tables, etc. use the PURIFY SUBR directly. Since purification is
an extremely expensive operation, it is recommended that you collect together
the things you wish to purify into a LIST, VECTOR, etc. and purify that
structured object.
Once purification has occurred, several things may be done to recover wasted
garbage collected space. The user can get rid of the "PURITY" PACKAGE by
doing a
~~<KILL-PURITY>~~ <KILL:PURITY>
The user can also remove much of the overhead of keeping a group around by
UNASSIGNing the group-name. Removals of this type should be followed by an
explicit call to the garbage collector invoking the 'hairy' GC feature, as
much of the storage to be regained is pointed to by associations. This can
be done by
<GC 0 T>
In order to save a file with purified MDL objects you must SAVE. Restoring
a SAVEd file with purified MDL objects will cause those objects to share
with any other MDL RESTOREd from the same SAVE file.
Found along the way. The surviving
purity.mud(muddle/purity.28, 1976) spells its entryKILL:PURITY, with a colon --<ENTRY GROUP-PURIFY KILL:PURITY>-- and it answers"KILLED"; there is noKILL-PURITYin the file, so the struck name above is the manual's.SUBRFY's isKILL-SUBRFY, as printed. Purity itself is chapter 24's:PURIFYis aSUBRhere as there, and what pymdl keeps of purification is the observable --PURE?answers,UNPURIFYmakes an object writable (29, 3.10.4) -- with no pages to move, since there is no address space for them to be pure in.
6.5.2. Purifying an Environment
Many subsystems maintain a list containing pointers to all the static data
structures built by that subsystem: dispatch tables, data bases, and so on.
The list can be given to PURIFY to move all its components into the pure
area. However, there are other structures in garbage collected space that
may be purified: e.g., the RVECTORs of RSUBRs, RSUBR DECLs, and so
on.
The "CLEAN" PACKAGE examines these structures, looking for those which may
be purified. It may also be used for informational purposes. To get it
<USE "CLEAN">
"CLEAN" has one major ENTRY, CLEANUP, which examines every ATOM of every
OBLIST in the MDL. It may perform a variety of functions, but it is most
often used to make DECLs share storage and to accumulate a LIST of
purifiable structures. All of its arguments are optional.
<CLEANUP print?:boolean
reset?:boolean
decl?:boolean
gdecl?:boolean
pure?:boolean
check?:boolean
avoid:list-of-oblists>
print? is by default FALSE. If non-FALSE, information about each ATOM
examined will be printed as CLEANUP runs. This is a lot of information.
reset? is by default T. If non-FALSE, the LISTs of objects previously
collected will be reset before CLEANUP runs.
decl? is by default T. If non-FALSE, each DECL element will be made to
exist exactly once in the entire core image. E.g., there will be only one
copy of the DECL <LIST [REST FIX]> in the core image.
gdecl? is by default T. It is similar to decl?, but refers to GDECLs.
pure? tells whether to make a LIST of all the purifiable objects in the
core image. It is by default T.
check? tells whether to make LISTs of all the TYPEs, RSUBRs,
RSUBR-ENTRYs, etc. in the core image. It is by default T.
avoid is a LIST of OBLISTs not to look in; it is by default the
OBLISTs associated with "CLEAN" and "PURITY".
CLEANUP returns (if pure? is non-FALSE) a structure (also stored as the
GVAL of PURELST) which may be given to PURIFY.
The results of running CLEANUP may be examined by
<PRINT-CLEANUP>
As the object in running CLEANUP is to shrink the size of one's MDL and its
garbage-collected space, it is useful to be able to remove CLEAN after it
has done its work.
<FLUSH-CLEANUP>
removes everything associated with the PACKAGE from the MDL.
6.5.3. Purification Summary
In a simple case, one can purify a 'subsystem' of one group maximally by
<USE "PURITY" "CLEAN">
<GROUP-LOAD "foo">
<CLEANUP>
<GROUP-PURIFY foo>
<KILL:PURITY>
<FLUSH-CLEANUP>
<GC 0 T>
<SAVE "foo">
Measured (
tests/test_mudbug.py), the recipe run on a small group:CLEANUPanswers its census summary, aVECTOR, and leaves,PURELSTaLIST;<GROUP-PURIFY MYGRP ,OUTCHAN>printsBEGINNING PURIFICATIONand answers"DONE";<KILL:PURITY>answers"KILLED"and<FLUSH-CLEANUP>"PACKAGE REMOVED". The compiled-only primitives the two packagesFLOADfromNBINs on the era,PUREQandHASHER, are pymdl built-ins here (33.14).
6.6. TEMPLATEs
The PRIMTYPE TEMPLATE cuts down on the need for storage by allowing the
user to specify exactly what he wants a structured object to contain, similar
to 'structures' in PL/I or C.
To use this feature one must create a new TYPE of PRIMTYPE TEMPLATE.
This can be accomplished by using the RSUBR TEMPLATE. The procedure for
doing so is:
<USE "TEMPLATE">
<TEMPLATE name:atom ... specs ...>
where name is the name of the new TYPE and specs are specifications for
each element of the TEMPLATE. This returns the TYPE name of the
TEMPLATE and creates a creator of TEMPLATEs of TYPE name, called name
itself, which can be applied to arguments to create objects of that TYPE of
TEMPLATE.
The specification for the elements can be of several forms. It can be one of:
- a
TYPE: type:atom - a 2-element
LIST: (type:atom length:fix) - a 3-element
LIST: (type:atom length:fix count:fix)
Below are some examples along with explanations:
LIST is an 18 bit LIST pointer.
(FIX 18) is a halfword FIX (can be both positive and negative and is
checked for overflow).
(FLOAT 18) is an 18 bit FLOAT (which is the left halfword of a 'normal'
FLOAT and therefore somewhat restricts the precision).
(FIX n) (where n is less than 18) is a positive FIX of length n bits (is
not checked for overflow).
BOOLEAN is not a MDL TYPE, but a one bit FALSE or non-FALSE depending
on whether the bit is 0 or 1.
(UVECTOR 18 n) is an 18 bit UVECTOR pointer. The UVECTOR is of length
n. The same can be done for VECTORs.
(STRING 36 n) is a 36 bit string byte pointer. The STRING is of length
n.
ANY is not a MDL TYPE, rather anything can go here. This is relatively
inefficient to use in TEMPLATEs as it takes up 2 words.
In order to provide more flexibility in using TEMPLATEs, two other fields
are allowed, an optional field and a rest field. The optional field allows
the user to create TEMPLATE TYPEs which will have the same basic
structure but which can have optional elements determined when the actual
TEMPLATE is created. The rest field, like the optional field, allows
elements to be optional but specifies a pattern for any elements that are
added on. It is analogous to REST in DECLs. Separation of fields is
accomplished by the use of the strings "REST" and "OPTIONAL". For
example:
<TEMPLATE FOO FIX "OPTIONAL" LIST BOOLEAN "REST" FLOAT>
This creates a TYPE FOO of PRIMTYPE TEMPLATE which always has a FIX
as the first element, can have a LIST as a second element, can have a one
bit T or #FALSE () as the third element and can have any number of
FLOATs from the fourth element on.
6.6.1. Use of TEMPLATEs
TEMPLATE TYPEs may be thought of as primitive TYPEs, in that they each
have a unique storage representation. On the other hand, the TYPEPRIM of
any TEMPLATE TYPE is TEMPLATE. A primitive TEMPLATE (which cannot
truly exist in the language) would look like
{ element-1 element-2 ... element-n }
All TEMPLATE TYPEs are represented as NEWTYPEs of this primitive
TEMPLATE TYPE:
#type-name { ... elements ... }
This method is similar to the usual method in MDL for representing any new
TYPE, in that a RESTed TEMPLATE will be printed 'CHTYPEd to its
PRIMTYPE.' Note that a TEMPLATE so printed cannot be read by READ: a
'primitive TEMPLATE' cannot exist. It is best to avoid printing RESTed
TEMPLATEs.
Below are some examples of the use of TEMPLATEs.
<USE "TEMPLATE">
<TEMPLATE BAR FIX "OPTIONAL" BOOLEAN "REST" (FIX 18) (FLOAT 18)> ⇒ BAR
<BAR 1> ⇒ #BAR {1}
<BAR 1 T> ⇒ #BAR {1 T}
<BAR 1 <> 1 1.0> ⇒ #BAR {1 #FALSE () 1 1.0}
<SET A <BAR 1 <> 1 1.9 2>> ⇒ #BAR {1 #FALSE () 1 1.8984375 2}
<PUT .A 1 6> ⇒ #BAR {6 #FALSE () 1 1.8984375 2}
<PUT .A 4 1.999> ⇒ #BAR {6 #FALSE () 1 1.9960937 2}
<TEMPLATE BAR (STRING 36 4) "REST" ANY> ⇒ #FALSE ("ALREADY A TEMPLATE")
<TEMPLATE BAR1 (STRING 36 4) "REST" ANY> ⇒ BAR1
<SET A <BAR1 "HELP" 2 () <>>> ⇒ #BAR1 {"HELP" 2 () #FALSE ()}
<PUT .A 1 "GOOD"> ⇒ #BAR1 {"GOOD" 2 () #FALSE ()}
<PUT .A 1 "GOOD-BYE"> ⇒ *ERROR* TEMPLATE-TYPE-VIOLATION
<TYPEPRIM BAR> ⇒ TEMPLATE
<TEMPLATE-DATA BAR> ⇒ (FIX "OPTIONAL" BOOLEAN "REST" (FIX 18) (FLOAT 18))
pymdl's own. These are the manual's own examples, and the answers are the manual's to the digit: an 18-bit
FLOATfield holds1.9as1.8984375and1.999as1.9960937, because pymdl'sTEMPLATEstores a halfword float as the left half of a PDP-10 float (chapter 21), and a four-character(STRING 36 4)refuses"GOOD-BYE"with the era'sTEMPLATE-TYPE-VIOLATION. The manual's printed transcript of the last line shows*ERROR* TEMPLATE-TYPE-VIOLATION PUT LISTENING-AT-LEVEL 2 PROCESS 1, the 55's shape. TheTEMPLATEpackage itself was compiled code on the era (muddle/templt.fbin, anRPACKAGE), so here it is apymac, this machine's compiled form of the same package (33.14), over the interpreter's ownTEMPLATEmachinery;TEMPLATE-DATA, fromTEMHLP, gives back the raw element specification a type was made from.
6.6.2. Assembly of TEMPLATEs
Once a set of TEMPLATE TYPEs is created, as for the TYPE definitions of
a subsystem, it saves time to store away the 'compiled' TEMPLATE generators
and not recreate them each time the definitions are to be used.
The "TEMHAK" PACKAGE modifies files which define TEMPLATE TYPEs to
contain the TEMPLATE descriptions and RSUBRs rather than the calls to
TEMPLATE. It is only useful, of course, when the TEMPLATEs are defined
in a file which will not normally be edited, since the new files are in
'NBIN' format. To load this PACKAGE,
<USE "TEMHAK">
The PACKAGE has two entries:
<TEMPLATE-DUMP group:atom file:string>
takes the group and modifies it such that <USE "TEMPLATE"> becomes
<USE "TEMHLP">, and all top-level invocations of TEMPLATE are replaced
by calls to BUILD-TEMPLATE (for the TEMPLATE descriptions), SETGs of
the TEMPLATE-generating RSUBRs, and the GLUE bits for the RSUBRs.
<FILE-TEMPLATE input:string output:string>
takes an input file and performs the same service, GROUP-DUMPing the result
to the optional output file (by default the same file with second name
"NBIN"). This is useful for files which contain nothing but TYPE
definitions, a common practice in large subsystems.
If the TEMPLATE TYPEs are defined in a file which will be edited
frequently, a different set of routines is used after creating the TEMPLATE
TYPEs:
<DUMP-TEMPLATES descriptions:string>
places the TEMPLATE descriptions (not the RSUBRs) in the specified
descriptions file. It does so for all TEMPLATE TYPEs currently defined.
<DUMP-RSUBRS rsubrs:string template-type:atom ... >
will perform the same service for the TEMPLATE-generating RSUBRs of the
TYPEs given as the second and later arguments to DUMP-RSUBRS.
There will now be two files, one containing the TEMPLATE descriptions and
the other the RSUBRs. These may now be used to create the TEMPLATE
TYPEs without USEing "TEMPLATE". To do so:
<USE "TEMHLP">
This defines the RSUBRs needed to take the TEMPLATE descriptions and make
them useful to MDL.
<FLOAD descriptions:string>
the file of descriptions (the file created with DUMP-TEMPLATES): this
must be loaded before the RSUBRs file. Then load the RSUBRs file (the
file created by DUMP-RSUBRS):
<FLOAD rsubrs:string>
For maximum convenience, it may be necessary to put a FORM in files that
create TEMPLATEs: if the TEMPLATE files described here exist, FLOAD
them; otherwise, <USE "TEMPLATE"> and create the TEMPLATEs from scratch.
It is of course possible to manually merge the two TEMPLATE definition
files (preferably by using GROUP-LOAD and GROUP-DUMP), so long as the
TEMPLATE descriptions precede the TEMPLATE RSUBRs.
TEMPLATE RSUBRs are created with GLUE bits, so it is possible to glue them
into groups and to purify them.
Found along the way. The vendored transcription of this chapter ended at "a different set of routines is used after creating the
TEMPLATETYPEs:" -- its page range stopped one scanned page short, and everything fromDUMP-TEMPLATESon was missing. The text above is restored from the page image (printed page 114) and the transcription's last page has been put back.pymdl's own.
TEMHAK's two entries are here, andFILE-TEMPLATEround-trips: a file ofTEMPLATEcalls comes back as<USE "TEMHLP">andBUILD-TEMPLATEforms that a fresh interpreter loads to the same types (tests/test_template.py).TEMHLP'sBUILD-TEMPLATErebuilds a type from its description unconditionally, since a dumped file must be re-loadable; the era file also carried the generatorRSUBRs as machine code, and here the generator is rebuilt from the description, which is this machine's compiled form of the same thing.DUMP-TEMPLATESandDUMP-RSUBRS-- the routines of the restored page -- are not defined here: no surviving source or binary carries them, and the file-basedTEMHAKcovers the case they served.