[self-interest] self in self - plan B

Jecel Assumpcao Jr jecel at merlintec.com
Mon Nov 22 18:31:32 UTC 1999


I already wrote one Self in Self and liked the experience very much.
The problem was that I never did finish debugging tinySelf 1, so it
only handles very trivial expressions. But if it were fixed, it should
be able to run any Self program since it included everything Self has
and then added some (a model for parallel execution). Most of the
added complexity isn't needed for just running Self and is the reason
why debugging it was so complicated (it had stuff like tail message
elimination which made things hard to understand).

But tinySelf 1 can never run standalone. First of all, it is an
interpreter and what we need is a compiler. It also uses these
features from Self 4.0 (I already wrote about this in a message
called "desnarfing tinySelf"):

  1) the parser
  2) the memory system/garbage collector
  3) the primitives

There are several options for the parser (fix the one in 'tests.self',
use Mango, translate the one in C++ or the one I wrote in C, do
a new one), none of them very complicated.

The memory manager is a little complicated by the fact that Self
uses 30 bit integers and the CPU 32 bit ones, but Self 4.1 has a
a solution for this (from the PEP Java-in-Self effort, I suppose?)
and the Squeak implementation is a good example.

We can divide the primitives into these groups:

  A) simple primitives, like _IntegerAdd:, which should be directly
     generated by the compiler
  B) access to the memory system - these should be taken care of
     automatically when creating the memory manager
  C) access to the host OS, glue routines to proxy functions and
     data types - they could stay as they are, initially
  D) others - I can't remember which others there are, right now

So the really big deal is to write the Self compiler (bytecodes
to X86 machine language) in Self. There is nothing in Self that
would make this harder than in any other language. Quite the
contrary - I can't think of a better alternative for dealing with
very complex and dynamic data structures.

We could:

  - write a program that would read a description of the CPU and
    generate a code generator for it Self automatically. This would
    be a kind of "mango for the back end".
or
  - write the back end in a very abstract style, suitable for any
    CPU. Then we would create a child object for a specific CPU
    and implement the abstract methods ('generateJump:') manually.
    The inlining compiler would optimize most of the implied
    indirections, so the final code would be similar to that of
    the first option (I am talking about how fast the code
    generator runs, not the quality of the code it generates)
or
  - write the back end to generate a very primitive and abstract
    machine code. A patttern matcher in the final pass would
    translate that into the actual machine language for each CPU

I would say that the GCC is mostly the first option, with some
of the second one mixed in. For a long time, I have mostly
considered the second option. It is a classic example of an
"object oriented framework" and I think several people would
find it easy enough to understand to do ports to the various
CPUs.

The first two alternatives generate very fast code generators
(important for both the NIC and the SIC) but they have very
limited code transformation capabilities. In the second option,
most of the compiler state would be in the arguments of the
various method calls instead of in an explicit data structure.
So I am seriously looking into the third option since I think
high quality code scheduling should be a very high priority
when dealing with modern CPUs.

Are there any issues that I am forgetting, or neat alternatives
that I am not aware of?

-- Jecel



More information about the Self-interest mailing list