Т. Пратт, М. Зелковиц - Языки программирования - разработка и реализация (4-е издание_ 2002) (1160801), страница 32
Текст из файла (страница 32)
В другом варианте тело макроса может бьггь уже частично оттранслнравано, так что семантический анализа- 110 Глава 3. Вопросы трансляции языка тор может обрабатывать его непосредственно, вставляя соответствуютций объек.гный код и создавая соответствующие элементы таблицы, а затем продолжить анализ исходной программы, Операция, выполняемая во время холтиляции, — это операция, которая должна выполняться во время трансляции и осуществлять контроль над трансляцией исходной программы. В языке С имеется несколько таких операций. Операция 1)с)е1т пе позволяет вычислять значение констант и выражений до начала компиляции программы.
Конструкция Фт Ыт)ет' (Ы-с)е)1пео) позволяет транслировать какой-то один из нескольких альтернативных фрагментов кода в зависимости от наличия илн отсутствия определенных переменных. Эти переключатели позволяют программисту менять последовательность компилируемых операторов. Например, для компиляции альтернативных версий программ можно использовать содержащий текст исходной програлтмы файл, подобный следующему: абеттпе рс !*либор иеиду верскяии кода для РС к для ЫМ)Х*/ Ргоргаш Ыгттекэ атгое1 рс у*если определено рс, то требуется код длв РС*т я*выполнение кода версии РС*т !*наприиер. вывод в систеие М~сгоаота Ытпбоыа*Х ае1ае к*в:погптение иода версии ПИ)Хит' х*наприиер. вывод в оболо ке моттг х и;гбоиа*х т)енот т 3.2.2.
Синтез объектной программы На закл ючитель пои этапе трансляции происходит создание выполняемой программы на основе того, что было сделано семантическим анализатором. Этот этап обязательно включает генерации) кода и может также включать оптимизацию получившейся программы. Если подпрограммы транслировались отдельно или использовались библиотечные подпрограммы, то необходима заключительная стадия редактирования св>щей и загрузки, чтобы получить полностью пригодную для выполнения программу.
Оптимизация. Обычно результатом работы семантического анализатора является оттранслированная выполняемая программа, представленная в некотором промежуточном коде, внутреннее представление которого может быть цепочкой операций и операндов или таблицей, содержащей последовательности операций и операндов. На основе этого внутреннего прсдставле)шя генераторы кода могут создавать выходной объектньш код в нужном формате. Тем не менее еще до генерации этого кода обы шо происхолпт нскоторая оптимизация программы в сс внутреннем представлении. Для семантического анализатора типичной ситуацией является гснераппя внутреннего представления программы по частям, как если бы анализировалнсь отдельные фрагменты исход.птй программы.
Обычно семантическому анал плат ору безразлична та часть кода, которая уже сгецерирована, Но такая фрагментарная организация работы анализатора может привести к созданию чрезвычайно неэффективного кода (папрттхтер, в регистр, в котором может быть сохранено значение из некоторой области памяти в конце одного сгенерированного фрагмента, немедленно персзагружается значение из 3.2. Этапы трансляции 111 той же самой области памяти в начале следующего фрагмента). Например, оператор >т = в ° с ° з может быть сгенерирован в такой промежуточный код: (а> Тавр> - В .
С (Ы Тевр2 = Тегвр1 + 0 (С> 4 = Тевр2 Его непосредственный перевод приведет к созданию следующего неэффективного кода: 1. Загрузит~ З в регистр (лз (а>> 2. Добавлть С н озону регистру 3. Сохранит~ значение регистра в Тевр1 4. Загрузит~ в регистр Тевр1 (из ( Ы > 5. Добави ч 3 и эгону регистру б Сохранит~ значение регистра в Теяо2 7. Загрузлть в регистр Телр2 (из (с» Я Сохоанить зна~ение регистоа в А Инструкции 3, 4, 6 и 7 являются лишними, так как все промежуточные данные можно хранить в регистре, а потом сохранить результат в А.
Обычно оказывается предпочтительным позволить семантическому анализатору строить такие неэффективные последовательности команд, чтобы потом па стадии оптимизации заменить их другими, лишенными таких очевидных недостатков. Многие компиляторы не ограничиваются простой оптимизацией подобного сорта, а анализируют программу для выявления других нозможпостей усовершенствования (например, однократное вычисление общих полвыражений, вынесение инвариантных операций из тела цикла, оптимизация использования регистров, оптимизация вычисления формул доступа к алементам массива).
Вопросам оптимизации посвящены многочисленные исследования, разработано много сложных и топких методов оптимизации (см. библиографию в конце этой главы). Генерация кода. После оптимизации оттранслированной во внутренний код программы она должна быть преобразована в инструкции ассемблера или в машинный код, или в какую-либо другую форму объектного кода, которая и станет результатом трансляции. Процесс генерации включает в себя определенное форматирование той информации, ко горая содержи гся во внутреннем представлении программы. Получщппийся при генерации код либо может быть уже непосредственно выполняемым кодом, либ>о его придется подвергнуть дальнейшей обработке (например, люжет потребоваться обработка ассемблером или редактирование связей и загрузка).
Редактирование связей и загрузка. Этот заключительный зтап трансляции не является обязательным. Фрагмситьт кода, полученные в результате разлсльной трансляции подпрограмм, на данном этапе объединяются в единую вы полняемуто программу в ее окончательном виде. Выполняемые программы, полученные на предыдутцих агапах трансляции, как правило, имеют почти окончательный вид. Исключение составляют те места, где имеются обращения к внешним данным или друг там подпрограммам. Связи между этими разрозненными фрагментами задаются в присоединенных тиблицалг загрузчики, созланных транслятором. За- 112 Глава 3.
Вопросы трансляции языка грузчик с редиктировицием связей (или редактор связей) загружает различные фрагменты оттранслированного кода в память и затем использует присоединенные таблицы загрузчика для связывания этих фрагментов в единую программу, вставляя соответствующие данные и адреса подпрограмм в код по мере надобности. В итоге получается выполняемая программа в окончательной форме, готовая к работе. Раскрутка Часто транслятор для какого-либо нсшого язаяка пишется на этом же языке. Например, исходный компилятор для Разса! был написан на Разса1 и предназначался для работы иа виртуальной машине, использующей Р-код.
При этом возникает естественный вопрос: с чего начинать? Если исходный компилятор для Равса1 был написан на Разса!, то как он сам был откомпилирован? Эта ситуация носит названиераскрутют. Один из способов решения этой проблемы — откомпилировать компилятор вручную в его Р-код. Это довольно скучное занятие, хотя и не слишком сложное. Когда компиляция закончена, интерпретатор Р-кода может выполнить такой оттранслированный вручную компилятор. После того как появился работаклций компилятор языка Разса!, можно произвести оптимизацию сгенерированного кода для улучшения самого процесса компиляции. Переход ца новую машину нс представляет проблемы, поскольку уже имеется исходный компилятор на предыдущей машине, который можно использовать для получения Р-кода. Потребуется также изменить интерпретатор Р-кода, по эта задача также не очень сложна.
Диагностика ошибок при помощи компилятора В 60-е гг., ко~да пакетная обработка вызывала болыпие задержки в получении результатов компиляции, стала популярной диагностика ошибок при помощи компиляторов. Во врезке <Обзор языка 3,2» представлен краткий обзор работ в области развития компиляторов, диагностирующих ошибки, для СОВС 131), СОРЕ и Р1,УС [321, проводившихся в Корнсльском университете (СогпеП Пичегз1ту).
В данном случае преследовалась цель создать быстро работающий компилятор, поскольку выполнение учебных программ обычно вани мадо доли секунды и после правильного выполнения о пих можно было забыть. 3.3. Формальные модели трансляции Как было отмечено в предыдущем разделе, часть теории компиляции, относяпгаяся к распознаванию синтаксических структур, достаточно стандартна и основывается, как правило, на теории контекстно-свободных языков.
Мы посвятим несколько страниц краткому обзору этой теории. Формальное определение синтаксиса языка программирования называется обычно граммишикой по аналогии с общепринятой терминологией для естественных языков. Грамматика состоит из набора правил (называемых привилплеи подстановки, или про<)укциями'), определяю- В отечественной литературе этот термин употрсбллетсл довольно редко — Прь чеч. ииуч ред 3.3. формальные модели трансляции 1 13 тцих последовательности символов (или лексем), которые образуют допустимые для определяемого языка программы. Формальная грамматика — это грамматика, в которой используется строго определенная система обозначений.