Based on my previous plan for three layers of laziness, I've come up with the following general approach to writing the instructions for a function body.
First, by the time we are ready to write the function body code, we should have parsed the expression returned by the function into an expression tree, the nodes of which are function calls of some kind, and the branches of which are parameters to those function calls.
We will process the branches first in such a way that, once processing is finished, the result of each parameter's evaluation sits on the stack in order. When all of the branches are finished, we'll write the function call itself.
Prior to beginning to write the assembly code, we'll check the tree for duplicate function calls--calls where all of the same parameters are passed to the same function. Usually these will be unevaluated expressions passed in as parameters, or known to the function as environment variables. For each duplicate we will allocate two local variables, one to store the result of the function evaluation, and the second to store a flag indicating whether the function has already been evaluated.
If we think that the result of the function is to be used later, we'll set the corresponding flag, pop it off of the stack after evaluation, store it as a local variable, then push it back onto the stack. Later, rather than reevaluating the function call, we'll simply push the local variable onto the stack.
At the end, of course, well write a ret (return) operation.
Down the road, it might be worthwhile to consider including function calls inline, since there is essentially no overhead except for the larger size of the executable.