Subprogram control

The Copy-Rule

Copy-rule: the effect of subprogram call is same as if code were copied into calling routine. But this is not general:

  1. no recursion
  2. no implicit calls
  3. no co-routines
  4. no scheduled calls - at a later point of time
  5. no multi-tasking - single thread of execution

Activation record created anew at each call - and destroyed at return.

Need an (CIP, CEP) (current instruction pointer, current environment pointer) pair

At call: activation record created, CEP points to it, CIP points to first instruction of subprogram.

At return: activaton record destroyed, CEP set to previous EP, CIP set to previous IP.

The (IP,EP) pair fix the environment under which you execute. But how do we resolve non-local references?

Static and Dynamic Scope

Scope: range of statements over which a variable is known

Static scope - Scope is dependent on the syntax of the program.

Dynamic scope - Scope is determined by the execution of the program.

Static nested scope - A variable is accessible in the procedure it is declared in, and all procedures internal to that procedure, except a new declaration of that variable name in an internal procedure will eliminate the new variable's scope from the scope of the outer variable.

A variable declared in a procedure is local in that procedure; otherwise it is global.

A review of static scope rules:

Q and T are declarations of procedures within P, so scope of names Q and T is same as scope of declaration a.
R and S are declarations of procedures in Q.
U is a declaration of a procedure in T.

Problem is: How to manage this execution stack?

Two pointers perform this function:
1. Dynamic link pointer points to activation record that called (invoked) the new activation record. It is used for returning from the procedure to the calling procedure.
2. Static link pointer points to the activation record that is global to the current activation record (i.e., points to the activation record of the procedure containing the declaration of this procedure).


 

Example

In R: C := B+A; C local, A and B global
For each variable, get pointer to proper activation record.
Assume AR is current activation record pointer (R).
1. B is one level back:
Follow AR.SL to get AR containing B.
Get R-value of B from fixed offset L-value
2. A is two levels back:
Follow (AR.SL).SL to get act. rec. containing A.
Add R-value of A from fixed offset L-value
3. C is local. AR points to correct activation record.
Store sum into L-value of C

Advantages and disadvantages of Dynamic binding

  1. Provides a great deal of program flexibility
  2. When a name is used, the declaration that applies to that name can not be determined by merely examining the program.
  3. Type may be different each time the routine is called. Thus, dynamic scoping clashes with static typing - type checking must be done at execution time.

    Every variable must have a descriptor.

    Descriptors must be of varying size (structures take more room to describe than primitive types).

    Often implemented by interpreter (slower anyway, so increased time to check types isn't a factor)

Languages are designed so that a particular binding may be performed at a given time - but it is up to the implementor.

Without static scoping, nothing about nonlocal names can be determined during translation.
Must be done at execution time (and redone each time statement is encountered)

Advantages and Disadvantages of Static Scope

  1. Aids readability
  2. Allows production of considerably more efficient executable code.
  3. Convenient access to globals
  4. Too much data access (no way to avoid knowing about enclosing scopes)

   begin 
   boolean b := true;
   procedure p;
   begin
      print (b)
   end;
   begin
      boolean b := false
      P
   end
end

What is printed?

  1. static: true
  2. dynamic: false

Parameters and Parameter Transmission

When subprogram is called with an actual-parameter expression, the expression is evaluated (usually) at the time of call
The data object that results becomes the actual parameter

Establishing the correspondence

  1. positional - good for small lists
  2. by explicit name
    Example: (Ada) SUB(Y=>ACT_Y, MAX=>100)
  3. May be default values (Ada, C++) if no parameter is supplied

Order of evaluation of parameters is often not specified by language definition. Is in some (left-to-right, or right-to-left)

Transmission types

  1. IN-OUT type parameters

  2. IN type only parameters
  3. Out only type parameters
    formal parameter is a local variable with no initial value
    copied back at termination of subprogram

    Pass by result

    Explicit function Values: may be considered an extra OUT parameter

    1. return(expr)
    2. value to be returned by assignment to function name

Unevaluated Parameters: Transmission by Name

Used in Algol: theoretical significance

New interest in functional languages - delayed evaluation

Substitute name (in calling environment) for formal parameter

The name location binding is delayed until (and established fresh each time) the formal parameter is encountered.

Implemented by passing parameterless subprograms (thunks) rather than variable name. An expression needs to be evaluated IN the proper environment. Don't have mechanism to do that other than thru procedure call.

Whenever formal parameter is referenced, a call is made to thunk, which evaluates the parameter in the proper (caller) environment and returns proper resulting value (or location)

Example:

procedure R(var i,j: integer);
begin
  var  m:integer;
  m := 5;
  i := i + 1;
  j := j + 1;
  write(i,j);
end;

m := 2;
for(i=0;i<10;i++) c[i]=10*i;
R(m,c[m]);

pass by reference: adds 1 to M and c[2]
Pass by name: adds 1 to m and c[3]

Example for Call by Name

b1: begin real x,y;
      procedure G(t): name t;
      begin integer w;
            w := 10;
            y := 20;
	    print t
	    x = 0
	    print t
      end G;
      y := 0.0;
  b2: begin real y;
        y := 0.5;
        x := 1.0;
        call G(y-x)
      end
end


thunk()
  return(y-x)
end;

If name parameter is on left hand side, thunk would have to return the address of the element.

Parameter Passing Techniques

Show what is printed assuming (in turn) each of the parameter passing techniques. If the parameter passing method is illegal for the example, indicate it.

  1. main()
    { integer a,c;
       procedure A(int x,int y);
       { integer a=y+7;
        c = 4; y=x-1; print (a,x,y,c)
       };
       a = 1;  c=3;
       call A(a,a); print(a,c);
       c = 5; call A(c,a); print(a,c);
    }
    1. call by value:
    2. call by reference:
    3. call by value-result:

  2. main()
    { // no globals
       procedure A(int x,int y);
       { integer a=3, c=0;
         x=y+7;
         c = x+y;  print (a,x,y,c)
       };
    
       procedure B ();
       { integer a,c;
         a = 1; c=3;
         call A(a,a); print(a,c);
         c = 5; call A(c,a+c); print(a,c);
       }
    
       call B();
    }
    1. call by name:

  3. main()
    { integer i, c, a[0..9] = {1,2,3,4,5,6,7,8,9,10};
    
       procedure D(int x,int y);
       { x = 3;
         c = i + i *i -5;
         print (x,y,c,i)
       };
    
       i := 1;
       call D(a[i],i++)
       print( i,c,a);
    }

    1. call by value:
    2. call by name:
    3. call by value-result;

Subprograms as parameters:

Corresponding formal parameter is of type subprogram name

Problems

If types of parameters are not required and separate compilation is possible, cannot do type checking.

As in passing labels as parameters, need an (ip, ep) (instruction pointer, environment pointer) pair

Are three choices of environment

  1. the environment of the subprogram to which it is passed (shallow binding)

    Not appropriate for block structures languages because of static binding of variables

  2. the environment of the subprogram which is passed (deep binding)
  3. the environment of the subprogram which passes the subprogram (used)

0 begin procedure P(R,b);
         value b,R; boolean b; procedure  R;
 
1    integer i;
2        procedure Q;
3        begin
4           i :=  i + 1;
5        end Q;
 
6    i := 0;
7    if b then P(Q,not b) else  R;
8    print (i);
9  end P;
 
10     P(P,true);
end

  1. We have two Q Parameters. One declared in the first call to P and one declared in the second call to P. These two Q's have the same IP, but different EPs.
  2. The value of the formal parameter R for the first call to P is an (ip,ep) pair corresponding to itself. The value of the formal parameter R for the second call to P is an (ip,ep) pair corresponding to the Q declared in the first call to P.

Statement Labels as Parameters

Can't simply pass address - need (cip,cep) pair

Need to update the dynamic chain of activation records.

Dynamic Scope

Creation of an implicit nonlocal environment via current dynamic chain

If no nesting of subprograms (APL, LISP, and SNOBOL4), there is no static structure to base scope rules.

Rule: use most recently created association for X
Trouble: changes between activations requiring dynamic type checking
Only used when dynamic type checking must exist for other reasons

Implementation Issues:
Local environment for each subprogram is made part of its activation record

At each reference, dynamic chain searched until association is found which is costly
Need to store identifiers themselves
No base plus offset calculation is possible

Alternative Central Referencing Environment table:
contains all currently active identifier associations

Figure 7.17

Saves following multiple pointers and searching for matching identifier names.

  1. Contains one value for each identifier, regardless of the number of different occurrences of id
  2. activation flag: indicates whether or not association is active
  3. Can use base plus offset into central table
  4. identifier name need not be present during run time, unless names can be generated during execution
  5. Subprogram entry and exit are more costly:

Static Scope and Block Structure

Search for variable is done during compilation - stack of symbol tables.

Store count of activation records in static chain.

Alternative: The Display Implementation

Avoids following multiple pointers during execution (even though number of pointers to follow is known at compiler time)

On subprogram entry: pointers to static chain of activations are copied into display

With display (chain #, offset)

  1. consider first entry as subscript into the display of activation record pointers
  2. compute base plus offset

 ______________
0|environment 0|
1|environment 1|
2|environment 2|
3|environment 3|
4|environment 4|
 ______________
Subscript is "how many pointers to follow". Array contains address of the activation record accessed by following the number of pointers indicated by the subscript.