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.
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.
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.
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
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
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
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
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
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 recordCarefully 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):
- the return address
- the parameters (just one in this case: the 4-byte actual parameter value for Fred's formal parameter x)
- the reserved space for the old display register
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.
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
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 PCNotice that the last three instructions just
- restore D1 to its saved value
- move the stack pointer down so that all of the local variables have been removed from the activation record.
- return to the point of call by popping the top of the stack (which contains the return value) into the PC (which means that the next instruction to be executed will be the one whose address is in the PC, which is the correct address as saved on the stack).
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 tmov (-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
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 tmov (-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:
- at the call to Fred, the activation record for Fred is started according to the model. That is, space for Fred's display register is reserved, then the actual parameter for Fred (i) is pushed, and then a jsr instruction to Fred's label (L2) is inserted. When this jsr instruction is executed, it will push the value in the PC onto the stack and load the PC with the address of label L2, effectively causing a jump.
- The instruction immediately after the jsr instruction is the one to which the return will be made after the previous call to Fred. At this point, the remainder of Fred's activation record must be removed, which takes off the space for the parameter and Fred's display register save area.
- The rest of Mary's code is pretty straightforward. One thing to notice is the use of indirect addressing (the @). This is used for accessing parameter d in Mary, which has mode in out. Thus, the value at (8)D1 is not d's value, but the address of d. The indirect addressing mode in the computer takes care of this. For example, in executing "push @(8)D1", the computer treats the value at (8)D1 as an address and uses that address to find the actual value of d.
- At the end of Mary, we generate the same kind of code that we do at the end of any procedure: we put in code to restore the value that D1 had before Mary was called, code to move the stack pointer down the number of bytes taken up by the local variables (effectively popping them from the AR), and code to return to the point after which Mary was called.
Finally, Mary's symbol table is popped as the compiler is done parsing Mary.
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. -- MainSymbol 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 t11 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 D043 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 Mary51 sub SP,12,SP ;pop Mary's 2 parameters and display save area from stack52 mov -12(SP),D0 ;restore D0 to its previous value 53 sub SP,12,SP ;remove D0's AR from the stack 54 HaltNotice the following:
- The first thing the code for Main does is set up Main's activation record. The entire activation record is set up here, because there is no call to main from some other place.
- When Mary is called, Mary's activation record is started (space is left for Mary's display register, the value of a is pushed onto the stack as Mary's first actual parameter, and. since b is the second actual parameter intended for a formal parameter in Mary, b's address must be computed and pushed onto the stack rather than b's value. The address of b is calculated by adding the contents of D0 and 8, which leaves their sum, and hence b's address, on the stack.
- after the return from the call to Mary, first, the remaining part of Mary's activation record is removed from the stack
- finally, Main's activation record is removed from the stack after D0 is restored to its original value.