Опубликован: 19.03.2004 | Уровень: специалист | Доступ: платный
Лекция 8:

Компиляция функциональных программ

< Лекция 7 || Лекция 8: 123 || Лекция 9 >
Аннотация: В данной лекции изучаются требования к компиляции функциональных программ и строится определение компилятора. Для Лиспа такое определение написано на Лиспе, как и определение интерпретатора. Рассматриваются Венская методика определения языков программирования, а именно отображение абстрактного синтаксиса языка на язык абстрактной машины, и приемы оптимизационного программирования. Разложение программы на функции с разным уровнем отладки является отправной точкой при выборе оптимизационных решений. Компиляция программ рассматривается как один из методов оптимизации процессов, осуществляемый как символьное преобразование — трансляция с исходного языка высокого уровня на язык низкого уровня, допускающий оптимизирующую кодогенерацию.
Ключевые слова: абстрактная машина, SECDED, спецификация машинно-зависимых аспектов, семантика, Лисп, определение, машинно-независимость, переносимость, функциональная переменная, символьная обработка, рекурсивное определение, уточняемость и детализируемость определений, компилятор, программа, выражение, ассемблер, код программы, ссылка, опыт, транслятор, функция, основание, метод раскрутки, bootstrapping, код компилятора, псевдо-функция, compile, диагностика, анализ, интерпретатор, свободная переменная, типовый контроль, компиляция, интерпретация, комплект Лисп-системы, механизм пакетов, память, ядро, мусорщик, переменные связи, связанная переменная, переменная, диагностическое сообщение, значение, nil, special, трансляция, распределение памяти, функциональный аргумент, ячейка, стек, адрес, declare, список переменных, индикатор, функциональная константа, function, maplist, трассировка, trace, функциональный подход, абстрактный синтаксис, синтаксис, кодогенерация, распознаватель, селектор, представление, Паскаль, константы, операции, пустой оператор, присваивание, исполнение, оператор безусловного перехода, goto, голова списка, CDR, поле, семантическая функция, flet, композиция функций, операторы, безымянная функция, корректность, чистый результат выражения

Компилятор и требования к коду программы

Описанная в предыдущей лекции специальная абстрактная машина SECD удобна для спецификации машинно-зависимых аспектов семантики Лиспа. Такой подход позволяет не загромождать определение компилятора, добиться его прозрачности, но главное, такое определение может быть машинно-независимым и переносимым [ 3 ] .

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

  1. единое пространство функций, их аргументов и всех обозначений, роль которых определяется по контексту при интерпретации форм;
  2. разрешение функциональных переменных, значение которых конструируется (вычисляется) в процессе их интерпретации. Это позволяет вводить частичные определения, уточняемые по мере необходимости;
  3. самоопределение основных механизмов символьной обработки и, следовательно, открытый характер системы программирования, поддерживающей функциональное программирование;
  4. мягкость пространственно-временных ограничений, без точных численных оценок по отдельным параметрам;
  5. поощрение рекурсивных определений;
  6. предельная уточняемость и детализируемость определений, управление временем их существования и выполнения.

Лисп-компилятор — это программа, написанная на Лисп, которая транслирует S-выражения, определяющие функции, в машинные подпрограммы. Это средство оптимизации, позволяющее ускорить работу программ от двух до ста раз, чем было бы при простой интерпретации.

Когда компилятор вызывается для компиляции функций, он находит определение функции в списке свойств названия функции. Компилятор транслирует найденное исходное S-выражение в объектное S-выражение, которое представляет подпрограмму на языке ассемблера. Ассемблер после этого ассемблирует код программы. Затем в списке свойств функции размещается ссылка на код программы.

Опыт показывает, что скомпилированная программа может работать намного раз быстрее, чем интерпретируемая программа, в зависимости от ее природы. Скомпилированные программы могут быть и экономичнее с точки зрения расхода памяти, требуя 50–80% от полного объема.

Основная часть компилятора — транслятор или функция, отображающая S-выражения, которые обозначают функции, на язык ассемблера. Единственное основание для того, чтобы рассматривать компилятор как псевдо-функцию, состоит в том, что он изменяет свойства названий функций по завершении компиляции.

Лисп-компилятор имеет уникальную историю. Он развивался пошаговым образом ( метод раскрутки ) [ 1 ] .

  1. Компилятор был написан и отлажен как Лисп-программа, состоящая из набора определений функций в виде S-выражений. Это позволило компилировать любые Лисп-программы и строить специализированные расширения Лисп-интерпретатора.
  2. Компилятору была дана команда скомпилировать себя самого. Данная операция называется раскруткой ( bootstrapping ). На это потребовалось более 5 минут на IBM 7090, поскольку значительная часть компилятора в основном интерпретировалась. В результате было создано эффективное расширение Лисп-системы, способное компилировать Лисп-программы и строить расширения Лисп-интерпретатора.
  3. Чтобы исключить повторение медленной раскрутки при дальнейших шагах установки системы, весь код компилятора заново был введен на языке ассемблера.
  4. Была создана системная лента, и компилятор стал загружаемым на уровне ассемблера.
  5. Процесс раскрутки был повторен до полного формирования кода Лисп-системы.

Компилятор вызывается псевдо-функцией COMPILE. Аргумент COMPILE — список названий функций, которые следует компилировать. Каждый атом в списке должен иметь определение функции в своем списке свойств до компиляции.

Обработка каждой функции происходит в три шага. Во-первых, S-выражение, задающее функцию, транслируется в текст на уровень ассемблера. При отсутствии S-выражения, компилятор сообщает об этом и переходит к другой функции. Во-вторых, текст программы на уровне ассемблера транслируется в код программы. И, наконец, если никаких ошибок не обнаружено, то S-выражение функции может быть удалено из списка свойств. Когда некоторые ошибки указывают на появление необъявленной переменной, компилятор предупреждает об этом и продолжает работу. Такая диагностика будет дополнительно уточнена при анализе значений переменной.

При написании большой Лисп-программы лучше отлаживать отдельные функции, используя интерпретатор, а компилировать только те из них, которые уже хорошо изучены.

Программист, планирующий применять компилятор, должен обратить внимание на следующие моменты.

  1. Нет необходимости компилировать все функции, которые используются лишь эпизодически. Интерпретатору доступны скомпилированные функции. Компилированные функции, использующие интерпретируемые функции, могут вычислять их непосредственно при счете.
  2. Порядок выполнения компиляции не имеет значения. Даже нет необходимости определять все функции до тех пор, пока они не понадобятся при счете. (Исключение из этого правила — специальные формы. Они должны быть определены до того, как компилируется их вызов.)
  3. При динамическом использовании LABEL результирующая функция не может быть скомпилирована полностью.
  4. Свободные переменные в компилируемых функциях должны объявляться до компиляции функций (См. лекцию 7).

Последнее требование проясняет роль типового контроля в стандартных, ориентированных на компиляцию без интерпретации, системах программирования.Компиляция всех объектов осуществляется без анализа фактических данных, а это и означает, что на момент компиляции переменные, как правило, являются свободными. Интерпретация располагает более полной информацией, связывающей необходимые для вычислений переменные с конкретными значениями, тип которых определен.

Компилятор и ассемблер могут быть удалены из комплекта Лисп-системы. В целом существует механизм пакетов, позволяющий управлять составом функций и объектов, включаемых в комплект. При удалении части системы освободившаяся память может быть присоединена к списку свободной памяти. Имеются реализации, в которых выделено минимальное ядро системы, все остальные функции загружаются по мере необходимости, а мусорщик может рассматривать память от неиспользуемых функций как свободную.

< Лекция 7 || Лекция 8: 123 || Лекция 9 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?

Алина Ленкова
Алина Ленкова
Россия, Ставрополь, СФ МГУПИ, 2014
Валерий Ромашов
Валерий Ромашов
Россия