- •Тема 2. Основные принципы построения трансляторов
- •Трансляторы, компиляторы и интерпретаторы - общая схема работы
- •Формальное определение транслятора
- •Определение компилятора. Отличие компилятора от транслятора
- •Многопроходные и однопроходные компиляторы
- •Определение интерпретатора. Разница между интерпретаторами и трансляторами
- •Трансляция в ассемблер
- •Методики создания компилятора
- •Метод раскрутки
- •Кросс-транслятор
- •Виртуальная машина
- •Компиляция "на лету"
- •Этапы трансляции. Общая схема работы транслятора
- •Лексический анализ
- •Синтаксический анализ
- •Семантический анализ
- •Оптимизация кода
- •Генерация кода
- •Просмотры
Кросс-транслятор
Пусть у нас есть два компьютера: компьютер M с языком ассемблера A и компьютер M1с языком ассемблера A1. Кроме того, предположим, что имеется компилятор KA1: P→A1, а сам компьютер M по каким-то причинам не доступен либо пока еще не существует компилятор KA: P→A. Нас интересует компилятор KA: L→A. В такой ситуации мы можем использовать M1 в качестве инструментальной машины и написать компилятор KP: L→A, который принято называть кросс-транслятором (cross-compiler). Как только машина M станет доступной, мы сможем перенести KP на M и "раскрутить" его с помощью KA. Понятно, что это решение достаточно трудоемко, поскольку могут возникнуть проблемы при переносе, например, из-за различий операционных систем.
Под переносимой (portable) программой понимается программа, которая может без перетрансляции выполняться на нескольких (по меньшей мере, на двух) платформах. В связи с этим возникает вопрос о переносимости объектных программ, создаваемых компилятором. Компиляторы, созданные по методикам, рассмотренным выше, порождают непереносимые (non-portable) объектные программы. Поскольку компилятор, в конечном итоге, является программой, то мы можем говорить и о переносимых компиляторах. Одним из способов получения переносимых объектных программ является генерация объектной программы на языке более высокого уровня, чем язык ассемблера. Такие компиляторы иногда называют конвертерами (converter).
Виртуальная машина
Другой способ получения переносимой (portable) объектной программы связан с использованием виртуальных машин (virtual machine). При таком подходе исходный язык транслируется в коды некоторой специально разработанной машины, которую никто не собирается реализовывать "в железе". Затем для каждой целевой платформы пишется интерпретатор виртуальной машины.
Понятно, что архитектура виртуальной машины должна быть разработана таким образом, чтобы конструкции исходного языка удобно отображались в систему команд и сама система команд не была слишком сложной. При выполнении этих условий можно достаточно быстро написать интерпретатор виртуальной машины.
Одна из первых широко известных виртуальных машин была разработана в 70-х годах Н.Виртом при написании компилятора Pascal-P. Этот компилятор генерировал специальный код, названный P-кодом и представляющий собой последовательность инструкций гипотетической стековой машины. Сегодня идея виртуальных машин приобрела широкую известность благодаря языку Java, компиляторы которого обычно генерируют так называемый байт-код, т.е. объектный код, который представляет собой последовательность команд виртуальной Java-машины.
Рис. 2.7. Схема работы виртуальной машины
Компиляция "на лету"
Рис. 2.8. Схема работы JIT-компилятора
Основная неприятность, связанная с использованием виртуальных машин, заключается в том, что обычно время выполнения интерпретируемой программы значительно больше, чем время работы программы, оттранслированной в "родной" машинный код. Для того, чтобы увеличить скорость работы приложений, была разработана технология компиляции "на лету" (Just-In-Time compiling; иногда такой подход называют также динамической компиляцией). Идея заключается в том, что JIT-компилятор генерирует машинный код прямо в оперативной памяти, не сохраняя его. Это приводит к значительному увеличению скорости выполнения приложения.
Часто JIT-компилятор используется вместе с интерпретатором виртуальной машины.
Организовывается это следующим образом. Вначале сгенерированный байт-код поступает на вход интерпретатору виртуальной машины, которая его интерпретирует. Одновременно с интерпретатором работает программа, которая вычисляет время интерпретации какого-то куска байт-кода, например, процедуры. Если оказывается, что время интерпретации некоторого куска кода достаточно большое, то вызывается JIT-компилятор, который транслирует его в "родные" машинные коды. Когда при выполнении приложения произойдет повторное обращение к этому куску кода, то он уже не будет интерпретироваться, а будет выполняться сгенерированный фрагмент машинного кода.
Использование связки "компилятор+интерпретатор+JIT-компилятор" позволяет заметно повысить скорость выполнения исходной программы, причем переносимость кода, создаваемого компилятором, естественно, сохраняется.
