<rss version='2.0'><channel><title>Juho Snellman's Weblog</title><link>https://www.snellman.net/blog/</link><description>Lisp, Perl Golf</description><item><title>Numbers and tagged pointers in early Lisp implementations</title><link>https://www.snellman.net/blog/archive/2017-09-04-lisp-numbers/</link><description>
  &lt;p&gt;
    There was a bit
    of &lt;a href=&#039;https://news.ycombinator.com/item?id=15121859&#039;&gt;discussion
    on HN about data representations in dynamic languages&lt;/a&gt;, and
    specifically having values that are either pointers or immediate
    data, with the two cases being distinguished by use of tag bits in
    the pointer value:
  &lt;/p&gt;

  &lt;blockquote&gt;
    &lt;blockquote&gt;
      If there&#039;s one takeway/point of interest that I&#039;d recommend looking at, it&#039;s the novel way that Ruby shares a pointer value between actual pointers to memory and special &quot;immediate&quot; values that simply occupy the pointer value itself [1].
    &lt;/blockquote&gt;
    This is usual in Lisp (compilers/implementations) and i wouldn&#039;t be surprised if it was invented on the seventies once large (i.e. 36-bit long) registers were available.
  &lt;/blockquote&gt;

  &lt;p&gt;I was going to nitpick a bit with the following:&lt;/p&gt;

  &lt;blockquote&gt;
  &lt;p&gt;
    The core claim here is correct; embedding small immediates inside
    pointers is not a novel technique. It&#039;s a good guess that it was
    first used in Lisp systems. But it can&#039;t be the case that its
    invention is tied into large word sizes, those were in wide use
    well before Lisp existed. (The early Lisps mostly ran on 36 bit
    computers.)
  &lt;/p&gt;

  &lt;p&gt;
   It seems more likely that this was tied into the general migration
   from word-addressing to byte-addressing. Due to alignment constraints,
   byte-addressed pointers to word-sized objects will always have unused
   bits around. It&#039;s harder to arrange for that with a word-addressed
   system.
  &lt;/p&gt;
  &lt;/blockquote&gt;

  &lt;p&gt;
    But the latter part of that was speculation, maybe I should try to
    check the facts first before being tediously pedantic?
    Good call, since that speculation was wrong. Let&#039;s take a tour
    through some early Lisp implementations, and look at how they
    represented data in general, and numbers in particular.
  &lt;/p&gt;

&lt;read-more&gt;&lt;/read-more&gt;

  &lt;h3&gt;Table of Contents&lt;/h3&gt;

  &lt;ul&gt;
  &lt;li&gt; &lt;a href=&#039;#problem&#039;&gt;The problem with integers&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#lispi&#039;&gt;LISP I&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#lisp1.5&#039;&gt;LISP 1.5&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#pdp1&#039;&gt;Basic PDP-1 LISP&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#m460&#039;&gt;M 460 LISP&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#pdp6&#039;&gt;PDP-6 LISP&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#bbn&#039;&gt;BBN LISP&lt;/a&gt;
  &lt;li&gt; &lt;a href=&#039;#conclusion&#039;&gt;Conclusion&lt;/a&gt;
  &lt;/ul&gt;

  &lt;a name=&#039;problem&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;The problem with integers&lt;/h3&gt;

  &lt;p&gt;
    Before we get started, let&#039;s state the problem that tagged pointers
    solve. In a
    dynamically typed programming language, the language
    implementation must be able to distinguish between values of
    different types. The obvious implementation is boxing; all values
    are treated as blobs of memory allocated somewhere on the heap,
    with an envelope containing metadata such as the type and (maybe)
    the size of the object.
  &lt;/p&gt;

  &lt;p&gt;But this means that integers now have tons of overhead. They use
    up heap space, need to be garbage collected, and new memory needs
    to be constantly allocated for the results of arithmetic
    operations. Since integers are so critical to almost all kinds of
    computing, it would be great to minimize the overhead.  And
    ultimately, to eliminate the overhead completely by encoding small
    integers as recognizably invalid pointers.
  &lt;/p&gt;

  &lt;a name=&#039;lispi&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;LISP I&lt;/h3&gt;

  &lt;p&gt;
    I wasn&#039;t super hopeful about finding out exactly what numbers looked like
    in the original Lisp implementation. As far as I know, the source
    code hasn&#039;t been preserved. Now, the original paper describing Lisp
    (&lt;a href=&#039;http://edge.cs.drexel.edu/regli/Classes/Lisp_papers/McCarthy-original-LISP-paper-recursive.pdf&#039;&gt;
    Recursive Functions of Symbolic Expressions and their Computation
    by Machine, Part I
    &lt;/a&gt;) isn&#039;t quite as theoretical as the title suggests. For
    example it describes the memory allocator and garbage collector on
    a reasonable systems level. But it doesn&#039;t mention numbers at all;
    this is a system for symbolic computation, so numbers might as
    well not exist.
  &lt;/p&gt;

  &lt;p&gt;
    The &lt;a href=&#039;https://kyber.io/rawvids/LISP_I_Programmers_Manual_LISP_I_Programmers_Manual.pdf&#039;&gt;
      LISP I Programmer&#039;s Manual&lt;/a&gt; from 1960 is more illuminating, though not
      entirely consistent. In one place the manual claims that LISP I
      only supports floats, and you&#039;ll need to wait until LISP II to
      use integers. But the rest of the document happily describes the
      exact memory layout of integers, so who can tell.
  &lt;/p&gt;

  &lt;p&gt;
    A floating point value looks like this:
  &lt;/p&gt;

  &lt;img src=&#039;/blog/stc/images/lisp-numbers/lisp1.0-float.png&#039; /&gt;

  &lt;p&gt;
    Let&#039;s say we have the value 1.0 in a LISP I program. This value
    is actually pointer to a word. How do we know what the type of the
    pointed to word is? If the upper half of that word is -1, it&#039;s a
    symbol. Otherwise it&#039;s a cons. (The use of -1.0 and 1.0 as the
    example floats in this picture is unfortunate, since it looks like
    the -1.0 and -1 are somehow related. That&#039;s not the case, -1 is
    the universal tag value for atoms, and independent of the exact
    floating point values.)
  &lt;/p&gt;

  &lt;p&gt;
    So the number 1.0 is a symbol? Technically yes, since at this stage of Lisp&#039;s
    evolution everything is either a symbol or a cons. There are no
    other atoms. We can find out if the symbol represents a number by
    following the linked list starting from the &lt;code&gt;cdr&lt;/code&gt; of
    the symbol (a pointer stored in the lower half of the word). If
    we find the symbol &lt;code&gt;NUMB&lt;/code&gt; on the list, it&#039;s some kind
    of number. If we find the symbol &lt;code&gt;FLO&lt;/code&gt;, it&#039;s a floating
    point number, and the property list will be pointing to a word
    that contains the raw floating point value that this number
    represents.
  &lt;/p&gt;

  &lt;p&gt;
    There&#039;s a detail here that&#039;s kind of amazing. Notice that 1.0 and
    -1.0 share the same list structure. The only difference is that
    -1.0 has the symbol &lt;code&gt;MINUS&lt;/code&gt; in the list, after which
    the list merges with the list of 1.0. What a fabulously
    inefficient representation! Not only do you have to do a bunch of
    pointer chasing just to find the actual value of a number, but
    then you&#039;ll get to do it again to find out the sign!
  &lt;/p&gt;

  &lt;p&gt;
    The question I can&#039;t answer just from reading this document is how
    exactly the raw floating point value is handled. Surely the
    garbage collector must know not to interpret those raw
    bits as pointer data? There is a very detailed example of the
    memory layout for an integer on pages 94-95, but even with that
    example I just don&#039;t see where the type information is stored.
    It&#039;s clearly not based on address ranges (the raw values are mixed
    in with the other words), nor the pointer value (all the pointers
    are stored as 2&#039;s complement), nor the 6 unused bits in the
    machine word.
  &lt;/p&gt;

  &lt;p&gt;
    Suggestions welcome. My best guess is that the example is
    inaccurate.
  &lt;/p&gt;

  &lt;a name=&#039;lisp1.5&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;LISP 1.5&lt;/h3&gt;

  &lt;p&gt;The LISP 1.5 Programmer&#039;s Manual from 1962 explains in a very
    concise manner how numbers worked in that implementation:
  &lt;/p&gt;

  &lt;img src=&#039;/blog/stc/images/lisp-numbers/lisp1.5-int.png&#039; /&gt;

  &lt;p&gt;Numbers are still considered to be symbols, and symbols are
    still marked with -1 as the &lt;code&gt;car&lt;/code&gt;. But the standard
    symbol property list is now gone; instead the symbol is pointing
    directly to the memory that stores the raw integer value. How
    does the program know not to follow that pointer as a list? As the
    document says, that&#039;s specified by &quot;certain bits in the tag&quot;.
  &lt;/p&gt;

  &lt;p&gt;
    The tag? What&#039;s the tag? The IBM 704 had a 36-bit word size but
    just a 15 bit address space. The words were split (on the ISA
    level) into a 3 bit &quot;prefix&quot;, 15 bit &quot;address&quot;, 3 bit &quot;tag&quot;, and
    15 bit &quot;decrement&quot;.  Since Lisp values are pointers, only the two
    15 bit regions are useful for that. One of the 3 bit regions has
    been repurposed by the Lisp implementation to mark the pointers
    to raw data.
  &lt;/p&gt;

  &lt;p&gt;
    This is a clear improvement over LISP I, but a number is still
    represented as an untagged pointer to a tagged pointer to the raw
    value. Why is the intermediate word there at all, why not go
    directly with a tagged pointer to the raw value? Maybe code size?
  &lt;/p&gt;

  &lt;p&gt;
    In parallel to that, the address space has now been split into
    multiple separate pieces, with the cons cells being allocated from
    a different range of addresses than plain data like numbers and
    string segments. It could well be that the tagged pointer is
    irrelevant to the GC, which just makes its decisions on what&#039;s a
    pointer based on whether the pointer is contained in the &quot;full
    word space&quot; or the &quot;free space&quot;. The tags would then be used
    just for implementing &lt;code&gt;NUMBERP&lt;/code&gt;.
  &lt;/p&gt;

  &lt;a name=&#039;pdp1&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;Basic PDP-1 LISP&lt;/h3&gt;

  &lt;p&gt;
    For a L. Peter Deutsch joint,
    &lt;a href=&#039;http://s3data.computerhistory.org/pdp-1/DEC.pdp_1.1964.102650371.pdf&#039;&gt;
      The LISP implementation for the PDP-1 Computer
    &lt;/a&gt; proves to be a surprisingly unsatisfying document. It&#039;s almost
    exclusively user documentation, with no information on the systems
    architecture. Well, except a full source code listing. Guess we&#039;ll
    have to look at that, then.
    &lt;code&gt;NUMBERP&lt;/code&gt; is the easiest starting point:
  &lt;/p&gt;

&lt;pre&gt;
/// (&quot;is a number&quot;)
/NUMBERP
nmp,    lac i 100
        and (jmp
        sad (jmp
        jmp tru
        jmp fal
&lt;/pre&gt;

  &lt;p&gt;The main thing that need to be known from the rest of the code is
    that the interpreter stores a pointer to the Lisp value that&#039;s
    currently operated on value at address &lt;code&gt;100&lt;/code&gt; (octal).
  &lt;/p&gt;

  &lt;p&gt;First &lt;code&gt;&quot;lac i 100&quot;&lt;/code&gt; follows the pointer to read the
    first data words of the value into the accumulator. The next line
    looks bizarre; due to the way the PDP-1 macro-assembler
    works, &lt;code&gt;&quot;and (jmp&quot;&lt;/code&gt; effectively means &lt;code&gt;&quot;and
    600000&quot;&lt;/code&gt;. So this instruction is masking away all but the
    top two bits of the accumulator, and &lt;code&gt;&quot;sad
    (jmp&quot;&lt;/code&gt; is checking whether the result of the masking equals octal
    &lt;code&gt;600000&lt;/code&gt;. It appears that there is nothing special about the
    pointer to a number, but numbers are identified by having the top
    two bits set in the pointed-to value.
  &lt;/p&gt;

  &lt;p&gt;The next step in understanding the layout is the code for reading
  the raw value of a number.&lt;/p&gt;

&lt;pre&gt;
/get numeric value
vag,    lio i 100
        cla
        rcl 2s
        sas (3
        jmp qi3
        idx 100
        lac i 100
        rcl 8s
        rcl 8s
        jmp x
&lt;/pre&gt;

  &lt;p&gt;&lt;code&gt;&quot;lio i 100&quot;&lt;/code&gt; loads the current Lisp value into the IO register.
    &lt;code&gt;&quot;cla&quot;&lt;/code&gt; sets the accumulator to zero. &lt;code&gt;&quot;rcl
    2s&quot;&lt;/code&gt; then rotates the combination of the IO register and
    accumulator by 2 bits.  The accumulator now contains as its
    low bits the previous high two bits of the IO register. &lt;code&gt;&quot;sas
    (3&quot;&lt;/code&gt; compares the accumulator to 3; if they&#039;re not equal we
    jump to qi3 (the error routine for &quot;non-numeric arg for
    arith&quot;). &lt;code&gt;&quot;idx 100&quot;&lt;/code&gt; moves the pointer to the next word
    of the value, and &lt;code&gt;&quot;lac i 100&quot;&lt;/code&gt; reads that word into
    the accumulator. And finally the combination of the two registers
    is rotated by 16 bits, so that we end up with the raw 18 bit value
    in the accumulator. Written out step by step the process looks
    like this:
  &lt;/p&gt;

  &lt;pre&gt;
    . == Bit with value of 0
    ! == Bit with value of 1
    ? == Bit with unknown value
    0-9, A-H == bits of the integer value

    X                    X+1
------------------------------------------------
    [!!23456789ABCDEFGH] [................01]

    IO                   AC
------------------------------------------------
Load IO from address X
    [!!23456789ABCDEFGH] [??????????????????]
Clear AC
    [!!23456789ABCDEFGH] [..................]
Rotate left by 2
    [23456789ABCDEFGH..] [................!!]
Check AC == 3
Load AC from address X+1
    [23456789ABCDEFGH..] [................01]
Rotate left by 8
    [ABCDEFGH..........] [........0123456789]
Rotate left by 8
    [..................] [0123456789ABCDEFGH]
&lt;/pre&gt;

  &lt;p&gt;
    Clearly an integer is now represented by a pointer to two words
    that has a special tag in the high bits of the first word. This
    implementation got rid of the extra layer of indirection in LISP
    1.5; an integer is now just a pointer to tagged data. But we&#039;re
    still left with the storage of a one-word integer requiring three
    words.
  &lt;/p&gt;

  &lt;p&gt;
    Why use a layout that requires shuffling data around this much,
    instead of just having the tag in X and the raw value in X+1?  It
    seems awfully inconvenient. My best guess is that the top 1-2 bits
    of the second word are reserved for the GC, e.g. for use as mark
    bits. But understanding exactly how the GC works is maybe a project
    for another day.
  &lt;/p&gt;

  &lt;a name=&#039;m460&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;M 460 LISP&lt;/h3&gt;

  &lt;p&gt;
    Before starting research for this article, I&#039;d never heard of the
    early Lisp implementation for the Univac M 460. A description of
    the system can be found in the 1964
    collection &lt;a href=&#039;https://scholar.google.com/scholar?cluster=1071332420478270292&amp;hl=en&amp;as_sdt=0,5&amp;sciodt=0,5&#039;&gt;
    The programming language LISP: Its operation and applications
    &lt;/a&gt;.
  &lt;/p&gt;

  &lt;blockquote&gt;
    Numbers and print names are placed in free storage using the
    device that sufficiently small (i.e., less than 2^10) half-word
    quantities appear to point into the bit table area and so don&#039;t
    cause the garbage collector any trouble. A number is stored as a
    list of words (a flag-word and from 1 to 3 number words, as
    required), each number word containing in its CAR part 10
    significant bits and sign. Thus an integer whose absolute value
    is less than 2^11 will occupy the same amount of storage
    (2 words) as in 7090 LISP 1.5.
  &lt;/blockquote&gt;

  &lt;p&gt;
    This is another bit of progress! The key insight on the road to
    tagged pointers is that invalid parts of the address space can be
    used to distinguish between pointers and immediate data. Another
    important insight in this paper is that most numbers in a program
    are going to be small, so it might make sense to have variable
    representations for numbers of different magnitude. But it&#039;s not a
    full realization of the concept yet, immediate small numbers are
    not accessible directly by the user. They are internal to the
    implementation, used as a building block for boxed integers of
    various levels of inefficiency.
  &lt;/p&gt;

  &lt;p&gt;
    The paper gets even better once we get a few more pages in, since
    for characters M 460 Lisp does take that final step:
  &lt;/p&gt;

  &lt;blockquote&gt;
Each character in the character set available on the M 460
(including tab, carriage return, and others) is represented internally
by an 8-bit code (6 bits for the character (up to case),
1 bit for case, and 1 bit for color). To facilitate the manipulation
of character strings within our LISP system, we permit
such character literals to appear in list structure as if they
were atoms, i.e. pointers to property lists. These literals can,
where necessary, be distinguished from atoms since they are less
than 2^8 in magnitude and hence, viewed as pointers, don&#039;t point
into free storage (where, as in 7090 LISP, property lists are
stored). The predicate charp simply makes this magnitude test.
  &lt;/blockquote&gt;

  &lt;p&gt;
    That&#039;s about as clear a case of using embedding immediate data in
    pointers as it gets. It&#039;s just that the tag is rather large (22
    highest bits, rather than the 1-4 lowest bits you&#039;d expect today).
    And it&#039;s also dealing with characters rather than numbers, so
    let&#039;s carry on with the investigation a bit longer.
  &lt;/p&gt;

  &lt;a name=&#039;pdp6&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;PDP-6 LISP&lt;/h3&gt;

  &lt;p&gt;
    The June 1966 report on
    &lt;a href=&#039;https://dspace.mit.edu/bitstream/handle/1721.1/5899/AIM-098.pdf&#039;&gt;PDP-6 LISP&lt;/a&gt;
    has the following to say on integers:
  &lt;/p&gt;

  &lt;blockquote&gt;
    Fixed-point numbers &amp;gt;= 0 and &amp;lt; about 4000 are represented by
    a &quot;pointer&quot; 1 greater than their value, and no additional list structure.
    All other numbers use a pointer to full-word space as part of an atom
    header with a FIXNUM or FLONUM indicator.
  &lt;/blockquote&gt;

  &lt;p&gt;
    This is starting to get close to the modern fixnum, except for no
    facility for immediate negative numbers and a tiny range. (This is
    a machine with 36 bit words and 18 bit pointers; one would hope
    for a bit more than 12 bits for immediate integers).
  &lt;/p&gt;

  &lt;a name=&#039;bbn&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;BBN LISP&lt;/h3&gt;

  &lt;p&gt;
    &lt;a href=&#039;http://www.dtic.mil/dtic/tr/fulltext/u2/647601.pdf&#039;&gt;
      Structure of a LISP system using two-level storage
    &lt;/a&gt; is a wonderful systems design paper from November 1966,
    describing BBN LISP for a PDP-1 with 16K of
    core memory, 88K of absurdly slow drum memory, and no hardware
    paging support. How do you make efficient use of the drum memory?
    By some clever data layout, software-driven paging, and a
    locality-optimizing memory allocator.
  &lt;/p&gt;

  &lt;p&gt;
    So it&#039;s actually a paper I thought was totally worth reading just
    for its own sake. But for the purposes of this post, this is the
    money quote:
  &lt;/p&gt;

  &lt;blockquote&gt;
LISP assumes that it is operating in an environment containing
128K words, that is from 0 to 400,000 octal. Only 88K actually
exist on the drum. The remaining portion of the address space
is used for representation of small integers between -32,767
and 32,767 (offset by 300,000 octal), as described below.
  &lt;/blockquote&gt;

  &lt;p&gt;
    The paper describes a machine with both an 18-bit word size
    and address space, with 16-bit signed fixnums embedded in the
    pointers. That&#039;s about as good as it gets. (Though not quite
    optimal; they&#039;re using bit 17 as the integer tag, but what
    happened to bit 18? The paper doesn&#039;t say, but odds are that
    it&#039;s again a GC mark bit).
  &lt;/p&gt;

  &lt;p&gt;
    The particularly observant reader might have noticed that this
    machine had 104K words of physical memory, but the described
    tagging scheme only leaves 64K words addressable. What&#039;s up with
    that? On one level it&#039;s exactly what M 460 LISP and PDP-6 Lisp
    were doing: that 40K of address space stores things that can&#039;t be
    directly pointed to from another Lisp value. But those other
    implementations were just opportunistically reusing the
    parts of address space that contained native code.
  &lt;/p&gt;

  &lt;p&gt;By contrast, BBN LISP carefully arranged for there to exist as
    much of such storage as possible, and for it to be located above
    the address 200,000 (octal).&lt;/p&gt;

  &lt;img src=&#039;/blog/stc/images/lisp-numbers/bbn-lisp-layout.png&#039; /&gt;

  &lt;p&gt;The most clever example of that is the representation of
    symbols. The first implementations we saw just implemented symbols
    as a list of properties indexed by name (e.g. name, value cell,
    function cell, etc). An obvious optimization is to allocate a
    symbol as a single larger block of memory with fixed slots for the
    most common properties, and a generic property list slot to
    contain anything else.&lt;/p&gt;

  &lt;p&gt;
    What BBN Lisp does instead is allocate a symbol in multiple
    separate blocks rather than a single contiguous one. A pointer to
    the symbol will point to the block of value cells, so reading the
    value cell is trivial. What if you want to read another property,
    e.g. the function? We look at the offset of the value cell pointer
    to the start of the value cell block, and access the function cell
    block at the same offset. In modern parlance it ends up as an
    structure-of-arrays layout rather than an array-of-structures.
  &lt;/p&gt;

  &lt;p&gt;
    In addition to getting more address space for fixnums, they also
    got exactly the same kind of locality improvements that an
    structure-of-arrays would be used for today. So it was an
    all-around neat optimization.
  &lt;/p&gt;

  &lt;p&gt;
    There is also an &lt;a href=&#039;http://www.softwarepreservation.org/projects/LISP/bbnlisp/BBN940LispPrelimSpec_Oct1966.pdf&#039;&gt;early design document for BBN 940 LISP&lt;/a&gt; from almost the same time as the above paper. It appears
to describe the kind of elaborate tagging scheme that a modern Lisp
might use, and places the tags in the low bits where they&#039;re easier to
test for/eliminate. And they even call heap-allocated numbers &quot;boxed&quot;!
I had no idea this terminology was in use 50 years ago. The relevant
section:
  &lt;/p&gt;

  &lt;blockquote&gt;
&lt;p&gt;
There will be a maximum of 16 pointer types of
objects in the 940 LISP System. These are (numbered in octal)
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; 00. S-expressions (nonatomic)
&lt;li&gt; 01. Identifiers (literal atoms)
&lt;li&gt; 02. Small Integers
&lt;li&gt; 03. Boxed Large Integers
&lt;li&gt; 04. Boxed Floating Point Numbers
&lt;li&gt; 05. Compiled Function - Lambda Type
&lt;li&gt; 06. Compiled Function - Lambda Type - Indef Args
&lt;li&gt; 07. Compiled Function - Mu Type - Args Paired
&lt;li&gt; 10. Compiled Function - Mu Type - List of Args
&lt;li&gt; 11. Compiled Function - Macro
&lt;li&gt; 12. Array - Pointers
&lt;li&gt; 13. Array - Integers
&lt;li&gt; 14. Array - FP #s
&lt;li&gt; 15. Strings - Packed Character Arrays
&lt;li&gt; 16.
&lt;li&gt; 17. Pushdown List Pointers
&lt;/ul&gt;

&lt;p&gt;
Each pointer will be contained in one 940 word of 24
bits. Bits 0 and 1 will be nominally empty, and may in some
cases be used by the system (e.g. bit 0 for garbage collection)
or perhaps even the user (in S-expressions). The four bits
2-5 will contain the type number for this pointer. The 18
bits 6-23 will contain an effective address (in the LISP
drum file) where the referenced information is stored.
&lt;/p&gt;

&lt;/blockquote&gt;

&lt;p&gt;It looks like they ended up not using this design
    for BBN 940 LISP, and it instead uses an extended version of the
    segmented memory scheme from the PDP-1 implementation described
    earlier in this section. But even if these particular bits
    weren&#039;t practical to use with that hardware, at this point
    just about all the ideas for tagged pointers have definitely
    been invented.&lt;/p&gt;

  &lt;a name=&#039;conclusion&#039;&gt;&lt;/a&gt;
  &lt;h3&gt;Conclusion&lt;/h3&gt;

  &lt;p&gt;
    The initial LISP I implementation in 1960 had the least efficient
    implementation of numbers this side of church numerals, where even
    just getting the value might imply chasing half a dozen
    pointers. But new implementations optimized that layout
    aggressively. By 1964, the M 460 LISP implementation had arrived
    at the general solution of using pointers to invalid parts of
    the address space for storing immediate data, but user-accessible
    integers were still boxed; the only use for the unboxed integers
    was as an internal building block. In 1966 PDP-6 LISP applied the
    idea of tagged immediate data to tiny positive integers, and the
    PDP-1 based BBN LISP took the idea to the logical conclusion, and
    allowed immediate storage of integers of almost the full machine
    word.
  &lt;/p&gt;

  &lt;p&gt;
    I would not have guessed that these optimizations were discovered
    and applied so early and so aggressively. It&#039;s also noteworthy
    that this was independent of both the machine word size, address
    space size, and addressing mode of the machine. The first fully
    fledged implementation I found was on a machine with 18 bit words, 18
    bits of address space, and word-addressing. That should have been
    just about the worst case!
  &lt;/p&gt;

  &lt;p&gt;There&#039;s an interesting tangent with how MacLISP ended up
    reversing this progress in the &#039;70s and going back to boxed
    integers, since they wanted to have just a single integer
    representation. I won&#039;t go into the details since this post
    already grew longer than intended. But for those interested in the
    subject &lt;a href=&#039;https://dspace.mit.edu/bitstream/handle/1721.1/6279/AIM-421.pdf&#039;&gt;AI Memo 421&lt;/a&gt; is a fun read.
  &lt;/p&gt;

  &lt;p&gt;
    Was the technique definitely first used in Lisp? These
    implementations are early enough that there aren&#039;t a ton of other
    possibilities. The only ones I can think of would be APL and
    Dartmouth BASIC. If anyone can find documentation on earlier
    uses of storing immediate data in tagged pointers, please
    let me know and I&#039;ll edit the article.
  &lt;/p&gt;
</description><author>jsnell@iki.fi</author><category>LISP</category><category>HISTORY</category><pubDate>Mon, 04 Sep 2017 15:00:00 GMT</pubDate><guid permaurl='true'>https://www.snellman.net/blog/archive/2017-09-04-lisp-numbers/</guid></item><item><title>The origins of XXX as FIXME</title><link>https://www.snellman.net/blog/archive/2017-04-17-xxx-fixme/</link><description>
&lt;p&gt;
  The token &lt;code&gt;XXX&lt;/code&gt; is frequently used in source code
  comments as a way of marking some code as needing attention.
  (Similar to a &lt;code&gt;FIXME&lt;/code&gt; or &lt;code&gt;TODO&lt;/code&gt;, though at
  least to me &lt;code&gt;XXX&lt;/code&gt; signals something far to the hacky
  end of the spectrum, and perhaps even outright broken).

&lt;p&gt;
  It&#039;s a bit of an odd and non-obvious string though, unlike
  &lt;code&gt;FIXME&lt;/code&gt; and &lt;code&gt;TODO&lt;/code&gt;. Where did this
  convention come from? I did a little bit of light software archaeology
  to try to find out. To start with, my guesses in order were:

&lt;ul&gt;
  &lt;li&gt; MIT (since it sometimes feels like that&#039;s the source of
    90% of ancient hacker shibboleths)
  &lt;li&gt; Early Unix (probably the most influential codebase that&#039;s
    ever existed)
  &lt;li&gt; Some kind of DEC thing (because really, all the world was
    a PDP)
&lt;/ul&gt;

&lt;read-more&gt;&lt;/read-more&gt;

&lt;h3&gt;Other uses of &lt;code&gt;XXX&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;
  It turns out that &lt;code&gt;XXX&lt;/code&gt; and &lt;code&gt;xxx&lt;/code&gt; are
  incredibly annoying things to search for in old code. I&#039;d bet it&#039;s
  the most common sequence of 3+ identical letters in source code.
  That means there&#039;s a ton of false positives to sift through.
  Here&#039;s a few examples of the kind of stuff that will be found.

&lt;p&gt;
  By far the most common use of &lt;code&gt;XXX&lt;/code&gt; in old is for it to
  be some kind of a &lt;b&gt;template placeholder&lt;/b&gt;. This makes some sense;
  &lt;code&gt;x&lt;/code&gt; for an unknown value has an obvious long history
  that predates computing. These templates might be used to describe
  the exact data layout of something, like in the following bits from the
  Apollo guidance computer:
&lt;/p&gt;

&lt;pre&gt;
# 17    ASTRONAUT TOTAL ATTITUDE      3COMP   XXX.XX DEG FOR EACH
# 18    AUTO MANEUVER BALL ANGLES     3COMP   XXX.XX DEG FOR EACH
# 19    BYPASS ATTITUDE TRIM MANEUVER 3COMP   XXX.XX DEG FOR EACH
# 20    ICDU ANGLES                   3COMP   XXX.XX DEG FOR EACH
# 21    PIPAS                         3COMP   XXXXX. PULSES FOR EACH
# 22    NEW ICDU ANGLES               3COMP   XXX.XX DEG FOR EACH
# 23    SPARE
# 24    DELTA TIME FOR AGC CLOCK      3COMP   00XXX. HRS. DEC ONLY
&lt;/pre&gt;

&lt;p&gt;Or as just a wildcard for a bunch of related names, like the in
  this Lisp Machine source code:&lt;/p&gt;

&lt;pre&gt;
;Q-FASL-xxxx refers to functions which load into the cold load, and
; return a &quot;Q&quot;, i.e. a list of data-type and address-expression.
;M-FASL-xxxx refers to functions which load into Maclisp, and
; return a Lisp object.
&lt;/pre&gt;

&lt;p&gt;Or as actual templates-as-program, with parts of
  an input remains while others (those marked with &lt;code&gt;XXX&lt;/code&gt;)
  are programatically replaced. For example temporary file generation
  in in UNIXv5:&lt;/p&gt;

&lt;pre&gt;
                f = ranname(&quot;/usr/lpd/dfxxx&quot;);
&lt;/pre&gt;

&lt;p&gt;And finally, it could denote parts of persistent data structures
  that were reserved for future use (or no longer used), for example
  in CPM:&lt;/p&gt;

&lt;pre&gt;
/* THE FILE CONTROL BLOCK FORMAT IS SH0WN BELOW:
   --------------------------------------------------------
   /    1 BY / 8 BY / 3 BY / 1 BY /2BY/1 BY/ 16 BY /
   /F1LETYPE/   NAME / EXT / REEL NO/XXX/RCNT/DM0 DM15/
   --------------------------------------------------------

   FILETYPE     :       0E5H IF AVAILABLE (OTHERWISE UNDEFINED NOW)
...
   XXX          :       UNUSED FOR NOW
   RCNT         :       RECORD COUNT IN FILE (0 TO , 127)
&lt;/pre&gt;

&lt;p&gt;
  A less savoury use of &lt;code&gt;XXX&lt;/code&gt; is as an identifier for
  something that didn&#039;t even qualify to have a real name. Most
  commonly it&#039;d be the name of a branch target, like in a very
  early version of the C compiler:
&lt;/p&gt;

&lt;pre&gt;
    xxx:
        if (o==KEYW) {
                if (cval==EXTERN) {
                        o = symbol();
                        goto xxx;
                }
&lt;/pre&gt;

&lt;p&gt;It could also be used to name variables. The following is from the
  FORTRAN II compiler for the IBM 704 from 1958. (I don&#039;t read 704
  assembler, so maybe I&#039;m misinterpreting what&#039;s going on in that
  program. It seems funny enough that I wanted to include it here
  anyway).
&lt;/p&gt;

&lt;pre&gt;
XXXXXX SYN 0  THE APPEARANCE OF THIS SYMBOL IN   F4400370
       REM       THE LISTING INDICATES THAT ITS  F4400380
       REM       VALUE IS SET BY THE PROGRAM.    F4400390
&lt;/pre&gt;

&lt;p&gt;
  Some DEC code seems to have gone really overboard with this, with
  single source files having half a dozen different &lt;code&gt;XXXYYY&lt;/code&gt;
  identifiers. (Sorry, had to use YYY as the placeholder there for
  obvious reasons).

&lt;p&gt;
  Finally, there are all kinds of bizarre one-off uses. TENEX seems
  to have used &lt;code&gt;XXX&lt;/code&gt; for implementing rubout. That is,
  when you&#039;d press backspace to delete something you&#039;ve typed, it&#039;d
  print out XXX on the teletype to mark the deletion. (Rather than
  try to move the cursor back). Some kind proto-instant messaging
  program from 1976 written in Interlisp that I found would just print
  &lt;code&gt;XXX&lt;/code&gt; as the error message for invalid user input.

&lt;p&gt;
  Now, sorry if the above parts were kind of tedious. But there is
  actually a point here. Turns out that &lt;code&gt;XXX&lt;/code&gt; is
  a really stupid marker to use for
  a &lt;code&gt;FIXME&lt;/code&gt;. Looking at the Panda TOPS-20 distribution,
  there are 3083 instances of XXX, none of which are &lt;code&gt;FIXME&lt;/code&gt;s.
  Just about anything else would be easier to
  find. This makes its use as one of the three main
  &lt;code&gt;FIXME&lt;/code&gt;-markers all the more puzzling.

&lt;h3&gt;&lt;code&gt;XXX&lt;/code&gt; as a &lt;code&gt;FIXME&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;
  To get the negative results out of the way, there is absolutely no
  sign of this being an MIT or DEC thing. &lt;code&gt;XXX&lt;/code&gt; as &lt;code&gt;FIXME&lt;/code&gt;
  doesn&#039;t appear on ITS or TOPS-20 disks, nor does it appear in any of
  the mountains of really old Lisp code that I happened to have around;
  I don&#039;t think it makes it to Lisp-land until the mid-&#039;80s. It&#039;s
  also absent in smaller collections of old code from other sources.
&lt;/p&gt;

&lt;p&gt;
  No, this seems to definitely be a Unix thing. There are a couple of
  interesting possibilities in early BSD. First, there&#039;s the following
  lines in a package of &lt;a href=https://github.com/dspinellis/unix-history-repo/commit/b41454192b6489951f36873ca3a792e9b1a73c92&gt;troff macros that
    first appeared in 2BSD, with a copyright date of 1978&lt;/a&gt;:

&lt;pre&gt;
..
.de (t                 \&quot; XXX temp ref to (z
.(z \\$1 \\$2
..
.de )t                 \&quot; XXX temp ref to )t
.)z \\$1 \\$2
&lt;/pre&gt;

&lt;p&gt;I&#039;m pretty sure these are not actually a &lt;code&gt;FIXME&lt;/code&gt;.
  It looks like the convention in this code was to mark
  &lt;code&gt;.de&lt;/code&gt; commands with three character tags depending
  on their type, as explained in the beginning of the file:

&lt;pre&gt;
+.\&quot;	Code on .de commands:
+.\&quot;		***	a user interface macro.
+.\&quot;		&amp;&amp;&amp;	a user interface macro which is redefined
+.\&quot;			when used to be the real thing.
+.\&quot;		$$$	a macro which may be redefined by the user
+.\&quot;			to provide variant functions.
+.\&quot;		---	an internal macro.
&lt;/pre&gt;

&lt;p&gt;These lines seem to have been commands that didn&#039;t fit into
  those existing categories, and needed a new tag.&lt;/p&gt;

&lt;p&gt;
  Next up, there&#039;s a bunch of very promising looking changes to
  the troff C source in the summer of 1980. Stuff like:
&lt;/p&gt;

&lt;pre&gt;
if(j == &#039; &#039;){
        storeword(i,width(i));  /* XXX */
        continue;
}
&lt;/pre&gt;

&lt;p&gt;
  That certainly looks like a classic &lt;code&gt;FIXME&lt;/code&gt;. But I think
  this is another dead end. It turns out that after this change there are
  37 &lt;code&gt;/* XXX */&lt;/code&gt; comments in code that didn&#039;t use to have any.
  And when comparing to Unix v7 source code, it looks like basically
  every single line that was changed got marked with one. So it&#039;s
  unlikely that these are actual &lt;code&gt;FIXME&lt;/code&gt;s. I think this
  was just the author making sure they could identify their changes,
  in case they wanted to reintegrate with &quot;upstream&quot;.
&lt;/p&gt;

&lt;p&gt;Soon after that BSD moves to SCCS, and we start getting fine-grained
  changes rather than huge code-dumps. From there, it&#039;s easy to find
  &lt;a href=&#039;https://github.com/dspinellis/unix-history-repo/commit/9e295a2f65c046125ece0ad68f142f59df4c3400&#039;&gt;the first &lt;code&gt;/* XXX */&lt;/code&gt; commit&lt;/a&gt;
  from Nov 9, 1981. This one is interesting in a few ways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;This is definitely a &lt;code&gt;FIXME&lt;/code&gt;; just a few very special
    parts of the code got tagged, and many of them got rewritten soon
    after.
  &lt;li&gt;After this commit, the use of &lt;code&gt;/* XXX */&lt;/code&gt; starts
    spreading quickly through the BSD codebase and eventually to
    other authors.
  &lt;li&gt;A closer reading of the commit shows something interesting:
    a bunch of &lt;code&gt;/* ### */&lt;/code&gt; comments. Going through the earlier
    history, it seems that Bill Joy had been marking
    his &lt;code&gt;FIXME&lt;/code&gt;s with &lt;code&gt;###&lt;/code&gt;,
    and halfway through this commit changed to using
    &lt;code&gt;XXX&lt;/code&gt;. I don&#039;t know why, or whether these two markers were
    intended to have slightly different semantics (like &lt;code&gt;###&lt;/code&gt;
    was code that needed to be fixed, &lt;code&gt;XXX&lt;/code&gt; was code that
    was commented out and needed to be fixed and re-enabled).
    But &lt;code&gt;XXX&lt;/code&gt; quickly became the preferred form.
&lt;/ul&gt;

&lt;pre&gt;
                        if (rcv_empty(tp)) {                    /* 16 */
-                               tcp_close(tp, UCLOSED);
+                               sowakeup(tp-&gt;t_socket); /* ### */
+/* XXX */                      /* tcp_close(tp, UCLOSED); */
                                nstate = CLOSED;
                        } else
&lt;/pre&gt;

&lt;p&gt;(On a personal note, as someone who goes out of their way to read through any
  published TCP stacks, I&#039;m kind of amused that a search for a random
  historical trivia leads me to a damn TCP stack).&lt;/p&gt;


&lt;p&gt;Leaving it at that seems like a good story. And I&#039;d already
  checked basically all of Bell Labs code that I could find. It&#039;s not
  in Unix v2-v7 and not in the Programmer&#039;s Workbench. But then I
  decided to check Unix v1 just for completeness sake, and got very
  confused. Because...
&lt;/p&gt;

&lt;pre&gt;
/ XXX fix me, I dont quite understand what to do here or
/ what is done in the similar code below e407:
/ cmp   r5, u.count / see if theres enough room
/ bgt   1f
mov     r5,u.count / read text+data into core
&lt;/pre&gt;

&lt;p&gt;WTF? It doesn&#039;t get any clearer than that. But where did it come from?
  And if this convention was used at
  Bell Labs in 1970, where did &lt;code&gt;XXX&lt;/code&gt; disappear for a
  decade?&lt;/p&gt;

&lt;p&gt;Turns out this was a false alarm. The only reason we have
  the Unix v1 source code in the first place is that a team of people
  transcribed the source from PDF scans to text. Then they went on to
  make it possible to compile the code and run it in an emulator. As
  part of this latter work, a block of code was added to the source.
  And a bit unfortunately it was this patched version rather than the
  &quot;original&quot; that made it to the Unix History Repo.  This comment was
  actually from 2008, not 1971.
&lt;/p&gt;

&lt;p&gt;There&#039;s actually an interesting story behind that extra block of
  code, &lt;a href=https://www.usenix.org/legacy/events/usenix09/tech/full_papers/toomey/toomey.pdf&gt;as told by Toomey&lt;/a&gt;. After finally getting the v1
  kernel transcribed, compiled, and running, they hit the
  problem of the only having two userland programs available:
  &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;sh&lt;/code&gt;. Everything else was using
  a more recent executable header. To be able to do anything at all
  with the system, they needed to add support for &quot;0407 binaries&quot; as
  opposed to the &quot;0405&quot; ones the kernel supported natively.

&lt;p&gt;
  What about C code outside of Unix distributions? It&#039;s actually kind
  of hard to find any of that from before 1982.
  There might be an earlier instance in Gosling Emacs, though it
  differs from the modern form by going for a full 9 &lt;code&gt;X&lt;/code&gt;s:
&lt;/p&gt;

&lt;pre&gt;
#ifdef HalfBaked
/*    sigset (SIGINT, InterruptKey); *//*XXXXXXXXX*/
    sigset (SIGINT, InterruptKey);/*XXXXXXXXX*/
#endif
&lt;/pre&gt;

&lt;p&gt;
And there&#039;s a Changelog entry from July 1981, which seems to match
up perfectly with both the functionality of the code, and the surrounding
&lt;code&gt;ifdef&lt;/code&gt;:
&lt;/p&gt;

&lt;pre&gt;
Tue Jul  7 12:51:44 1981  James Gosling  (jag at VLSI-Vax)
        ... I also installed Dave
        Dyer&#039;s hack to allow ^G&#039;s to interrupt execution immediatly.  This
        has a rather major bug, and is the reason that I didn&#039;t implement
        it a long time ago: if you type ^G while Emacs is doing output,
        then all queued-but-not-printed characters get lost and Emacs no
        longer has any idea of what the screen looks like. It is pretty
        much impossible for Emacs to tell whether or not this has
        happened. You end up having to type ^L now and then.  The
        &quot;HalfBaked&quot; switch in config.h controls the compilation of this
        facility, ...
&lt;/pre&gt;

&lt;p&gt;But thankfully this code has RCS history starting from 1986, and
 somebody did in fact edit this code in 1986 with no functional changes,
 but adding the commented out copy and the
 &lt;code&gt;XXXXXXXXX&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
 #ifdef HalfBaked
-    sigset (SIGINT, InterruptKey);
+/*    sigset (SIGINT, InterruptKey); *//*XXXXXXXXX*/
+    sigset (SIGINT, InterruptKey);/*XXXXXXXXX*/
 #endif
&lt;/pre&gt;

&lt;p&gt;
  And those are the only signs of &lt;code&gt;XXX&lt;/code&gt; in
  applications that could predate the BSD usage. Both were
  red herrings, caused by how difficult it&#039;s to actually find
  pristine copies of source code that old. It was very lucky that
  the Gosling Emacs comment was added after the code was put to RCS,
  and made not in the five year interval between the original commit
  and the project starting to use RCS.
&lt;/p&gt;

&lt;p&gt;So it seems likely that this convention was invented by Bill Joy
  in BSD. If he wasn&#039;t the first one, he was certainly the one that
  popularized it. Why he chose to switch to the rather inconvenient
  &lt;code&gt;XXX&lt;/code&gt; from &lt;code&gt;###&lt;/code&gt; is unclear.
  &lt;/p&gt;

&lt;p&gt;If you can find an earlier occurence (or know of good collections
  of pre-1981 C source code), please let me know and
  I&#039;ll update the post.&lt;/p&gt;

</description><author>jsnell@iki.fi</author><category>HISTORY</category><pubDate>Mon, 17 Apr 2017 18:00:00 GMT</pubDate><guid permaurl='true'>https://www.snellman.net/blog/archive/2017-04-17-xxx-fixme/</guid></item><item><title>The most obsolete infrastructure money could buy - my worst job ever</title><link>https://www.snellman.net/blog/archive/2015-09-01-the-most-obsolete-infrastructure-money-could-buy/</link><description>
&lt;p&gt;
Today marks the 10th anniversary of the most bizarre, and possibly
the saddest, job I ever took.

&lt;p&gt;
The year was 2005. My interest in writing a content management
system in Java for the company that bought our startup had been
steadily draining away, while my real passion was working on
compilers and other programming language infrastructure
(mostly &lt;a href=&#039;http://www.sbcl.org/&#039;&gt;SBCL&lt;/a&gt;). One day I spotted a
job advert looking for compiler people, which was a rare occurrence in
that time and place. I breezed through the job interview, but did not
ask the right questions and ignored a couple of warning signs. Oops.

&lt;p&gt;
It turned out to be a bit of an adventure in retrocomputing.

&lt;read-more&gt;&lt;/read-more&gt;

&lt;h3&gt;The bizarre&lt;/h3&gt;

&lt;p&gt;
This was the former internal tools unit of a very large company, let&#039;s
call them X. For some reason X had split off the unit and sold
(given?) it to a moderately large consulting company, whom we shall
call Y. I was going to work at Y. The reason they needed compiler
people was that they were about to take over the maintenance of a C
compiler suite (compiler, linker, assembler, etc). Except I&#039;d
misunderstood them as taking over the maintenance from X. That wasn&#039;t
the case. Actually the compiler was from another very large company,
Z, who were discontinuing all support. So X bought the source code
from Z for very significant $$$, and needed somebody (Y) to actually
do something with it. In fact it wasn&#039;t even just one compiler suite
as I&#039;d initially understood, it was two. Woo, double the compilers to
play with!

&lt;p&gt;
I started in September, but some schedules had slipped and we wouldn&#039;t
actually have anything to work with for a month or two. So I had
plenty of time to acclimatize there. Which is good, because it&#039;s like
I&#039;d stepped into some strange parallel dimension where the 80s never
ended. You know, the kind of place where you need access to some old
documentation, and eventually find it&#039;s stored in an ingenious in-house
source control system built on top of &lt;a href=&#039;https://en.wikipedia.org/wiki/Revision_Control_System&#039;&gt;RCS&lt;/a&gt;.

&lt;p&gt;
For example on my first day I found that X was running what was
supposedly largest VAXcluster remaining in the world, for doing their
production builds. Yes, dozens of &lt;a href=&#039;https://en.wikipedia.org/wiki/VAX&#039;&gt;VAX&lt;/a&gt;en running &lt;a href=&#039;https://en.wikipedia.org/wiki/OpenVMS&#039;&gt;VMS&lt;/a&gt;, working as a
cross-compile farm, producing x86 code. You might wonder a
bit about the viability of the VAX as computing platform in the year
2005. Especially for something as cpu-bound as compiling. But don&#039;t
worry, one of my new coworkers had as their current task evaluating
whether this should be migrated to VMS/Alpha or to VMS/VAX running
under a VAX emulator on x86-64! &lt;a href=&#039;#ftnt0&#039;
name=&#039;ftnt_ref0&#039;&gt;[0]&lt;/a&gt;

&lt;p&gt;
Why did this company need to maintain a specific C compiler anyway?
Well, they had their own ingenious in-house programming language that
you could think of as an imperative Erlang with a Pascal-like syntax
that was compiled to C source &lt;a href=&#039;#ftnt1&#039;
name=&#039;ftnt_ref1&#039;&gt;[1]&lt;/a&gt;. I have no real data on how much
code was written in that language, but it&#039;d have to be tens of
millions lines at a minimum.


&lt;p&gt;
The result of compiling this C code would then be run on an ingenious
in-house operating system that was written in, IIRC, the late
80s. This operating system used the 386&#039;s segment registers to
implement multitasking and message passing. For this, they needed the
a compiler with much more support for segment registers than normal.
Now, you might wonder about the wisdom of relying on segment registers
heavily in the year 2005. After all use of segment registers had been
getting slower and slower with every generation of CPUs, and in x86-64
the segmentation support was essentially removed. But don&#039;t worry,
there was a project underway to migrate all of this code to run on
Solaris instead &lt;a href=&#039;#ftnt2&#039; name=&#039;ftnt_ref2&#039;&gt;[2]&lt;/a&gt;.

&lt;p&gt;
After a couple of months of twiddling my thumbs and mostly reading
up on all this mysterious infrastructure, a huge package arrived
addressed to this compiler project. But... We were supposed to get
a source dump. Why does the package need two men to carry it? Did
somebody play a practical joke on us, and send the source as
printouts?

&lt;p&gt;
Why it&#039;s the server that we&#039;ll use for compiling one of the compiler
suites once we get the source code! A Intel System/86 with a genuine
80286 CPU, running Intel &lt;a href=&#039;https://en.wikipedia.org/wiki/Xenix&#039;&gt;Xenix&lt;/a&gt; 286 3.5. The best way to interface with all
this computing power is over a 9600 bps serial port. Luckily the
previous owners were kind enough to pre-install &lt;a href=&#039;https://en.wikipedia.org/wiki/Kermit_(protocol)&#039;&gt;Kermit&lt;/a&gt; on the spacious
40MB hard drive of the machine, and I didn&#039;t need to track down a
floppy drive or a Xenix 286 Kermit or rz/sz binary. God, what primitive
pieces of crap that machine and OS were.

&lt;p&gt;
You might wonder about the wisdom of using a 15-20 year old machine as
the sole method of building a piece of software. It&#039;s dog slow and
obviously will break sooner or later. In fact I raised this very issue
and suggested maybe imaging the hard drive and getting everything
running virtualized. That idea was nixed since the machine was old and
fragile, we couldn&#039;t risk poking around in the inside. It&#039;d be really
hard to replace, when they went hunting for this machine from antique
computer specialists, they only found two remaining working
units &lt;a href=&#039;#ftnt3&#039; name=&#039;ftnt_ref3&#039;&gt;[3]&lt;/a&gt;.

&lt;p&gt;
This might be a good time to say that computationally speaking,
I was raised by the wolves on a
SunOS 4 server (which I ended up sysadmining for a few hundred users).
My personal email was still going over &lt;a href=&#039;https://en.wikipedia.org/wiki/UUCP&#039;&gt;UUCP&lt;/a&gt; in 2005.
The highlight of my previous weekend (in 2015, when I&#039;m writing this)
was finding what looks like a partial source repository for a Lisp
implementation written before I was born, and which appeared to have
been completely lost to time. It was on a copy of some old backup
tapes from an &lt;a
href=&#039;https://en.wikipedia.org/wiki/Incompatible_Timesharing_System&#039;&gt;ITS&lt;/a&gt;
server, and I don&#039;t even remember how or when those ended up on my harddrive.
Which is to say, I like old computer systems more than is reasonable.

&lt;p&gt;
But even by my standards this level of computational archeology was
going a bit too far. And the rabbit hole still had a little bit deeper
to go.

&lt;p&gt;
A couple of weeks later the source drop arrived. I&#039;ll talk about the
other compiler later, let&#039;s tackle this one that needed to be built
on a 286 first.

&lt;p&gt;
So it was written in &lt;a href=&#039;https://en.wikipedia.org/wiki/PL/M&#039;&gt;PL/M&lt;/a&gt;. (Wait, is that even a
thing? That&#039;s not a thing, right?). And it was last modified in the mid 80s. I&#039;d like to say the
build instructions were generated using a typewriter, but it could be
that my memory is playing tricks on that. Some of the components
didn&#039;t build cleanly, and required various Makefile tweaks with
excruciating round trip times for every test. Because, you know,
this is a 286.

&lt;p&gt;
The hard drive wasn&#039;t large enough for all of the components either,
so the process of rebuilding everything would be:

&lt;ul&gt;
  &lt;li&gt;Upload the linker source tarball over the 9600 bps serial connection from a Linux server acting as a frontend
  &lt;li&gt;Unpack it
  &lt;li&gt;Build
  &lt;li&gt;Download the linker binary back to safety
  &lt;li&gt;Remove the source and the build artifacts
  &lt;li&gt;Repeat the same for all five components of the system.
&lt;/ul&gt;

&lt;p&gt;
Just the data transfers for each component took an hour.  But after a
long time fighting with it I had a script that with a single keystroke
generated bit-identical binaries when compared to the ones that had
apparently been in use for almost the last 20 years.

&lt;p&gt;
I was pretty worried though, it&#039;d be really hard to actually make any
use of this source. There was no documentation except for the build
instructions, we&#039;d need to reverse engineer everything. There wouldn&#039;t be
any training either from company Z either, frankly it&#039;s a miracle if
anyone who originally worked on the software was still with the company. Nobody
knew PL/M. The roundtrip time from making a change on the build
machine to having a binary on a machine capable of actually running
it was at least an hour. And we didn&#039;t have a source level debugger for
this, so that&#039;d mean an hour just to add a single debug &lt;code&gt;printf&lt;/code&gt;.
(Wait, not a debug &lt;code&gt;printf&lt;/code&gt; of course. A debug
whatever-it-is-that-PL/M-uses-for-io). It&#039;d be pure pain.

&lt;p&gt;
I expressed these concerns, and was told not to worry.
&lt;br&gt;- &amp;quot; Oh, we&#039;ll never want to make changes to this compiler, not
enough code is compiled with it these days for that to be worth it. The more
modern suite is the important one.&amp;quot;
&lt;br&gt;- &amp;quot;Wait? I just spent a month elbow deep in PL/M and
Xenix/286 over a 9600bps Kermit connection, and you&#039;re telling me
we&#039;re never going to actually use any of this?!&amp;quot;
&lt;br&gt;- &amp;quot;Right, we just needed to verify that we really got what we
bought.&amp;quot;

&lt;p&gt;
I didn&#039;t really know whether to be happy about not having to do any
more work on that crap, or angry about the waste of time.

&lt;h3&gt;The sad&lt;/h3&gt;

&lt;p&gt;
That concluded the bizarre retrocomputing part of the story. We
now get to the part with sad dysfunctional corporate politics. If
you&#039;re just reading this for the laughs, maybe just skip to the end.

&lt;p&gt;
The more modern compiler suite wasn&#039;t a spring chicken either. It
had to be compiled specifically on Visual Studio 6. There were again
no design docs, nor tests. The lack of tests was explained as being
due to third party IP concerns. The lack of documentation we never
got an answer for.

&lt;p&gt;
Unlike the truly ancient compilers, this one was easy to build. But
what could we possibly do with it? So I read through the compiler,
tried to understand what each file did, did some experiments and wrote
some notes.

&lt;p&gt;
We arranged a big meeting with senior engineers from all the relevant
departments of X. The agenda was to figure out what improvements
they&#039;d want in the compiler. It was pretty dispiriting. Half of
them seemed to think it&#039;d be better not to touch it at all, since we&#039;d
probably just break it. Even those who weren&#039;t completely opposed to
changes couldn&#039;t think of anything they really needed. Finally
someone took pity on me, and noted that the compiler isn&#039;t very smart
about scheduling segment register loads, and those were expensive
operations. Maybe that could be improved?

&lt;p&gt;
After the meeting one of the managers told me that it was really our job
to come up with projects that the customer wanted to buy, not the other
way around. And it usually couldn&#039;t just be a general project for minor
improvements, it&#039;d need clear and ideally measurable goals.
The projects would also need to be pretty large to justify all the
overhead. It should go without saying that this is an absolutely
insane way of doing platform development, but it&#039;s something that
follows directly from the incentives of the two parties.  How anyone
at X thought that anything good would come out of this, I don&#039;t know.

&lt;p&gt;
But never mind that. Our initial project for taking over the compiler
maintenance was well funded, and vague enough that it was easy to
argue that proving capability of shipping some kind of improvements is
a core deliverable. We could at least proceed with the only improvement
anyone had shown any interest in.

&lt;p&gt;
So I implemented a new peephole optimizer stage for the segment
registers, and even got the code reviewed by the original authors of
the compiler when they came over from Z to give us a training session.
It seemed to work, but as mentioned above we didn&#039;t have a test suite
and building one would take a long time and a lot of work. (Excellent!
We can propose that as a project later!).

&lt;p&gt;
We couldn&#039;t even run any of the production code since that would
require the ingenious in-house operating system. The only way to get
any performance numbers and confidence in the changes being correct
would be to schedule a load test in X&#039;s test lab. Unfortunately weeks
and weeks of discussion over that never got us both the lab time and
the people from their side who would have been needed. It&#039;s of course
understandable; whether these compiler changes got released or not
wouldn&#039;t make a difference to these people, who had their own actual
work to do. But it also made it very hard to see how we could ship
this change. The justification would be improved performance, but
with no numbers it&#039;d be a hollow claim.

&lt;p&gt;
That&#039;s when it dawned on me that there was never going to be any real
compiler work there. These special compilers would not really matter
once X migrated away from the custom OS, which would have to happen.
Oh, sure it&#039;d need to be &amp;quot;maintained&amp;quot; just in case a
customer running 20 year old code needed a bugfix.
Given the dysfunctional processes, it seemed pretty clear that the
costs for any improvements would be massive in the short active
development life these systems had remaining. They&#039;d probably spent a
seven figure sum on this project as insurance, but actually doing
something with the code? No way.

&lt;p&gt;
All of this infrastructure was just going to be on life support while
it was being replaced by new systems that would in turn be obsoleted
in five years. But nothing would ever actually go away, all this cruft
would just accumulate and accumulate, nominally supported for
ever. And you&#039;d have these extremely good engineers doing this
completely insane work, having been moved working from a prestigious high
tech company to a despised consulting firm.

&lt;p&gt;
And how do you even get out of that job? I imagined myself in a job
interview in 2010, trying to explain how useful my extensive knowledge
of Xenix, PL/M build systems, and VMS would be to my prospective new
employer.  There might be a time when you just stop keeping up with
tech, but doing it in your 20s is really not that time :)

&lt;h3&gt;Coda&lt;/h3&gt;

&lt;p&gt;
So I quit without arranging for another job first, assuming that
something would probably turn up. In an amazing
display of serendipity, during my notice period ITA Software posted to
the SBCL mailing list that they wanted to pay somebody to work on SBCL
improvements for them, which was pretty much my dream gig at the time
&lt;a href=&#039;#ftnt4&#039; name=&#039;ftnt_ref4&#039;&gt;[4]&lt;/a&gt;. Perfect timing.

&lt;p&gt;
Ok, that&#039;s all. You can now proceed with the one-upping with stories
of developing new production software on a physical IBM 1401 in this
millennium, or something ;-)

&lt;h3&gt;Footnotes&lt;/h3&gt;

&lt;div class=&#039;footnotes&#039;&gt;

&lt;p&gt;
&lt;a href=&#039;#ftnt_ref0&#039; name=&#039;ftnt0&#039;&gt;[0]&lt;/a&gt;
I don&#039;t know the outcome of that evaluation.

&lt;p&gt;
&lt;a href=&#039;#ftnt_ref1&#039; name=&#039;ftnt1&#039;&gt;[1]&lt;/a&gt;
No, transpiling is not a word no matter how much you people
try to make it one.

&lt;p&gt;
&lt;a href=&#039;#ftnt_ref2&#039; name=&#039;ftnt2&#039;&gt;[2]&lt;/a&gt;
And that leads us to the question of whether Solaris is really
what you want to be migrating to in 2005.

&lt;p&gt;
&lt;a href=&#039;#ftnt_ref3&#039; name=&#039;ftnt3&#039;&gt;[3]&lt;/a&gt;
Wait?! There are two machines in the world that you think can be used to build
this software, and we only bought one of them? &amp;quot;Oh, yes. It would
have been a pretty good idea to buy the second one as a spare. Let&#039;s
do that now!&amp;quot;

&lt;p&gt;
&lt;a href=&#039;#ftnt_ref4&#039; name=&#039;ftnt4&#039;&gt;[4]&lt;/a&gt;
Of course if one worry with the job at Y was that I&#039;d be unemployable
due to only having worked on boring and obsolete technology, one might
wonder about the long term career prospects in Common Lisp compilers.
But look, in 2006 CL was really going places!
&lt;/div&gt;
</description><author>jsnell@iki.fi</author><category>GENERAL</category><category>HISTORY</category><pubDate>Tue, 01 Sep 2015 17:30:00 GMT</pubDate><guid permaurl='true'>https://www.snellman.net/blog/archive/2015-09-01-the-most-obsolete-infrastructure-money-could-buy/</guid></item></channel></rss>