3 CO and CG
3 CO and CG
3 CO and CG
Optimization is the final stage of compiler, though it is optional. This is a program transformation
technique, which tries to improve the code by making it consume less resources (i.e. CPU, Memory)
and deliver high speed. In optimization, high-level general programming constructs are replaced by
very efficient low-level programming codes.
A code optimizing process must follow the three rules given below:
1. The output code must not, in any way, change the meaning of the program.
2. Optimization should increase the speed of the program and if possible, the program
should demand less number of resources.
3. Optimization should itself be fast and should not delay the overall compiling process.
Efforts for an optimized code can be made at various levels of compiling the process.
1. At the beginning, users can change/rearrange the code or use better algorithms to write the
code.
2. After generating intermediate code, the compiler can modify the intermediate code by address
calculations and improving loops.
3. While producing the target machine code, the compiler can make use of memory hierarchy
and CPU registers.
High-level optimization is a language dependent type of optimization that operates at a level in the
close vicinity of the source code. High-level optimizations include in lining where a function call
is replaced by the function body and partial evaluation which employs reordering of a loop,
alignment of arrays, padding, layout, and elimination of tail recursion.
Most of the code optimizations performed fall under intermediate code optimization which is
language independent. This includes:
Low-level Optimization is highly specific to the type of architecture. This includes the following:
1. Register allocation – Here, a big number of target program variables are assigned to a small
number of CPU registers. This can happen over a local register allocation or a global register
allocation or an inter-procedural register allocation.
2. Instruction Scheduling – This is used to improve an instruction level parallelism that in turn
improves the performance of machines with instruction pipelines. It will not change the
meaning of the code but rearranges the order of instructions to avoid pipeline stalls.
Semantically ambiguous operations are also avoided.
3. Floating-point units utilization – Floating point units are designed specifically to carry out
operations of floating point numbers like addition, subtraction, etc. The features of these units
are utilized in low-level optimizations which are highly specific to the type of architecture.
4. Branch prediction – Branch prediction techniques help to guess in which way a branch
functions even though it is not known definitively which will be of great help for the
betterment of results.
5. Peephole and profile-based optimization – Peephole optimization technique is carried out
over small code sections at a time to transform them by replacing with shorter or faster sets of
instructions. This set is called as a peephole.
Profile-based optimization is performed on a compiler which has difficulty in the prediction of
likely outcome of branches, sizes of arrays, or most frequently executed loops. They provide
the missing information, enabling the compilers to decide when needed.
Optimization can broadly be categorized into two- Machine Independent and Machine dependent.
Machine-independent optimization phase tries to improve the intermediate code to obtain a better
output. The optimized intermediate code does not involve any absolute memory locations or CPU
registers.
Machine-dependent optimization is done after generation of the target code which is transformed
according to target machine architecture. This involves CPU registers and may have absolute memory
references.
To conclude, code optimization is certainly required as it provides a cleaner code base, higher
consistency, faster sites, better readability and much more.
do
{
item = 10;
value = value + item;
} while(value<100);
This code involves repeated assignment of the identifier item, which if we put this way:
MCA IV SCRI COMPILER
Sem ET DESIGN
Item = 10;
do
{
value = value + item;
} while(value<100);
should not only save the CPU cycles, but can be used on any processor.
Machine Dependent 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.
Machine independence includes two types
i. Function Preserving
ii. Loop optimization
Function preserving
Common Sub Expression Elimination
The expression that produces the same results should be removed out from the code
Example
BO AO
T1 = 4+i T1 = 4+i
T2 = T2 +T1 T2 = T2 +T1
T3 = 4 * i T4 = T2 + T1
T4 = T2 + T3
Constant folding
If expression generates a constant value then instead of performing its calculation again and
again we calculate it once and assign it.
Example
BO AO
T1 = 512 T1 = 2.5
Copy Propagation
BO AO
T1 = X T2 = T3 + T2
T2 = T3 + T2 T3 = T1
T3 = X
Dead Code Elimination
Dead code is one or more than one code statements, which are:
o Either never executed or unreachable,
o Or if executed, their output is never used.
Thus, dead code plays no role in any program operation and therefore it can simply be
eliminated.
Partially dead code Elimination
There are some code statements whose computed values are used only under certain
circumstances, i.e., sometimes the values are used and sometimes they are not.
Such codes are known as partially dead-code.
The above control flow graph depicts a chunk of program where variable ‘a’ is used to assign
the output of expression ‘x * y’.
Let us assume that the value assigned to ‘a’ is never used inside the loop.
Immediately after the control leaves the loop, ‘a’ is assigned the value of variable ‘z’, which
would be used later in the program.
We conclude here that the assignment code of ‘a’ is never used anywhere, therefore it is
eligible to be eliminated.
Likewise, the picture below depicts that the conditional statement is always false, implying
that the code, written in true case, will never be executed, hence it can be removed.
BO AO
Strength Reduction
It specifies the operators such as multiplication and division can be replaced by a addition and
subtraction respectively.
The multiplication operator can be easily replaced by left shift operator a<<1 Division can be
replaced by a a>>1 operator.
BO AO
T1 = a * 2 a<<1
T1 = a / 2 a >> 1
Frequency Reduction
Loop Distribution
It specifies the values in a particular loop to be assigned to a array keeps of varing i.e the array
location in which a loop need to be work again and again. We can use two different loops which
allows loop distribution
Peephole Optimization
This optimization technique works locally on the source code to transform it into an
optimized code. By locally, we mean a small portion of the code block at hand.
These methods can be applied on intermediate codes as well as on target codes.
A bunch of statements is analyzed and are checked for the following possible optimization:
At compilation level, the compiler searches for instructions redundant in nature. Multiple loading and
storing of instructions may carry the same meaning even if some of them are removed. For example:
MOV x, R0
MOV R0, R1
We can delete the first instruction and re-write the sentence as:
MOV x, R1
2. Unreachable code
Unreachable code is a part of the program code that is never accessed because of
programming constructs.
Programmers may have accidently written a piece of code that can never be reached.
void add_ten(int x)
{
return x + 10;
printf(“value of x is %d”, x);
}
In this code segment, the printf statement will never be executed as the program control
returns back before it can execute, hence printf can be removed.
•In this code segment, the printf statement will never be executed as the program control returns
back before it can execute, hence printf can be removed.
3. Flow of control optimization
There are instances in a code where the program control jumps back and forth without
performing any significant task.
These jumps can be removed. Consider the following chunk of code:
...
MOV R1, R2
GOTO L1
...
L1: GOTO L2
L2: INC R1
In this code, label L1 can be removed as it passes the control to L2. So instead of jumping to
L1 and then to L2, the control can directly reach L2, as shown below:
...
MOV R1, R2
GOTO L2
...
L2: INC R1
There are occasions where algebraic expressions can be made simple. For example, the
expression a = a + 0 can be replaced by a itself and the expression a = a + 1 can simply be
replaced by INC a.
5. Strength reduction
The target machine can deploy more sophisticated instructions, which can have the capability
to perform specific operations much efficiently.
If the target code can accommodate those instructions directly, that will not only improve the
quality of code, but also yield more efficient results.
CODE GENERATION
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.
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, that is, 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,
*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 speeds 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
d:=a+e
(a)
MOV b,R0
ADD c,R0
MOV R0,a
(b)
MOV a,R0
ADD e,R0
MOV R0,d
MCA IV SCRI COMPILER
Sem ET DESIGN
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 :
1.Register allocation - the set of variables that will reside in registers at a point in the program is
selected.
2. Register assignment - the specific register that a value picked•
3.Certain machine requires even-odd register pairs for some operands and results. For example,
consider the division instruction of the form :D x, y
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.
Questions: