Local Call

Named and anonymous call are called full calls, to distinguish them from local call. When making full calls, the compiler must make many worst-case assumptions that aren't necessary in a local call. The advantage of local call is that the compiler can choose to use only those parts of the full call sequence that are actually necessary.

In local call, we always know the function being called, so we never have to do argument count checking, and can always use an immediate branch for the control transfer. If the function doesn't return to more than one place, then can just use a simple branch, or even drop through.

The argument passing TNs may be allocated anywhere. The caller allocates the stack frame for the called function, moving any non-register arguments into the passing locations in the callee's frame.

If we are calling a local function that doesn't always return the same number of values, then we must use the same values returning mechanism that is used in full call, but we don't have to use the standard registers.

A tail-recursive local call doesn't require any call VOP. We just use Move VOPs to put the arguments into the passing locations and then jump to the the start of the code for the function. We don't have to do any stack hackery since we use the same stack frame format for all the functions compiled at the same time. In many cases tail-recursive local calls can be entirely optimized away, since they involve only some moves and a branch. We preference the argument values to the passing locations of the called function, making it likely that no move will be necessary. Often the control transfer can be done by simply dropping through.

We have to do some funny stuff with local calls in order to get the lifetimes for the passing locations right, since lifetime analysis skips directly from the return point to the call point, ignoring the uses of the passing locations in the called function. Similarly, we pretend that a block ending in a return has no successors.

call-local (arg*) "fun" => value multiple-call-local (arg*) "fun" => start end val0 ... val<n> Call-Local is used for calls to local functions that are forced to use the unknown-values passing convention. Value is the first return value register; we don't really do anything to it, but we specify it as a result to represent the assignment done by the calling function.

Multiple-Call-Local is similar, but specifies all the values used by the unknown-values convention. Default-Values may be used to receive a specific number of values.

known-call-local (arg*) "fun" => value* This VOP is used for local calls to functions where we can determine at compile time that the number of values returned is always the same. In this case, we don't need to indicate the number of values, and can pass them in separate TNs. The Values are the actual return locations. We don't really do anything to the return values; we just specify them as results to represent the assignment done by the called function.

known-return (return-pc value*) "fun" This VOP is used for returning from local calls using the known return values convention. The specified return Values are moved into the passing locations in the caller's frame.

If we know that the function we are calling is non-recursive, then we can compile it much like a tail-recursive call. We must have a call VOP to compute the return PC, but we don't need to allocate a frame or save registers. We just set up the arguments in the frame and do the call.

We require simple functions to use the known-values convention. It would be possible to support unknown values, but it would potentially require BLT'ing return values out of the frame and on to the top of the stack. Supporting unknown values would also require a bunch more VOPs, since we need different call and return VOPs for simple call.

Known values return causes no problems, since the callee knows how many values are wanted. We store the values directly into the current frame, since it is also the caller's frame.

known-call-simple () "fun" => return-pc known-return-simple (return-pc) "fun" Similar to the non-simple VOPs, but don't allocate or deallocate frames, and assume that argument and value passing is done with explicit Move VOPs.