Structure and Interpretation of Computer Programs



Yüklə 2,71 Mb.
Pdf görüntüsü
səhifə193/222
tarix08.08.2018
ölçüsü2,71 Mb.
#61085
1   ...   189   190   191   192   193   194   195   196   ...   222

    (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




Yüklə 2,71 Mb.

Dostları ilə paylaş:
1   ...   189   190   191   192   193   194   195   196   ...   222




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə