3.2 Representation of Language Elements
3.2.3 Expressions
Because of the diversity of machine instruction sets, we can only give the general principles behind the mapping of expressions here. An important point to remember throughout the discussion, both here and in Section 3.2.4, is that the quality of the generated code is deter- mined by the way it treats cases normally occurring in practice rather than by its handling of the general case. Moreover, local code characteristics have a greater impact than any op- timizations on the overall quality. Figure 3.5 shows the static frequencies of operations in a large body of Pascal text. Note the preponderance of memory accesses over computation, but remember that indexing generally involves both multiplication and addition. Remember also that these are static frequencies; dynamic frequencies might be quite dierent because a program usually spends about 90% of its time in heavily-used regions accounting for less than 10% of the overall code.
Structure Tree Operator Percent of All Operators
Access a variable 27
Assign 13
Select a eld of a record 9.7
Access a value parameter 8.1
Call a procedure 7.8
Index an array (each subscript) 6.4
Access an array 6.1
Compare for equality (any operands) 2.7
Access a variable parameter 2.6
Add integers 2.3
Write a text line 1.9
Dereference a pointer variable 1.9 Compare for inequality (any operands) 1.3
Write a single value 1.2
Construct a set 1.0
not 0.7
and 0.7
Compare for greater (any operands) 0.5
Test for an element in a set 0.5
or 0.4
All other operators 3.8
Figure 3.5: Static Frequencies of Pascal Operators [Carter, 1982]
Single target machine instructions directly implement operations appearing in the struc- ture tree only in the simplest cases (such as integer arithmetic). A node of the structure tree generally corresponds to a sequence of machine instructions, which may appear either directly in the generated code or as a subroutine call. If subroutines are used then they may be gathered together into an interpreter consisting of a control loop containing a large case statement. The operations are then simply selectors used to choose the proper case, and may be regarded as instructions of a new (abstract) machine. This approach does not really answer the question of realizing language elements on a target machine; it merely changes the target machine, hopefully simplifying the problem.
3.2 Representation of Language Elements 49 the cost of the transfers in and out. It would therefore be used only if commensurate savings in space were possible. Some care must be taken in evaluating the tradeos, because both open and closed sequences usually involve setup code for the operands. It is easy to overlook this code, making erroneous assumptions about the operand locations, and thereby arrive at the wrong decision. Recall from Section 3.1.3 that it is sometimes possible to take advantage of unused operation codes to access closed instruction sequences. Depending upon the details of the hardware, the time overhead for this method may be either higher or lower than that of a conventional call. It is probably most useful for implementing facilities that might be provided by hardware. The typical example is oating point arithmetic on a microprocessor with integer operations only. A oating point operation usually involves a long sequence of instructions on such a machine (which may not even be capable of integer multiplication or division), and thus the entry/exit overhead is negligible. If the user later adds a oating- point chip, and controls it with the previously unused operation codes, no changes to the code generator are required. Even when dierent operation codes are used the changes are minimal.
An object, label or procedure is addressable if its eective address can be expressed by the relevant access path of an instruction. For entities that are not addressable, additional operations and temporary storage are required to compute the eective address. The allow- able combinations of operation and access function exert a very strong inuence upon the code generation process because of this. On the Motorola 68000, for example, specication of the operation can be largely separated from selection of the access path, and operand ad- dressability is almost independent of the operator. Many IBM 370 instructions, on the other hand, work only when the second operand is in a register. In other cases memory access is possible, but only via a base register without indexing. This leads to the problem that an operand may be addressable in the context of one operation but not in the context of another. When an instruction set contains such asymmetries, the simplest solution is to dene the abstract machine for the source-to-target mapping with a uniform access function, reserving the resources (usually one or two registers) needed to implement the uniform access function for any instruction. Many code sequences require additional resources internally in any event. These can often be standardized across the code sequences and used to provide the uniform access function in addition. The only constraint on resources reserved for the uniform access function is that they have no inter-sequence meaning; they can be used arbitrarily within a sequence.
Consider the tree for an expression. The addressability of entities described by leaves is determined by the way in which the environment is encoded in the machine state. (We shall discuss possibilities for environment encoding in Section 3.3.) For entities described by interior nodes, however, the addressability depends upon the code sequence that implements the node. It is often possible to vary a code sequence, without changing its cost, to meet the addressability requirements of another node. Figure 3.6 shows a typical example. Here the constraints of the IBM 370 instruction set require that a multiplicand be in the odd- numbered register of a pair, and that the even-numbered register of that pair be free. Similarly, the optimum mechanism for converting a single-length value to double-length requires its argument to be in the even register of the pair used to hold its result. An important part of the source-to-target mapping design is the determination of the information made available by a node to its neighbors in the tree, and how this information aects the individual code sequences.
Interior nodes whose operations yield addresses, such as indexing and eld selection nodes, may or may not result in code sequences. Addressability is the key factor in this decision: No code is required if an access function describing the node's result can be built, and if that access function is acceptable to the instruction using the result. The richer the set of
L R1,I
A R1,J Result in R1
M R0,K Multiplicand from R1, product to (R0,R1) D R0,L Dividend from (R0,R1)
a) Code for the expression ((
i
+j
)k=l
)L R0,I
A R0,J
A R0,K Result in R0
SRDA R0,32 Extend to double, result in (R0,R1) D R0,L Dividend from (R0,R1)
b) Code for the expression ((
i
+j
+k
)=l
)Figure 3.6: Optimum Instruction Sequences for the IBM 370
access functions, the more nodes can be implemented simply by access function restructuring. In fact, it is often possible to absorb nodes describing normal value operations into access functions that use their result. Figure 3.7 is a tree for
b
[i
+12]. As we shall see in Section 3.3, the local byte arrayb
might have access function 36(13) on an IBM 370 (here register 13 gives the base address of the local contour, and 36 is the relative byte location ofb
within that contour). After loading the value ofi
into register 1, the eects of the index and addition nodes can be combined into the access function 48(13,1). This access function (Figure 3.3a) can be used to obtain the second argument in any RX-format instruction on the IBM 370.+
i 12
b
INDEX
Figure 3.7: Tree for a Typical Array Access
Some machines incorporate automatic incrementing or decrementing of a register content into certain access functions. These facilities are easy to use in source-to-target mappings for special purposes such as stack manipulation. Their general use, for example in combining the increment of a loop control variable with the last use of that variable as an index, is much more dicult because it leads to `combinatorial explosion' in the number of cases that the code generator must examine. Such optimizations should be provided by a separate process (peephole optimization), rather than being incorporated into the source-to-target mapping.
Many Boolean expressions occur in contexts such as conditional statements and loops, where the result is used only to determine the ow of control. Moreover, most of these ex- pressions either are relations themselves or are composed of relations. On the majority of computers a relation is evaluated by performing a comparison or arithmetic operation and then executing a transfer of control based upon the result. The upshot is that such expres- sions can be implemented most conveniently by omitting Boolean computations completely! Figure 3.8 illustrates the concept, which is called a jump cascade.
The concept of a jump cascade is completely independent of the concept of short-circuit evaluation discussed in Section 2.3. It appears that Figure 3.8 is performing short-circuit evaluation because, for example,
c
is not fetched unless the value ofa
is less than that of3.2 Representation of Language Elements 51
b
. But fetching a simple variable has no side eect, and hence the short-circuit evaluation is not detectable. Ifc
were a parameterless function with a side eect then it should be invoked prior to the start of the code sequence of Figure 3.8b, and thec
in that code sequence would represent temporary storage holding the function result. Thus we see that questions of short-circuit evaluation aect only the relative placement of code belonging to the jump cascade and code for evaluating the operands of the relations.if
(a < b )and
(c = d )or
(e > f )then
statement ;a) A conditional
L R1,
a
C R1,
b
BNL L10 Note condition reversal here L R1,
c
C R1,
d
BEQ L1 Condition is not reversed here L10 L R1,
e
C R1,
f
BNH L2 Reversed
L1
:::
Code forstatementL2
:::
Code following the conditional b) IBM 370 code corresponding to (a)Figure 3.8: Jump Cascades