Hi!
This is a nice example of how 4tH compiles and optimizes code. The basic idea is that 4tH's optimizer HELPS to optimize code that would otherwise be hard to achieve (or only through dirty tricks, which are hard to understand) - NOT to clean up thoughtless code.
This is a nice example - it comes from a .BMP loading routine:
: rgb! ( a --)
/BMPpix BMPpix over accept = if \ invert RGB values
/BMPpix 0 do BMPpix /BMPpix 1- i - chars + c@ over i chars + c! loop drop
;then drop BMPabort \ abort on read error
;
1203| branch 1227
1204| literal 3
1205| literal 1441674
1206| over 0
1207| accept 0
1208| = 0
1209| 0branch 1225
1210| literal 3
1211| literal 0
1212| do 1223
1213| literal 1441674
1214| literal 2
1215| r@ 0
1216| - 0
1217| + 0
1218| c@ 0
1219| over 0
1220| r@ 0
1221| + 0
1222| c! 0
1223| loop 1212
1224| drop 0
1225| exit 0
1226| drop 0
1227| branch 1175
blabla
Now, /BMPpix equals 3. We can deduct that from opcode 1210, where it is the upper limit of a DO..LOOP. That means that this snippet "/BMPpix 1-" should normally translate to "3 1-" - but it doesn't. It compiles to opcode 1214, which is a single "2". Of course, we could also have written: "/BMPpix i 1+ -" but then the optimizer could not have added that "1+" to "i" (since "i" changes throughout the loop), and it would have resulted in "3 i 1+ -", which is four instructions instead of one. In a loop no less. That is called for every single pixel in the file. Even for a 800*600 pixel file that is 480K times which you could have saved by simply rearranging a few instructions.
We can see another saving in 1224-1227. Normally one would write ".. drop else drop BMPabort then". ";THEN" compiles to "EXIT THEN", so we leave the word right after the first "DROP". Which means that anything that comes after it is never reached. "BMPabort" can also be "tail called" (tail call optimization) - and the futile BRANCH from ELSE to the EXIT is completely futile - and hence optimized away. Another effect of the tail call is that the Return Stack becomes less crowded (since the BRANCH to BMPabort doesn't require a return address).
Compare these two variants to see the full effect:
: foo drop ;
: bar dup if drop else foo then ;
0| branch 2 foo
1| drop 0
2| exit 0
3| branch 9 bar
4| dup 0
5| 0branch 7
6| drop 0
7| branch 8
8| branch 0 foo
9| exit 0
: foo drop ;
: bar dup if drop ;then foo ;
0| branch 2 foo
1| drop 0
2| exit 0
3| branch 8 bar
4| dup 0
5| 0branch 7
6| drop 0
7| exit 0
8| branch 0 foo
Now, both will compile properly - and are completely valid. There is nothing wrong with either variant. It's just that one of them is slightly faster and shorter.
Hans Bezemer