* Input Variables In the IR, input variables do NOT correspond to actual block arguments. The IRGenerator does create input variables for these block arguments to be passed, but local variables corresponding to each argument are also created, and the input variables are subsequently copied into these local variables at the start of the block. This helps to enforce a read-only policy for input variables, and provides for better possible register usage in argument passing by isolating them from any actual stack location. It also allows that input variables may be reused later for other purposes (like resends) despite modifications to the actual arguments. * Closure Lifting For the case of the IR, it is safe to assume that any local variable used in a block other than the one it was declared in must be lifted. Any other local variables, however, which are only referenced in the block they were declared either because the block was simple (i.e. [| :n | n + 1]) or as a result of inlining various control flow messages (i.e. ifTrue:ifFalse:, whileTrue:, ...) should not be lifted and instead located on the stack. To this effect, a simple analysis of walking over the block and all its sub-blocks, marking any variables (using the nonLocalUses slot) that are being used in a block but not declared in it will identify those with need to be lifted. The IRGenerator is initially overly-conservative in that ALL local variable loads and stores generate LoadFreeVariable and StoreFreeVariable instructions. After the above analysis, those variables that are not actually "free" should be lowered such that: LoadFreeVariable temporary, localVariable becomes: SetVariable temporary, localVariable and: StoreFreeVariable localVariable, temporary becomes: SetVariable localVariable, temporary Now, the simplest way to lift all the free variables is to simply stuff them all into an array, that represents the environment record. This array is allocated on entry to the function, and free variables are loaded from and stored into this array, with an entry in the array for each variable. All remaining LoadFreeVariable and StoreFreeVariable instructions are rewritten to load from or store to this array, respectively. This proceeds recursively on any embedded closures. But before rewriting embedded closures, input variables must first be added so that the environment records may be passed into them. The environment records are then passed as extra inputs to the InstantiateClosure instructions using the embedded closure. Note that the environment records are themselves accessed in variables, so the extra variable passed as input to InstantiateClosure will different depending on where the instruction is located. Also note that for a deeply embedded closure to access an environment record, it MUST be passed to ALL intervening closures or else it would itself constitute a free variable reference and cause circularity.