(preserving ’(env continue)
proc-code
(preserving ’(proc continue)
(construct-arglist operand-codes)
(compile-procedure-call target linkage)))))
The code to construct the argument list will evaluate each operand into
val
and then
cons
that value
onto the argument list being accumulated in
argl
. Since we
cons
the arguments onto
argl
in
sequence, we must start with the last argument and end with the first, so that the arguments will appear
in order from first to last in the resulting list. Rather than waste an instruction by initializing
argl
to
the empty list to set up for this sequence of evaluations, we make the first code sequence construct the
initial
argl
. The general form of the argument-list construction is thus as follows:
<
compilation of last operand, targeted to val>
(assign argl (op list) (reg val))
<
compilation of next operand, targeted to val>
(assign argl (op cons) (reg val) (reg argl))
...<compilation of first operand, targeted to val>
(assign argl (op cons) (reg val) (reg argl))
Argl
must be preserved around each operand evaluation except the first (so that arguments
accumulated so far won’t be lost), and
env
must be preserved around each operand evaluation except
the last (for use by subsequent operand evaluations).
Compiling this argument code is a bit tricky, because of the special treatment of the first operand to be
evaluated and the need to preserve
argl
and
env
in different places. The
construct-arglist
procedure takes as arguments the code that evaluates the individual operands. If there are no operands
at all, it simply emits the instruction
(assign argl (const ()))
Otherwise,
construct-arglist
creates code that initializes
argl
with the last argument, and
appends code that evaluates the rest of the arguments and adjoins them to
argl
in succession. In
order to process the arguments from last to first, we must reverse the list of operand code sequences
from the order supplied by
compile-application
.
(define (construct-arglist operand-codes)
(let ((operand-codes (reverse operand-codes)))
(if (null? operand-codes)
(make-instruction-sequence ’() ’(argl)
’((assign argl (const ()))))
(let ((code-to-get-last-arg
(append-instruction-sequences
(car operand-codes)
(make-instruction-sequence ’(val) ’(argl)
’((assign argl (op list) (reg val)))))))
(if (null? (cdr operand-codes))
code-to-get-last-arg
(preserving ’(env)
code-to-get-last-arg
(code-to-get-rest-args
(cdr operand-codes))))))))
(define (code-to-get-rest-args operand-codes)
(let ((code-for-next-arg
(preserving ’(argl)
(car operand-codes)
(make-instruction-sequence ’(val argl) ’(argl)
’((assign argl
(op cons) (reg val) (reg argl)))))))
(if (null? (cdr operand-codes))
code-for-next-arg
(preserving ’(env)
code-for-next-arg
(code-to-get-rest-args (cdr operand-codes))))))
Applying procedures
After evaluating the elements of a combination, the compiled code must apply the procedure in
proc
to the arguments in
argl
. The code performs essentially the same dispatch as the
apply
procedure
in the metacircular evaluator of section 4.1.1 or the
apply-dispatch
entry point in the
explicit-control evaluator of section 5.4.1. It checks whether the procedure to be applied is a primitive
procedure or a compiled procedure. For a primitive procedure, it uses
apply-primitive-procedure
; we will see shortly how it handles compiled procedures. The
procedure-application code has the following form:
(test (op primitive-procedure?) (reg proc))
(branch (label primitive-branch))
compiled-branch
<code to apply compiled procedure with given target and appropriate linkage>
primitive-branch
(assign <target>
(op apply-primitive-procedure)
(reg proc)
(reg argl))
<linkage>
after-call
Observe that the compiled branch must skip around the primitive branch. Therefore, if the linkage for
the original procedure call was
next
, the compound branch must use a linkage that jumps to a label
that is inserted after the primitive branch. (This is similar to the linkage used for the true branch in
compile-if
.)
(define (compile-procedure-call target linkage)
(let ((primitive-branch (make-label ’primitive-branch))
(compiled-branch (make-label ’compiled-branch))
(after-call (make-label ’after-call)))
(let ((compiled-linkage
(if (eq? linkage ’next) after-call linkage)))
(append-instruction-sequences
(make-instruction-sequence ’(proc) ’()
‘((test (op primitive-procedure?) (reg proc))
(branch (label ,primitive-branch))))
(parallel-instruction-sequences