Page:AIM-443.djvu/6



We did not push a return address for  since the call to   is not an argument form. Notice that this uniform discipline lends itself to passing arguments on the stack, above the return address. The called procedure is then responsible for popping the arguments. On the other hand, if argument passing does not use the stack, we can permute the  of the return address with the argument set-up code:

If our machine has a  instruction, then a peephole optimization [McK65] can now transform the  /  sequence into a single   instruction. Thus we see that a procedure call can be uniformly treated as a, with the   instruction considered an optimization (rather than vice versa!).

There are a couple of difficulties with this idea. One is that the stack is often used to hold information other than return addresses, such as stack-allocated data structures and intermediate results. This is no problem, as it turns out; it is merely necessary to clean up the stack just before calling, rather than just after calling  ; then the original   and the   will be consecutive, and so can be expressed as a. {Note Shuffle Arguments}

This leads to a second difficulty, which is that some languages would  F to refer globally to stack-allocated structures within , such as the dynamic values of   and. In this case we cannot clean up the stack until after calling. There is, however, some evidence [Wul73] that such global variables are as "harmful" as  statements; in any case it is a good idea to minimize their use, and to define variables to be lexical in scope by default. It turns out that in most programming languages (COBOL, PL/I, FORTRAN. and LISP in particular) the distinction between lexical and dynamic scoping doesn't matter most of the time anyway. We should leave the compiler free to produce the best possible code; only in cases where structures are explicitly declared to be dynamically referenced should the compiler be forced to leave them on the stack in an otherwise tail-recursive situation.

In general, procedure calls may be usefully thought of as  statements which also pass parameters, and can be uniformly encoded as   instructions. This is a simple, universal technique, to be contrasted with the more powerful recursion-removal techniques such as those in [Dar76]. Our approach results in all tail-recursive procedure calls being compiled as