The variables of the source languages are mapped into memory addresses.
x = x + 1;Translated code (in some assembly language). Assume that x is mapped into the memory location 1010 and that operations can only be performed on registers. Let R1 be a register.
LOAD 1010,R1 //copy content of location 1010 in R1 ADD R1,#1 //add constant 1 to R1 STO R1,1010 //copy content of R1 in location 1010One of the main issues is the association between variables and locations: it is in general impossible to know at compile time how many variables are going to be created at run time (because of recursive procedures containing local variables and because of dynamic variables). Hence we cannot solve all the allocations at compile time. Additionally, we want to be efficient in the management of the memory: We only want to allocate storage for a variable when needed, and we want to deallocate when possible.
We will call lifetime of a variable the period of time during execution in which the variable has storage allocated for it.
We want lifetime to correspond to the need for the variable - must be at least as long as the need. Typically, we compromise and choose standard times for allocation and deallocation:
In this kind of implementation, the memory of the machine is divided in three parts:
We will discuss here how the stack is handled. Let us consider the various operations that take place when a procedure is called:
Let us summarize the informations that must be present on an AR:
In languages with nested procedures, however, the situation is more complicated. Next section is dedicated to discussing this issue.
There are two possible scoping rules which determine the declaration which should be associated to the occurrence of a non-local variable x in p:
In this way, we can always find the addess of a non-local variable x: we just follow the chain of the static links until we "find" a declaration for x. (Actually, in real implementations the number of static links we need to traverse is determined statically, and the address for x in the AR is dertermined by a fixed offset.)
In dynamically-scoped languages we don't need a static link: the declaration valid for a variable x can be found by following the chain of the dynamic links.
procedure p p
procedure q / \
<body of q>; / \
procedure r q r
procedure s |
<body of s>; |
<body of r>; s
<body of p>;
We have that:
procedure p;
var x,y: integer; // variables x,y local to p
procedure q(y: integer); // procedure q local to p
begin if x = y then r else write(x) end; // body of q
procedure r; // procedure r local to p
var x : integer; // variable x local to r
begin x := 2; if y = 2 then q(x) else write(x) end; // body of r
begin // begin body of p
x:= 1;
y:= 2;
q(x)
end; // end body of p
We have that an activation of p prints:
caller of p p I A.R. where p is declared
^ ------------------ ^
CL |___|_ | |
| | |
------------------ |
| _|______| SL
| |
------------------
|-->| ----- |<-|---|----|
| | x | 1 | | | | |
| | ----- | | | |
| | y | 2 | | | | |
| | ----- | | | |
CL | ------------------ | | |
| | | |
| q I | | |
| ------------------ | | |
|___|_ | | | |
| | | | |
------------------ | | |
| _|__|SL | |
| | | |
------------------ | |
|-->| ----- | | |
| | y | 1 | | | |
| | ----- | | |
CL | ------------------ | |
| | |
| r I | |
| ------------------ | |
|___|_ | | |
| | | |
------------------ | |
| _|______| SL |
| | |
------------------ |
|-->| ----- | |
| | x | 2 | | |
| | ----- | |
CL | ------------------ |
| |
| Q II |
| ------------------ |
|___|_ | |
| | |
------------------ |
| _|___________| SL
| |
------------------
| ----- |
| y | 2 | |
| ----- |
------------------
CL = Control Link
SL = Static Link