CD Unit 4

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

III BTECH II-SEM, CSE: COMPILER DESIGN

UNIT- IV
Run time Environment
Storage organization:
• From compiler writer perspective, executing program runs in its own logical address space
in which each program value has location
• Management and organization of logical address space is shared between compiler,
operating system and target machine. Os maps logical address to physical address.
• Run time representation of object program in logical address space consist of data and
program area.
Code
Static
heap

Free memory
Stack
• Run time storage comes in block of memory (continuous bytes).byte is 8 bits and 4 bytes
form a machine word multi byte objects are stored in consecutive bytes.
• Storage layout for data objects is strongly influenced by addressing constraints of target
machine.
• Size of generated target code is fixed at compile time. So compiler can place target code in
statically determined area code usually in low end of memory.
• Global constants and information supported to garbage collection may be known at
compile time, and these are placed in statically determined area static.
• Addresses of code and static area objects are compiled in to target code.
• To maximum utilization of space at runtime, other 2 areas are stack and heap. these are
dynamic, their size can change as program executes.

Static versus dynamic storage allocation:


• Layout and allocation of data to memory location in runtime environment are key issues in
storage management.
• Storage allocation decision is static, if it can made by compiler looking only at text of
program.
• Storage allocation decision is dynamic, if it can decide only while program is running.
• Many compilers use some combination of following strategies for dynamic storage
allocation.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 1


III BTECH II-SEM, CSE: COMPILER DESIGN
1. Stack storage:
Names local to procedure are allocated space on stack it supports normal call/return
policy for procedure.
2. Heap storage:
Data that may out live (not local) call to procedure are allocated space on heap of
reusable storage.
• To support heap, garbage collection enables run time system to detect useless data
elements and reuse their storage.

Static allocation of space:


• Almost all compilers that use procedures functions or methods manage runtime
environments as stack.
• Each time procedure called, space for local variable is pushed on stack, and when
procedure terminates that space is popped off stack.

Activation trees:
• Stack allocation would not be feasible if procedure calls or activations of procedures did
not nest in time.
Example: program that reads 9 integers into array ‘a’ and sort them using recursive quick sort
algorithm.
int a[11];
void readArray()
{
/*read 9 integers into a[1]_ _ _ _ _a[9]*/
Int i;
______
}
int partition(int m, int n)
{
/*pick separator value v1 and partitions a[m_ _ _ _ _n] to a[m_ _ _ _ _p-1] are less than v
and a[p+1_ _ _ _ n] are greater than v */
______
}
Void Quick sort(int m, int n)
{
Int i;

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 2


III BTECH II-SEM, CSE: COMPILER DESIGN
if(n>m)
{
i = partition(m, n);
Quick sort(m, i-1);
Quick sort(i+1, n);
}
}
main(){
read Array();
a[0] = -9999;
a[10] = 9999;
Quick sort(1, 9);
}
• Main function has three tasks. It calls readArray and then calls Quick sort on array and call
to partition will split array base on separator.
• In the above, procedure activations are nested in time. If activation of procedure p call
procedure q then activation of q ends before activation of p end. There are 3 common
cases.
1. Activation of q terminates normally then control resumes after point of p at which call
to q mode.
2. Activation of q either directly or indirectly aborts in that case p ends simultaneously
with q.
3. Activation of q terminates with exception that q can’t handle but p may handle it. In
such situation q has terminated while p will continues. If p not handles it then p also
terminated.
• Activation of procedure during running of program can be viewed by tree are called
activation tree. Each node corresponds to one activation.
• Possible activation of above program is
enter main()
enter readarray()
leave readarray()
enter Quick sort(i,9)
enter partition(1,9)
leave partition(1,9)
enter Quick sort(1,3)
____

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 3


III BTECH II-SEM, CSE: COMPILER DESIGN
leave Quick sort(1,3)
enter Quick sort(5,9)
____
leave Quick sort(5,9)
leave Quick sort(i,9)
leave main()
• Activation tree is

• Stack will enabled several relationships between tree and behavior of program.
1. Sequence of procedure calls corresponds to pre order traversal of tree.
2. Sequence of returns corresponds to post order traversal of tree.
3. Order in which activations called is order they appear along path to N and returns
should be reverse order.

Activation records:
• Procedure calls and returns are usually managed by runtime stack called control stack.
• Each live activation has activation record on control stack with root of activation tree at
bottom.
• We draw control stacks with bottom of stack than top. Elements appear at lowest are
closest to top.
• Here is a list of kinds of data that might appear in an activation record. These are vary with
language implemented it.
1. Temporary values, such as those rising from evaluation of expression.
2. Local data belongs to procedure which activation record this is.
3. Saved machine states, information about state of machine before call procedure.
Information includes return address and registers used by calling procedure.
4. Access link may be needed to locate data needed by called procedure in other
activation record.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 4


III BTECH II-SEM, CSE: COMPILER DESIGN
5. Control link, pointing to activation record of caller.
6. Space for return value of called function if any, again not all called procedure return a
value.
7. Actual parameters used by calling procedure. Commonly these are not place in record.
Some cases it is possible.

Actual Parameters
Returned Values
Control Link
Access Link
Saved Machine Status
Local Data
Temporaries

Example: Below picture shows run time, stacks as control slow through the activation tree
here a is global .so, space is allocated for it before execution starts.

(a) Frame for main (b) r is activated

(a) r is popped and q(1,9) is pushed (b) control returns to q(1,3)


• Control reaches first call in body main, procedure is activated and its activation record
pushed onto control returns from this activation, its record is popped.
• Control then reaches call to q and activation record for this call is placed on top of stack.
When q(1,q) returns, stack again has only activation record for main.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 5


III BTECH II-SEM, CSE: COMPILER DESIGN
• Same procedure will be done for remaining nodes of activation tree. When a procedure is
recursive it is normal to have several of its activation records on stack at same time.
Calling sequences:
• Procedure calls are implemented by calling sequences, which consists of code that allocates
activation record on stack.
• Return sequences is similar code to restore state of machine.
• The code in calling sequence is divide between calling procedure (caller) and procedure it
calls (caller) and there is no extract division of runtime tasks between them.
• When designing calling sequences and layout of activation records, the principles are
helpful.
1. Values communicated between caller and callee are generally placed at beginning of
callee’s activation record. So they are close to caller’s activation record.
2. Fixed length items are generally placed in middle. Those items include control link,
access link and machine status fields.
3. Items whose size may not be known early are placed at the end of activation record.
Those items include local variables and temporary variables.
4. We must locate top of stack pointer common approach is locate at end of fixed length
fields in activation record.

• How caller and callee might cooperate in managing stack should be shown in the above.
• The calling sequence and it’s division between caller and callee are as follows:
1. Caller evaluates actual parameters.
2. Caller stores return address and old value of top_sp into activation record of callee.
3. Callee saves register value and other status information.
4. Callee initializes it’s local data and begin execution.
• Suitable, corresponding return sequence is
1. Callee place return value next to parameters.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 6


III BTECH II-SEM, CSE: COMPILER DESIGN
2. Callee restores top-sp and other registers and branches to return address, those place
in caller status field.
3. top_sp has decremented, caller knows where return value is relative to current value of
top_sp. then caller use this value.
• Above calling and return sequences allow number of arguments of called procedure to vary
from call to call.

Variable length data on stack:


• Allocation of space for objects whose size not known at compile time are allocated on stack.
• Reason for prefer placing objects on stack is to avoid expenses of garbage collecting.
• Common strategy for allocating variable length arrays is shown below. Same schema works
for objects of any type.

• In the above diagram, procedure p has 3 local arrays whose sizes can’t be determined at
compile time.
• Storage allocation of these arrays is not part of activation record for p, although it does
appear on stack.
• Only a pointer to beginning of each array appears in activation record itself. When p
executing array elements accessed through these pointers.
• Activation record of q, called by p begins after array of p. Any variable length array of q is
after activation record of q.
• Access to data on stack is through two pointers top and top_sp.
Top marks actual top of stack. it points at which next activation record begins.
Top_sp is used to find local, fixed length fields of top activation record
• Code to reposition top and top-sp can generated at compile time,in terms of size that is
known at runtime
• When q returns, top_sp can restore from save control link in activation record for q.new
value of top is top_sp minus the length of fields in q’s activation record.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 7


III BTECH II-SEM, CSE: COMPILER DESIGN

Access to Nonlocal data on stack:


• Here we consider how procedures access their data that not belongs to procedure.
• Access becomes more complicated in languages where procedures can be declared inside
other procedures.
• ML language permits both nested function declarations and functions as class objects;i.e
function takes function as arguments and return functions as values.

Data accessing without nested procedures


• In c family of languages, all variables defined either with in single function or outside any
function.
• It is impossible to declare one procedure whose scope is entirely within another procedure.
• For language that do not allow nested procedure declarations, allocation of storage for
variables and access to those are simple:
1. Global variables are allocated static store locations of those variables are fixed and
known at compile time.
2. Any other name must be local to activation at top of the stack .We may access these
variables through top-sp pointer of the stack.

Issues with nested procedure:


• Access become far more complicated when languages allows procedure declaration to be
nested.
• Reason is that knowing at compile time that the declaration of p is immediately nested with
in q doesn't tell us relative positions of their activation records at runtime.
• Finding declaration that applies to nonlocal name x in nested procedure p is static decision.
It can be done by static scope rule for blocks.
• X is declared in enclosing procedure q finding relevant activation of q from activation of p
is dynamic. It requires additional runtime information about activation.
• One possible solution to this problem is to use access links.

Language with Nested Procedure Declaration:


• C family of languages and many other familiar languages do not support nested procedure.
• History of nested Procedure in language is long, one such language in ML(Meta Language).
1. ML is functional language, means variable, once declared and initialized are not
changed.
2. Variables are designed and they have their unchangeable values initialized by
statement of form.
val (name) =<expression>

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 8


III BTECH II-SEM, CSE: COMPILER DESIGN
3. Functions are defined using syntax:
fun (name) (<arguments>) =<body>
4. For function bodies we use let-statements of form.
let <list of definitions > in <statements> end
• Scope of each such definition consists of all following definitions up to in and all statements
up to end.
• Function definitions can be nested. Example body of p contains let statement include
definition of other function q.

Nesting depth:
• Give nesting depth 1to procedures that are not nesting with in any other Procedure.
• All C functions are at nesting depth 1. If Procedure p is defined with in Procedure at nesting
i then nesting depth of p is i+1
Example:
fun sort(inputFile, outputFile) =
let
val a = array( 11,0 );
fun readArray(inputFile) = -------- ;
------- a -------- ;
fun exchange( i, j ) =
------- a -------- ;
fun quicksort( m, n )
let
val v = -------- ;
fun partition( y, z ) =
------- a -------- v -------- exchange --------
in
------- a -------- v -------- partition ---------- quicksort
end
in
------- a -------- readArray ---------- quicksort
End ;
• In above ML program, only function at nesting depth 1 is outermost function, sort which
reads array as a 9 integers and sort them using quicksort algorithm.
• In an array a, first argument says array 11 elements and second arguments says array a
holds value 0.
• Sort consist several functions: readArray, exchange and quick sort. readArray and
exchange each access array a.
• Each of the above function immediately within the function is at nesting depth 1, their
nesting depths are all 2.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 9


III BTECH II-SEM, CSE: COMPILER DESIGN
• In quick sort, local value v, the pivot for the partition is declared. These partitions uses
pivot value v. Partition is immediately with in function at nesting depth 2,It is depth 3.
• Quick sort accesses variable a and v, the function partition and itself recursively. Outer
function accesses a and call the two Procedure readArray and quicksort.

Access links:
• Direct implementation of normal static scope rule for nested functions is obtained by
access links of each activation record.
• If Procedure p is nested immediately within the Procedure q then access links in any
activation of p points to most recent activation of q.
• Access links form a chain from activation record at top of the stack to sequence of
activation at lower nesting depth.
Example: Sequence of stacks will show result of function sort. Here function name represent
first letters, show some data and access links for each activation.
We see situation after sort called read Array to load input in to array a then called
Quicksort(1,9).The access link from Quicksort(1,9) points to activation record sort.l
In successive steps we see recursive call to Quicksort(1,3) followed by partition which calls
exchange. Quicksort(1,3) points to sort for same array of Quicksort (1,9).

Exchange access link by passes activation records for Quicksort and partition , since exchange
is nested immediately with in sort.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 10


III BTECH II-SEM, CSE: COMPILER DESIGN
Manipulating Access links:
• Simple case occurs when Procedure calls to particular Procedure whose name is given
explicitly in Procedure call.
• Harder case is when call is to Procedure parameter in that ,Procedure call is not known
until runtime and nested depth is differ for different executions of call.
• What should happen when Procedure q calls Procedure p explicitly? There are 2 cases:
1. Procedure p is at higher nesting depth than q. p must be defined immediately with in q
then access link from p must lead to q.
2. Procedure p is at lower or equal nesting depth than q in p=q case links for p and q are
same .if p is lesser than access link from q leads to p.

Access links for procedure parameters:


• When procedure p is passed to another procedure q as parameter and q then calls its
parameter .It is possible that q does not know context in which p appears in program.
• It is impossible for q to know how to set access link for p solution to this problem is when
procedures are used as parameters then caller needs to pass access link with parameter.
Example: Below function a has functions b and c nested within It. Function b has function
valued parameter f. Function c defines within it a function d and c then calls b with actual
parameter d.
fun a(x) =
let
fun b(f) =
----- f ---;
fun c(y) =
let
fun d(z) = ----
in
--- b(d) ---
end
in
--- c(1) ---
end ;
• First, a calls c. We place activation record for c above a on stack .Access link for c points to
record for a in c calls b(d).
• Within this activation record actual parameter d and It’s access link together form. Formal
parameter f in activation record for b.
• In effect of calling d, activation record of d appears on the stack. Access link to place in
activation record is found in value for parameter in f ; link is to activation record for c, c
immediately surrounds d

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 11


III BTECH II-SEM, CSE: COMPILER DESIGN

Displays:
• One problem with the access link approach to non local data is that if the nesting depth gets
large, we may have to follow long chain of links to reach data.
• Efficient implementation uses auxiliary array d called display, which consists of one pointer
for each nesting depth.
• d[i] is pointer to highest activation record on stack for any procedure at nesting depth i.

• Advantage of display is if procedure P is executing and it needs to access element x belongs


to procedure q, we need to look only in d[i] where i is nested depth of q.
• In order to maintain display correctly we need to save previous values of display entries in
new activation records.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 12


III BTECH II-SEM, CSE: COMPILER DESIGN

Heap Management:
• Heap is portion of data store that is used for data that lives indefinitely, or until program
explicitly deletes it.
• Local variables becomes inaccessible when procedure end, many languages enable us to
creates objects or other data whose existence not tied with procedure that creates them
example new in java and C++.
• In this memory manager is subsystem that allocates and deallocates space within heap. It
serves interface between application programs and OS.
• Garbage collection is the process of finding spaces within heap that are no longer used by
program and reallocates it to other data items.
• Languages like java; it is garbage collection that deallocates memory. It is important
subsystem of memory manager.

Memory Manager:
• It keeps track of all free spaces in heap storage at all times it performs basically two
functions.
1. Allocation:
• When program requests memory for variable or object memory manager
processes contiguous heap memory of fixed size.
• If memory or space satisfies with free space in heap. If no space of needed size
available it increases space by getting virtual memory from OS.
• If space exhausted then memory manager passes information to application
program.
2. Deallocation:
• Memory manager returns deallocation space to pool of free spaces, so it can reuse
space to others.
• Memory manager do not return memory to OS even if program’s heap usage drops.
• Memory management is simpler if
(a) All allocation request, were for chunks of same size.
(b) Storage released predictably, the first allocated first deallocated.
• Lisp holds (a) but in some conditions supports (b) also generally most of languages holds
neither (a) nor (b).
• Properties of Memory Managers are:
1. Space Efficiency:
• Memory manager minimizes total heap space needed by a program.
• It allows large programs to run in fixed virtual address space.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 13


III BTECH II-SEM, CSE: COMPILER DESIGN
2. Program Efficiency:
• Memory manager should make good use of memory subsystem to allow programs
to run faster.
• Time taken to execute instructions can vary widely depending on where objects
placed in memory.
3. Low over head:
• Memory allocation and deallocation are frequent operations in many programs; it is
important that these operations are efficient as possible with low over head used.
• Over head of managing large objects is less important, because it can be decrease
over large amount of computation.

Memory Hierarchy of computers:


• Memory management and compiler optimization must explain how memory behaves.
• Modern machine designed with it, so programmers write code without concerning with
memory.
• Efficiency of programs is determined by number of instruction executed and how long each
instruction executed.
• Time taken to executes instruction vary different from part of memory from nano to
milliseconds.
• We can build small and fast storage or large and slow storage. but not both large and fast.
• All modern computers arrange their storage as memory hierarchy, it consists of series of
storage elements, with smaller faster are closer to processor and larger slower are far
away.
• Processor has small number of registers who content under control of software, next one
or more levels of cache (static RAM) sizes from kilobytes to several megabytes in size.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 14


III BTECH II-SEM, CSE: COMPILER DESIGN
• Next level of hierarchy is physical memory from hundreds of megabytes or gigabytes of
dynamic RAM.
• Next level is virtual memory, which is implemented by gigabytes of disks.
• Here machine first looks for data in closet level, if data is not there then looks for next level
and soon.
• Data is transferred as blocks of contiguous storage. large blocks used in lower level of
hierarchy
1. Between main memory and cache, data transferred as cache lines, which are 32 to 256
bytes long.
2. Between virtual memory and main memory, data transferred as pages, which are 4k to
64kbytes long.

Locality in programs:
• Most programs exhibit high degree of locality, they spend most of time executing small
fraction of code and only data.
• Program has temporal locality if memory locations it accesses are likely to be accessed
again with in short time.
• Program has spatial locality if memory locations close to location accessed are likely
accessed with in short time
• most programs spend 90% of their executing is 10% of code because
1. Program contains many instructions that never execute programs built with
components and libraries use only small fraction of provided functionality.
2. Only small fraction of code invoked is actually executed in typical run of program.
3. A typical program spends most of its time executing innermost loops are recursive
cycles in program.
• In modern computers locality, placing most common instructions and data in fast but small
storage by the lower average memory access time of program.
• If we know which instructions are executed heavily fastest caches is not enough then
adjusts storage dynamically.

Optimization using memory hierarchy:


• Policy of keeping most recently used instructions in cache tends to work well.
• When new instruction is executed, there is high probability of executing next instruction.
• Effective technique to improve spatial locality of instruction is complier place basic blocks
in same page.
• We can improve temporal and spatial locality of data access in program by changing data
layout or order of computation.
GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 15
III BTECH II-SEM, CSE: COMPILER DESIGN
• It is better to bring some data from disk to main memory to perform computation well.
• Same concept can be applied to data in physical memory, in caches and registers.

Reducing fragmentation:
• At beginning of program, heap is contiguous unit of free space. at program allocation and
de allocation of memory, this space is broken into free and used chunks of memory.
• We refer to free chunks of memory as holes .At each allocation request, memory manager
place requested chunk memory into large enough hole.
• Unless a hole of exactly right size is found, we need to split some hole for creating small
hole.
• At each de allocation request, freed chunks are added back to pool of free space. we
coalesce contiguous holes into larger , as hole can be smaller.
• If we are not careful, free memory is fragmented to large number of small holes, because of
this, no hole is large enough to satisfy request even though space is there.

Best fit and next fit object placement:


• We reduce fragmentation by controlling how memory manager places new objects in heep.
• Good strategy for minimizing fragments for programs is to allocate request memory in
smallest available hole that is large enough.
• This best fit algorithm tends to spare large holes to satisfy subsequent large results.
• Alternative method called first fit, where objects is placed in first(lowest address)hole in
which it fits .It takes less time to place objects, but provide less performance than best fit.
• To implement best fit placement efficiently, we create separate free space chunks into bins,
according to this sizes.
• Example lea memory manager used in GNU C compiler GCC. Align all chunks 8 byte
boundaries there is bins for multiplies of 8 byte chunks from 16 bytes to 512 bytes.
• There is chunk of free space is extended by requesting more pages from OS , called
wilderness chunk and it is larger sized bin because of extensibility.
• Binning makes it easy to find best fit chunks.
1. if small sizes request from lea manager there is bin for chunks of that size only we may
take any chunk from bin.
2. For sizes that do not have private bin, we find one bin that allowed to include chunks of
desired size bin can use first fit ,If fit is not exact remainder of chunk is placed in bin
with smaller size.
3. If target bin is empty, or all chunk in that bin are too small then repeat process with
bin for next large sizes or we reach wilderness chunk.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 16


III BTECH II-SEM, CSE: COMPILER DESIGN
• Best fit placement tends to improve space utilization but it is not best in terms of spatial
locality.
• Chunks allocated at same time by program should place close together to improve spatial
locality.
• One useful adaption of best fit algorithm is to modify placement when chunk of exact
requested size can’t found.
• In the above case, we use next fit strategy, trying to allocate object in chunk that has last
been split. When enough space for new object remains in chunk.
• Next fit also tends to improve the speed of allocation operation.

Managing and coalescing free space:


• When object is deallocated manually, memory manager make its chunk free and allocated
again it to other.
• It may also possible to combine(coalesce) that chunk with adjacent chunks of heap to form
larger chunk.
• If we keep bin for chunks of one fixed size. We can’t coalesce adjacent blocks of that size
because it double the size.
• Simple allocation and deallocations scheme is to keep bit map, with one bit for each chunk
in bin 1 indicates chunk is occupied and 0 indicates it is free.
• When chunk is deallocated , we change it 1 to 0 and when chunk is allocated we change it 0
bit to 1.
• Without binning, there are 2 data structures that are useful to support coalescing of
adjacent free blocks.
Boundary tag:
• At both low and high ends of each chunk, whether free or allocated, we keep vital
information.
• At both ends, we keep free/used bit that indicates block is allocated or free.
• Adjacent to each free/used bit is count of number of bytes in chunk.

Doubly Linked, Embedded Free list:


• Free chunks are also in doubly linked list pointers for list is within blocks themselves
say adjacent to boundary tags at either end.
• Contiguous free space should be combine to single list these chunks maintain 2
boundaries tags and 2 pointers even object is single byte.
• Order of chunks on free list is left unspecified. Example, list could be sorted by size ,
thus facilitating best fit placement.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 17


III BTECH II-SEM, CSE: COMPILER DESIGN
Example: below fig shows part of heap with three adjacent chunks A, B, C with size with bit
map and pointers shows doubly linked one to other.

Manual Deallocation Request:


• Ideally, any storage that will no longer be accessed should be deleted in manual memory
management programmer explicitly arrange deallocation of data.
• In addition to considering difficulties with manual deallocation, we describe some
techniques, these help to programmers.

Major problems with manual Deallocation:


• Common mistakes with manual deallocation are 2 forms:
1. Failing ever to delete data that can’t be referred is called memory leak error.
2. Referencing deleted data is dangling pointer dereference error.
• It hard for programmer to tell program never referred to scan storage in future .so, first
common mistake is not deleting storage that never referred.
• Because of this memory leak execution of program is slowed down due to increase
memory use.
• Automatic garbage collection handles memory leaks by deallocating all garbage. In some
case, programmers remove references to objects that never referred, so objects can
deallocated automatically.
• Second mistake is lead to delete some storage and then try to refer to data in deallocation
storage pointer to deallocated storage is called dangling pointers.
• Once freed storage has been reallocated to new variable any read, write or deallocation
that follows pointer and tries to use objects it points to as dereferencing the pointers.
• Reading and writing through dangling pointers can create conflicts between new and old
variables.
• Unlike memory leaks, dereferencing dangling pointers after fixed storage is reallocated is
always creates program error.
• Common examples of program errors are dereferences null pointers and accessing are out
of bounds array element .it is better to detect such errors than to corrupt the results of
program.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 18


III BTECH II-SEM, CSE: COMPILER DESIGN
Programming conventions and tools:
• We present some popular conventions and tools, those help programmers to handling
memory management.
1. Object ownership:
• It is useful when objects lifetime can statically reasoned owner is pointer to object.
Owner is responsible for either deleting object or passing object to another owner.
• It is possible to have other non owning pointers to same objects , these are over
written any time and attempt to delete object not possible.
• It eliminates memory leaks but it doesn’t solve dangling pointer reference
problem
2. Reference Counting:
• It is useful when object’s lifetime needs to be determined dynamically. When
reference to object created, use increment the reference count.
• When object is deleted based on count reaches to zero.
• It will eliminate all dangling pointer references, since there is no outstanding
reference to any deleted object.
• It is expensive because it improves overhead on every operation that stores
pointer.
3. Region-based allocation:
• It is useful for collections of objects whose life-times are tied to specific phases in
computations.
• Objects are created to be used in some step of computations we allocate all such
objects in some region and it should be deleted after computation step computed.
• It has limited applicability .it is very efficient whenever it can be used. It
deallocated all objects in same region at once.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 19


III BTECH II-SEM, CSE: COMPILER DESIGN

UNIT- IV
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.

Source Front end Intermediate Code Intermediate Code Target


Program code optimizer code Generator program

Symbol
Table

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.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 20


III BTECH II-SEM, CSE: COMPILER DESIGN
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.
• Labels in three-address statements have to be converted to addresses of instructions.
For example,
j: goto i generates jump instruction as follows :
➢ 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:

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 21


III BTECH II-SEM, CSE: COMPILER 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 :
➢ Register allocation– the set of variables that will reside in registers at a point in
the program is selected.
➢ Register assignment– the specific register that a variable will reside in is picked.
• 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.

Target Machine:
• Familiarity with the target machine and its instruction set is a prerequisite for
designing a good code generator.
• The target computer is a byte-addressable machine with 4 bytes to a word.
• It has n general-purpose registers, R0, R1, . . . , Rn-1.
• It has two-address instructions of the form:
op source, destination
Where, op is an op-code, and source and destination are data fields.
• It has the following op-codes :
MOV (move source to destination)
ADD (add source to destination)
SUB (subtract source from destination)
• The source and destination of an instruction are specified by combining registers and
memory locations with address modes.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 22


III BTECH II-SEM, CSE: COMPILER DESIGN

Address modes with their assembly-language forms


MODE FORM ADDRESS ADDED COST

Absolute M M 1

Register R R 0

Indexed c(R) c+contents(R) 1

indirect register *R contents(R) 0

contents(c+
indirect indexed *c(R) 1
contents(R))
Literal #c c 1

For example: MOV R0, M stores contents of Register R0 into memory location M;
MOV 4(R0), M stores the value contents (4+contents (R0)) into M.

Instruction costs:
• Instruction cost = 1+cost for source and destination address modes. This cost
corresponds to the length of the instruction.
• Address modes involving registers have cost zero.
• Address modes involving memory location or literal have cost one.
• Instruction length should be minimized if space is important. Doing so also minimizes
the time taken to fetch and perform the instruction.
For example: MOV R0, R1 copies the contents of register R0 into R1. It has cost one,
since it occupies only one word of memory.
• The three-address statement a : = b + c can be implemented by many different
instruction sequences :
i) MOV b, R0
ADD c, R0 cost = 6
MOV R0, a
ii) MOV b, a
ADD c, a cost = 6
iii) Assuming R0, R1 and R2 contain the addresses of a, b, and c :
MOV *R1, *R0
ADD *R2, *R0 cost = 2
• In order to generate good code for target machine, we must utilize its addressing
capabilities efficiently.
GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 23
III BTECH II-SEM, CSE: COMPILER DESIGN

Basic Blocks and Flow Graphs:


Basic Blocks
• A basic block is a sequence of consecutive statements in which flow of control enters
at the beginning and leaves at the end without any halt or possibility of branching except at
the end.
• The following sequence of three-address statements forms a basic block:
t1 : = a * a
t2 : = a * b
t3 : = 2 * t 2
t4 : = t1 + t3
t5 : = b * b
t6 : = t4 + t5

Basic Block Construction:


Algorithm: Partition into basic blocks
Input: A sequence of three-address statements
Output: A list of basic blocks with each three-address statement in exactly one block
Method:
1. We first determine the set of leaders, the first statements of basic blocks. The rules we
use are of the following:
a. The first statement is a leader.
b. Any statement that is the target of a conditional or unconditional goto is a leader.
c. Any statement that immediately follows a goto or conditional goto statement is a
leader.
2. For each leader, its basic block consists of the leader and all statements up to but not
including the next leader or the end of the program.

• Consider the following source code for dot product of two vectors a and b of length 20
begin
prod :=0; i:=1;
do begin
prod :=prod+ a[i] * b[i]; i :=i+1;
end
while i <= 20
end

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 24


III BTECH II-SEM, CSE: COMPILER DESIGN
• The three-address code for the above source program is given as :
(1) prod := 0
(2) i := 1
(3) t1 := 4* i
(4) t2 := a[t1] /*compute a[i] */
(5) t3 := 4* i
(6) t4 := b[t3] /*compute b[i] */
(7) t5 := t2*t4
(8) t6 := prod+t5
(9) prod := t6
(10) t7 := i+1
(11) i := t7
(12) if i<=20 goto (3)
(3)

Basic block 1: Statement (1) to (2)


Basic block 2: Statement (3) to (12)

Optimization of Basic Blocks:


• A number of transformations can be applied to a basic block without changing the set of
expressions computed by the block. Two important classes of transformation are :
1. Structure-preserving transformations
2. Algebraic transformations
1. Structure preserving transformations:
a) Common sub expression elimination:
a:=b+c a:=b+c
b:=a–d b:=a-d
c:=b+c c:=b+c
d:=a–d d:=b
Since the second and fourth expressions compute the same expression, the basic
block can be transformed as above.
b) Dead-code elimination:
Suppose x is dead, that is, never subsequently used, at the point where the statement
x : = y + z appears in a basic block. Then this statement may be safely removed
without changing the value of the basic block.
c) Renaming temporary variables:
A statement t: = b + c (t is a temporary) can be changed to u : = b + c(u is a new
temporary) and all uses of this instance of t can be changed to u without changing the

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 25


III BTECH II-SEM, CSE: COMPILER DESIGN
value of the basic block.
Such a block is called a normal-form block.
d) Interchange of statements:
Suppose a block has the following two adjacent statements:
t1: = b + c
t2: = x + y
We can interchange the two statements without affecting the value of the block if and
only if neither x nor y is t1 and neither b nor c is t2.

2. Algebraic transformations:
Algebraic transformations can be used to change the set of expressions computed by a
basic block into an algebraically equivalent set. Examples:
i) x: = x + 0 or x : = x * 1 can be eliminated from a basic block without changing the set of
expressions it computes.
ii) The exponential statement x: = y * * 2 can be replaced by x: = y * y.

Flow Graphs:
• Flow graph is a directed graph containing the flow-of-control information for the set of
basic blocks making up a program.
• The nodes of the flow graph are basic blocks. It has a distinguished initial node.
• E.g.: Flow graph for the vector dot product is given as follows:
B1
prod : = 0
i:=1

t1 : = 4 * i
t2 : = a [ t1 ]
t3 : = 4 * i B2
t4 : = b [ t3 ]
t5 : = t2 * t4

t6 : = prod + t5
prod : = t6

t7 : = i + 1

i : = t7

if i <= 20 goto B2

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 26


III BTECH II-SEM, CSE: COMPILER DESIGN
• B1 is the initial node. B2 immediately follows B1, so there is an edge from B1 to B2. The
target of jump from last statement of B1 is the first statement B2, so there is an edge from
B1 (last statement) to B2 (first statement).
• B1 is the predecessor of B2, and B2 is a successor of B1.

Loops:
• A loop is a collection of nodes in a flow graph such that
1. All nodes in the collection are strongly connected.
2. The collection of nodes has a unique entry.
• A loop that contains no other loops is called an inner loop.

Next-Use Information:
• If the name in a register is no longer needed, then we remove the name from the register
and the register can be used to store some other names
Input: Basic block B of three-address statements
Output: At each statement i: x= y op z, we attach to i the liveliness and next-uses of x, y and z.
Method: We start at the last statement of B and scan backwards.
1. Attach to statement i the information currently found in the symbol table regarding
the next-use and liveliness of x, y and z.
2. In the symbol table, set x to “not live” and “no next use”.
3. In the symbol table, set y and z to “live”, and next-uses of y and z to i.

Symbol Table:

Names Liveliness Next-use


X not live no next-use
Y Live i
Z Live i

A Simple Code Generator:


• A code generator generates target code for a sequence of three- address statements and
effectively uses registers to store operands of the statements.
• For example: consider the three-address statement a := b+c
It can have the following sequence of codes:
ADD Rj, Ri Cost = 1 // if Ri contains b and Rj contains c
(or)

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 27


III BTECH II-SEM, CSE: COMPILER DESIGN
ADD c, Ri Cost = 2 // if c is in a memory location
(or)
MOV c, Rj Cost = 3 // move c from memory to Rj and add
ADD Rj, Ri

Register and Address Descriptors:


• A register descriptor is used to keep track of what is currently in each registers. The
register descriptors show that initially all the registers are empty.
• An address descriptor stores the location where the current value of the name can be
found at run time.

A code-generation algorithm:
• The algorithm takes as input a sequence of three-address statements constituting a basic block.
For each three-address statement of the form x : = y op z, perform the following actions:
1. Invoke a function getreg() to determine the location L where the result of the
computation y op z should be stored.
2. Consult the address descriptor for y to determine y’, the current location of y. Prefer the
register for y’ if the value of y is currently both in memory and a register. If the value of
y is not already in L, generate the instruction MOV y’, L to place a copy of y in L.
3. Generate the instruction OP z’ , L where z’ is a current location of z. Prefer a register to a
memory location if z is in both. Update the address descriptor of x to indicate that x is in
location L. If x is in L, update its descriptor and remove x from all other descriptors.
4. If the current values of y or z have no next uses, are not live on exit from the block, and
are in registers, alter the register descriptor to indicate that, after execution of x : = y op
z , those registers will no longer contain y or z.

Generating Code for Assignment Statements:


• The assignment d : = (a-b) + (a-c) + (a-c) might be translated into the following three-
address code sequence:
t:=a–b
u:=a–c
v:=t+u
d:=v+u
with d live at the end.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 28


III BTECH II-SEM, CSE: COMPILER DESIGN
Code sequence for the example is:
Statements Code Generated Register descriptor Address descriptor
Register empty
MOV a, R0
t:=a-b R0 contains t t in R0
SUB b, R0

MOV a , R1
u:=a-c R0 contains t R1 contains u t in R0 u in R1
SUB c , R1

v:=t+u ADD R1, R0 R0 contains v R1 contains u u in R1 v in R0

ADD R1, R0 d in R0
d:=v+u R0 contains d
MOV R0, d d in R0 and memory

Generating Code for Indexed Assignments


• The table shows the code sequences generated for the indexed assignment statements
a : = b [ i ] and a [ i ] : = b
Statements Code Generated Cost

a : = b[i] MOV b(Ri), R 2

a[i] : = b MOV b, a(Ri) 3

Generating Code for Pointer Assignments


• The table shows the code sequences generated for the pointer assignments
a : = *p and *p : = a
Statements Code Generated Cost
a : = *p MOV *Rp, a 2
*p : = a MOV a, *Rp 2

Generating Code for Conditional Statement


Statement Code
if x < y goto z CMP x, y
CJ < z (jump to z if code is negative)

x : = y +z MOV y, R0
if x < 0 goto z ADD z, R0
MOV R0,x
CJ < z

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 29


III BTECH II-SEM, CSE: COMPILER DESIGN
Peephole Optimization:
• Peephole optimization is small moving window on target code.
• Peephole optimization works by finding peepholes(short sequence of target
instructions)and replace them by shorter or faster sequence of instructions.
• Advantage of peephole optimization is that each improvement applied increases
opportunities and shows additional improvements.
• Peephole optimization transformations are:
1. Elimination of redundant instructions
2. Dead Code elimination
3. Flow control optimization(Unnecessary jumps in code is eliminated)
4. Algebraic Simplification
5. Reduction in Strength
6. Use machine Idioms

1. Elimination of Redundant instructions:


• Code generation algorithm can produce code for a = b + c, d = a + e as given below:
MOV b, R0
ADD c, R0
MOV R0, a
MOV a, R0
ADD c, R0
MOV R0, d
• After elimination of redundant instructions:
MOV b, R0
ADD c, R0
ADD e, R0
MOV R0, d
• Here the instructions 3 and 4 are redundant load and store instructions. These
instructions will not affect values after their execution. So we removed it.

2. Dead Code Elimination:


• Removal of an unreachable code is an opportunity for peephole optimization.
Example:
void add_item(int x)
{
return x + 10;
printf("value of x is %d", x);
}
• Here printf statement is not executed for that reason it would be removed.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 30


III BTECH II-SEM, CSE: COMPILER DESIGN
3. Flow of Control Optimization:
• In a program control jumps back and forth without performing any significant task.
These jumps can be removed.
Example:
MOV R1, R2
GOTO L1
............
L1: GOTO L2
L2: INC R1
• After eliminating unnecessary jumps:
............
MOV R1, R2
GOTO L2
............
L2: INC R2

4. Algebraic Simplification:
• Few algebraic identifiers that occur frequently enough and are worth considering like
x=x+0
x=x*1
• They do not alter value of x. If we keep them as it is, it produces 6 statements that are
no use in 3 address code. So we removed these statements.

5. Reduction in Strength:
• Optimization deals with replacing expensive operations by cheaper one. example is
given below:
1. x2 ⇒ x * x
2. Division by constant ⇒ multiplication by constants

6. Use of Machine Idioms:


• It is better to make use of rich instructions set supported by target machine instead
blindly applying code generation algorithms. This will improve code.
• Example: x = x + 1, if we apply code generation algorithm mentioned in elimination
of redundant instruction. We use hardware instructions like auto increments and
auto decrements, example: INR x.

GEETHANJALI INSTITUTE OF SCIENCE AND TECHNOLOGY, NELLORE Y.v.R 31

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy