Code Optimization PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 25

Code optimization

Introduction
● 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.
● It rearranges the tree generated by parser such that to consume fewer
resources and to produces more speed.
● The meaning of the code is not altered.
● It increases the execution speed of the program
● It reduces the storage space
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.
Main Types of Code Optimization are -

1. 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
2. Intermediate code optimization
1. The elimination of common sub-expressions – This type of compiler optimization probes for the instances of identical
expressions by evaluating to the same value and researches whether it is valuable to replace them with a single variable
which holds the computed value.

2. Constant propagations – Here, expressions which can be evaluated at compile time are identified and replaced with
their values.

3. Jump threading – This involves an optimization of jump directly into a second one. The second condition is eliminated if it
is an inverse or a subset of the first which can be done effortlessly in a single pass through the program. Acyclic chained jumps are
followed till the compiler arrives at a fixed point.

4. Loop invariant code motion – This is also known as hoisting or scalar promotion. A loop invariant contains expressions
that can be taken outside the body of a loop without any impact on the semantics of the program. The above-mentioned movement
is performed automatically by loop invariant code motion.

5. Dead code elimination – Here, as the name indicates, the codes that do not affect the program results are eliminated. It
has a lot of benefits including reduction of program size and running time. It also simplifies the program structure. Dead code
elimination is also known as DCE, dead code removal, dead code stripping, or dead code strip.

6. Strength reduction – This compiler optimization replaces expensive operations with equivalent and more efficient ones,
but less expensive. For example, replace a multiplication within a loop with an addition.
3. Low-level Optimization
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.
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.
Machine Independent Optimization
In this 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:

This code involves repeated assignment of the identifier item, which if we put this
way:

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
1. Common Sub Expression Elimination
The expression that produces the same results should be removed out from the code

Example :- T1 = 4+i T1 = 4+i


T2 = T2 +T1 T2 = T2 +T1
T3 = 4 + i T4 = T2 + T1
T4 = T2 + T3

2. Constant folding

If expression generates a constant value then instead of performing its calculation again and
again we calculate it once and assign it.
3. Copy Propagation
In this propagation a F value is been send to G and G value is been send to H We
can eliminate G variable directly assigning the value of F to H.
T1 = X T2 = T3 + T2
T2 = T3 + T2 T3 = T1
T3 = X

4. Dead Code Elimination


Dead code is one or more than one code statements, which are:
Either never executed or unreachable,
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.
Loop Optimization
We are going to perform optimization on loops.

1. Code Motion

It specifies on a condition if we perform some operations to be carried out


and then compare for a condition.

Instead of that perform the calculation outside the loop and assign a value in
the calculation.
2. 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.
3. Frequency Reduction

In this case if a expression inside a loop is not dynamically affected by a loop we


calculate it outside the loop and use the value inside the loop.
4. 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
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.
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 :
● a. Linear representation such as postfix notation
● b. Three address representation such as quadruples
● c. Virtual machine representation such as stack machine code
● d. 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.
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.
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:
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 where, x - dividend
even register in even/odd register pair y-divisor 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.

You might also like