Кодогенерация
Кодогенерация — часть процесса компиляции, когда специальная часть компилятора, кодогенератор, конвертирует синтаксически корректную программу в последовательность инструкций, которые могут выполняться на машине. При этом могут применяться различные, в первую очередь машинно-зависимые оптимизации. Часто кодогенератор является общей частью для множества компиляторов. Каждый из них генерирует промежуточный код, который подаётся на вход кодогенератору.
Обычно на вход генератора кода подаётся дерево разбора[англ.] или абстрактное синтаксическое дерево. Дерево преобразуется в линейную последовательность инструкций промежуточного языка (например, в трехадресный код).
Сложные компиляторы, как правило, делают несколько проходов через различные промежуточные формы кода. Этот многоступенчатый процесс используется потому, что многие алгоритмы оптимизации кода проще реализовать каждый отдельно, или же потому, что какой-то шаг оптимизации зависит от результата отработки другого шага. Кроме того, при такой организации, легко создать один компилятор, который будет создавать код для нескольких платформ, так как достаточно заменить последний шаг генерации кода (бэкэнд, англ. backend).
Дальнейшие этапы компиляции могут и не относиться к «генерации кода», в зависимости от того, насколько значительными будут изменения, вносимые ими. Так, локальная оптимизация вряд ли может называться «генерацией кода», однако сам генератор кода может включать в себя этап локальной оптимизации.
Задачи генератора кода
правитьВ дополнение к основной задаче — преобразованию кода из промежуточного представления в машинные инструкции — генератор кода обычно пытается оптимизировать создаваемый код теми или иными способами. Например, он может использовать более быстрые инструкции, использовать меньше инструкций, использовать имеющиеся регистры и предотвращать избыточные вычисления.
Некоторые задачи, которые обычно решают сложные генераторы кода:
- Выбор инструкций: какие инструкции использовать
- Планирование инструкций: в каком порядке размещать эти инструкции. Планирование — это оптимизация, которая может значительно влиять на скорость выполнения программы на конвейерных процессорах
- Размещение в регистрах: размещение переменных программы в регистрах процессора.
Выбор инструкций обычно выполняется рекурсивным обходом абстрактного синтаксического дерева, в этом случае сравниваются части конфигураций дерева с шаблонами; например, дерево W:=ADD(X,MUL(Y,Z))
может быть преобразовано в линейную последовательность инструкций рекурсивной генерации последовательностей t1:=X
и t2:=MUL(Y,Z)
, а затем инструкцией ADD W,t1,t2
.
В компиляторах, использующих промежуточный язык, может быть две стадии выбора инструкций — одна для конвертирования дерева разбора в промежуточный код, а вторая (следует намного позже) — для преобразования промежуточного кода в инструкции целевой системы команд. Вторая стадия не требует обхода дерева: она может выполняться последовательно и обычно состоит из простой замены операций промежуточного языка соответствующими им кодами операций. На самом деле, если компилятор фактически является транслятором (например, один переводит Eiffel в C), то вторая стадия генерации кода может включать построение дерева из линейного промежуточного кода.
Генерация кода во время выполнения
правитьКогда генерация кода происходит во время выполнения программы, как в JIT, важно, чтобы весь процесс генерации кода был эффективен как по времени, так и по используемой памяти. Например, при интерпретации регулярных выражений, чаще создаются недетерминированные конечные автоматы, чем детерминированные, потому что они создаются быстрее и занимают меньше памяти. Несмотря на то, что создаётся, в общем, менее эффективный код, генерация кода в JIT может предоставить возможность профилирования информации, доступной только во время выполнения программы.
Литература
править- Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Ульман. Компиляторы: принципы, технологии и инструментарий = Compilers: Principles, Techniques, and Tools. — 2-е изд. — М.: Вильямс, 2008. — ISBN 978-5-8459-1349-4.
- Робин Хантер. Основные концепции компиляторов = The Essence of Compilers. — М.: «Вильямс», 2002. — С. 256. — ISBN 5-8459-0360-2.