Translating Procedures and Functions

Translating procedures and functions in a programming language that allows arbitrary nesting of the two requires that we consider eight points in the source code.

Translation Points

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 (e.g., parameter types and modes) into the symbol table currently on top of the stack, and then to push a new symbol table on the top of the stack for the new procedure.  All of the new procedure's parameters (names, types, modes) 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 aprocedure 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.
Point 8—Function Return
This is much like procedure return (point 7) except that care must be taken to leave the functional return value in a place where it will be the top of stack value after the activation record is totally removed from the stack either by the code generated at this point or the code executed right after the call is made to the function.

With respect to point 1, we have previously presented the need to insert a branch instruction at the start of intermediate code generation that will cause execution to begin at the main program at run time. Recall that the reason for this is that our compiler will generate code for all declared procedures and functions before it generates code for the main program. Thus, when the program starts executing this jump instruction will ensure that the translated code for the main program (at the end of the generated intermediate code) will be executed first.

With respect to point 2, we have spent lots of time on the symbol table already with respect to procedures, functions, and the main program. If it wasn't clear before, we now see that we will need a label in the symbol table for the main program so that the juimp can be made to that label and so that that label can be inserted in to the code at the beginning of code generation for the main program.

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 parameter m
. . .
actual parameter 1
old display register

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 for the called procedure. So, code must be generated to

When the call instruction is executed at run time, that execution will result in the PC being pushed onto the stack (which is the address of the next instruction right after the call in the calling routine) followed by a jump to the label for the called procedure. This pushed PC value is what allows the called procedure to return to the proper point when it ends.

The green top part of the activation record is constructed from code generated when the called procedure begins execution. You may notice that the you must generate the code for building the top (green) part of the stack before you generate the code for the bottom part of the activation record, but that at run time the code that builds the bottom part of the activation record will always be executed first.