Compiling for Different Parameter Modes

and

A Look at Arrays


Different Modes for Similar Tasks

Parameter modes have developed over time as new programming languages evolved to handle to issues: The main modes that we will discuss are listed below, with similar ones listed on the same line.

 

 

 

Ada Example

Mode 1

value

read only

in

Mode 2

value-result

reference

in out

Mode 3

result

 

out

Modes listed on the same row of the table produce similar results, but may require much different implementations in the compiler.

Example -- Value vs Read Only

Consider the following programs
program Fred;                         program Fred;
  var x : integer;                      var x : integer;
  procedure Mary(a: value integer);     procedure Mary(a: read only integer);
    var b: integer;                     var b: integer;
    
    begin --Mary                        begin --Mary
      . . .                               . . .
      b := a + 5;                         b := a + 5;
      a := b * 10;                        a := b * 10;
      . . .                               . . .
    end Mary;                           end Mary;
  begin -- Fred                       begin -- Fred
    . . .                               . . .
    x := 5;                             x := 5;
    Mary(x);                            Mary(x);
    . . .                               . . .
  end Fred;                           end Fred;
The activation records on the run time stack for these two look like the following as Mary's code body begins to execute:
   __________________________                __________________________
  |                          |              |                          |
  | Mary's local variable b  |              | Mary's local variable b  |
  |                          |              |                          |
  |__________________________|              |__________________________|
  |                          |              |                          |
  | Mary's formal parameter a|              | Mary's formal parameter a|
  |            5             |              |           400            |
  |__________________________|              |__________________________|
  |                          |              |                          |
  | Control info for Mary    |              | Control info for Mary    |
  |__________________________|<--D1         |__________________________|<--D1
  |                          |              |                          |
  | Fred's local variable x  |<--400        | Fred's local variable x  |<--400
  |            5             |actual        |            5             |actual
  |__________________________|address       |__________________________|address
  |                          |              |                          |
  | Control info for Fred    |              | Control info for Fred    |
  |__________________________|<--D0         |__________________________|<--D0

 

Generated code for the line b := a + 5; in these two cases would look like:
       push D1(12) -- a                          push @D1(12) -- a indirect
       push #5                                   push #5
       adds                                      adds
       pop  D0(16) -- b                          pop  D0(16)  -- b
For the next line, a := b * 10; we get
       push D0(16) -- b                          Compiler error:  attempt to
       push #10                                  write to read only variable
       muls                                      a.
       pop  D0(12) -- a

In both cases, the desired effect that the actual parameter value not be changed is maintained.  However, in the case of value parameters, a copy is made of the actual parameter, and the programmer can change the copy at will (the original doesn't change).  In the read only case, the philosophy is this:  if you are trying to change the parameter, maybe you meant to actually use the mode value-result, so we won't let you make changes to the actual parameter, and this will serve as a warning to you to check your code to see if you really implemented the parameters as you intended.

The common names for parameter modes are given below.  Some are identical to others.  Some have the same overall effect as others but must be implemented differently.  Look at them at try to determine how the compiler would need to be written to implement the various modes.


Compiling Arrays

Points of Interest

There are four points of interest when compiling arrays:

Representing Arrays in the Symbol Table

Consider the following definition of an array
a : array(1..10, 1..20) of Integer;
How would a along with all of its attributes be represented in a symbol table?  The structure for representing the attributes looks very complex, and it is.  However, its complexity can be managed through uniformity. Usually, such an array would be declared through a user defined type, such as
type Table is array(1..10, 1..20) of Integer;
a1, a2 : Table;
This allows the programmer to declare multiple variables of the same type.   The identifier Table is stored in the symbol table for this scope with all of the appropriate attributes, including that Table is a type name.  Then identifiers a1 and a2 can be entered into the symbol table with pointers to Table as their attributes. Things can get even more complex thant this.  For example, Ada allows unconstrained array types:
type Table_2 is array (Integer Range <>, Integer Range <>) of Real;
b1: Table_2(1..10, 1..20);
b2: Table_2(-5..5, -5..5);
This allows b1 and b2 to be expressed as the same type (e.g., for passing as parameters) with different constraints on the indices.  

Code for Generating an Activation Record Containing Arrays

Now suppose that we are generating code to build the activation record with definitions:
var 
  x      : Integer;
  a1, a2 : Table;
How is this done?  We calculate the sizes of x, a1, and a2 from the symbol table and add them to the running total of the amount of AR space needed for local variables.  

One complication is that for unconstrained arrays, the actual size of the arrays cannot be determined until run time, so code must be generated that leaves space for the address of such an array, and then code that computes the size of the array, leaves room on the run time stack for it, and places its starting address into the space left for its address on the stack.

Code Generation for Array References

Consider a statement as simple as
X := a(i,j);
Code must be generated that

Optimizations

Note that such an array is often accessed in a nested loop, the outer loop running i and the inner loop running j.  In this case, the range checking for i, and the computation of the expression i, only needs to be done in the outer loop.

Indexing Modes in Machine Language

Arrays, regardless of their dimensions, are stored as one-dimensional arrays internally, and code must be generated to access the correct point in the one dimensional internal array to get to the value intended by the indexes in the source code.  Virtually all machine languages have a special addressing mode, called "indexing mode," to go along with the other modes (direct addressing mode and indirect addressing mode).  This mode is used for accessing arrays.  A register contains the base address of the array and an indexing register contains the offset into the array (the index).  The index register is automatically added to the register containing the base address to compute the correct offset, or index, into the internal 1-D array.