Code Generation
Code Generation
Symbol
Table
1
manipulated by target machine. Semantic checking is done prior to code generation. The code
generation phase can proceed on the assumption that its input is free of errors.
Target Programs
The output of code generator is the target program. There are many forms of target program.
- Absolute machine language
- Relocatable machine language
- Assembly language
Producing Absolute machine language program as output has the advantages that it
can be placed in a fixed location in memory and immediately executed. The small programs
can be compiled and executed quickly.
Eg:- The Compilers WATFIV, PL/C produce absolute code as output.
Producing a relocatable machine program as output allows subprograms to be
compiled separately. The set of relocatable object modules can be linked together and loaded
for execution by a linking loader. The pain is linking and loading the relocatable object
modules and the gain is to call other previously compiled programs from an object module. If
the target machine does not handle relocation automatically, the compiler must provide
explicit relocation information to the loader to link the compiled program segments.
Producing assembly program as output is easier; we can generate the symbolic
instructions and use the macro facilities of the assembler to generate the code.
Assembly code is generated as the output for the machine with a small memory where
a compiler must use several passes. The price paid is the assembly step after code generation.
Memory Management:
In code generation phase, addresses of names in the source program is allocated.
Names in the three address statement refer to a symbol table entry for the name.
Symbol table entries were created as the declarations in the procedure were
encountered. The type in the declarations determines the width, ie the amount of storage
needed for the name. From the symbol table information a relative address can be
determined for the name in a data area for the procedure. During the machine code
generation, the labels in the three address statements are converted into the addresses of
instructions.
If a reference j:goto i, is encountered and ‘i’ is less than j, then we generate jump
instruction with the target address equal to the machine location of the first instruction
corresponds to quadruple i. If the jump is forwarded then list for ‘i’ is created and stores the
2
machine address of the instruction whose target address refers ‘i’. When ‘i’ quadruple is
processed the address of ‘i’ is back patched all the instruction which refers ‘i’.
Instruction Selection:
The nature of instruction set determines the difficulty of instruction selection.
The uniformity and completeness of the instruction set are important factors.
Instruction speeds and machine idioms are other important factors.
If the efficiency of target program is not important then instruction selection is
straight forward.
During Target code generation, Each three address statement will be translated into code
sequence.
Eg.
x :=y+z
Then code generator will generate
Mov y,R0
ADD z, R0
Mov R0, x
This type of translation will result the poor code
Eg:
a:=b+c
d:=a+e
mov b, R0
add c, R0
mov R0, a
mov a, R0
Redundant add e, R0
mov R0,d
The quality of the generated code is determined by its speed and size.
The target machine with rich instruction set may provide several ways of
implementing a given operation. Since the cost differences between efficient
implementations are different, so we select the efficient code for implementing the
instruction.
3
Eg:
Suppose the target machine supports INC instruction, then the three address statement
a=a+1 will be implemented as
inc a efficient
mov a, R0 poor
add #1, R0
mov R0, a
Deciding which code sequence is best for a given three address construct is depends
on in which context the construct appears.
Register Allocation:-
Instructions involving register operands are shorter and faster than instruction
involving memory operands. Efficient utilization of registers is important for generating
good code. There are two problems associated with the use of registers.
i.During Register allocation : We select the set of variables that will reside
in registers at a point in a program.
ii. During a Subsequent register assignment phase: We pick the specific
register for a variable.
Optimal assignment of registers to variables is difficult and the problem is NP-
Complete. Some machine requires register pairs for some operands and Results.
Eg:
Div x,y
In the above division instruction, the 64 bit dividend occupies and even/odd register
pair whose even register is ‘x’. ‘y’ register holds the divisor. After division the even register
holds the remainder and the odd register holds the quotient.
Eg: Optimal Code Sequence
T:=a+b LOAD R1, a
T:= T*c ADD R1, b
T:=T/d MUL R0, c
DIV R0, d
STORE T, R1
4
Choice Of Evalution Order
The order in which the computations are performed can affect the efficiency of target
code. Some computation orders require fewer registers to hold the intermediate results than
others. Picking the best order is the difficult problem. The problem can be avoided by
generating the code for three address statements in the order in which they have been
produced by the intermediate code generator.
TARGET MACHINE:-
For good code generation, familiarity with the target machine and instruction set is
important.
Consider our target machine is a byte addressable machine with four bytes to a word
and there are ‘n’ general purpose registers named as R0,R1,……….Rn-1
It has two address instructions of the form.
OP Source, destination
5
Eg:
Mov R0,m
Stores the contents of R0 into M
Mov 4(R0),M
Store the Contents (4+Contents(R0)) into M
Mov *4(R0),M
Stores the contents (Contents (4+contents(R0))) into M.
Mov #1,R0
Loads the constant 1 into R0.
INSTRUCTION COSTS:
Cost of an instruction is one plus the costs associated with the source and destination
address modes, ie indicated by the added cost. This cost corresponds to the length of the
instruction.
Eg:1
Mov R0,R1
Cost =1+0+0 =1
Eg:2
Add #1,R3
Cost =1+1+0 =2
Eg:3
Sub 4(R0), *12(R1)
Cost =1+1+1
=3
Eg:4
Mov *R1,*R0 =1+0+0=1
Add *R2,*R0 =1+0+0=1
Cost = 2
Eg:5
Mov b,R0 = 1+1+0
Add c,R0 = 1+1+0
Mov R0,a = 1+0+1
6
RUNTIME STORAGE MANAGEMENT:-
Runtime storage management discusses what code to generate to manage the
activation records at runtime.
Activation Record:-
It is defined as the block of storage used to store information needed during an
execution of a procedure. Storage for the local names to the procedure also appears in the
activation record.
The space for activation record can be allocated depends upon storage allocation
strategies.
There are two standard storage allocation strategies
Static allocation
Stack allocation
In static allocation, the position of an activation record in memory is fixed at compile
time.
In stack allocation, a new activation record is pushed onto the stack for each
execution of procedure. The record is popped when the activation ends.
The activation record for a procedure has fields to hold parameters, results, local data
and temporary results. The runtime memory for the procedure is divided into areas for code,
static data and a stack.
Eg:
Consider two procedures C&P
C P
Action1 Action 3
Call P Return
Action2
Halt
Static Allocation:
It discusses the code needed to implement static allocation.
The call statement in the intermediate code is implemented by a sequence of two
target machine instructions.
MOV #here+20, callee.static_area
GOTO callee.code_area
7
The MOV instruction saves the return address and GOTO instruction transfers control
to the target code for the called procedure. The attributes callee.static_area, and
callee.code_area, refer the address of the activation record and the first instruction for the
called procedure respectively.
The address #here+20 is the return address, ie, the address of the instruction
following the CALL instruction. The code for the procedure ends with the return except the
first procedure has no caller, so it ended by Halt instruction.
The instruction return in the called procedure is implemented by
GOTO * callee.static_area
This instruction transfers the control to the address saved at the beginning of
the activation record.
Stack Allocation
In stack allocation, the position of activation record is not known until run time.
The position is stored in a register, so the words in the activation record can be
accessed as offsets from the value in this register. The indexed address mode is used for this
purpose. The register SP is used as the pointer to the beginning of the activation record on the
top of the stack.
The procedure call statement increments SP and transfers control to the called
procedure. The return statement decrements SP and deallocates the activation record of the
called procedure.
The code for the first procedure initializes the stack by setting SP to the start of the
stack area in memory. It can be implemented as follows:
MOV #stackstart, SP
Code for the first procedure
Halt
The procedure call sequence increments SP, saves the return address and transfers
control to the called procedure. That is implemented as follows:
ADD #caller.recordsize, SP
MOV #here+16, *SP
GOTO callee.code_area.
The attribute caller.recordsize represents the size of the activation record, #here+16 is
the return address, it is saved in the address pointed to by SP.
The return statement can be implemented as follows, ie the called procedure
transfers the control to the return address using the following.
8
Goto * O(SP)
Where, O(SP) - Address of the first word of activation record
*O(SP) - The return address information
The goto statement is followed by,
SUB #caller.recordsize, SP
This deallocates the activation record for the procedure and restores SP to its previous
value.
BASIC BLOCK:
A basic block is a sequence of consecutive three address statements in which flow of
control enters at the beginning and leaves at the end without halt or branching.
Eg:
T1 :=a*a
T2 :=a*b
T3 :=b*b
T4 :=T2+T3
A name in the basic block is said to be live at the given point if its value is used after
that point in the program even though it is used in another basic block.
Algorithm for partition Three Address Statements into Basic Block
Input:
A sequence of three address statements
Output:
A list of basic blocks with each three address statement is in exactly one block.
ALGORITHM:
1.First determine the set of leaders, the first statements of basic blocks.
i. The first statement is a leader.
ii. Any statement that is the target of conditional or unconditional goto is a leader.
iii. Any statement that immediately follows the conditional or unconditional goto
is a leader.
2. For each leader, its basic block consists of the leader and all statements upto
statement which is preceded by the next leader or the end of the program.
9
Eg:
1. i=0
2. T1=7
3. T2=T1*i
4. T3=b+T2
5. i=i+1
6. if i<=20 goto(3)
Leader statements are -> (1) and (3)
B1
1. i=0
2. T1=7
B2
3. T2=T1*i
4. T3=b+T2
5. i=i+1
6. if i<=20 goto(3) 1
10
a:= b+c
b:=a-d
c:=b+c
d:=a-d
The first and third statements having the same expression on right, but the third
statement, taking redefined value of ‘b’ from second statements. So, the first and third are not
a common expressions. The second and fourth statements are common expressions, so the
basic block is transformed into
a:= b+c
b:=a-d
c:=b+c
d:=b
Dead Code Elimination
When the variable is never used after its computation is called dead variable and the
computation can be safely removed without changing the value of the basic block.
Eg:
x:=y+z
:
: x is not used after its computation, so ‘x’ is dead then the statement
x:=y+z can be removed.
Renaming of Temporary Variables:
Suppose we are having three address statement T:=b+c in our basic block and ‘T’ is
temporary and change this statement into U:=b+c, where ‘U’ is a new temporary variable and
change all references of T into U, the value of basic block is not changed. This equivalent
basic block is called as normal form block.
Interchange of Statements:
The adjacent two statements can be interchanged without affecting the value of basic
block. This transformation is possible if either of two statements is not refer one another
T1:=b+c
T2:=x+y
11
Algebraic Transformations:
There are number of algebraic transformations applied to the basic block to change
the block into algebraically equivalent set.
The familiar ones are simplify expressions or replace expensive operations by
cheaper ones.
Eg:-
The expressions
x=x+0
and
x=x*1 are eliminated from basic block without changing its value.
FLOW GRAPH:
Flow graph is defined as the directed graph which can be formed by adding flow
control information to the set of basic blocks. The nodes of the flow graph are the basic
blocks.
One node is called as initial node whose leader is the first statement.
There is a directed edge from block B1 to B2 iff
-There is a conditional or unconditional jump from the last statement of
B1 to the first statement of B2.
- B2 immediately follows B1 in the order of the program.
Eg:
i=0
T1=7
T2=T1*i
T3=b+T2
I=i+1
If i<=20 goto(3)
12
REPRESENTATION OF BASIC BLOCK
Basic block can be represented by variety of data structures.
Record structure
Linked list structure
In record structure, each basic block is represented by a record consisting of a count
of the number of quadruples, followed by pointer to leader and the list of predecessors and
successors of the block.
In linked list structure each basic block is represented by linked list of
quadruples.
13
STORAGE FOR TEMPORARY NAMES
Whenever the intermediate calculations are done, the temporary variable is created to
hold the result. If more number of temporary variables is created then it degrades the
efficiency of target code. In general, the two temporaries are packed into the same location if
they are not live simultaneously. Next use information can be applied to pack the
temporaries. Normally, the temporaries are packed into the registers rater than memory
locations.
Eg:
T1:=a*a T1:=a*a
T2:=a*b => T2:=a*b
T3:=2*T2 By packing T3:=2*T2
T4:=T1+T2 temporaries T1:=T1+T2
Eg: for the expression a=b+c the syntax tree and DAG is represented as follows
a=b+c
Syntax tree DAG
The difference between DAG and syntax tree is, in DAG common subexpressions are
identified and they have single parent, whereas in syntax tree the common subexpressions are
represented by duplicated subtrees.
14
Eg: 1 a=(b+c)*(b+c)
Syntax tree DAG
Eg:2
i = i+10
DAG Syntax tree
15
DAG
DAG CONSTRUCTION
To construct the DAG for a basic block we have to process each statement of the
block in turn.
Suppose the statement x=y+z, then we look for the nodes represent the current values
of y and z. These nodes may be leaves or the interior nodes.
The new node ‘+’ is created with the left child y and right child z and the new node is
labeled as x.
If any node representing the value of y+z, then we have to add the label x to that
node.
If any node is already labeled by x, then we remove that label indicating that the
value of x is updated.
STEPS:
1. If node(y) is undefined, Create the leaf labeled y and in the case(i) if node(z) is
Undefined, Create the leaf labeled z.
2. In case(i) determine if there is any node labeled ‘op’, if so, make its left child as
16
node(y) and right child as node(z). If the node is not available create that node,
and name the new node as node ‘n’. In case(ii) determine whether there is a node
labeled ‘op’ is so make its single child as node(y). If not create the node and
name it as node n.
3. Remove x from previous created nodes. Append ‘x’ as the label to the new node
’n’ found in step(2). The new node ‘n’ is node(x).
Eg:
From the DAG, we can simplify the three address statements as,
t1 = 4*i
t2 = a[t1]
t4 = b[t1]
t5 = t2*t4
t1and t3 have the same values and refer the same node in the DAG.
Applications of DAG:
1. It detects Common Subexpressions in Basic block.
2. We can determine which identifiers have their value used in the block.
3. We can determine value of which statements that could be used outside the block.
17
1. If their registers are needed for another computation.
2. Before the procedure call, jump statements.
Eg:
There are number of possibilities to generate the target code for the three address
statement, a : = b+c depending upon where b and c values are stored.
If b and c values are stored in the register Ri, Rj respectively, the we can produce the
instruction with cost one as
ADD Rj, Ri
If Ri contains ‘b’ and ‘c’ is in memory location, then we can generate the sequence
ADD c, Ri cost =2
Or
Mov c, Rj
ADD Rj, Ri cost = 3
If the value of ‘c’ is used subsequently, then we have to select the second type of
implementation, so that ‘c’ value is taken from Register Rj
Register and Address Descriptors:
The code generation algorithm uses descriptors to keep track of register contents and
addresses for names.
i. The Register descriptor keeps what value currently in each register. This is
necessary whenever a new register is needed. Initially, the register descriptor shows that all
registers are empty. Whenever, the basic block is progressed, each register can store the
value of zero or more names.
ii. An Address Descriptor keeps the location of the name, where the current value of
name is stored. The location may be a register, a stack location or a memory address. This
information is stored in a symbol table to determine the accessing method for a name.
Code Generation Algorithm:
Code generation algorithm takes as input a sequence of three statements from a basic
block.
For each statement of the form x :=y op z the algorithm performs the following actions:-
1. The function getreg() is invoked to determine the location L where the result
of the computation y op z is stored. ‘L’ may be register or memory location.
2. Consult the address descriptor for ‘y’ to determine y’ i.e. the current location
of y. If it is not in L, then create the instruction Mov y’,L to place the copy of y in L.
18
3. Generate the instruction op z’, L where z’ is the current location of z. Update
the address descriptor of x to indicate that x is in ‘L’. If ‘L’ is a register, update it’s
descriptor to indicate it has the value of x and remove x from all other register descriptors.
4. If y and z have no next use after the execution of x := y op z, then alter the
register descriptors corresponding to y and z as, those registers no longer will contain y and z
respectively.
19
Then the code generation algorithm will produce the code sequence as follows:
CODE REGISTER ADDRESS
STATEMENT GENERATED DESCRIPTOR DESCRIPTOR
S
Registers empty
20
21