You are on page 1of 57

Code Generation

Issues in the design of Code Generation

• Input to the code generator


• Target program
• Instruction Selection
• Register allocation
• Choice of evaluation order
Choice of Evaluation Order

• The order in which computations are performed can


effect the efficiency of the target code.
• Picking a best order is a difficult NP-complete
problem.
• When instructions are independent, their evaluation
order can be changed.
Target Machine Model
• Our target computer models a three address machine with load
and store operations, computation operations, jump operations
and conditional jumps.

• Our machine:
- Byte addressable machine
- Has n general purpose registers, R0, R1, …., Rn-1.
• Following kinds of instructions are available:

• Load operations: LD dst, addr (dst = addr)


This instruction loads the value in location addr into location
dst.
LD r1, r2 is a register-to-register copy in which content of
register r2 are copied into register r1.

• Store operations:
ST x, r stores the value in register r into the location x.
(x = r).
• Computation operations:
OP dst, src1, src2
where Op is a operator like ADD or SUB.
SUB r1, r2, r3 computes r1 = r2 – r3.
• Unconditional Jumps:
BR L causes control to branch to the machine instruction with
label L.
• Conditional Jumps:
Bcond r, L where r is a register, L is a label.
BLTZ r, L causes a jump to label L if the value in register
r is less than zero.
x= y- z can be implemented by machine instructions:
LD R1, y
LD R2, z
SUB R1, R1, R2
ST x, R1

B = a[i]
LD R1, i // R1= i
MUL R1, R1, 8 // R1 = R1*8
LD R2, a(R1) // R2= contents(a + contents(R1))
ST b, R2 // b = R2
Optimization
• Optimization is the process of transforming a piece of code to
make more efficient(either in terms of time or space) without
changing its output or side-effects.

• The only difference visible to the code’s user should be that it


runs faster and/or consumes less memory. It is really a
misnomer that the name implies you are finding an “optimal”
solution— in truth, optimization aims to improve, not perfect,
the result.
Types of Optimization
• Optimization can be categorized broadly into
two types :
1. Machine Independent
2. Machine dependent
Machine Independent Optimization
• The compiler takes in the intermediate code and transforms a
part of the code that does not involve any CPU registers and/or
absolute memory locations. For example
do {
item = 10;
value = value + item ;
} while(value<100);

• This code involves repeated assignment of the identifier item,


which if we put this way:
Item = 10;
do
{
value = value + item ;
} while(value<100);

• It should not only save the CPU cycles, but can be used on any
processor.
Machine Dependant Optimization
• Machine-dependent optimization is done after the
target code has been generated and when the code is
transformed according to the target machine
architecture.
• It involves CPU registers and may have absolute
memory references rather than relative references.
• Machine dependent optimizers put efforts to take
maximum advantage of memory hierarchy.
A Simple Code generator
• It considers each three-address instruction in turn, and keeps track of
what values are in what registers so it can avoid generating
unnecessary loads and stores.

• There are four principal uses of registers:


1. In most machine architectures, some or all of the operands of an
operation must be in registers in order to perform the operation.
2. Registers make good temporaries - places to hold the result of a sub-
expression while a larger expression is being evaluated.
3. Registers are often used to help with run-time storage management,
for example, to manage the run-time stack, including the maintenance
of stack pointers and possibly the top elements of the stack itself.
4. Registers are used to hold global values that are computed in one
basic block and used in other blocks.
Register and Address Descriptors
• Register descriptor keeps track of the variable names
whose current value is in that register.

• Address descriptor keeps track of the location where


the current value of that variable can be found.
The Code Generation Algorithm
Machine Instructions for Operations:

• For a three-address instruction such as x = y + z, do the


following:
1. Use getReg(x = y + z) to select registers for x, y, and z. Call
these Rx, Ry, and Rz.
2. If y is not in Ry (according to the register descriptor for Ry),
then issue an instruction LD Ry, y', where y' is one of the
memory locations for y (according to the address descriptor
for y).
3. Similarly, if z is not in Rz, issue an instruction LD Rz, z',
where z' is a location for z.
4. Issue the instruction ADD Rx, Ry, Rz.
Rules for updating Register and address
descriptors
1. For the instruction LD R, x
(a) Change the register descriptor for register R so it holds
only x.
(b) Change the address descriptor for x by adding register
R as an additional location.

2. For the instruction ST x, R, change the address descriptor for x


to include its own memory location.
3. For an operation such as ADD Rx,Ry,Rz
(a) Change the register descriptor for Rx so that it holds only x.
(b) Change the address descriptor for x so that its only location
is Rx.
(c) Remove Rx from the address descriptor of any variable
other than x.

4. When we process a copy statement x = y, after generating the load


for y into register Ry, if needed, and after managing descriptors as
for all load statements:
(a) Add x to the register descriptor for Ry.
(b) Change the address descriptor for x so that its only location
is Ry.
t=a–b
u=a–c
v=t+u
a=d
d=v+u
R1 R2 R3 a b c d t u v
a b c d

t=a–b
LD R1, a
LD R2, b
SUB R2, R1, R2
a tt a,R1 b c d R2

u=a–c
LD R3, c
SUB R1, R1, R3

u tt c a b c, R3 d R2 R1
.v = t + u
ADD R3, R2, R1
u tt v a b c d R2 R1 R3

a=d
LD R2, d
u Ta, d v R2 b c d,R2 R1 R3

d=v+u
ADD R1, R3, R1
d Ta v R2 b c R1 R3

Exit
ST a, R2
ST d, R1 d Ta v a,R2 b c d,R1 R3
Rules for picking Ry for y
Possibilities for value of R
Selection for Register Rx
Peephole optimization
Eliminating Redundant Loads and Stores

LD R0, a
ST a, R0
• We can delete the store instruction because whenever it is
executed, the first instruction will ensure that the value of a
has already been loaded into register R0.
Eliminating Unreachable Code
• An unlabeled instruction immediately following an
unconditional jump may be removed.
if debug == 1 goto LI
goto L2
LI: print debugging information
L2:

The above code sequence can be replaced by


if debug != 1 goto L2
print debugging information
L2:
If debug is set to 0 at the beginning of the program, constant
propagation would transform this sequence into

if 0 != 1 goto L2
print debugging information
L2:

• All statements that print debugging information are


unreachable and can be eliminated.
Flow-of-Control Optimizations
• Unnecessary jumps are eliminated either in the intermediate
code or in target code.
• We can replace the sequence
goto L1
…..
L1: goto L2
by the sequence
goto L2
…..
L1: goto L2
• If there are no jumps to L1, then statement L1: goto L2 can be
eliminated.
if a < b goto L1
….
L1: goto L2
Can be replaced by the sequence
if a < b goto L2
…..
L1: goto L2
Algebraic Simplification and Reduction in
Strength
• The algebraic identities can also be used by a peephole
optimizer to eliminate three address statements such as
x=x+0
or x = x * 1

• Reduction in strength can be applied in the peephole to replace


expensive operations by cheaper ones.
x2 = x * x
Register Allocation and Assignment
Global register Allocation
Usage Counts
Code sequence using global register assignment
Register Assignment for Outer Loops
Live variable Analysis
• Algorithm for live variable analysis:

1. IN[EXIT] = ᶲ;
2. for(each basic block B other than EXIT) IN[B] = ᶲ;
3. While( changes to any IN occur)
for(each basic block B other than EXIT)
{
OUT[B] = ᴜS a successor of B IN[S];
IN[B] = useB ᴜ (OUT[B] – defB);
}

You might also like