Open In App

Code Optimization in Compiler Design

Improve
Improve
Like Article
Like
Save
Share
Report

The code optimization in the synthesis phase is a program transformation technique, which tries to improve the intermediate code by making it consume fewer resources (i.e. CPU, Memory) so that faster-running machine code will result. Compiler optimizing process should meet the following objectives :

  • The optimization must be correct, it must not, in any way, change the meaning of the program.
  • Optimization should increase the speed and performance of the program.
  • The compilation time must be kept reasonable.
  • The optimization process should not delay the overall compiling process.

When to Optimize? 

Optimization of the code is often performed at the end of the development stage since it reduces readability and adds code that is used to increase the performance. 

Why Optimize? 

Optimizing an algorithm is beyond the scope of the code optimization phase. So the program is optimized. And it may involve reducing the size of the code. So optimization helps to:

  • Reduce the space consumed and increases the speed of compilation.
  • Manually analyzing datasets involves a lot of time. Hence we make use of software like Tableau for data analysis. Similarly manually performing the optimization is also tedious and is better done using a code optimizer.
  • An optimized code often promotes re-usability.

Types of Code Optimization: The optimization process can be broadly classified into two types :

  1. Machine Independent Optimization: This code optimization phase attempts to improve the intermediate code to get a better target code as the output. The part of the intermediate code which is transformed here does not involve any CPU registers or absolute memory locations.
  2. 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 the memory hierarchy.

Code Optimization is done in the following different ways:

1. Compile Time Evaluation:

C




(i)   A = 2*(22.0/7.0)*r 
      Perform 2*(22.0/7.0)*r at compile time.
(ii)  x = 12.4
      y = x/2.3 
      Evaluate x/2.3 as 12.4/2.3 at compile time.


2. Variable Propagation:

C




//Before Optimization 
c = a * b                                               
x = a                                                  
till                                                           
d = x * b + 4 
  
  
//After Optimization 
c = a * b  
x = a
till
d = a * b + 4


3. Constant Propagation:  

  • If the value of a variable is a constant, then replace the variable with the constant. The variable may not always be a constant.

Example: 

C




(i)  A = 2*(22.0/7.0)*r
     Performs 2*(22.0/7.0)*r at compile time.
(ii)  x = 12.4
      y = x/2.3
      Evaluates x/2.3 as 12.4/2.3 at compile time.
(iii) int k=2;
      if(k) go to L3;
      It is evaluated as :
      go to L3 ( Because k = 2 which implies condition is always true)


4. Constant Folding: 

  • Consider an expression : a = b op c and the values b and c are constants, then the value of a can be computed at compile time.

Example: 

C




#define k 5
x = 2 * k 
y = k + 5
   
This can be computed at compile time and the values of x and y are :
 x = 10
 y = 10


Note: Difference between Constant Propagation and Constant Folding: 

  • In Constant Propagation, the variable is substituted with its assigned constant where as in Constant Folding, the variables whose values can be computed at compile time are considered and computed. 

5. Copy Propagation: 

  • It is extension of constant propagation.
  • After a is assigned to x, use a to replace x till a is assigned again to another variable or value or expression.
  • It helps in reducing the compile time as it reduces copying.

Example : 

C




//Before Optimization
c = a * b                                              
x = a                                                 
till                                                          
d = x * b + 4
 
 
//After Optimization
c = a * b 
x = a
till
d = a * b + 4


6. Common Sub Expression Elimination: 

  • In the above example, a*b and x*b is a common sub expression.

7. Dead Code Elimination: 

  •  Copy propagation often leads to making assignment statements into dead code.
  • A variable is said to be dead if it is never used after its last definition.
  • In order to find the dead variables, a data flow analysis should be done.

Example: 

C




c = a * b                                               
x = a                                               
till                                                         
d = a * b + 4  
 
//After elimination :
c = a * b
till
d = a * b + 4


8. Unreachable Code Elimination: 

  • First, Control Flow Graph should be constructed.
  • The block which does not have an incoming edge is an Unreachable code block.
  • After constant propagation and constant folding, the unreachable branches can be eliminated.
     

C++




#include <iostream>
using namespace std;
 
int main() {
  int num;
  num=10;
    cout << "GFG!";
    return 0;
  cout << num; //unreachable code
}
//after elimination of unreachable code
int main() {
  int num;
  num=10;
    cout << "GFG!";
    return 0;
 }


9. Function Inlining: 

  • Here, a function call is replaced by the body of the function itself.
  • This saves a lot of time in copying all the parameters, storing the return address, etc.

10. Function Cloning: 

  • Here, specialized codes for a function are created for different calling parameters.
  • Example: Function Overloading

11. Induction Variable and Strength Reduction: 

  • An induction variable is used in the loop for the following kind of assignment i = i + constant. It is a kind of Loop Optimization Technique. 
  • Strength reduction means replacing the high strength operator with a low strength.

Examples: 

C




Example 1 :
Multiplication with powers of 2 can be replaced by shift left operator which is less
expensive than multiplication
a=a*16
// Can be modified as :
a = a<<4
 
Example 2 :
i = 1;                                                                     
while (i<10)                                                         
{                                                                            
   y = i * 4;
}
 
 
//After Reduction
i = 1
t = 4
{
  while( t<40)
  y = t;
  t = t + 4;
}


Loop Optimization Techniques: 

1. Code Motion or Frequency Reduction: 

  • The evaluation frequency of expression is reduced.
  • The loop invariant statements are brought out of the loop. 

Example: 

C




a = 200;
 while(a>0)
 {
     b = x + y;
     if (a % b == 0)
     printf(“%d”, a);
   }
 
 
//This code can be further optimized as
a = 200;
b = x + y;
while(a>0)
 {
     if (a % b == 0}
     printf(“%d”, a);
   }


2. Loop Jamming: 

  • Two or more loops are combined in a single loop. It helps in reducing the compile time.

Example: 

C




// Before loop jamming
for(int k=0;k<10;k++)
{
 x = k*2;
}
 
for(int k=0;k<10;k++)
{
 y = k+3;
}
 
//After loop jamming
for(int k=0;k<10;k++)
{
 x = k*2;
 y = k+3;
}


3. Loop Unrolling: 

  • It helps in optimizing the execution time of the program by reducing the iterations.
  • It increases the program’s speed by eliminating the loop control and test instructions.

Example: 

C




//Before Loop Unrolling
 
for(int i=0;i<2;i++)
{
  printf("Hello");
}
 
//After Loop Unrolling
 
printf("Hello");
printf("Hello");


Where to apply Optimization? 

Now that we learned the need for optimization and its two types,now let’s see where to apply these optimization.

  • Source program: Optimizing the source program involves making changes to the algorithm or changing the loop structures. The user is the actor here.
  • Intermediate Code: Optimizing the intermediate code involves changing the address calculations and transforming the procedure calls involved. Here compiler is the actor.
  • Target Code: Optimizing the target code is done by the compiler. Usage of registers, and select and move instructions are part of the optimization involved in the target code.
  • Local Optimization: Transformations are applied to small basic blocks of statements. Techniques followed are  Local Value Numbering and Tree Height Balancing.
  • Regional Optimization: Transformations are applied to Extended Basic Blocks. Techniques followed are Super Local Value Numbering and Loop Unrolling.
  • Global Optimization: Transformations are applied to large program segments that include functions, procedures, and loops. Techniques followed are Live Variable Analysis and Global Code Replacement.
  • Interprocedural Optimization:  As the name indicates, the optimizations are applied inter procedurally. Techniques followed are Inline Substitution and Procedure Placement.

Advantages of Code Optimization:

Improved performance: Code optimization can result in code that executes faster and uses fewer resources, leading to improved performance.

Reduction in code size: Code optimization can help reduce the size of the generated code, making it easier to distribute and deploy.

Increased portability: Code optimization can result in code that is more portable across different platforms, making it easier to target a wider range of hardware and software.

Reduced power consumption: Code optimization can lead to code that consumes less power, making it more energy-efficient.

Improved maintainability: Code optimization can result in code that is easier to understand and maintain, reducing the cost of software maintenance.

Disadvantages of Code Optimization:

Increased compilation time: Code optimization can significantly increase the compilation time, which can be a significant drawback when developing large software systems.

Increased complexity: Code optimization can result in more complex code, making it harder to understand and debug.

Potential for introducing bugs: Code optimization can introduce bugs into the code if not done carefully, leading to unexpected behavior and errors.

Difficulty in assessing the effectiveness: It can be difficult to determine the effectiveness of code optimization, making it hard to justify the time and resources spent on the process.



Last Updated : 06 Apr, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads