Точный порядок инструкций вложенной комбинации, видимое через состояние вычисление операндов и единое соглашение о применении для всех процедур.
Вопрос для размышления
Какие контракты порядка и представления должен сохранять скомпилированный код применения?
Компиляция оператора перед выражениями его операндов
Компиляция операндов слева направо с сохранением порядка аргументов
Генерация одной инструкции call после того, как готово полное состояние комбинации
Наблюдение эффектов порядка исходного кода без опоры на неопределенное расписание
Использование общего лексического окружения и соглашения об аргументах между представлениями процедур
Сохранение явного различия между диспетчеризацией примитивных, интерпретируемых и скомпилированных процедур
Учебный компилятор генерирует load для оператора вычитания, затем весь код для вызова левой процедуры record, затем весь код для вызова правой процедуры record и в конце одну внешнюю инструкцию call. Процедура с состоянием record добавляет метки в observations, поэтому возвращаемый список (left right) подтверждает, что выполнение следует сгенерированному порядку исходного кода. Результат вычитания также подтверждает порядок аргументов.
Вторая программа расширяет границы вызовов. Код вычислителя вызывает скомпилированное замыкание add-three, а скомпилированный код вызывает интерпретируемую процедуру double. Оба пути передают упорядоченный список аргументов через apply-any, а dispatch-log сохраняет фактические теги primitive, interpreted и compiled. Это фиксирует соглашение о вызовах, используемое в уроке.
Код SICP11,199 из 1,048,576 байт UTF-8
(begin(define(tagged-list?expressiontag)(and(pair?expression)(eq?(carexpression)tag)))(define(self-evaluating?expression)(or(number?expression)(string?expression)(boolean?expression)))(define(quoted?expression)(tagged-list?expression'quote))(define(assignment?expression)(tagged-list?expression'set!))(define(definition?expression)(tagged-list?expression'define))(define(if?expression)(tagged-list?expression'if))(define(lambda?expression)(tagged-list?expression'lambda))(define(begin?expression)(tagged-list?expression'begin))(define(definition-variableexpression)(if(symbol?(cadrexpression))(cadrexpression)(car(cadrexpression))))(define(definition-valueexpression)(if(symbol?(cadrexpression))(caddrexpression)(cons'lambda(cons(cdr(cadrexpression))(cddrexpression)))))(define(compile-sequenceexpressions)(cond((null?expressions)'((literalok)))((null?(cdrexpressions))(compile-expression(carexpressions)))(else(append(compile-expression(carexpressions))(cons'(discard)(compile-sequence(cdrexpressions)))))))(define(compile-operandsoperands)(if(null?operands)'()(append(compile-expression(caroperands))(compile-operands(cdroperands)))))(define(compile-expressionexpression)(cond((self-evaluating?expression)(list(list'literalexpression)))((symbol?expression)(list(list'loadexpression)))((quoted?expression)(list(list'literal(cadrexpression))))((assignment?expression)(append(compile-expression(caddrexpression))(list(list'set(cadrexpression)))))((definition?expression)(append(compile-expression(definition-valueexpression))(list(list'define(definition-variableexpression)))))((if?expression)(append(compile-expression(cadrexpression))(list(list'branch(compile-expression(caddrexpression))(compile-expression(cadddrexpression))))))((lambda?expression)(list(list'closure(cadrexpression)(compile-sequence(cddrexpression)))))((begin?expression)(compile-sequence(cdrexpression)))((pair?expression)(append(compile-expression(carexpression))(append(compile-operands(cdrexpression))(list(list'call(length(cdrexpression)))))))(else(error"unknown expression for compiler"expression))))(define(pair-bindingsvariablesvalues)(cond((and(null?variables)(null?values))'())((null?variables)(error"too many arguments"))((null?values)(error"too few arguments"))(else(cons(cons(carvariables)(carvalues))(pair-bindings(cdrvariables)(cdrvalues))))))(define(make-framevariablesvalues)(cons'*frame*(pair-bindingsvariablesvalues)))(define(frame-bindingsframe)(cdrframe))(define(extend-environmentvariablesvaluesenvironment)(cons(make-framevariablesvalues)environment))(define(lookup-variable-valuevariableenvironment)(if(null?environment)(error"unbound compiled variable"variable)(let((binding(assocvariable(frame-bindings(carenvironment)))))(ifbinding(cdrbinding)(lookup-variable-valuevariable(cdrenvironment))))))(define(define-variable!variablevalueenvironment)(let*((frame(carenvironment))(binding(assocvariable(frame-bindingsframe))))(ifbinding(set-cdr!bindingvalue)(set-cdr!frame(cons(consvariablevalue)(frame-bindingsframe)))))(list'definedvariable))(define(set-variable-value!variablevalueenvironment)(if(null?environment)(error"unbound compiled assignment"variable)(let((binding(assocvariable(frame-bindings(carenvironment)))))(ifbinding(begin(set-cdr!bindingvalue)(list'assignedvariable))(set-variable-value!variablevalue(cdrenvironment))))))(define(make-primitiveimplementation)(list'primitiveimplementation))(define(primitive?procedure)(tagged-list?procedure'primitive))(define(primitive-implementationprocedure)(cadrprocedure))(define(make-compiled-procedureparameterscodeenvironment)(list'compiledparameterscodeenvironment))(define(compiled-procedure?procedure)(tagged-list?procedure'compiled))(define(compiled-parametersprocedure)(cadrprocedure))(define(compiled-codeprocedure)(caddrprocedure))(define(compiled-environmentprocedure)(cadddrprocedure))(define(split-callstackcount)(define(loopremaining-stackremaining-countarguments)(if(=remaining-count0)(list(carremaining-stack)arguments(cdrremaining-stack))(loop(cdrremaining-stack)(-remaining-count1)(cons(carremaining-stack)arguments))))(loopstackcount'()))(define(run-codecodestackenvironment)(if(null?code)(liststackenvironment)(let*((instruction(carcode))(tag(carinstruction)))(cond((eq?tag'literal)(run-code(cdrcode)(cons(cadrinstruction)stack)environment))((eq?tag'load)(run-code(cdrcode)(cons(lookup-variable-value(cadrinstruction)environment)stack)environment))((eq?tag'closure)(run-code(cdrcode)(cons(make-compiled-procedure(cadrinstruction)(caddrinstruction)environment)stack)environment))((eq?tag'define)(let((result(define-variable!(cadrinstruction)(carstack)environment)))(run-code(cdrcode)(consresult(cdrstack))environment)))((eq?tag'set)(let((result(set-variable-value!(cadrinstruction)(carstack)environment)))(run-code(cdrcode)(consresult(cdrstack))environment)))((eq?tag'discard)(run-code(cdrcode)(cdrstack)environment))((eq?tag'branch)(let*((selected(if(carstack)(cadrinstruction)(caddrinstruction)))(branch-state(run-codeselected(cdrstack)environment)))(run-code(cdrcode)(carbranch-state)(cadrbranch-state))))((eq?tag'call)(let*((parts(split-callstack(cadrinstruction)))(procedure(carparts))(arguments(cadrparts))(caller-stack(caddrparts)))(if(primitive?procedure)(run-code(cdrcode)(cons(apply(primitive-implementationprocedure)arguments)caller-stack)environment)(if(compiled-procedure?procedure)(let*((call-environment(extend-environment(compiled-parametersprocedure)arguments(compiled-environmentprocedure)))(call-state(run-code(compiled-codeprocedure)'()call-environment))(value(car(carcall-state))))(run-code(cdrcode)(consvaluecaller-stack)environment))(error"not a compiled procedure"procedure)))))(else(error"unknown compiled instruction"instruction))))))(define(make-global-environment)(list(cons'*frame*(list(cons'+(make-primitive+))(cons'-(make-primitive-))(cons'*(make-primitive*))(cons'/(make-primitive/))(cons'=(make-primitive=))(cons'<(make-primitive<))(cons'>(make-primitive>))(cons'list(make-primitivelist))(cons'cons(make-primitivecons))(cons'car(make-primitivecar))(cons'cdr(make-primitivecdr))(cons'null?(make-primitivenull?))))))(define(compile-and-runexpression)(let*((code(compile-expressionexpression))(environment(make-global-environment))(state(run-codecode'()environment)))(list(car(carstate))codeenvironment)))(definecombination'(-(record(quoteleft)20)(record(quoteright)3)))(defineprogram'(begin(defineobservations(quote()))(define(append-listleftright)(if(null?left)right(cons(carleft)(append-list(cdrleft)right))))(define(recordlabelvalue)(begin(set!observations(append-listobservations(listlabel)))value))(defineresult(-(record(quoteleft)20)(record(quoteright)3)))(listresultobservations)))(defineresult(compile-and-runprogram))(list(carresult)(mapcar(compile-expressioncombination))))
В первом запуске проследите внешний load оператора, каждую инструкцию левого вызова record, каждую инструкцию правого вызова record и только затем вызов вычитания. В запуске интерфейса отделите подготовку аргументов от диспетчеризации представлений в apply-any и убедитесь, что каждое тело получает аргументы в объявленном порядке параметров.
Попробуйте сами
Измените программу и сравните результат.
Скомпилируйте вызов list с тремя операндами, каждый из которых записывает отдельную метку, затем оберните один операнд в вызов скомпилированного замыкания. Спрогнозируйте как теги инструкций, так и хронологический журнал dispatch-log.
Показать подсказку
Сначала завершите код оператора. Каждый операнд добавляет свой полный код в порядке исходного кода, а финальный call принимает полученный упорядоченный список аргументов.