only in function position, but some of these references occur within other
partially or fully closed functions, then this function must be partially
closed. By this we mean that the environment for the closure must be "consed
up", but no pointer to the code need be added on as for a full closure. This
function will always be called from places that know the name of the function
and so can just perform a G0 to the code, but those such places which are
within closures must have a complete copy of the necessary environment.
(3) In other cases (functions bound to variables referenced only in function
position and never within a closed function, or functions occurring in
function position of LAMBDA-combinations), the function need not be closed.
This is because the environment can always be fully recovered from the
environment at the point of call.
In order to determine this information, it is necessary to determine, for each node, the set of variables referred to from within closed functions at or below that node. Thus this process and the process of determining which functions to close are highly interdependent, and so must be accomplished in a single pass.
The second pass also generates a name for each LAMBDA-expression (to be used as tags in the output code, as discussed in the examples earlier), and for non-closed functions determines which variables will be assigned to "registers" or "memory locations". For these non-closed functions it may determine that certain variables need not be assigned locations at all (they are never referenced, or are bound to other non-closed functions — the latter circumstance is important when a variable is known to denote a certain function, but the optimizer was too conservative to perform beta-substitution for fear of duplicating code and thus wasting space). Finally, for each variable which is (logically, at run time not necessarily actually) bound to a known function (and