CD Unit V
CD Unit V
CD Unit V
UNIT- V
Code Optimization
Introduction:
• It is possible for programmer to perform compiler using his or her knowledge in choosing
better algorithm and data items.
• Optimization is process of transformation of code to an efficient code in terms of space and
time without changing the meaning of code.
• Following constraints are to be considered while applying techniques for code
improvements.
1. Transformation preserves meaning of program. i.e., target code should ensure semantic
equivalence with source program.
2. Program efficiency must be improved by measurable amount without changing
algorithm used in program.
3. When technique is applied on special format, then it is worth transforming to that
format only.
4. Some transformations are applied only after detailed analysis, which is time
consuming. It is not worthy if program runs very few number of times.
2. Copy propagation:
• If there are copy statements of form x=y then in copy propagation use of x is
replaced by y in subsequent expressions.
• Copy propagation is possible if none of the variable is changed after this argument.
Example: consider following code:
a=b+c
d=c //copy statement
........
e=b+d-3
Loop Optimization:
• The major source of code optimization is loops. Most of runtime is spent inside loops
which can be reduced by reducing instructions in the loop.
1. Code motion
2. Elimination of induction variable
3. Strength reduction
1. Code motion :
• It reduces number of instructions in loop by moving instruction outside a loop.
• Here we move instructions or expressions that result is same value independent of
number of times .Loop is executed and place them at beginning of loop.
while(x != n - 2)
{
x = x + 2;
}
• Here n-2 is loop invariant computation it should remain unchanged because of that
it places outside of loop.
m=n-2
while(x != m)
{
x = x + 2;
}
Example:
int a[10], b[10];
void fun(void)
{
int i, j, k;
GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 4
III BTECH II-SEM, CSE: COMPILER DESIGN
for(i=0, j=0, k=0; i<10; i++)
a[j++] = b[k++];
return;
}
• Here i, j, k are induction variables. These are incremented by 1 each time loop is
executed. By changing the above code we can eliminate j, k induction variables.
3. Strength Reduction:
• Strength Reduction is process of replacing expensive operation by equivalent
cheaper operations on target machine.
• On many machines multiplication replaced by addition and subtraction because
these have less than multiplications.
Example:
for(i=1; i<5; i++)
{
x = 4 * i;
}
• The instruction x=4*i in loop can be replaced by equivalent addition instruction as
x=x+4.
for(i=1; i<5; i++)
{
x = x + 4;
}
Flow graph
*In the flow graph above,
• Initial node, node1 dominates every node.
• node 2 dominates itself
• node 3 dominates all but 1 and 2.
• node 4 dominates all but 1, 2 and 3.
• node 5 and 6 dominates only themselves ,since flow of control can skip around
either by going through the other.
• node 7 dominates 7,8 ,9 and 10.
• node 8 dominates 8,9 and 10.
• node 9 and 10 dominates only themselves.
• The way of presenting dominator information is in a tree, called the dominator tree in
which the initial node is the root.
• The parent of each other node is its immediate dominator. Each node d dominates only
its descendents in the tree.
Dominator tree
D(1)={1} D(6)={1,3,4,6}
D(2)={1,2} D(7)={1,3,4,7}
D(3)={1,3} D(8)={1,3,4,7,8}
D(4)={1,3,4} D(9)={1,3,4,7,8,9}
D(5)={1,3,4,5} D(10)={1,3,4,7,8,10}
Natural Loop:
• One application of dominator information is in determining the loops of a flow graph
suitable for improvement.
• The properties of loops are
• A loop must have a single entry point, called the header. This entry point-dominates all
nodes in the loop, or it would not be the sole entry to the loop.
• There must be at least one way to iterate the loop(i.e.)at least one path back to the
header.
• One way to find all the loops in a flow graph is to search for edges in the flow graph
whose heads dominate their tails. If a→b is an edge, b is the head and a is the tail. These
types of edges are called as back edges.
Example:
In the above graph,
7 → 4 4 DOM 7
10 →7 7 DOM 10
4→3
8→3
9 →1
Inner loop:
• If we use the natural loops as “the loops”, then we have the useful property that unless
two loops have the same header, they are either disjointed or one is entirely contained
in the other. Thus, neglecting loops with the same header for the moment, we have a
natural notion of inner loop: one that contains no other loop.
• When two natural loops have the same header, but neither is nested within the other,
they are combined and treated as a single loop.
Pre-Headers:
• Several transformations require us to move statements “before the header”.
Therefore begin treatment of a loop L by creating a new block, called the preheater.
• The pre-header has only the header as successor, and all edges which formerly
entered the header of L from outside L instead enter the pre-header.
• Edges from inside loop L to the header are not changed.
• Initially the pre-header is empty, but transformations on L may place statements in it.
Code Optimization:
• In compiler design, code optimization is a program transformation technique that
tries to improve the intermediate code to consume fewer resources such as CPU,
memory, etc., resulting in faster machine code.
• There are two types of code optimization techniques.
1. Local optimization- This code optimization applies to a small block of statements.
Examples of local optimization are variable/constant propagation and common
sub expression elimination.
2. Global optimization- This code optimization applies to large program segments
like functions, loops, procedures etc. An example of global optimization is data
flow analysis.
Control-Flow Constraints:
• The second set of constraints comes from the control flow. The control flow value of
Si will be equal to the control flow values into Si+1 if block B contains statements S1,
S2,........, Sn. That is:
IN[Si + 1] = OUT[Si], for all i = 1 , 2, ....., n – 1.
• In the above example, the expression L1: 4 * i is an available expression since this
expression is available for blocks B2 and B3, and no operand is getting modified.
Reaching Definition:
• A definition D is reaching a point x if D is not killed or redefined before that point. It is
generally used in variable/constant propagation.
Example:
• In the above example, D1 is a reaching definition for block B2 since the value of x is
not changed (it is two only) but D1 is not a reaching definition for block B3 because
the value of x is changed to x + 2. This means D1 is killed or redefined by D2.
Live Variable:
• A variable x is said to be live at a point p if the variable's value is not killed or
redefined by some block. If the variable is killed or redefined, it is said to be dead.
• It is generally used in register allocation and dead code elimination.
Example:
Constant Propagation:
• Constant Propagation is process of recognizing and evaluating constant expressions
at compile time rather than run time.
Example: Pi=2217
void area_per(int r)
{
float area, perimeter;
area = Pi * r * r;
print area, perimeter;
}
• At line 1, variable Pi is constant and its value 3.413 is assigned at compile time.
• At line 5 and 6 value of Pi is assigned with 3.413 in runtime.
Benefits of PRE:
• We can significantly improve the performance of a program by eliminating
partial redundancies.
1. Execution Time: PRE will reduce the number of instructions that are needed
to be executed to run a program. Since our system has fewer instructions to
execute, it will result in a faster execution time.
2. Power Consumption: PRE will reduce the number of instructions that are
needed to be executed, which will save us power and make our system more
power efficient.
3. Readability: PRE will improve the readability of our program by removing
repeated code and redundancies, which will make it easier for the developers
to read and maintain the code.
4. Debug: PRE will remove unwanted or unnecessary code from our code, which
will make debugging easier and enable developers to find and correct any
errors in the code.
PRE algorithms:
There are various algorithms that are available in the market for our use. Some of the
algorithms that are used worldwide are:
1. Lazy Code Motion Algorithm (LCM)
2. Global Value Numbering Algorithm (GVN).
The Lazy Code Motion Algorithm (LCM):
• The Lazy code motion algorithm (LCM) aims to minimize the total number of
computations that is to be performed to execute a program.
• LCM is the algorithm that works by identifying partially redundant expressions.
And then, it will move them to a point in the control flow graph where they are
guaranteed to be executed at most once.
• It is a Four-Step Algorithm:
1. The first step uses anticipation to determine where the expression can be
placed.
2. The second step will be computing the earliest and latest points for each
expression.