Cs8602 Unit 4 Access To Nonlocal Data On The Stack
Cs8602 Unit 4 Access To Nonlocal Data On The Stack
It is very important to know that how procedures access their data, specially the mechanism for finding
data used within a procedure p but that does not belong to p. Access becomes more complicated in
languages where procedures can be declared inside other procedures. We therefore begin with the
simple case of C functions, and then introduce a language, ML, that permits both nested function
declarations and functions as "first-class objects;" that is, functions can take functions as arguments and
return functions as values. This capability can be supported by modifying the implementation of the run-
time stack.
Data Access without Nested Procedures: In the C family of languages, all variables are defined either
within a single function or outside any function ("globally"). Most importantly, it is impossible to declare
one procedure whose scope is entirely within another procedure. Rather, a global variable v has a scope
consisting of all the functions that follow the declaration of v, except where there is a local definition of
the identifier v.
Variables declared within a function have a scope consisting of that function only, or part of it, if the
function has nested blocks. For languages that do not allow nested procedure declarations, allocation of
storage for variables and access to those variables is simple:
1. Global variables are allocated static storage. The locations of these variables remain fixed and are
known at compile time. So to access any variable that is not local to the currently executing procedure,
we simply use the statically determined address.
2. Any other name must be local to the activation at the top of the stack. We may access these variables
through the topsp pointer of the stack.
An important benefit of static allocation for globals is that declared procedures may be passed as
parameters or returned as results (in C, a pointer to the function is passed), with no substantial change
in the data-access strategy. With the C static-scoping rule, and without nested procedures, any name
nonlocal to one procedure is nonlocal to all procedures, regardless of how they are activated. Similarly,
if a procedure is returned as a result, then any nonlocal name refers to the storage statically allocated
for it.
Issues with Nested Procedures: Access becomes far more complicated when a language allows
procedure declarations to be nested and also uses the normal static scoping rule; that is, a procedure
can access variables of the procedures whose declarations surround its own declaration, The reason is
that knowing at compile time that the declaration of p is immediately nested within q does not tell us
the relative positions of their activation records at run time. In fact, since either p or q or both may be
recursive, there may be several activation records of p and/or q on the stack.
Finding the declaration that applies to a nonlocal name x in a nested procedure p is a static decision; it
can be done by an extension of the static-scope rule for blocks. Suppose x is declared in the enclosing
procedure q. finding the relevant activation of q from an activation of p is a dynamic decision; it requires
additional run-time information about activations. One possible solution to this problem is to use
"access links”.
A Language with Nested Procedure Declarations: The C family of languages and many other familiar
languages do not support nested procedures, so we introduce one that does. The history of nested
procedures in languages is long. Algol 60, an ancestor of C, had this capability, as did its descendant
Pascal, a once-popular teaching language. Of the laterlanguages with nested procedures, one of the
most influential is ML, and it is this language whose syntax and semantics we shall borrow .ML is a
functional language, meaning that variables, once declared and initialized, are not changed. There are
only a few exceptions, such as the array, whose elements can be changed by special function calls.
Variables are defined, and have their unchangeable values initialized, by a statement of the form:
The definitions are normally v a l or fun statements. The scope of each such definition consists of all
following definitions, up to the in, and all the statements up to the end. Most importantly, function
definitions can be nested. For example, the body of a function p can contain a let-statement that
includes the definition of another (nested) function q. Similarly, q can have function definitions within its
own body, leading to arbitrarily deep nesting of functions.
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.
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
• The assignment d : = (a-b) + (a-c) + (a-c) might be translated into the following three-address code
sequence:
Code sequence for the example is:
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.
• 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.
• Labels in three-address statements have to be converted to addresses of instructions. For example,
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)
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 subproblems :
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.
• 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.
• In static allocation, the position of an activation record in memory is fixed at compile time.
• In stack allocation, a new activation record is pushed onto the stack for each
execution of a procedure. The record is popped when the activationends.
• The following three-address statements are associated with the run-time allocation
and de allocation of activation records:
1. Call,
2. Return,
3. Halt, and
4. Action, a placeholder for other statements.
• In this allocation scheme, the compilation data is bound to a fixed location in the memory and it
does not change when the program executes.
• As the memory requirement and storage locations are known in advance, runtime support
package for memory allocation and de-allocation is not required.
• In a static storage-allocation strategy, it is necessary to be able to decide at compile time exactly
where each data object will reside at run time. In order to make such a decision, at least two
criteria must be met:
1. The size of each object must be known at compile time.
2. Only one occurrence of each object is allowable at a given moment during program
execution.
• Because of the first criterion, variable-length strings are disallowed, since their length cannot be
established at compile time. Similarly dynamic arrays are disallowed, since their bounds are not
known at compile time and hence the size of the data object is unknown.
• Because of the second criterion, nested procedures are not possible in a static storage-allocation
scheme. This is the case because it is not known at compile time which or how many nested
procedures, and hence their local variables, will be active at execution time.
Dynamic Allocation
Stack Allocation
• Procedure calls and their activations are managed by means of stack memory allocation.
• It works in last-in-first-out (LIFO) method and this allocation strategy is very useful for recursive
procedure calls.
Static allocation can become stack allocation by using relative addresses for
storage in activation records. In stack allocation, the position of activation record is
stored in register so words in activation records can be accessed as offsets from the
value in this register.
Initialization of stack:
MOV #stackstart , SP /*
initializes stack */ Code for
the first procedure
HALT /* terminate execution */
Runtime environment manages runtime memory requirements for the following entities:
• Code: It is known as the text part of a program that does not change at runtime. Its memory
requirements are known at the compile time.
• Procedures: Their text part is static but they are called in a random manner. That is why, stack
storage is used to manage procedure calls and activations.
• Variables: Variables are known at the runtime only, unless they are global or constant. Heap
memory allocation scheme is used for managing allocation and de-allocation of memory for
variables in runtime.
Heap Allocation
• As shown in the image above, the text part of the code is allocated a fixed am unt of memory.
• Stack and heap memory are arranged at the extremes of total memory allocated to the
program. Both shrink and grow against each other.
STORAGE ORGANIZATION
An executable program generated by a compiler will have the following organization in memory
on a typical architecture (s
uch as on MIPS):
address when a function is called, but it is also used for allocating some of the local variables of
a function during the function call, as well as for some bookkeeping.
Activate Record
It is used to store the current record and the record is been stored in the stack.
It contains return value .After the execution the value is been return.
It can be called as return value.
Parameter
Local Data
The data that is been used inside the function is called as local address
Temporary Data
Links
Status