|
|
Ada Example |
||
Mode 1 |
value |
read only |
in |
Mode 2 |
value-result |
reference |
in out |
Mode 3 |
result |
out |
The activation records on the run time stack for these two look like the following as Mary's code body begins to execute: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;
__________________________ __________________________ | | | | | 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.
in
value
copy
read only
in out
var
reference
copy in copy out
out
write only
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 asa : array(1..10, 1..20) of Integer;
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 is array(1..10, 1..20) of Integer;a1, a2 : Table;
This allows b1 and b2 to be expressed as the same type (e.g., for passing as parameters) with different constraints on the indices.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);
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.var x : Integer; a1, a2 : Table;
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 must be generated thatX := a(i,j);
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.