[Self-interest] general ifTrue (was: Proposal for a modernized Self dialect)

Jecel Assumpcao Jr jecel at merlintec.com
Thu Dec 16 15:37:23 UTC 2021

Diego Gomez Deck wrote on Thu, 16 Dec 2021 11:01:30 +0100
> > On Dec 15, 2021, at 8:00 PM, David Ungar wrote:
> > 
> > My memory is fuzzy, but I think that Jecel may be closer to the truth, here.
> > I was proud that original Self has only 8 bytecodes, but I recall expanded it for Klein.
> Where I can find information about these ?8 byte codes?.   Are they described in any paper?

I always looked in Urs Hölzle's thesis when I needed a reminder of the
bytecodes. It is very likely that this was also in one of the papers and
in Craig Chambers' thesis as well (the bytecodes didn't change between
Self 2 and Self 3).

Since there are only 8 it is easy for me to list them here (top 3 bits
are the op code, bottom 5 bits the operand):

0: extend - extends the operand of the next instruction with its own
1: self - pushes the receiver on the stack (ignores operand)
2: literal - pushes the value in the literal vector indexed by its
operand on the stack
3: non local return - returns from the most external lexical context
with the value on the top of the stack (ignores operand)
4: directee - selects a specific parent for the following instruction,
which must be a resend
5: send - sends a message to the object on the top of the stack with the
literal indexed by the operand as the selector
6: implicit self send - sends a message to the current receiver (lookup
actually starts in the local context) with the literal indexed by the
operand as the selector
7: resend - like "super" in Smalltalk

Note that the "literal" bytecode does something special if the literal
in question happens to be a block object. Instead of pushing the block
itself it creates a block activation (from which a block method
activation will later be created by sending 'value' to it).

In Self 1.0 we had inner methods, such that

    3 + ( 4 * 5)

translated to

   literal (| | 4 * 5)
   literal 3
   send '+'

Pushing an inner method was like pushing the equivalent block and
sending 'value' to it immediately. In practice the potential this
feature allowed (basically being able to declare temporary variables in
any expression) was never used and in Self 1.2 the parser translated the
same code to

   literal 5
   literal 4
   send '*'
   literal 3
   send '+'

Note that the 'send' bytecode isn't really needed except to save a
little memory. In theory each activation has a :self* argument parent
slot so this bytecode would do the exact same thing:

   implicit self send 'self'

Though that would make the literal vector have one more entry.

I also came up with an idea to replace the 'non local return' bytecode.
If each activation had a slot named '^' with a continuation object as
its value, then

   implicit self send '^'

would use that continuation with the current top of the stack as its
value, which is the desired semants. Block method activations would not
have this slot, and this would automatically make the system do the
right thing. One inconvenience of this idea is that due to the syntax
priority of binary selectors you would need parenthesis around the
return value:

   ^(x max: y)

and not

   ^x max: y

For Self 4.1.2 the bytecodes were changed to something like what Little
Smalltalk uses. The opcode is the top 4 bits and the operand the bottom
4 bits:

0: index - extend the index field of the next bytecode
1: literal - push the literal onto the top of stack (tos)
2: send - send the message with the literal as selector and tos as
3: implicit self send - send the message to self with the literal as
4: extended - see below
5: read local - access local slot
6: write local - change value of local slot
7: lexical level - change what "local" means for previous instructions
8: branch always - jump to indicated bytecode (literal must be smallInt)
9: branch if true - only jump if tos == true
10: branch if false - only jump if tos == false
11: branch indexed - tos is an index into a "branch vector"
12: delegatee - changes the next "send" into a directed resend

13 to 15 were initially unused. The extended bytecode used the operand
to select between:

0: push self - puts the current receiver on the tos
1: pop - eliminates the tos
2: non local return - returns from this block's "home context"
3: undirected resend - like "super" in Smalltalk

This information can be found by exploring a regular Self snapshot.
Currently you can ask a mirror for a method which bytecode set it uses.

-- Jecel

More information about the Self-interest mailing list