Tunes

Patrick Premont premont@cs.toronto.edu
Wed, 3 Apr 1996 10:33:07 -0500


> 
> On some network somewhere in cyberspace, Nathan Hawkins transmitted:
> > 
> > Organization of Low Level code in layers:
> > 
> > 1. very processor specific code: on x86, all kinds of stupidity.
> > 2. LLL core: words which can be represented in a relatively small number of
> >    instructions, and can always be compiled inline.
> > 3. memory management and hardware stuff: page management, interrupts
> > 4. LLL environment: heap management, "user" variables, etc.
> > 5. LLL modules: standard modules?
> > 
> > Note: these layers are just useful ideas to keep in one's head. It wouldn't
> > be reasonable to expect everything to fit the above oversimplification.
> > 
> > VERY LOW LEVEL CONVENTIONS:
> > 
> > I'm currently using the following register conventions:
> > 
> > ebx is used for TOS, and eax, ecx, and edx are scratch.
> >   Possibly eax should be used for TOS? ebx is used in most Forth, but I
> >   can't remember why, and if it's important. eax would be better for various
> >   stupid x86 reasons. Gcc saves ebx, so maybe that was a factor...
> > 
>   EBX is used probably because in several Forth implimentations on the 8088
> (80186/80286) used BX for TOS, since once could use BX to reference memory
> (on the 8086/8088/80186/80286 only four registers could be used as an index,
> BX, SI, DI and BP, with BP using SS: as a default segment).
> 
>   On the 386 or higher, any of the following can be used to reference
> memory: EAX, EBX, ECX, EDX, ESI, EDI, EBP and ESP (with EBP and ESP using
> SS: as a default segment).  If you write your "Forth" in C, avoid using EAX
> for TOS, as most (if not all) C functions don't bother saving EAX.
> 
>   You might also think of using EAX, EBX, ECX, EDX, ESI, EDI and EBP as TOS. 
> Then SWAP, ROT, DUP, etc become fairly cheap and if you generate native
> code, those words can be optimized out (by keeping track of which register
> is "on top".  JaxForth for the Amiga does this I think).

That's one of the things optimizing compilers do, cache the top of the
stack in the registers. I wrote parts of a simple Scheme compiler which
did that.

Nathan, what does it mean for you the LLL ? It seems you are defining as
some kind of Forth. But then the problem of choosing the registers etc...
is part of the problem of compiling the LLL to machine code.

If however by LLL you mean the set of low-level conventions that will
be used in Tunes, you are talking about something different where the
choice of registers is relevent.

LLL has been used to describe both so I'm not completely sure what you
mean. The set of low-level conventions will have to fit in a nice theory
which will make the HLL capable of choosing the low-level conventions.
I'm working on that right now (first class representations). It's difficult
to say at this point if/how you/we should take that into account. Hopefuly
I'll have something usable before we have invested a lot of work
in particular conventions... My system might not impose many modifications
but I can't be sure at this point.

As for the LLL as a standard low-level language (not the set of
low-level convetions), I think you should be clear about what you want
it for. You seem to say that you like programing at the low-to-mid
level. That's all right but what can Tunes bring to that level ?  I
don't think that is where Tunes will be special. I think that in the
finished product, the low-to-mid level programmer will either be
enhancing the compiler/partial-evaluator or programming at the high
level. Low level stuff is something to be generated automatically not
written by hand in large quantity.

Unless you want to write a lot of low-level code directly, you don't
need a language like Forth with a syntax, modules, name-spaces
(vocabularies),... These are considerations for the human writing the code.
A intermediate stack language (as is often used in compilation) is enough.
It does not need to have it own character-stream syntax, its programs can
simply exist as a structure of (Scheme for now) objects in the compiler.
Giving it a character-stream syntax CAN be done but you start to
want a lot of language features for humans (name-spaces,...).

If the LLL is to be the (portable) language to which the HLL compiles
(when it wants to), then Forth would not be the LLL (I wouldn't want
to compile to Forth) but another language that is compiled to the LLL
in a straightforward way. But I'd rather have this language be (as
much as possible) a subset or something similar to a subset of the HLL.

> > I need to have some kind of heap management, and I'm leaning toward using
> > something more like malloc than the Forth setup. I want to be able to
> > FORGET individual words, as well as whole vocabularies. Normal Forth can't
> > really do either, which I think is stupid.
> > 
>   Who says you have to use Forth?  A Forth like language could be used,
> taking the best features of Forth, and dumping the bad stuff (I don't like
> the way Forth handles vocabularies myself).

If you generate the LLL code from a Scheme program (the compiler,
which will become a HLL program once bootstraped), then you can have
"chunks-of-code" object which you can bind to Scheme variables, put in
lists, whatever... You don't need vocabularies. But we seem to be
departing from the "Scheme program generates code" model of
compilation for a "Scheme program applies transformation rules to
generate code" model. In that case you could probably use the
environements of the HLL for you vocabularies. I'm just trying
to unify the language you want with a subset of the HLL as I said earlier.

>   Traditionally, a "process" owns resources.  A "thread" ("task" in
> Amigaspeak) doesn't.  A "thread" is a resource.  Along with "devices",
> "files", "memory".  A "thread" is a unit of execution, and in a loose sense,
> "owns" the CPU, which is a resource, and some memory (out of which it is
> executing), which is a resource. 

Unless absolutely necessary, I guess we shouldn't define this kind of
thing in the LLL. It is the HLL which will guide the selection of
these things. Hmm.. damn I don't like this LLL/HLL dichotimy at times.
They are linked so tighly that idealy you'd have only one genius
desing the whole thing in one piece. Since that's not going to happen,
we'll need a lot of communication.

> > An alternative would be a design I've been told about, involving nestable
> > word definitions, like this:
> > 
> > : foo : bar ; ;
> > 
> > where foo:bar or some related syntax would be used. I think this would be
> > too complicated, although I as I recall, Pascal allows something like this for
> > functions. I never used it.

Hmm... lexical scoping. I take that for granted. Scheme has it. The
HLL will have it. Why redo it again for a Forth-like language ?

>   Pascal used it, but the only function that could call the nested functions
> was the one it was nested in (in your example above, foo could only call
> bar, since bar was local to foo).

You mean bar could only be called by foo, the function foo is defined
in, ... going outward. Note that with proper distinction between
names and objects, you can only use the lexical name locally but there is
nothing stoping you from using the object anywhere. In Scheme for
example, a function can return another function defined in its scope and
therefore potentially allow that other function to be used outside of
the scope where it was defined.

Patrick