Библиотека сайта rus-linux.net
Компилятор Glasgow Haskell
Глава 5 из книги "Архитектура приложений с открытым исходным кодом", том 2.Оригинал: The Glasgow Haskell Compiler
Авторы: Simon Marlow и Simon Peyton-Jones
Перевод: Н.Ромоданов
5.2. Общий взгляд на проект
На самом высоком уровне, компилятор GHC можно разделить на следующие три отдельные части
- Сам компилятор. По сути, это программа на языке Haskell, работа которой заключается в преобразовании исходного кода на языке Haskell в исполняемый машинный код.
- Загружаемые библиотеки. GHC поставляется с набором библиотек, которые мы называем загрузочными библиотеками, поскольку они представляют собой библиотеки, от которых зависит сам компилятор. Наличие этих библиотек в дереве исходного кода означает, что компилятор GHC может сам себя развертывать (bootstrap). Некоторые из этих библиотек очень тесно связаны с компилятором GHC, поскольку в них реализованы низкоуровневые функции, такие как тип
Int
, в терминах примитивов, которые определяются компилятором и системой времени выполнения. Другие библиотеки являются библиотеками более высокого уровня и они менее зависимы от компилятора, например, библиотекаData.Map
. - Система времени выполнения RTS (Runtime System). Это большая библиотека кода на языке C, который обрабатывает все задачи, связанные с выполнением откомпилированного кода языка Haskell, в том числе со сборкой мусора, планированием потоков выполнения, профилированием, обработкой исключений и так далее. Система RTS компонуется с каждой откомпилированной программой на языке Haskell. На систему RTS была потрачена значительная часть усилий по разработке, которые были вложены в компилятор GHC, а проектные решения, реализованные в ней, относятся к наиболее сильным сторонам языка Haskell, например, его эффективной поддержке одновременного выполнения операций и распараллеливания. Мы опишем систему RTS более подробно в разделе 5.5.
По сути, эти три отдельные части в точности соответствуют трем подкаталогам дерева исходных кодов компилятора GHC: compiler
, libraries
и rts
, соответственно
Мы здесь не будем тратить много времени на обсуждение загрузочных библиотек, поскольку они с архитектурной точки зрения малоинтересны. Все ключевые проектные решения воплощены в компиляторе и системе времени выполнения, поэтому мы посвятим оставшуюся часть этой главы обсуждению этих двух компонентов.
Оцениваем размер кода
В последний раз мы подсчитывали количество строк в компиляторе GHC в 1992 году — см. «The Glasgow Haskell compiler: a technical overview» («Компилятор Glasgow Haskell: технический обзор»), обзор на конференции JFIT, 1992, поэтому интересно посмотреть, как с тех пор все изменилось. На рис.5.1 приведены данные о количестве строк кода в компиляторе GHC в его основных компонентах; текущие значения сравниваются с теми, что были в 1992 году.
Модуль | Строки (1992 г.) | Строки (2011 г.) | Увеличение |
Синтаксический анализ | 1055 | 4098 | 3,9 |
Модуль переименований | 2828 | 4630 | 1,6 |
Проверка типов | 3352 | 24097 | 7,2 |
Сокращение синтаксического разнообразия языка | 1381 | 7091 | 5,1 |
Основные преобразования | 1631 | 9480 | 5,8 |
Преобразования STG | 814 | 840 | 1 |
Распараллеливания данных в языке Haskell | — | 3718 | — |
Генерация кода | 2913 | 11003 | 3,8 |
Генерация нативного кода | — | 14138 | — |
Генерация кода LLVM | — | 2266 | — |
Интерактивная среда GHCi | — | 7474 | — |
Абстрактный синтаксис языка Haskell | 2546 | 3700 | 1,5 |
Ядро языка | 1075 | 4798 | 4,5 |
Язык STG | 517 | 693 | 1,3 |
Язык C-- (был язык Abstract C) | 1416 | 7591 | 5,4 |
Представление идентификаторов | 1831 | 3120 | 1,7 |
Представление типов | 1628 | 3808 | 2,3 |
Прелюдия определений | 3111 | 269 | 0,9 |
Утилиты | 1989 | 7878 | 3,96 |
Профилирование | 191 | 367 | 1,92 |
Всего в компиляторе | 28275 | 139955 | 4,9 |
| |||
Система времени выполнения | |||
Весь код на языках C и C-- | 43865 | 48450 | 1,10 |
Рис.5.1: Количество строк в компиляторе GHC, прошлое и настоящее
В этих цифрах есть несколько примечательных особенностей:
- Несмотря на почти 20 летнюю безостановочную разработку компилятор увеличился в размерах только в 5 раз, от приблизительно 28000 строк и до 140000 строк кода на языке Haskell. При добавлении нового кода мы со всей страстью обновляем старый код, поддерживая основной код настолько обновленным, насколько это возможно.
- Есть несколько новых компонентов, хотя их размер равен приблизительно 28000 новых строк. Большая часть новых компонентов связана с генерацией кода: генераторы нативного кода для различных процессоров и генератор кода LLVM. (Бывший «Low Level Virtual Machine», проект LLVM, в состав которого входил универсальный генератор кода, позволяющий создавать код для различных процессоров. Дополнительную информацию смотрите в http://llvm.org/, и в главе о LLVM в первом томе сборника «Архитектура приложений с открытым исходным кодом»). Инфраструктура интерактивного интерпретатора GHCi также добавила более 7000 строк кода.
- Наибольшее увеличение размера произошло в единственном компоненте — модуле проверки типов, где было добавлено более 20 000 строк кода. Это неудивительно, учитывая, что большая часть недавних исследований с использованием GHC была связана с расширением системы типов (например, типы GADT [PVWW06] и семейства типов Type Families [CKP05]).
- Много кода было добавлено в основном компоненте Main, и это отчасти связано с тем, что ранее был скрипт на Perl, называемый «драйвером» и имеющим 3000 строк кода, который был переписан на языке Haskell и был перемещен в другое место в компиляторе GHC, а также поскольку была добавлена возможность компиляции нескольких модулей.
- Система времени выполнения практически не увеличилась: несмотря на то, что появилось много новых функциональных возможностей и было выполнено портирование на другие платформы, увеличение размера кода составляет не более, чем 10%. Приблизительно в 1997 году мы ее полностью переписали.
- В компиляторе GHC есть сложная система сборки, которая сегодня включает в себя около 6000 строк кода для GNU Make. Именно она была полностью переписана четвертый раз около двух лет назад, причем при каждом переписывании размер кода сокращался.
Компилятор
Мы можем выделить в компиляторе следующие три части:
- Менеджер компиляции (compilation manager), который отвечает за компиляцию нескольких исходных файлов на языке Haskell. Задача менеджера компиляции состоит в том, чтобы выяснить, в каком порядке компилировать отдельные файлы, и решить, какие модули не нужно перекомпилировать, потому что ни одна из их зависимостей не была изменена с того момента, когда они были последний раз скомпилированы.
- Собственно компилятор языка Haskell (Haskell compiler), который осуществляет компиляцию отдельного исходного файла на языке Haskell. Внутри компилятора GHC мы обозначаем его как
Hsc
. Как вы догадываетесь, здесь происходит большая часть действий. Результат работы Hsc зависит от того, какой выбран генератор: ассемблер, код на языке LLVM или байт-кода. - Конвейер (pipeline), который отвечает за объединение вместе с Hsc всех внешних программ, необходимых для компиляции файла с исходным кодом на языке Haskell в объектный код. Например, файлу с исходным кодом на языке Haskell может потребоваться предварительная обработка с препроцессором C перед тем, как он будет передан
Hsc
, а результат работыHsc
, который, как правило, представляет собой файл на языке ассемблера, для создания объектной файла должен быть передан в ассемблер.
Компилятор является не просто исполняемым файлом, который выполняет эти функции; он сам является библиотекой с большим интерфейсом API, который можно использовать для создания других инструментальных средств, работающих с исходным кодом на языке Haskell, например, среды разработки IDE и аналитических инструментальных средств.
Продолжение статьи: Компиляция кода на языке Haskell