Библиотека сайта rus-linux.net
LLVM
Глава 11 из книги "Архитектура приложений с открытым исходным кодом", том 1.
Оригинал: LLVM
Автор: Chris Lattner
Перевод: А.Панин
11.2. Существующие реализации языков программирования
Хотя достоинства архитектуры трех фаз бесспорны и хорошо описаны в книгах по разработке компиляторов, на практике данные архитектурные решения практически никогда не были реализованы в полной мере. Рассматривая реализации языков программирования (в момент начала работы над LLVM) , вы можете обнаружить, что реализации таких языков, как Perl, Python, Ruby и Java не содержат общего кода. Более того, такие проекты, как Glasgow Haskell Compiler (GHC) и FreeBASIC позволяют генерировать машинный код для разных моделей центральных процессоров, но их реализации жестко привязаны к одному языку исходного кода, который они поддерживают. Существует также широкий круг технологий компиляции специального назначения, на основе которых реализуются JIT-компиляторы для работы с изображениями, регулярными выражениями, драйверами графических карт, а также работы в других областях, требующих интенсивного использования центрального процессора.
Следует отметить тот факт, что существуют как минимум три истории успеха, связанные с этой архитектурой, а первой из них являются виртуальные машины Java и .NET. Эти системы предоставляют JIT-компилятор, окружение времени исполнения и достаточно хорошо проработанный формат байткода. Это означает то, что любой язык, который может быть скомпилирован в формат байткода (и таких языков множество3), может использовать преимущества оптимизатора и JIT наряду с использованием окружения времени исполнения. При этом стоит учесть тот факт, что эти реализации ограничивают гибкость выбора окружений времени исполнения: обе реализации осуществляют эффективную JIT-компиляцию, сборку мусора и используют проработанную объектную модель. В итоге такой подход позволяет получить приемлемую производительность в случаях, когда компилируемые языки, такие, как язык C, не полностью подходят по критериям (в качестве примера можно привести проект LLJVM).
Второй историей успеха является скорее всего самый неудачный и при этом самый популярный подход повторного использования технологий компилятора: преобразование переданного исходного кода в исходный код языка C (или какого-либо другого языка) и обработка его с помощью одного из существующих компиляторов C. Этот подход позволяет повторно использовать оптимизатор и генератор кода и является достаточно гибким решением, позволяющим контролировать окружение времени исполнения, а также данное решение позволяет участникам разработки системы предварительной обработки кода без лишних сложностей понять, реализовать и поддерживать ее. К сожалению, данный подход осложняет эффективную реализацию обработки исключений, позволяет использовать только ограниченные возможности отладки, замедляет процесс компиляции и может приводить к проблемам при работе с языками, требующими наличия гарантированных хвостовых вызовов (или других функций, не поддерживаемых языком C).
Завершающей список успешной реализацией данной модели является компилятор GCC4. GCC поддерживает множество систем предварительной обработки кода и разрабатывается активным и многочисленным сообществом. В течение длительного промежутка времени GCC был компилятором языка C с поддержкой различных целевых архитектур и с наличием поддержки нескольких дополнительных языков программирования, реализованной с использованием сложных приемов. По прошествии лет сообщество разработчиков GCC медленно реализовывало более совершенную архитектуру. В версии GCC 4.4 используется отдельное представление кода оптимизатором (известное как "Кортежи GIMPLE" - "GIMPLE Tuples"), которое в большей степени отделено от представления системы предварительной обработки кода, чем использующееся ранее. Также существуют системы предварительной обработки кода для языков Fortran и Ada, использующие необработанные деревья стандартного синтаксического анализа (AST).
Хотя эти продукты и были успешны, возможности их использования жестко ограничены, так как они проектировались как монолитные приложения. В качестве примера можно упомянуть о том, что компилятор GCC невозможно встроить в другие приложения, его невозможно использовать в качестве JIT-компилятора или интрепретатора, а также невозможно выделить и повторно использовать отдельные части GCC без задействования всего компилятора. Разработчикам, желающим использовать систему предварительной обработки кода для языка C++ из GCC в качестве инструмента для генерации документации, индексации кода, рефакторинга и статического анализа приходилось использовать GCC как монолитное приложение, выводящее интересующую их информацию в формате XML или разрабатывать расширения для использования стороннего кода в GCC.
Существует множество причин по которым отдельные части GCC не могут использоваться повторно в качестве библиотек, включающее в себя такие причины, как чрезмерное использование глобальных переменных, недостаточное использование инвариантов, некачественно проработанные структуры данных, распределенная кодовая база и использование макросов, которые затрудняют компиляцию кода для поддержки более чем одной комбинации из системы предварительной обработки кода и системы генерации кода. Наиболее сложные для исправления проблемы являются следствием недоработок в первоначальной архитектуре и возраста компилятора. В особенности GCC страдает от недоработок в распределении уровней и создании абстракций: система генерации кода получает доступ к дереву стандартного синтаксического анализа системы предварительной обработки кода для генерации отладочной информации, система предварительной обработки кода генерирует структуры данных для системы генерации кода и весь компилятор зависит от глобальных структур данных, изменяемых с помощью интерфейса командной строки.
Сноски
- http://en.wikipedia.org/wiki/List_of_JVM_languages
- Сейчас название расшифровывается как "GNU Compiler Collection".
Далее: 11.3. Представление кода в LLVM: LLVM IR