CSC 808: Compiler Construction
Lecture Notes 4: Storage Allocation Strategies in Compiler Design
•
A compiler is a program that converts HLL(High-Level Language) to
LLL(Low-Level Language) like machine language. It is also responsible
for organizing the memory layout of a program by allocating space for
global and local variables, constants, and dynamic data structures.
• As the program begins execution, it is under the control of the
operating system, which sets up the environment by allocating
space for the whole process.
• Compiler is responsible for deciding which variable gets which
part of the memory.
• For example, the compiler needs to decide whether variables
are best placed in the stack (for local variables) or in the heap
(for dynamically allocated memory) or data segment (global and
stack variables)
Below is an example of a C code to demonstrate how a C compiler
typically decided memory allocation.
There are mainly three types of Storage Allocation Strategies which
compiler uses:
Static Storage Allocation
Static storage allocation is the simplest form of memory allocation. In this
strategy, the memory for variables is allocated at compile time. The
compiler determines the memory addresses for all variables before the
program starts running, and these addresses remain the same throughout
the program’s execution.
For example, in languages like C or C++, when you declare global
variables or static variables, the memory for these variables is allocated
using static storage.
int number = 1; // Global
static int digit = 1;
Advantages of Static Storage Allocation
1. The allocation process is straightforward and easy to
understand.
2. The memory is allocated once only at compile time and remains
the same throughout the program completion.
3. Memory allocation is done before the program starts taking
memory only on compile time.
Disadvantages of Static Storage Allocation
1. Not highly scalable and offers limited memory flexibility.
2. The size of the data must be known at the compile time.
3. Static storage allocation is not very efficient and there is
potential for memory wastage.
4.
Stack Storage Allocation
Stack storage allocation is used for memory allocation of local variables
of a function (including main), where memory is allocated at when the
function is called. In this case, memory is assigned in a Last In, First Out
(LIFO) manner, meaning that memory is allocated when a function is
called and deallocated when the function returns.
In C/C++, the local variables of a function are stored in the stack. Each
time a function is called, a new activation record (stack frame) is pushed
onto the stack to store the local variables. Once the function completes,
the stack frame is popped off, and the memory used is released.
// when we call the sum function below, memory
// will be allotted for the variables, a, b and ans
void sum(int a, int b){int ans = a+b; cout<<ans;}
Advantages of Stack Storage Allocation
1. Stack allocation provides very fast memory access and
management.
2. Memory is automatically managed and deallocated when a
function completes.
3. Memory allocation and deallocation is simple (compared to
heap) as completely controlled by compiler.
Disadvantages of Stack Storage Allocation
1. Stack has a limited size which can lead to potential stack
overflow errors.
2. It cannot handle dynamic or large memory requirements
effectively.
3. The fixed memory size restricts flexibility as programmers have
no control about allocation and deallocation of memory.
Heap Storage Allocation
Heap storage allocation is another form of dynamic memory allocation,
but unlike the stack, it allows memory to be allocated at runtime for
variables that can grow or shrink during execution. The heap is used for
memory that is not automatically deallocated when a function call ends.
This type of memory allocation is used when you need to store objects or
data that are not tied to a specific function scope.
In C/C++, functions like malloc() or new are used to allocate memory in
the heap, and functions like free() or delete are used to deallocate the
memory.
int* ans = new int[5];
Advantages of Heap Storage Allocation
1. Heap allocation is useful when we have data whose size is not
fixed and can change during the run time.
2. We can retain the values of variables even if the activation
records end.
3. Heap allocation is the most flexible allocation scheme as
programmers can decide when to allocate and when to
deallocate.
Disadvantages of Heap Storage Allocation
1. Heap allocation is slower as compared to stack allocation.
2. There is a chance of memory leaks (in languages where
automatic garbage collection does not happen) if programmer
forgets to deallocate.
3. It can create dangling pointers / references if not handled
carefully.
Comparison of Storage Allocation Strategies
Memory Memory
Allocatio Manageme Efficienc Flexibilit
Strategy n Time nt y y Usage
Global
Static and
Compile Fixed, no
Allocatio High Low static
time deallocation
n variable
s
Memory Memory
Allocatio Manageme Efficienc Flexibilit
Strategy n Time nt y y Usage
Local
Stack variable
Automatic
Allocatio Runtime Fast Limited s,
(LIFO)
n function
calls
Manual Large or
Heap (allocation dynamic
Allocatio Runtime and Slower High data
n deallocation structure
) s
Conclusion
In conclusion, different storage allocation strategies play an important role
in determining the best-fit storage allocation strategy according to the
need of the user as the helps in determining how the memory is going to
be allocated and deallocated. Different storage allocation strategies have
their own advantages and disadvantages and the choice depends on the
factors like speed, memory allocation, efficiency, etc. So we can choose
the allocation strategy according to the requirement.
Frequently Asked Questions
What is the purpose of using storage allocation strategies in
compiler design?
Answer:
It is very important to use the right strategy for storage allocation as it can
directly affect the performance of the software.
Which storage allocation strategy is good if we compare Static and
Dynamic Allocation?
Answer:
It depends on many factors in some cases Static allocation will be good
or in some cases Dynamic allocation. For example, in static allocation, It
is easy to understand, the memory is allocated once only at compile time
and remains the same throughout the program completion so the
execution time will be controlled. Whereas in Dynamic allocation the
memory allocation and deallocation are dynamic changes according to
the user requirement. So one can choose accordingly.
What is Stack-based storage allocation?
Answer:
Stack allocation is commonly known as Dynamic allocation. Dynamic
allocation means the allocation of memory at run-time. Stack is a data
structure that follows the LIFO principle so whenever there is multiple
activation record created it will be pushed or popped in the stack as
activations begin and ends.
What is Heap based storage allocation?
Answer:
Heap allocation is used where the Stack allocation lacks if we want to
retain the values of the local variable after the activation record ends,
which we cannot do in stack allocation, here LIFO scheme does not work
for the allocation and de-allocation of the activation record. Heap is the
most flexible storage allocation strategy we can dynamically allocate and
de-allocate local variables whenever the user wants according to the user
needs at run-time.
Register Allocation Algorithms in Compiler Design
•
Register allocation is an important method in the final phase of the
compiler. Registers are faster to access than cache memory. Registers
are available in small size up to few hundred Kb. Thus, it is necessary to
use minimum number of registers for variable allocation. There are three
popular Register allocation algorithms.
1. Naive Register Allocation
2. Linear Scan Algorithm
3. Chaitin’s Algorithm
These are explained as following below.
1. Naïve Register Allocation:
• Naive (no) register allocation is based on the assumption that
variables are stored in Main Memory.
• We can’t directly perform operations on variables stored in Main
Memory.
• Variables are moved to registers which allows various
operations to be carried out using ALU.
• ALU contains a temporary register where variables are moved
before performing arithmetic and logic operations.
• Once operations are complete, we need to store the result back
to the main memory in this method.
• Transferring of variables to and from Main Memory reduces the
overall speed of execution.
a=b+c
d=a
c=a+d
Variables stored in Main Memory :
a b c d
2 fp 4 fp 6 fp 8 fp
Machine Level Instructions:
LOAD R1, _4fp
LOAD R2, _6fp
ADD R1, R2
STORE R1, _2fp
LOAD R1, _2fp
STORE R1, _8fp
LOAD R1, _2fp
LOAD R2, _8fp
ADD R1, R2
STORE R1, _6fp
Advantages:
• Easy to understand operations and the flow of variables from
Main memory to Registers and vice versa.
• Only 2 registers are enough to perform any operations.
• Design complexity is less.
Disadvantages:
• Time complexity increases as variables is moved to registers
from main memory.
• Too many LOAD and STORE instructions.
• To access a variable second time we need to STORE it to the
Main Memory to record any changes made and LOAD it again.
• This method is not suitable for modern compilers .
2. Linear Scan Algorithm:
Linear Scan Algorithm is a global register allocation mechanism
•
.
• It is a bottom up approach .
• If n variables are live at any point of time then we require ‘n’
registers .
• In this algorithm the variables are scanned linearly to
determine the live ranges of the variable based on which the
registers are allocated .
• The main idea behind this algorithm is that to allocate
minimum number of registers such that these registers can be
used again and this totally depends upon the live range of the
variables .
• For this algorithm we need to implement live variable
analysis of Code Optimization .
a=b+c
d=e+f
d=d+e
IFZ a goto L0
b=a+d
goto L1
L0 : b = a - d
L1 : i = b
Control Flow Graph :
• At any point of time the maximum number of live variables is 4
in this example . Thus we require 4 registers at maximum for
register allocation .
If we draw horizontal line at any point on the above diagram we can see
that we require exactly 4 registers to perform the operations in the
program .
Splitting :
• Sometimes the required number of registers may not be
available . In such case we may require to move some variables
to and from the RAM . This is known as spilling .
• Spilling can be done effectively by moving the variable which is
used less number of times in the program .
Disadvantages :
• Linear Scan Algorithm doesn’t take into account the “lifetime
holes” of the variable .
• Variables are not live throughout the program and this algorithm
fails to record the holes in the live range of the variable .
3.Graph Coloring (Chaitin’s Algorithm) :
• Register allocation is interpreted as a graph coloring problem .
• Nodes represent live range of the variable.
• Edges represent the connection between two live ranges .
• Assigning color to the nodes such that no two adjacent nodes
have same color .
• Number of colors represents the minimum number of registers
required .
A k-coloring of the graph is mapped to k registers .
Steps :
1. Choose an arbitrary node of degree less than k .
2. Push that node onto the stack and remove all of it’s outgoing
edges .
3. Check if the remaining edges have degree less than k, if YES
goto 5 else goto #
4. If the degree of any remaining vertex is less than k then push it
onto to the stack .
5. If there is no more edge available to push and if all edges are
present in the stack POP each node and color them such that
no two adjacent nodes have same color.
6. Number of colors assigned to nodes is the minimum number of
registers needed .
# spill some nodes based on their live ranges and then try again with
same k value . If problem persists it means that the assumed k value
can’t be the minimum number of registers .Try increasing the k value by
1 and try the whole procedure again .
For the same instructions mentioned above the graph coloring will be as
follows :
Assuming k=4
before coloring
After performing the graph coloring, final graph is obtained as follows
final graph with k(4) colors
Note : Any color(register) can be assigned to ‘i’ as it has no edge to any
other nodes .
https://www.geeksforgeeks.org/register-allocation-algorithms-in-
compiler-design/?ref=next_article