• No se han encontrado resultados

1.2. Fundamentos y conceptos teóricos

1.2.1. Variable independiente: Estrategias Organizacionales

1.2.1.2. Plan estratégico

As we have already noted in Section 2.5.2, all declared values in languages such as Pascal and SIMULA have restricted lifetimes. Further, the environments in these languages are nested: The extent of all objects belonging to the contour of a block or procedure ends before that of objects from the dynamically enclosing contour. Thus we can use a stack discipline to manage these objects: Upon procedure call or block entry, the activation record containing storage for the local objects of the procedure or block is pushed onto the stack. At block end, procedure return or a jump out of these constructs the activation record is popped o of the stack. (The entire activation record is stacked, we do not deal with single objects individually!)

An object of automatic extent occupies storage in the activation record of the syntactic construct with which it is associated. The position of the object is characterized by the base address,

b

, of the activation record and the relative location oset),

R

, of its storage within the activation record.

R

must be known at compile time but

b

cannot be known (otherwise we would have static storage allocation). To access the object,

b

must be determined at run time and placed in a register.

R

is then either added to the register and the result used as an indirect address, or

R

appears as the constant in a direct access function of the form `register+constant'.

Every object of automatic extent must be decomposable into two parts, one of which has a size that can be determined statically. (The second part may be empty.) Storage for the static parts is allocated by the compiler, and makes up the static portion of the activation record. (This part is often called the rst order storage of the activation record.) When a block or procedure is activated, the static part of its activation record is pushed onto the stack. If the activation record contains objects whose sizes must be determined at run time, this determination is carried out and the activation record extended. The extension, which may vary in size from activation to activation, is often called the second order storage of the activation record. Storage within the extension is always accessed indirectly via information held in the static part; in fact, the static part of an object may consist solely of a pointer to the dynamic part.

An array with dynamic bounds is an example of an object that has both static and dynamic parts. In most languages, the number of dimensions of an array is xed, so the size of the array descriptor is known at compile time. Storage for the descriptor is allocated by the compiler in the static part of the activation record. On encountering the declaration during execution, the bounds are evaluated and the amount of storage needed for the array elements is determined. The activation record is extended by this amount and the array descriptor is initialized appropriately. All accesses to elements of the array are carried out via the array descriptor.

We have already noted that at compile time we do not know the base address of an activation record; we know only the range to which it belongs. From this we must determine the base address, even in the case where recursion leads to a number of activation records belonging to the same range. The range itself can be specied by its block nesting depth,

bnd

, dened according to the following rules based on the static structure of the program:

The main program has

bnd

= 1.

A range is given

bnd

=

t

+1 if and only if the immediately enclosing range has

bnd

=

t

.

Bnd

=

t

indicates that during execution of the range the state consists of a total of

t

3.3 Storage Management 57 If, as in all ALGOL-like languages, the scopes of identiers are statically nested then at every point in the execution history of a program there is at most one activation record accessible at a given nesting depth. The base address of a particular activation record can then be found by noting the corresponding nesting depth at compile time and setting up a mapping

s

:

nestingdepth

!

baseaddress

during execution. The position of an object in the xed part of the activation record is fully specied by the pair (

bnd;R

); we shall therefore speak of `the object (

bnd;R

)'.

The mapping

s

changes upon range entry and exit, procedure call and return, and jumps out of blocks or procedures. Updating

s

is thus one of the tasks (along with stack pointer updating and parameter or result transmission) of the state-altering operations that we met in Section 2.5.2. We shall describe them semi-formally below, assuming that the stack is described by:

k :

array

[0 .. upper limit ]

of

storage cell;k top : 0 .. upper limit;

We assume further that a storage cell can hold exactly one address, and we shall treat address variables as integer variables with which we can index

k

.

The contour nesting and pointer to dynamic predecessor required by the contour model are represented by address values stored in each activation record. Together with the re- turn address, and possibly additional information depending upon the implementation, they constitute the `administrative overhead' of the activation record. A typical activation record layout is shown in Figure 3.11; the corresponding state change operations are given in Figure 3.12. We have omitted range entry/exit operations. As noted in Section 2.5.2, procedures and blocks can be treated identically by regarding a block as a parameterless procedure called `on the spot', or contours corresponding to blocks can be eliminated and objects lying upon them can be placed on the contour of the enclosing procedure. If blocks are to be given separate activation records, the block entry/exit operations are identical to those for procedures except that no return address is saved on entry and

ip

is not set on exit. Jumps out of blocks are treated exactly as shown in Figure 3.12c in any case.

Second-order storage

2 Return Address

1 Pointer to Dynamic Predecessor First-order storage 0 Pointer to Static Predecessor

Figure 3.11: Typical Activation Record Layout

The procedure and jump addresses indicated by the comments in Figures 3.12a and c are supplied by the compiler; the environment pointers must be determined at run time. If a procedure is invoked directly, by stating its identier, then it must lie within the current environment and its static predecessor can be obtained from the stack by following the chain of static predecessors until the proper block nesting depth is reached:

environment := ep ;

for

i := bndcaller

downto

bndprocedure

do

environment := k [environment ];

The value (bndcaller - bndprocedure) compile time and is usually small, so the loop is

sometimes `unrolled' to a xed sequence of environment := k[environment] operations.

k

[

k top

] := (* static predecessor of the procedure *);

k

[

k top

+ 1] :=

ep

; (* dynamic predecessor *)

k

[

k top

+ 2] :=

ip

; (* return address *)

ep

:=

k top

; (* current environment *)

k top

:=

k top

+ "

size

"; (* rst free location *)

ip

:= (* procedure code address *) a) Procedure entry

k top

:=

ep

;

ep

:=

k

[

k top

+ 1]; (* back to the dynamic predecessor *)

ip

:=

k

[

k top

+ 2];

b) Procedure exit

k top

:=

ep

;

ep

:= (* target environment of the jump *);

while

k

[

k top

+ 1]6=

epdo

k top

:=

k

[

k top

+ 1]; (* leave all intermediate environments *)

ip

:= (* target address of the jump *);

c) Jump out of a procedure

Figure 3.12: Environment Change Operations

When a procedure is passed as a parameter and then the parameter is called, the static predecessor cannot be obtained from the stack because the called procedure may not be in the environment of the caller. (Figures 2.3 and 2.5 illustrate this problem.) Thus a procedure parameter must be represented by a pair of addresses: the procedure entry point and the activation record address for the environment statically enclosing the procedure declaration. This pair is called a closure . When a procedure parameter is invoked, the address of the static predecessor is obtained from the closure that represents the parameter. Figure 3.13 shows the stack representing the contours of Figure 2.5; note the closures appearing in the activation records for procedure p.

Jumps out of a procedure also involve changing the state (Figure 3.12c). The mechanism is essentially the same as that discussed above: If the label is referenced directly then it lies in the current environment and its environment pointer can be obtained from the stack. A label variable or label parameter, however, must be represented by a closure and the environment pointer obtained from that closure.

Access to any object in the environment potentially involves a search down the chain of static predecessors for the pointer to the activation record containing that object. In order to avoid the multiple memory accesses required, a copy of the addresses can be kept in an array, called a display, indexed by the block nesting depth. Access to the object (

bnd;R

) is therefore provided by

display

[

bnd

]+

R

; we need only a single memory access, loading

display

[

bnd

] into a base register, to set up the access function.

The Burroughs 6000/7000 series computers have a 32-register display built into the hard- ware. This limits the maximum block nesting depth to 32, which is no limitation in practice. Even a restriction to 16 is usually no problem, but 8 is annoying. Thus the implementation of a display within the register set of a multiple-register machine is generally not possible, because it leads to unnatural restrictions on the block nesting depth. The display can be

3.3 Storage Management 59 22

location after 1 :

f

12 Activation record for procedure

q

19 5

i

= 0 11 (reference to

i

) 5 (

q

's environment)

entry point address for

q

Activation record for procedure

p

location after

p

(

q;i

) 5

12 0

i

= 2 4 (reference to

k

) 0 (empty's environment)

entry point address for empty Activation record for procedure

p

location after

p

(

empty;k

) 0

5 0

k

= 0

n

= 7

0 Activation record for procedure

outer

0 0 0 Note:

k top

= 22

ep

= 19

ip

= address of label 2

Figure 3.13: Stack Conguration Corresponding to Figure 2.5

allocated to a xed memory location, or we might keep only a partial display (made up of the addresses of the most-frequently accessed activation records) in registers. Which activation record addresses should be kept is, of course, program-dependent. The current activation record address and that of the outermost activation record are good choices in Pascal; the latter should probably be replaced with that of the current module in an implementation of any language providing modules.

If any sort of display, partial or complete, is used then it must be kept up to date as the state changes. Figure 3.14 shows a general procedure for bringing the display into synchronism with the static chain. It will alter only those elements that need alteration, halting when the remainder is guaranteed to be correct. In many cases the test for termination takes more time than it saves, however, and a more appropriate strategy may be simply to reload the entire display from the static chain.

Note that the full generality of

update display

is needed only when returning from a pro- cedure or invoking a procedure whose identity is unknown. If a procedure at level

bndnew

in the current addressing environment is invoked, the single assignment

display

[

bndnew

] :=

a

suces. (Here

a

is the address of the new activation record.) Display manipulation can become a signicant overhead for short procedures operating at large nesting depths. Recog- nition of special cases in which this manipulation can be avoided or reduced is therefore an

important part of the optimization of such procedures.

procedure

update_display (bndnew , bndold : integer ; a : address) : (* Make the display consistent with the static chain

On entry -

bndnew = nesting depth of the new activation record a = address of the new activation record

bndold = nesting depth of the current activation record On exit -

The display specifies the environment of the new contour *)

var

i : integer ; h : address ;

begin

(* update_display *) i := bndnew ; h := a ;

while

display[i] 6= h

or

i < bndold

do

begin

display[i] := h ;

i := i - 1; h := k[h] ;

end

end

; (* update_display *)

Figure 3.14: Setting the Display

In SIMULA and Ada, as in all languages that contain coroutines and concurrently- executing tasks, activation record creation and destruction need not follow a strict stack discipline. Each coroutine or task corresponds to a set of activation records, and these sets are growing and shrinking independently. Thus each coroutine or task requires an indepen- dent stack, and these stacks themselves follow a stack discipline. The result is called a tree or cactus stack and is most easily implemented in a segmented virtual memory. Implementation in a linear memory is possible by xing the sizes of the component stacks, but this can only be done when limitations can be placed upon recursion depth and spawning of further tasks.

Documento similar