Intermediate Code for Procedures
Procedures and their implementation will be discussed at length in Chapter 7, along with the run-time management of storage for names. We use the term function in this section for a procedure that returns a value. We briefly discuss function declarations and three-address code for function calls. In three-address code, a function call is unraveled into the evaluation of parameters in prepa-ration for a call, followed by the call itself. For simplicity, we assume that parameters are passed by value; parameter-passing methods are discussed in Section 1.6.6.
Example 6.25 : Suppose that a is an array of integers, and that f is a function from integers to integers. Then, the assignment
The first two lines compute the value of the expression a [ i ] into temporary t 2 , as discussed in Section 6.4. Line 3 makes t2 an actual parameter for the call on line 4 of f with one parameter. Line 5 assigns the value returned by the function call to t 3 . Line 6 assigns the returned value to n.
The productions in Fig. 6.52 allow function definitions and function calls. (The syntax generates unwanted commas after the last parameter, but is good enough for illustrating translation.) Nonterminals D and T generate declara-tions and types, respectively, as in Section 6.3. A function definition gener-ated by D consists of keyword define, a return type, the function name, for-mal parameters in parentheses and a function body consisting of a statement. Nonterminal F generates zero or more formal parameters, where a formal pa-rameter consists of a type followed by an identifier. Nonterminals S and E generate statements and expressions, respectively. The production for S adds a statement that returns the value of an expression. The production for E adds function calls, with actual parameters generated by A. An actual parameter is an expression.
Function definitions and function calls can be translated using concepts that have already been introduced in this chapter.
Function types. The type of a function must encode the return type and the types of the formal parameters. Let void be a special type that represents no parameter or no return type. The type of a function pop() that returns an integer is therefore "function from void to integer." Function types can be represented by using a constructor fun applied to the return type and an ordered list of types for the parameters.
Symbol tables. Let s be the top symbol table when the function definition is reached. The function name is entered into s for use in the rest of the program. The formal parameters of a function can be handled in analogy with field names in a record (see Fig. 6.18. In the production for D, after seeing define and the function name, we push s and set up a new symbol table
Env.push(top)] top = new Env(top);
Call the new symbol table, t. Note that top is passed as a parameter in n e w Env(top), so the new symbol table t can be linked to the previous one, s. The new table t is used to translate the function body. We revert to the previous symbol table s after the function body is translated.
• Type checking. Within expressions, a function is treated like any other operator. The discussion of type checking in Section 6.5.2 therefore carries over, including the rules for coercions. For example, i f / i s a function with a parameter of type real, then the integer 2 is coerced to a real in the call
( 2 ) .
Function calls. When generating three-address instructions for a function call id(E, E,... , E), it is sufficient to generate the three-address instruc-tions for evaluating or reducing the parameters E to addresses, followed by a param instruction for each parameter. If we do not want to mix the parameter-evaluating instructions with the param instructions, the attribute E.addr for each expression E can be saved in a data structure
such as a queue. Once all the expressions are translated, the param in-structions can be generated as the queue is emptied.
The procedure is such an important and frequently used programming con-struct that it is imperative for a compiler to good code for procedure calls and returns. The run-time routines that handle procedure parameter passing, calls, and returns are part of the run-time support package. Mechanisms for run-time support are discussed in Chapter 7.