52700.fb2
В куче мы храним разные данные. Данные бывают статическими (они нужны нам на протяжении выполне-
ния всей программы) и динамическими (время жизни динамических данных заранее неизвестно, например
это могут быть отложенные вычисления, мы не знаем когда ни нам понадобятся). У кучи также две опера-
ции: выделить блок памяти, эта операция принимает размер блока и возвращает адрес, по которому удалось
выделить память, и освободить память по указанному адресу. Регистры находятся в процессоре. В них можно
записывать и читать данные, при этом операции обращения к регистрам будут происходить очень быстро.
Посмотрим как GHC справляется с переводом процесса редукции синонимов на язык понятный нашему
компьютеру. Язык обновления стека и кучи. Это большая и трудная глава, читайте не спеша. Если покажется
совсем трудно – пропустите, вернётесь потом, когда захочется писать не только красивые, но и эффективные
программы.
10.1 Этапы компиляции
Рассмотрим этапы компиляции программы (рис. 10.1).
На первых трёх этапах происходит проверка ошибок. Сначала мы строим синтаксическое дерево про-
граммы. Если мы нигде не забыли скобки, не ошиблись в простановке ключевых слов, то этот этап успешно
| 155
Файл .hs
Построение синтаксического дерева
Разрешение имён
Проверка типов
Устранение синтаксического сахара
Core
Упрощение Core
Генерация кода для ghci
STG
Генерация Cmm
C
Native
LLVM
Рис. 10.1: Этапы компиляции
завершится. Далее мы приписываем ко всем функциям их полные имена. Дописываем перед всеми опреде-
лениями имя модуля, в котором они определены. Обычно на этом этапе нам сообщают о том, что мы забыли
определить какую-нибудь функцию, часто это связано с простой опечаткой. Следующий этап – самый важ-
ный. Происходит вывод типов для всех значений и проверка программы по типам. Блок кода, отвечающий
за проверку типов, является самым большим в GHC. Haskell имеет очень развитую систему типов. Многих
возможностей мы ещё не коснулись, часть из них мы рассмотрим в главе 17. Допустим, что мы исправили
все ошибки связанные с типами, тогда компилятор начнёт переводить Haskell в Core.
Core – это функциональный язык программирования, который является сильно урезанной версией
Haskell. Помните мы говорили, что в Haskell поддерживается несколько стилей (композиционный и декла-
ративный). Что хорошо для программиста, не очень хорошо для компилятора. Компилятор устраняет весь
синтаксический сахар и выражает все определения через простейшие конструкции языка Core. Далее проис-
ходит серия оптимизаций языка Core. На дереве описания программы выполняется серия функций типа Core
-> Core. Например происходит замена вызовов коротких функций на их правые части урвнений (встраивание
или inlining), выражения, которые проводят декомпозицию в case-выражениях по константам, заменяются
на соответствующие этим константам выражения. По требованию GHC может провести анализ строгости
(strictness analysis). Он заключается в том, что GHC ищет аргументы функций, которые могут быть вычисле-
ны более эфективно с помощью вычисления по значению и расставляет анотации строгости. И многие многие
другие оптимизации кода. Все они представлены в виде преобразования синтаксического дерева программы.
Также этот этап называют упрощением программы.
После этого Core переводится на STG. Это функциональный язык, повторяющий Core. Он содержит допол-