Module 11: Code Generation
Content:
Issues in the design of Code Generator, Basic Blocks and Flow graphs, Code Generation
algorithm, DAG representation of Basic Block.
Introduction:
The final phase in compiler model is the code generator. It takes as input an intermediate
representation of the source program and produces as output an equivalent target program. The
code generation techniques presented below can be used whether or not an optimizing phase
occurs before code generation.
Position of code generator
11.1 Issues in the Design of a Code Generator:
The following issues arise during the code generation phase:
1. Input to code generator
2. Target program
3. Memory management
4. Instruction selection
5. Register allocation
6. Evaluation order
1. Input to code generator:
The input to the code generation consists of the intermediate representation of the
source program produced by front end, together with information in the symbol table to
determine run-time addresses of the data objects denoted by the names in the intermediate
representation.
Intermediate representation can be:
Linear representation such as postfix notation
Three address representation such as quadruples
Virtual machine representation such as stack machine code
Graphical representations such as syntax trees and DAGs.
Prior to code generation, the front end must be scanned, parsed and translated into
intermediate representation along with necessary type checking. Therefore, input to code
generation is assumed to be error-free.
2. Target program:
The output of the code generator is the target program. The output may be:
a. Absolute machine language: It can be placed in a fixed memory location and can be
executed immediately.
b. Relocatable machine language: It allows subprograms to be compiled separately.
c. Assembly language: Code generation is made easier.
Module 11: Code Generation
3. Memory management:
Names in the source program are mapped to addresses of data objects in run-time
memory by the front end and code generator. It makes use of symbol table, i.e., a name in a
three-address statement refers to a symbol-table entry for the name. Labels in three-address
statements have to be converted to addresses of instructions.
For example, j: goto i generates jump instruction as follows:
if i < j, a backward jump instruction with target address equal to location of code
for quadruple i is generated.
if i > j, the jump is forward. We must store on a list for quadruple i the location of
the first machine instruction generated for quadruple j. When i is processed, the
machine locations for all instructions that forward jumps to i are filled.
4. Instruction selection:
The instructions of target machine should be complete and uniform. Instruction speed
and machine idioms are important factors when efficiency of target program is considered. The
quality of the generated code is determined by its speed and size. The former statement can be
translated into the latter statement as shown below:
a := b+c
Can be translated into
d := a+e
MOV b, R0
ADD c, R0
MOV R0, a
MOV a, R0 → This can be eliminated
ADD e, R0
MOV R0, d
5. Register allocation:
Instructions involving register operands are shorter and faster than those involving
operands in memory.
The use of registers is subdivided into two sub problems:
Register allocation – the set of variables that will reside in registers at a point in the
program is selected.
Register assignment – the specific register that a variable will reside in is picked.
Certain machines (IBM System/370) requires even-odd register pairs for some
operands and results. For example, consider the division instruction of the form:
D x, y
where,
x – dividend occupies an even register in even/odd register pair
y – divisor
After division, even register holds the remainder; odd register holds the quotient
6. Evaluation order:
The order in which the computations are performed can affect the efficiency of the
target code. Some computation orders require fewer registers to hold intermediate results than
others.
Module 11: Code Generation
11.2 Basic Blocks and Flow Graphs:
Basic Blocks:
A basic block is a sequence of consecutive statements in which flow of control enters
at the beginning and leaves at the end without any halt or possibility of branching except at the
end. The following sequence of three-address statements forms a basic block:
t1: = a * a
t2: = a * b
t3: = 2 * t2
t4: = t1 + t3
Basic Block Construction:
Example 1: Consider the following code for dot product of two vectors a and b of length 20
begin
prod := 0;
i := 1;
do begin
prod := prod + a[i] * b[i];
i := i + 1;
end
while i <= 20
end
Module 11: Code Generation
The three-address code for the above source program is given as:
1) prod := 0
2) i := 1
3) t1 := 4* i
4) t2 := a[t1] /*compute a[i] */
5) t3 := 4* i
6) t4 := b[t3] /*compute b[i] */
7) t5 := t2*t4
8) t6 := prod+t5
9) prod := t6
10) t7 := i+1
11) i := t7
12) if i<=20 goto (3)
Basic block 1: Statement (1) to (2)
Basic block 2: Statement (3) to (12)
Example 2: Consider the following source code which turns a 10 x 10 matrix a into an identity
matrix.
for i from 1 to 10 do
for j from 1 to 10 do
a[i, j] = 0.0;
for i from 1 to 10 do
a[i, i] = 1.0;
The three-address code for the above source program is given as:
1) i=1
2) j=1
3) t1 = 10 * i
4) t2 = t1 + j
5) t3 = 8 * t2
6) t4 = t3 – 88
7) a[t4] = 0.0
8) j=j+1
9) if j <= 10 goto (3)
10) i=i+1
11) if i <= 10 goto (2)
12) i=1
13) t5 = i – 1
14) t6 = 88 * t5
15) a[t6] = 1.0
16) i=i+1
17) if i <= 10 goto (13)
Module 11: Code Generation
First, instruction 1 is a leader by rule (1) of Algorithm. To find the other leaders, we
first need to find the jumps. In this example, there are three jumps, all conditional, at
instructions 9, 11 and 17. By rule (2), the targets of these jumps are leaders; they are instructions
3, 2 and 13, respectively. Then, by rule (3), each instruction following a jump is a leader; those
are instructions 10 and 12. Note that no instruction follows 17 in this code, but if there were
code following, the 18th instruction would also be a leader.
We conclude that the leaders are instructions 1, 2, 3, 10, 12, and 13. The basic block of
each leader contains all the instructions from itself until just before the next leader. Thus, the
basic block of 1 is just 1, for leader 2 the block is just 2. Leader 3, however, has a basic block
consisting of instructions 3 through 9, inclusive. Instruction 10's block is 10 and 11; instruction
12's block is just 12, and instruction 13's block is 13 through 17.
Flow Graphs:
Flow graph is a directed graph containing the flow-of-control information for the set of
basic blocks making up a program. The nodes of the flow graph are basic blocks. It has a
distinguished initial node.
E.g.: Flow graph for the vector dot product is given as follows:
B1 is the initial node. B2 immediately follows B1, so there is an edge from B1 to B2.
The target of jump from last statement of B1 is the first statement B2, so there is an edge from
B1 (last statement) to B2 (first statement). B1 is the predecessor of B2, and B2 is a successor
of B1.
Module 11: Code Generation
The set of basic blocks constructed in Example 2 yields the flow graph shown above.
The entry points to basic block B1, since Bl contains the first instruction of the program. The
only successor of Bl is B2, because Bl does not end in an unconditional jump and the leader of
B2 immediately follows the end of B1.
Block B3 has two successors. One is itself, because the leader of B3, instruction 3, is
the target of the conditional jump at the end of B3, instruction 9. The other successor is B4,
because control can fall through the conditional jump at the end of B3 and next enter the leader
of B4.
Only B6 points to the exit of the flow graph, since the only way to get to code that
follows the program from which we constructed the flow graph is to fall through the conditional
jump that ends B6.
Module 11: Code Generation
Next-Use Information:
If the name in a register is no longer needed, then we remove the name from the register
and the register can be used to store some other names.
Input: Basic block B of three-address statements
Output: At each statement i: x = y op z, we attach to i the liveliness and next-uses of x, y
and z.
Method: We start at the last statement of B and scan backwards.
1. Attach to statement i the information currently found in the symbol table
regarding the next-use and liveliness of x, y and z.
2. In the symbol table, set x to “not live” and “no next use”.
3. In the symbol table, set y and z to “live”, and next-uses of y and z to i.
Symbol Table:
Names Liveliness Next-use
x Not live No next-use
y Live i
z Live i
A Simple Code Generator:
A code generator generates target code for a sequence of three- address statements and
effectively uses registers to store operands of the statements. For example: consider the three-
address statement a := b+c. It can have the following sequence of codes:
ADD Rj, Ri Cost = 1 // if Ri contains b and Rj contains c
(or)
ADD c, Ri Cost = 2 // if c is in a memory location
(or)
MOV c, Rj Cost = 3 // move c from memory to Rj and add
ADD Rj, Ri
Register and Address Descriptors:
A register descriptor is used to keep track of what is currently in each registers. The
register descriptors show that initially all the registers are empty.
An address descriptor stores the location where the current value of the name can be
found at run time.
Module 11: Code Generation
11.3 A Code-Generation Algorithm:
The algorithm takes as input a sequence of three-address statements constituting a basic
block.
For each statement x := y op z
1. Set location L = getReg(y, z)
2. If y ∉ L then generate
MOV y’, L
where y’ denotes one of the locations where the value of y is available
3. Generate
OP z’, L
where z’ is one of the locations of z; Update register/address descriptor of x to
include L.
4. If y and/or z has no next use and is stored in register, update register descriptors to
remove y and/or z
1. Invoke a function getReg to determine the location L where the result of the computation y
op z should be stored.
2. Consult the address descriptor for y to determine y’, the current location of y. Prefer the
register for y’ if the value of y is currently both in memory and a register. If the value of y
is not already in L, generate the instruction MOV y’, L to place a copy of y in L.
3. Generate the instruction OP z’, L where z’ is a current location of z. Prefer a register to a
memory location if z is in both. Update the address descriptor of x to indicate that x is in
location L. If x is in L, update its descriptor and remove x from all other descriptors.
4. If the current values of y or z have no next uses, are not live on exit from the block, and are
in registers, alter the register descriptor to indicate that, after execution of x: = y op z, those
registers will no longer contain y or z.
Generating Code for Assignment Statements:
The assignment d: = (a-b) + (a-c) + (a-c) might be translated into the following three-
address code sequence:
t: = a – b
u: = a – c
v: = t + u
d: = v + u
with d live at the end.
Code sequence for the example is:
Statements Code Generated Register descriptor Address descriptor
Register empty
MOV a, R0
t: = a - b R0 contains t t in R0
SUB b, R0
MOV a, R1 R0 contains t t in R0
u: = a - c
SUB c, R1 R1 contains u u in R1
R0 contains v u in R1
v: = t + u ADD R1, R0
R1 contains u v in R0
ADD R1, R0 d in R0
d: = v + u R0 contains d
MOV R0, d d in R0 and memory
Module 11: Code Generation
Generating Code for Indexed Assignments:
The table shows the code sequences generated for the indexed assignment statements
a: = b[i] and a[i]: = b
Statements Code Generated Cost
a: = b[i] MOV b(Ri), R 2
a[i]: = b MOV b, a(Ri) 3
Generating Code for Pointer Assignments: a: = *p and *p: = a
Statements Code Generated Cost
a: = *p MOV *Rp, a 2
*p: = a MOV a, *Rp 2
Generating Code for Conditional Statements:
Statement Code
CMP x, y
if x < y goto z
CJ < z /* jump to z if condition code is negative */
MOV y, R0
x: = y +z ADD z, R0
if x < 0 goto z MOV R0, x
CJ < z
11.4 The DAG Representation for Basic Blocks:
A DAG for a basic block is a Directed Acyclic Graph with the following labels on
nodes:
1. Leaves are labelled by unique identifiers, either variable names or constants.
2. Interior nodes are labelled by an operator symbol.
3. Nodes are also optionally given a sequence of identifiers for labels to store the
computed values.
DAGs are useful data structures for implementing transformations on basic blocks. It
gives a picture of how the value computed by a statement is used in subsequent statements. It
provides a good way of determining common sub-expressions.
Module 11: Code Generation
Algorithm for construction of DAG:
Input: A basic block
Output: A DAG for the basic block containing the following information:
1. A label for each node. For leaves, the label is an identifier. For interior nodes, an
operator symbol.
2. For each node a list of attached identifiers to hold the computed values.
Case (i) x: = y OP z
Case (ii) x: = OP y
Case (iii) x: = y
Method:
Step 1:If y is undefined then create node(y).
If z is undefined, create node(z) for case(i).
Step 2:For the case(i), create a node(OP) whose left child is node(y) and right child is
node(z). (Checking for common sub expression). Let n be this node.
For case(ii), determine whether there is node(OP) with one child node(y). If not
create such a node.
For case(iii), node n will be node(y).
Step 3:Delete x from the list of identifiers for node(x). Append x to the list of attached
identifiers for the node n found in step 2 and set node(x) to n.
Example: Consider the block of three- address statements:
1) t1 := 4 * i
2) t2 := a [ t1 ]
3) t3 := 4 * i
4) t4 := b [ t3 ]
5) t5 := t2 * t4
6) t6 := p + t5
7) p := t6
8) t7 := i + 1
9) i := t7
10) if i <= 20 goto (1)
Stages in DAG Construction:
Module 11: Code Generation
Module 11: Code Generation
Module 11: Code Generation
Application of DAGs:
1. It can automatically detect common sub expressions.
2. It can determine which identifiers have their values used in the block.
3. It can determine which statements compute values that could be used outside the block.
Generating Code from DAGs:
The advantage of generating code for a basic block from its DAG representation is that,
from a DAG, we can easily see how to rearrange the order of the final computation sequence
than we can starting from a linear sequence of three-address statements or quadruples.
Rearranging the order:
The order in which computations are done can affect the cost of resulting object code.
For example, consider the following basic block:
t1: = a + b
t2: = c + d
t3: = e – t2
t4: = t1 – t3
Generated code sequence for basic block:
MOV a, R0
ADD b, R0
MOV R0, t1
MOV c, R0
ADD d, R0
MOV e, R1
SUB R0, R1
MOV t1, R0
SUB R1, R0
MOV R0, t4
Module 11: Code Generation
Rearranged basic block:
Now t1 occurs immediately before t4.
t2: = c + d
t3: = e – t2
t1: = a + b
t4: = t1 – t3
Revised code sequence:
MOV c, R0
ADD d, R0
MOV e, R1
SUB R0, R1
MOV a, R0
ADD b, R0
SUB R1, R0
MOV R0, t4
In this order, two instructions MOV R0, t1 and MOV t1, R0 have been saved.
A Heuristic ordering for DAGs:
The heuristic ordering algorithm attempts to make the evaluation of a node immediately
follow the evaluation of its leftmost argument. The algorithm shown below produces the
ordering in reverse.
Algorithm:
while unlisted interior nodes remain do
begin
select an unlisted node n, all of whose parents have been listed;
list n;
while the leftmost child m of n has no unlisted parents and is not a leaf do
begin
list m;
n: = m
end
end
Module 11: Code Generation
Example: Consider the DAG shown below:
Initially, the only node with no unlisted parents is 1 so set n=1 and list 1. Now, the left
argument of 1, which is 2, has its parents listed, so we list 2 and set n=2.
Now, we find the leftmost child of 2, which is 6, has an unlisted parent 5. Thus, we
select a new node 3, which is the only candidate. We list 3 and proceed down its left chain,
listing 4, 5 and 6. This leaves only 8 among the interior nodes so we list that.
The resulting list is 1234568 and the order of evaluation is 8654321.
Code sequence:
t8: = d + e
t6: = a + b
t5: = t6 – c
t4: = t5 * t8
t3: = t4 – e
t2: = t6 + t4
t1: = t2 * t3
This will yield an optimal code for the DAG on machine whatever be the number of registers.