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

Jecel Assumpcao Jr jecel at merlintec.com
Tue Dec 14 22:06:33 UTC 2021

Hernan Wilkinson wrote on Tue, 14 Dec 2021 16:19:03 -0300
> In all Smalltalks I know, you can disable that optimization made by the compiler.
> Juan Vuletich did that a couple of days ago in Cuis (http://www.cuis-smalltalk.org/)
> and ran [Compiler recompileAll] timeToRun. to see the performance impact.

In the case of Self the original bytecodes didn't even have jump
instructions so the Smalltalk-80 trick of optimizing certain control
structures was not possible. 'ifTrue:' and similar were actual message
sends at the bytecode level.

The correct semantics for the control structures depended on
polymorphism and blocks to do the conditional execution and on the
_Restart primitive (which creates an infinite loop) and non local
returns (which allows breaking out of a loop) for the rest.

At the virtual machine level, however, the Self 1 bytecode to machine
language compiler had a list of special message names that it handled
differently, and that included the control structure messages that
Smalltalk-80 handled differently at the bytecode level. This led to some
wildly inconsistent performance where if you used "succ" in your code it
would be way faster than if you used "inc" (I don't remember the actual
selectors involved) it would be much slower even though looking at the
sources they seemed to be the same.

Improvements in the type inference technology for Self 2 allowed the
list of special selectors to be eliminated so all bytecode sequences
became equally optimized. This made it possible for the compiler to
handle the case where the receiver for 'ifTrue:' was not a boolean.

Then next big change was the addition of jump bytecodes in Self 4.1.2
and the introduction of Smalltalk-80 style optimization of control
structures. Or at least it has to potential to do that, but selecting a
random method in the 2017 release of Self ('quicksortL:R:' in
benchmarks) with this source:

| i. j. w. x |
" quicksort the array from start to finish "
i: l.
j: r.
x: sortlist at: (l + r) / 2.
    [(sortlist at: i) < x] whileTrue: [i: i successor].
    [x < (sortlist at: j)] whileTrue: [j: j predecessor].
    i <= j ifTrue: [
        w: sortlist at: i.
        sortlist at: i Put: sortlist at: j.
        sortlist at: j Put: w.
        i: i successor.  j: j predecessor.
] untilFalse: [ i <= j].

l < j ifTrue: [quicksortL: l R: j].
i < r ifTrue: [quicksortL: i R: r]

which generated these bytecodes using the "twentieth century plus
argument count" instruction set:

 1:  readLocal 'l'
 2:  writeLocal 'i:'
 3:  pop
 4:  readLocal 'r'
 5:  writeLocal 'j:'
 6:  pop
 7:  implicitSelfSend 'sortlist'
 8:  readLocal 'l'
 9:  readLocal 'r'
10:  argumentCount 1
11:  send '+'
12:  literal 2
13:  argumentCount 1
14:  send '/'
15:  argumentCount 1
16:  send 'at:'
17:  writeLocal 'x:'
18:  pop
19:  literal <a block defined in ../objects/tests/benchmarks.self, line 5600>
20:  literal <a block defined in ../objects/tests/benchmarks.self, line 5608>
21:  argumentCount 1
22:  send 'untilFalse:'
23:  pop
24:  readLocal 'l'
25:  readLocal 'j'
26:  argumentCount 1
27:  send '<'
28:  literal <a block defined in ../objects/tests/benchmarks.self, line 5610>
29:  argumentCount 1
30:  send 'ifTrue:'
31:  pop
32:  readLocal 'i'
33:  readLocal 'r'
34:  argumentCount 1
35:  send '<'
36:  literal <a block defined in ../objects/tests/benchmarks.self, line 5611>
37:  argumentCount 1
38:  send 'ifTrue:'

So it seems that the jump bytecodes are ignored and all control
structures are still encoded as full message sends like in earlier
Selfs. This is why it is a good idea to always check instead of just

-- Jecel

More information about the Self-interest mailing list