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
self-interest@lists.selflanguage.org