Translating Procedures and Functions

Continued


In the last lecture we began tying all of the pieces of a simple compiler together by beginning to translate procedures and functions.  There were six points with which we were dealing.

Point 1 -- Branch Around Procedures:  Since code is generated in sequential order, there must be a branch inserted to get around the code for procedures and functions when the main program first starts executing.  It is assumed that program execution will start from the top when the translated program is actually run, so a jump instruction must be executed to force program execution to continue with the code that is the translation of the begin block for the main program.

Point 2 -- Procedure Declaration:  The point at which a procedure definition is first encountered requires symbol table calls to put the name of the procedure and its attributes into the symbol table and then to create a new symbol table on the top of the stack for the new procedure.  All of the new procedure's parameters and variables (along with their attributes) must be placed into this new table. 

Point 3 -- Activation Record Initialization Time. Consider the begin block of a procedure.  This is the entry point to the procedure, the code that is to start executing when this procedure is called.  At this point the compiler must  generate special IR related to starting a procedure running, such as dropping the label for the procedure and completing the setup of the activation record on the stack for this procedure (the initial part of the activation record setup for this procedure is done at the point of call).

Point 4 -- Procedure End:  When the end of a procedure is reached, actions must be taken to return to begin the process of removing the activation record from the stack for this procedure (the rest is done at the point of call) and returning to the point of call.

Point 5 -- Main Program Code Start:  The label generated in point 1 must be dropped here.  This is to ensure that a jump can be made from the start of the translated code around any intervening procedure and function code to this part of the program, which is to be where execution starts  The activation record for the main program must also be constructed at this point..

Point 6 -- Procedure Call:  A procedure call requires semantic actions that generate code to set up the calling sequence properly, to start the construction of the activation record for the called procedure, to put the actual parameters into the activation record for the called procedure, and then to make the actual jump to the procedure.

Point 7 -- Procedure Return:  Since code to place the actual parameters onto the run time stack is placed just before a call to a procedure, it makes sense that this part of the run time stack be handled by code that is inserted at the point of return from the call.  In some languages this code will take care of copying the values in the formal parameters back into the actual parameter locations as necessary (this won't be necessary in your project, because mPascal has only "copy" (non VAR) and "reference" (VAR) parameters.  The "copy" parameters don't need copied back into the actual parameters, because the actual parameters are not intended to change.  The "reference" parameters don't need copying, because the original actual parameter values were changed each time the corresponding formal parameter values were changed, because the address of the actual parameter was supplied rather than its value at call time.

We spent most of the time discussing symbol table issues (up through point 2).  We will now cover the rest of the points by way of an example.  To do so, we will quickly go through other points we have already covered. 

An Activation Record Model

In order to be able to translate a program, we need to have a model for an activation record in mind.  We have looked at activation records before, but now we must extend them to incorporate information about parameters and other stuff for managing procedure and function calls and returns.  Let's assume that every procedure and function, as well as the main program, has the following form for an activation record (note:  there are many different ways one could build an activation record; we give just one way):

 

 ____________________________
| local variable n           |
|____________________________|
|     . . .                  |
|____________________________|
| local variable 1           |
|____________________________| 
| caller's return address    |
|____________________________|
| actual-formal parameter m  |
|____________________________|
|     . . .                  |
|____________________________|
| actual-formal parameter 1  |
|____________________________|
| old display register value |
|____________________________|

In order to get this built up right, code has to be generated at both the point of call to a procedure and at the procedure heading.  The code at the point of call must build the stack up through the blue part, because only at that point is it known what the actual parameters are that are to be assigned to the formal parameters, and what the return value should be.  The code to reserve space in the activation record for the local variables must be generated in the procedure, because that is where the local variables are known.

We will see how this goes below.

A Sample Program

To try to make the presentation more fluid, we can look at the following program. 

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);
    end; -- Fred;
  procedure Mary(c : in integer; d: reference integer);
    var i : integer;
    begin -- Mary
      read(i);
      Fred(i);
      c := c + a;
      d := d + 1;
      write(c+d);
    end Mary;
  begin -- Main
    read(a,b);
    Mary(a,b);
  end. -- Main

To facilitate the discussion, we will show only the portion of the program compiled so far at each stage.

Stage 1

Compilation Point

program Main;

Symbol Table

After compiling this far, our compiler has constructed the start of a symbol table for Main that gives the name of the symbol table (Main) its nesting level (0), and the label to use for branching to the start of the translated code for the begin block for Main.

                  ________________ __
Top of stack --> | Main | 0 | L1  | -|-> // (next symbol table)        
                 |______|___|_____|__|

IR File

The compiler has also generated the code to jump to L1 where the translated part of the begin block will begin:

;program
jump L1

Stage 2

Compilation Point

program Main;
  var a, b : integer;

Symbol Table

Important Note: Up until now, we have always talked about our variables and parameters as if they were going to start at offset 0 from the start of the activation record on the run time stack when the program actually runs.  That was ok to think about initially, but in reality there are often other things that are placed on the run time stack before the start of the variables and parameters, such as the spot to save the old display register, a place to save the return address, and other control information.  For this discussion, we are going to assume that the first four bytes of each activation record for the main program or any procedure or function is reserved for holding a copy of the old display register.  Thus, we will start our variables and parameters at offset 8.  (In your project, all such offsets are 1).

                  ________________ __
Top of stack --> | Main | 0 | L1  | -|-> // (next symbol table)        
                 |______|___|_____|__|_______
                 | a    | Integer | 4 | VAR  |
                 |______|_________|___|______|
                 | b    | Integer | 8 | VAR  |
                 |______|_________|___|______|

IR File

No new code is generated.

;program
jump L1

Stage 3

Compilation Point

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);

Symbol Table

First, we remember that Main's symbol table must be modified to include all of the information necessary to compile a call to Fred.  Thus, the symbol table entry in Main for Fred includes the procedure name (Fred), the kind of the identifier (PROCedure), the label to jump to when code is generated to call Fred (L2), and a linked list of all of the parameter modes and types in this call.

                  ________________ __
Top of stack --> | Main | 0 | L1  | -|-> // (next symbol table)        
                 |______|___|_____|__|_______
                 | a    | Integer | 4 | VAR  |
                 |______|_________|___|______|
                 | b    | Integer | 8 | VAR  |
                 |______|_________|___|______|________    __________ __
                 | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
                 |______|_________|___|______|____|___|  |__|_______|__|

IR File

No new code is generated.

;program
jump L1

Stage 4

Compilation Point

We are still at the same point in the compilation, where the compiler not only needs to insert information about procedure Fred in Main's symbol table, but now also needs to build a new symbol table for Fred.

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);

Symbol Table

At this point, we need to include a new symbol table for Fred and put in it all of Fred's parameter names, types, offsets, and modes.

                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Fred | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | x    | Integer | 4 | Parm | in |
                 |______|_________|___|______|____|

 

IR File

At this point, we could now drop the label for procedure Fred into the IR File but if we did, we would need to have to also have a label and a jump to the begin block of this procedure.  The reason is that there might be some procedures or functions nested inside Fred, and if so, the code for these procedures and functions would be generated after this point.  So, we can just wait and drop the label for procedure Fred at the point where the begin block is encountered.  We choose, therefore, to generate no code here.

;program
jump L1

Stage 5

Compilation Point

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;

Symbol Table

At this point, we need to include the entry for variable t in Fred's symbol table  We are assuming (from our activation record model) that there will be a return address on the run time stack right after the parameters (that takes up four bytes) so we assume that variable t starts at 12 offset from D1.

                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Fred | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | x    | Integer | 4 | PARM | in |
                 |______|_________|___|______|____|
                 | t    | Integer | 12 | VAR  |    |
                 |______|_________|___|______|____|

 

IR File

No further cod is generated at this point.

;program
jump L1

Stage 6

Compilation Point

At this point we hit the begin block for Fred.  Here is where the label for Fred must be inserted, and code must be generated to be sure that Fred's activation record is properly completed on the run time stack when the translated code is actually run. 

Note:  From our activation record model, we assume that some of Fred's activation record is already set up by any call that is made to Fred.  In particular, we will assume that the space for the control information (space for saving the display register to be used in Fred) the parameters, and the return address are on the stack already, and that the SP is pointing to the first free stack space beyond these elements in the activation record for Fred.  That is, when we compile calls to Fred (which we will see later), each call is translated into code that partially builds up Fred's activation record by skipping space on the stack to hold the old display register, by pushing onto the stack any actual parameters to Fred, and by calling Fred, which automatically pushes the return address onto the stack.

This means that the code for Fred must leave space for the variables of Fred in the activation record.

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred

Symbol Table

There are no changes to the symbol table structure.

                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Fred | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | x    | Integer | 4 | PARM | in |
                 |______|_________|___|______|____|
                 | t    | Integer | 12| VAR  |    |
                 |______|_________|___|______|____|

 

IR File

No further cod is generated at this point.

;program
jump L1
;procedure Fred
label L2
;finish Fred's activation record (first part already done at call to Fred)
add SP,4,SP ;leave space in AR for Fred's variables (just t in this case)
mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
sub SP,16,D1 ;set D1 to point to the start of the activation record

Carefully note what is going on here with the last two instructions.  The code for the call to label L2 will be a result of translating some call to Fred (we will see an example later).  That call starts setting aside space for Fred's activation record by skipping four bytes to hold the old D0 (i.e., adding 4 to SP) and then pushing the actual parameters onto the stack (effectively increasing the SP by the size of all of the space needed for the parameters).  The last thing the call to Fred does is push the current value of the PC onto the stack (in Fred's activation record) to be used as the return address.  So, when Fred starts executing, the part of Fred's activation record that has already been constructed includes (from top to bottom):

If we assume that the return address takes up four bytes, this means that for procedure Fred, there are already 12 bytes reserved on the stack for the activation record for Fred at this time and that SP points to byte 12 beyond the start of Fred's activation record.  Adding 4 to the stack pointer thus leaves room in the activation record for Fred's variable t and leaves the stack pointer pointing to byte 16 beyond the start of the activation record (which is now the top of the run time stack). Thus, to store D1 into its reserved space at the start of the activation record we must store it at the current position of the SP minus 16. Finally, we need to set SP to have the address of the start of Fred's activation record.  We do this by the last line, which puts into D1 the value in SP minus 16, which is indeed the start of Fred's activation record.

Stage 7

Compilation Point

At this point we hit the begin compiling Fred's code.  This is old hat.. 

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);

Symbol Table

There are no changes to the symbol table structure.

                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Fred | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | x    | Integer | 4 | PARM | in |
                 |______|_________|___|______|____|
                 | t    | Integer | 12| VAR  |    |
                 |______|_________|___|______|____|

 

IR File

;program
jump L1
;procedure Fred
label L2
;finish Fred's activation record (first part already done at call to Fred)
add SP,4,SP ;leave space in AR for Fred's variables (just t in this case)
mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
sub SP,16,D1 ;set D1 to point to the start of the activation record
;code for Fred
push (4)D1  ;push x
push 1      ;push 1
adds        ;add the top two stack elements
pop (12)D1  ;store the result into t
push (12)D1 ;push t
writes      ;write t

Stage 8

Compilation Point

At this point we hit the end of Fred's code.  This is where we need to generate code to return to the calling point. 

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);
    end; -- Fred;

Symbol Table

The symbol table for Fred is popped from the stack.

                  ________________ __
Top of stack --> | Main | 0 | L1  | -|-> // (next symbol table)        
                 |______|___|_____|__|_______
                 | a    | Integer | 4 | VAR  |
                 |______|_________|___|______|
                 | b    | Integer | 8 | VAR  |
                 |______|_________|___|______|________    __________ __
                 | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
                 |______|_________|___|______|____|___|  |__|_______|__|
          
        
 

IR File

;program
jump L1
;procedure Fred
label L2
;finish Fred's activation record (first part already done at call to Fred)
add SP,4,SP    ;leave space in AR for Fred's variables (just t in this case)
mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
sub SP,16,D1   ;set D1 to point to the start of the activation record
;code for Fred
push (4)D1  ;push x
push 1      ;push 1
adds        ;add the top two stack elements
pop (12)D1  ;store the result into t
push (12)D1 ;push t
writes      ;write t
;notice that at this point SP is pointing to the same place that it was pointing
;when the code for Fred was started.  Now restore D1 to its saved value, and 
;remove all of Fred's local variables from the AR
mov (-16)SP,D1 ;restore D1's old value
sub SP,4,SP    ;effectively pop the local variables (t)
rts            ;pop the top of stack value into the PC

Notice that the last three instructions just

Stage 9

Compilation Point

Consider what the situation is in our compiler when we hit the beginning of Mary's code.

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);
    end; -- Fred;
  procedure Mary(c : in integer; d: in out integer);
    var i : integer;
    begin -- Mary

Symbol Table

The symbol table for Mary is complete


                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|______________________
         |       | Mary |         |   | PROC | L3 |  -|->|In|Integer| -|-> | in out | integer | -> //
         |       |______|_________|___|______|____|___|  |__|_______|__|___|________|_________|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Mary | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | c    | Integer | 4 | PARM | in |
                 |______|___|_____|__|_______ ____
                 | d    | Integer | 8 | PARM | in |
                 |______|_________|___|______| out|
                 | i    | Integer | 16| VAR  |    |
                 |______|_________|___|______|____|
          
        
 

IR File

Just as before, we produce code to save space in the activation record for the local variables of Mary, just above the return address that will be put there by the calling code.

;program
jump L1
;procedure Fred
label L2
;finish Fred's activation record (first part already done at call to Fred)
add SP,4,SP ;leave space in AR for Fred's variables (just t in this case)
mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
sub SP,16,D1 ;set D1 to point to the start of the activation record
;code for Fred
push (4)D1  ;push x
push 1      ;push 1
adds        ;add the top two stack elements
pop (12)D1  ;store the result into t
push (12)D1 ;push t
writes      ;write t
mov (-16)SP,D1 ;restore D1's old value
sub SP,4,SP    ;effectively pop the local variables
rts            ;pop the top of stack value into the PC
;procedure Mary
label L3
;finish Mary's activation record (first part already done at call to Mary)
add SP,4,SP    ;leave space in AR for Mary's variables (just i in this case)
mov D1,(-20)SP ;save old D1 in space reserved for it in the AR
sub SP,20,D1   ;set D1 to point to the start of the activation record

Stage 10

Compilation Point

We can now translate Mary's code up to the call to Fred.  One twist here is that parameter d in Mary is of mode in out, which means that d code must be generated by the compiler to access d through indirect addressing.

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);
    end; -- Fred;
  procedure Mary(c : in integer; d: in out integer);
    var i : integer;
    begin -- Mary
      read(i);
      Fred(i);
      c := c + a;
      d := d + 1;
      write(c+d);
    end Mary;

Symbol Table

The symbol table for Mary requires no changes.


                  ________________ __
          -----> | Main | 0 | L1  | -|-> // (next symbol table)        
         |       |______|___|_____|__|_______
         |       | a    | Integer | 4 | VAR  |
         |       |______|_________|___|______|
         |       | b    | Integer | 8 | VAR  |
         |       |______|_________|___|______|________    __________ __
         |       | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
         |       |______|_________|___|______|____|___|  |__|_______|__|______________________
         |       | Mary |         |   | PROC | L3 |  -|->|In|Integer| -|-> | in out | integer | -> //
         |       |______|_________|___|______|____|___|  |__|_______|__|___|________|_________|
         |
         |
         |_______________________________
                  ________________ __    |
Top of stack --> | Mary | 1 |     | -|-> |        
                 |______|___|_____|__|_______ ____
                 | c    | Integer | 4 | PARM | in |
                 |______|___|_____|__|_______ ____
                 | d    | Integer | 8 | PARM | in |
                 |______|_________|___|______| out|
                 | i    | Integer | 16| VAR  |    |
                 |______|_________|___|______|____|
          
        
 

IR File

Just as before, we produce code to save space in the activation record for the local variables of Mary, just above the return address that will be put there by the calling code.

;program
jump L1
;procedure Fred
label L2
;finish Fred's activation record (first part already done at call to Fred)
add SP,4,SP ;leave space in AR for Fred's variables (just t in this case)
mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
sub SP,16,D1 ;set D1 to point to the start of the activation record
;code for Fred
push (4)D1  ;push x
push 1      ;push 1
adds        ;add the top two stack elements
pop (12)D1  ;store the result into t
push (12)D1 ;push t
writes      ;write t
mov (-16)SP,D1 ;restore D1's old value
sub SP,4,SP    ;effectively pop the local variables
rts            ;pop the top of stack value into the PC
;procedure Mary
label L3
;finish Mary's activation record (first part already done at call to Mary)
add SP,4,SP    ;leave space in AR for Mary's variables (just i in this case)
mov D1,(-20)SP ;save old D1 in space reserved for it in the AR
sub SP,20,D1   ;set D1 to point to the start of the activation record
;Mary's code
read (16)D1  ;read i
;code to set up the initial part of the AR for Fred
;and make the call to Fred
add SP,4,SP ;leave space for Fred's display register
push (16)D1 ;push the value of i as the first (and only) actual parameter for Fred
jsr L2      ;call Fred (this pushes the PC onto the stack and jumps to L2)
;the return from Fred will arrive at this point, so the rest of the AR
;must be removed
sub SP,8,SP ; pop parameters and space for Fred's display register from the stack
;the rest of Mary's code
push (4)D1  ;push c
push (4)D0  ;push a
adds        ;add c and a
pop (4)D1   ;store the result into c
push @(8)D1 ;push d
push 1      ;push 1
adds        ;add d and 1
pop @(8)D1  ;store the result into d
push (4)D1  ;push c
push @(8)D1 ;push d
adds        ;add c and d
writes      ;write the result
;end Mary
mov (-20)SP,D1 ;restore D1's old value
sub SP,4,SP    ;effectively pop the local variables
rts            ;pop the top of stack value into the PC;
               ;this causes a return to the calling procedure


There are a few point to notice here:

Finally, Mary's symbol table is popped as the compiler is done parsing Mary.

Last Stage

Finally, let's look at compiling the code for Main.  It has one added twist in that an actual parameter must be set up for a formal in out parameter in the call to Mary.

Compilation Point

program Main;
  var a, b : integer;
  procedure Fred(x : in integer);
    var t : integer;
    begin -- Fred
      t := x + 1;
      write(t);
    end; -- Fred;
  procedure Mary(c : in integer; d: in out integer);
    var i : integer;
    begin -- Mary
      read(i);
      Fred(i);
      c := c + a;
      d := d + 1;
      write(c+d);
    end Mary;
  begin -- Main
    read(a,b);
    Mary(a,b);
  end. -- Main

Symbol Table

The symbol table stack just contains the symbol table for Main at this point..


                  ________________ __
Top of stack --> | Main | 0 | L1  | -|-> // (next symbol table)        
                 |______|___|_____|__|_______
                 | a    | Integer | 4 | VAR  |
                 |______|_________|___|______|
                 | b    | Integer | 8 | VAR  |
                 |______|_________|___|______|________    __________ __
                 | Fred |         |   | PROC | L2 |  -|->|In|Integer| -|-> //
                 |______|_________|___|______|____|___|  |__|_______|__|______________________
                 | Mary |         |   | PROC | L3 |  -|->|In|Integer| -|-> | in out | integer | -> //
                 |______|_________|___|______|____|___|  |__|_______|__|___|________|_________|
         
           

IR File

The code for Main is pretty straightforward, except that we now need to push b's address onto the stack as the actual parameter value in the call to Mary, because Mary is expecting an in out parameter as its second parameter.

    ;program
  0 jump L1
    ;procedure Fred
  1 label L2
    ;finish Fred's activation record (first part already done at call to Fred)
  2 add SP,4,SP ;leave space in AR for Fred's variables (just t in this case)
  3 mov D1,(-16)SP ;save old D1 in space reserved for it in the AR
  4 sub SP,16,D1 ;set D1 to point to the start of the activation record
    ;code for Fred
  5 push (4)D1  ;push x
  6 push 1      ;push 1
  7 adds        ;add the top two stack elements
  8 pop (12)D1  ;store the result into t
  9 push (12)D1 ;push t
 10 writes      ;write t
 11 mov (-16)SP,D1 ;restore D1's old value
 12 sub SP,4,SP    ;effectively pop the local variables
 13 rts            ;pop the top of stack value into the PC
    ;procedure Mary
 14 label L3
    ;finish Mary's activation record (first part already done at call to Mary)
 15 add SP,4,SP    ;leave space in AR for Mary's variables (just i in this case)
 16 mov D1,(-20)SP ;save old D1 in space reserved for it in the AR
 17 sub SP,20,D1   ;set D1 to point to the start of the activation record
    ;Mary's code
 18 read (16)D1  ;read i
    ;code to set up the initial part of the AR for Fred
    ;and make the call to Fred
 19 add SP,4,SP ;leave space for Fred's display register
 20 push (16)D1 ;push the value of i as the first (and only) actual parameter for Fred
 21 jsr L2      ;call Fred (this pushes the PC onto the stack and jumps to L2)
    ;the return from Fred will arrive at this point, so the rest of the AR
    ;must be removed
 22 sub SP,8,SP ; pop parameters and space for Fred's display register from the stack
    ;the rest of Mary's code
 23 push (4)D1  ;push c
 24 push (4)D0  ;push a
 25 adds        ;add c and a
 26 pop (4)D1   ;store the result into c
 27 push @(8)D1 ;push d
 28 push 1      ;push 1
 29 adds        ;add d and 1
 30 pop @(8)D1  ;store the result into d
 31 push (4)D1  ;push c
 32 push @(8)D1 ;push d
 33 adds        ;add c and d
 34 writes      ;write the result
    ;end Mary
 35 mov (-20)SP,D1 ;restore D1's old value
 36 sub SP,4,SP    ;effectively pop the local variables
 37 rts            ;pop the top of stack value into the PC;
                   ;this causes a return to the calling procedure
    ;begin Main
 38 label L1
    ;construct the activation record for Main (the whole thing, as Main
    ;is not called from anywhere)
 39 add SP,4,SP    ;leave room at the start of Main's AR to save D0
 40 add SP,8,SP    ;leave space for the two variables of Main, a and b, 
                   ;in Main's AR
 41 mov D0,(-12)SP ;save D0 in the first element of Main's AR
 42 sub SP,12,D0   ;put address of Main's first AR position into D0
 43 read (4)D0     ;read a
 44 read (8)D0     ;read b
    ;call to Mary
 45 add SP,4,SP ;leave space for display register in Mary's AR
 46 push (4)D0  ;push the value in a as the first actual parameter for Mary
    ;the following three instructions compute the address of b
 47 push D0 ;push the value in D0 onto the stack
 48 push 8  ;push 8 onto the stack
 49 adds    ;add the top two stack values (this is the address of b)
 50 jsr L3  ;call Mary
 51 sub SP,12,SP   ;pop Mary's 2 parameters and display save area from stack
 52 mov -12(SP),D0 ;restore D0 to its previous value
 53 sub SP,12,SP   ;remove D0's AR from the stack
 54 Halt

Notice the following: