Ремонт принтеров, сканнеров, факсов и остальной офисной техники


назад Оглавление вперед




[171]

который производит собственно вызов (он порождается с помощью compile-procedure-call). При склеивании последовательностей команд требуется сохранить регистр env на время вычисления оператора (поскольку в это время env может измениться, а он еще потребуется во время вычисления операндов), а регистр proc требуется сохранить на время построения списка аргументов (при вычислении операндов proc может измениться, а он потребуется во время собственно вызова процедуры). Наконец, все время следует сохранять continue, поскольку этот регистр нужен для связующего кода.

(define (compile-application exp target linkage)

(let ((proc-code (compile (operator exp) proc next)) (operand-codes (map (lambda (operand) (compile operand val next)) (operands exp)))) (preserving (env continue) proc-code

(preserving (proc continue) (construct-arglist operand-codes) (compile-procedure-call target linkage)))))

Код для построения списка аргументов вычисляет каждый операнд, помещая результат в val, а затем с помощью cons прицепляет его к списку аргументов, собираемому в argl. Поскольку мы по очереди нацепляем аргументы на argl через cons, нам нужно начать с последнего аргумента и закончить первым, чтобы в получившемся списке аргументы стояли в порядке от первого к последнему. Чтобы не тратить команду на инициализацию argl пустым списком, прежде чем начать последовательность вычислений, мы строим исходное значение argl в первом участке кода. Таким образом, общая форма построения списка аргументов такова:

(компиляция последнего операнда с целью val) (assign argl (op list) (reg val)) (компиляция следующего аргумента с целью val) (assign argl (op cons) (reg val) (reg argl))

(компиляция первого аргумента с целью val) (assign argl (op cons) (reg val) (reg argl))

Нужно сохранять argl при вычислении всех операндов, кроме как в самом начале (чтобы уже набранные аргументы не потерялись), а при вычислении всех операндов, кроме как в самом конце, нужно сохранять env (его могут использовать последующие вычисления операндов).

Компилировать код для аргументов довольно сложно, поскольку особым образом обрабатывается первый вычисляемый операнд, и в различных местах требуется сохранять argl и env. Процедура construct-arglist принимает в качестве аргументов участки кода, которые вычисляют отдельные операнды. Если никаких операндов нет вообще, она попросту порождает команду

(assign argl (const ()))

В остальных случаях она порождает код, инициализирующий argl последним аргументом, и добавляет к нему код, который по очереди вычисляет остальные аргументы и добавляет их к argl. Для того, чтобы аргументы обрабатывались


от конца к началу, нам следует обратить список последовательностей кода для операндов, подаваемый из 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))))))

Применение процедур

После того, как элементы комбинации вычислены, скомпилированный код должен применить процедуру из регистра proc к аргументам из регистра argl. Этот код рассматривает, в сущности, те же самые случаи, что и процедура apply из метациклического интерпретатора в разделе 4.1.1 или точка входа apply-dispatch из вычислителя с явным управлением в разделе 5.4.1. Нужно проверить какая процедура применяется - элементарная или составная. В случае элементарной процедуры используется apply-primitive-procedure; как ведется работа с составными процедурами, мы скоро увидим. Код применения процедуры имеет такую форму:

(test (op primitive-procedure?) (reg proc)) (branch (label primitive-branch)) compiled-branch

(код для применения скомпилированной процедуры с указанной целью и подходящим типом связи) primitive-branch (assign (целевой регистр)

(op apply-primitive-procedure) (reg proc)


(reg argl)) ( связующий код) after-call

Заметим, что если выбрана ветвь для скомпилированной процедуры, машина должна обойти ветвь для элементарной процедуры. Следовательно, если тип связи для исходного вызова процедуры был next, ветвь для составной процедуры должна использовать связь с переходом на метку, стоящую после ветви для элементарной процедуры. (Подобным образом работает связь для истинной ветви в 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 (append-instruction-sequences compiled-branch

(compile-proc-appl target compiled-linkage)) (append-instruction-sequences primitive-branch (end-with-linkage linkage (make-instruction-sequence (proc argl)

(list target)

((assign ,target

(op apply-primitive-procedure) (reg proc)

(reg argl)))))))

after-call))))

Ветви для элементарных и составных процедур, подобно истинной и ложной ветвям в compile-if, склеиваются через parallel-instruction-sequences, а не обыкновенной append-instruction-sequences, поскольку они не выполняются последовательно.

Применение скомпилированных процедур

Код, обрабатывающий применение процедур, - наиболее тонко устроенная часть компилятора, при том, что он порождает очень короткие последовательности команд. У скомпилированной процедуры (порожденной с помощью compile-lambda) имеется точка входа, то есть метка, указывающая, где начинается тело процедуры. Код, расположенный по этой метке, вычисляет результат, помещая его в val, а затем возвращается, исполняя команду (goto (reg continue)). Таким образом, если в качестве связи выступает метка, мы можем ожидать, что код для вызова скомпилированной процедуры (порождаемый с помощью compile-proc-appl) с указанным целевым регистром будет выглядеть так:



[стр.Начало] [стр.1] [стр.2] [стр.3] [стр.4] [стр.5] [стр.6] [стр.7] [стр.8] [стр.9] [стр.10] [стр.11] [стр.12] [стр.13] [стр.14] [стр.15] [стр.16] [стр.17] [стр.18] [стр.19] [стр.20] [стр.21] [стр.22] [стр.23] [стр.24] [стр.25] [стр.26] [стр.27] [стр.28] [стр.29] [стр.30] [стр.31] [стр.32] [стр.33] [стр.34] [стр.35] [стр.36] [стр.37] [стр.38] [стр.39] [стр.40] [стр.41] [стр.42] [стр.43] [стр.44] [стр.45] [стр.46] [стр.47] [стр.48] [стр.49] [стр.50] [стр.51] [стр.52] [стр.53] [стр.54] [стр.55] [стр.56] [стр.57] [стр.58] [стр.59] [стр.60] [стр.61] [стр.62] [стр.63] [стр.64] [стр.65] [стр.66] [стр.67] [стр.68] [стр.69] [стр.70] [стр.71] [стр.72] [стр.73] [стр.74] [стр.75] [стр.76] [стр.77] [стр.78] [стр.79] [стр.80] [стр.81] [стр.82] [стр.83] [стр.84] [стр.85] [стр.86] [стр.87] [стр.88] [стр.89] [стр.90] [стр.91] [стр.92] [стр.93] [стр.94] [стр.95] [стр.96] [стр.97] [стр.98] [стр.99] [стр.100] [стр.101] [стр.102] [стр.103] [стр.104] [стр.105] [стр.106] [стр.107] [стр.108] [стр.109] [стр.110] [стр.111] [стр.112] [стр.113] [стр.114] [стр.115] [стр.116] [стр.117] [стр.118] [стр.119] [стр.120] [стр.121] [стр.122] [стр.123] [стр.124] [стр.125] [стр.126] [стр.127] [стр.128] [стр.129] [стр.130] [стр.131] [стр.132] [стр.133] [стр.134] [стр.135] [стр.136] [стр.137] [стр.138] [стр.139] [стр.140] [стр.141] [стр.142] [стр.143] [стр.144] [стр.145] [стр.146] [стр.147] [стр.148] [стр.149] [стр.150] [стр.151] [стр.152] [стр.153] [стр.154] [стр.155] [стр.156] [стр.157] [стр.158] [стр.159] [стр.160] [стр.161] [стр.162] [стр.163] [стр.164] [стр.165] [стр.166] [стр.167] [стр.168] [стр.169] [стр.170] [стр.171] [стр.172] [стр.173] [стр.174] [стр.175] [стр.176] [стр.177] [стр.178] [стр.179] [стр.180] [стр.181] [стр.182] [стр.183] [стр.184] [стр.185] [стр.186] [стр.187] [стр.188] [стр.189] [стр.190] [стр.191] [стр.192] [стр.193] [стр.194] [стр.195] [стр.196]