Наши партнеры

UnixForum





Библиотека сайта 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