LOADn 2, 60 MULTr 1, 2 LOAD 2, minute ADDr 1, 2 LOADn 2, 1050 LOADn 3, TRUE SKIPEQr 1, 2 LOADn 1, FALSE
LOADr 1, 3 /* extra instruction */ LOAD 2, tired
ORr 1, 2
Figure 2.8: Code produced by the linear string walker
instructions which carry out the comparison. In the case of a simple Reverse Polish string the operator appears only once, so that the code for the ‘=’ node must move the computed value into its intended register – the ‘extra instruction’ shown in figure 2.8.
The string walking algorithm uses a stack of registers, so its status as ‘non- recursive’ is in doubt anyway. Note that it is impossible to vary the way in which the tree is walked, according to some code generation strategy, after the string has been generated. Use of a postfix string representation of the source pro- gram merely freezes the compiler into a particular kind of tree walk. Although the string-walking algorithm could be improved quite easily in some ways (e.g. avoiding ‘LOAD,ADDr’ instructions in sequence) most of the improvements which are introduced below and discussed in detail in later chapters involve changing the way the tree is walked and so are impossible (or at best extremely difficult) using any linear representation.
2.4
Improving the Tree Walker
Once you have a working translator, you are in business as a compiler-writer. Using tree walking will get you to this point faster. It will also help you to get farther and faster along the road to better code than any ad hoc solution could. Improving the efficiency of the code produced by a tree-walking translator means looking at the code fragments generated by one or more of the procedures and deciding that the procedure’s strategy was weak, or that it should look out for that particular source construct and generate a specially selected code fragment for it. The various possibilities are summarised in figure 2.9. It’s hard to decide where tactics shade into strategy: figure 2.10 shows an improved arithmetic ‘+’ procedure. This procedure saves an instruction and a register when the right operand is a leaf: I would classify this as a tactical manoeuvre. Using this
28 CHAPTER 2. INTRODUCTION TO TRANSLATION
1. Generate special code for a node with a special local property (tactics). 2. Alter the way the tree is walked when a node has a particular general
property (strategy).
3. Modify the tree before it is walked (optimise the tree).
4. Modify the code which was generated, after the tree walk has finished (optimise the code).
Figure 2.9: Tactics and strategy
• arithmetic ‘+’ (result to be in register k)
1. generate code to calculate value of left operand in register k 2. if right operand is a name then
generateADD k,<address of variable> else if right operand is a number then
generateADDn k,<value of number>
or ADD k,<place where value will be found> else
(a) generate code to calculate value of right operand in register k+1; (b) generateADDr k, k+1.
2.4. IMPROVING THE TREE WALKER 29 LOAD 2, hour MULTn 2, 60 ADD 2, minute LOADn 1, TRUE SKIPEQn 2, 1050 LOADn 1, FALSE OR 1, tired
Figure 2.11: Code produced by improved translator
procedure the first three instructions of figure 2.5 would be compressed into two and, if a similar improvement were made to the procedures which translate ‘=’ and ornodes, the translator would produce the code shown in figure 2.11 for the example expression. This code is a considerable improvement over that of figure 2.5 – it uses two rather than three registers, seven rather than eleven instructions – and it was reached by making simple and modular changes to the procedures of the translator. Chapter 5 shows that much more can be done about the code generated for arithmetic operator nodes, and chapter 6 shows that quite a different approach is possible for Boolean operation nodes. Improvements like that in figure 2.10 can also be made to the iterative string- walking procedure. Strategic alterations are far more difficult, however, since a linear representation doesn’t allow the translator to take account of the structure of an expression.
Some further tactical possibilities are shown in figure 2.12. These are ‘sub- optimisations’ because the code which is generated for a particular source frag- ment doesn’t depend on the control context within which the object code frag- ment will operate. True optimisations take account of this context and consider, for example, whether there are any registers which already hold the values of variables, whose contents can be used to avoid an unnecessary LOAD instruc- tion. To generate this code, the procedure for assignment nodes must look for nodes with a particular special property and generate a special code fragment for them. The number of possible tactical improvements is enormous and the translator should implement only those tactics which are cost-effective – i.e. where the improvement in object program efficiency is sufficient to justify the cost of recognising the tactical situation and also the frequency with which the situation arises is sufficient to justify checking each possible node in the tree. Modifying the tree and modifying the code (cases 3 and 4 in figure 2.9) are examples ofoptimisationtechniques. Case 3 is illustrated by ‘code motion’ out of a loop to reduce the number of times a fixed calculation is carried out (figure 2.13) and case 4 by ‘register remembering’ to eliminate unnecessary LOAD and
STOREinstructions (figure 2.14). Code modification can be done after translation has finished or, perhaps better, as a sort of filter on the code proposed by the translator as it generates it.
30 CHAPTER 2. INTRODUCTION TO TRANSLATION
1. Statement: a := 0 Tree: assign
a 0
Normal code: LOADn 1, 0 STORE 1, a
Optimised code: STOZ , a
2. Statement: b := b+<anything> Tree: assign
b +
b <anything> Normal code: Optimised code:
LOAD 1, <anything> ADD 1, b
STORE 1, b
LOAD 1, <anything> ADDST 1, b
3. Statement: c := c+1 Tree: assign
c +
c 1
Good code: LOADn 1, 1 ADDST 1, c
Better code: INCST , c
2.4. IMPROVING THE TREE WALKER 31 Program: for i = 1 to 100 do begin x := 0.5 * sqrt(y+z); a[i] := x; b[i] := y; .... end Tree: ... ... for ... ... i 1 100 compound x:=... a[i]:=x b[i]:=y ... program Altered tree: ... ... for ... ... i 1 100 compound x:=... a[i]:=x b[i]:=y ... program
Figure 2.13: Moving code out of a loop Program: a := b+1;
c := a
Original code: LOAD 1, b ADDn 1, 1
STORE 1, a /* possibly unnecessary */ LOAD 1, a /* certainly unnecessary */ STORE 1, c /* possibly unnecessary */
32 CHAPTER 2. INTRODUCTION TO TRANSLATION 1 INTEGER + INTEGER LOAD n, a
ADD n, b
2. REAL + INTEGER LOAD n, a LOAD n+1, b FLOATr n+1,
fADDr n, n+1
3. REAL + COMPLEX LOAD n, a
ADD n, breal
LOAD n+1, bimag
Figure 2.15: Code for various type combinations