# Глава 11. Компиляция во внутренний код (ocamlopt)

В этой главе описывается высокопроизводительный компилятор внутреннего кода Objective Caml `ocamlopt`, который компилирует исходные тексты Caml в объектные файлы с внутренним кодом и компонует последние в самостоятельные исполняемые файлы.

Этот компилятор доступен не на всех платформах. Генерируемый им код работает быстрее байткода, создаваемого `ocamlc`, однако возрастает как время компиляции, так и размер исполняемых файлов. Тем не менее, совместимость с байткодом исключительно выскока: один и тот же код работает одинаково вне зависимости от того, скомпилирован он `ocamlc` или `ocamlopt`.

Файлы с внутренним кодом нельзя смешивать с файлами с байкодом: программа должна быть скомилирована целиком либо `ocamlc`, либо `ocamlopt`. Объектные файлы с внутренним кодом, кроме того, не могут загружаться в интерактивную систему `ocaml`.

## 11.1 Обзор компилятора

`ocamlopt` имеет интерфейс командной строки, весьма схожий с `ocamlc`. Компилятор принимает те же типы аргументов:

- Аргументы, заканчивающиеся на `.mli` считаются исходными файлами для интерфейсов единиц компиляции. В интерфейсах указываются имена, экспортируемые единицами компиляции: там объявлены имена переменных и их типы, определены типы данных, объявлены абстрактные типы данных, и т.д. Из файла `X.mli` компилятор `ocamlopt` создаст файл `X.cmi` со скомпилированным интерфейсом. Результат идентичен файлу, создаваемому `ocamlc`.

- Аргументы, заканчивающиеся на `.ml` считаются исходными файлами для реализаций единиц компиляции. Реализации содержат определения для имен, экспортируемых единицей, а также выражения, вычисляемые на предмет их сторонних эффектов. Из файла `X.ml` компилятор `ocamlopt` создает два файла - `X.o`. с внутренним кодом и `X.cmx` с дополнительной информацией для компоновки и оптимизации клиентов единицы. На компилированную реализацию всегда следует ссылаться по имени `X.cmx` (`ocamlopt`, считает что любой файл `.o` скомпилирован из кода на языке С, а не Caml).

  Реализация сверяется с интерфейсом `X.mli` (если он существует), как описано в документации `ocamlc` (гл. 8).

- Аргументы, заканчивающиеся на `.cmx` считаются скомпилированным объектным кодом. Они компонуются с объектными файлы, полученными при компиляции аргументов `.ml`, и стандартной библиотекой Caml, давая в результате исполняемую программу во внутреннем коде. Порядок следования в командной строке аргументов `.cmx` и `.ml` важен: во время исполнения единицы компиляции инициализируются в том же порядке, так что во время компоновки ошибкой будет использовать компонент единицы до ее инициализации. Поэтому файл `X.cmx` должен стоять в командной строке компилятора прежде любых файлов `.cmx`, ссылающихся на единицу `X`.

- Аргументы, заканчивающиеся на `.cmxa` считаются библиотеками объектного кода. Такая библиотека в двух файлах (`lib.cmxa` и `lib.a`) содержит набор объектных файлов (`.cmx/.o`). Она создается командой `ocamlopt -a` (см. описание опций ниже). Объектные файлы в библиотеке компонуются как обычные файлы `.cmx` в том же порядке, в каком строился файл `.cma`. Единственное различие состоитв том, что, если в программе нет ссылок на какой-то объектный файл в библиотеке, то он и не компонуется с программой.

- Аргументы, заканчивающиеся на `.c`, передаются комплятору С, который генерирует объектный файл `.o`. Затем этот файл компонуется с программой.

- Аргументы, заканчивающиеся на `.o`, `.a` или `.so` (`.obj`, `.lib` и `.dll` под Windows) считаются объекнтыми файлами и библиотеками С. Они компонуются с программой.

Результатом стадии компоновки является обычный исполняемый файл. Для запуска ему не нужна `ocamlrun`.

## 11.2 Параметры

`ocamlc` разпознает следующие параметры командной строки:

-a

  Строит библиотеку (файл `.cmx/.a`) из объектных файлов (`.cmx/.o`), перечисленных в командной строке, не компонуя их в исполняемый файл. Имя библиотеки задается параметром `-o`. По умолчанию используется `library.cmxa`.

  Если в командной строке присутствуют параметры `-cclib` или `-ccopt`, они также сохраняются в библиотеке, а затем при автоматически добавляются к командам при компоновке, если только не задан параметр `-noautolink`.

-cclib -llibname

  Передать компилятору и компоновщику С опцию -llibname. В результате указанная библиотека С будет скомпонована с программой.

-ccopt option

  Передать указанную опцию компилятору и компоновщику С. Например, `-ccopt -L` dir заставит его искать библиотеки С в каталоге dir.

-compact

  Оптимизировать код по размеру, а не быстродействию. В результате получаются программы чуть меньшего размера, работающие чуть медленее. По умолчанию используется оптимизация по быстродействию.

-i

  Компилятор выводит все определенные имена, а также выведенные типы или определения при компиляции реализации (файла `.ml`). Это бывает полезно для проверки типов, распознанных компилятором. Кроме того, поскольку вывод соответствует синтаксису интерфейсов, он может помочь явно написать интерфейс (файл `.mli`) для файла: достаточно перенаправить вывод компилятора в файл `.mli` и убрать из результата декларации имен, экспорт которых не предполагается.

-I directory

  Добавляет `directory` к списку каталогов, в которых ищутся компилированные файлы интерфейсов (`.cmi`), компилированные объектные файлы (`.cmx`) и библиотеки (`.cmxa`). По умолчанию в первую очередь файлы ищутся в текущем каталоге, затем - в каталоге стандартной библиотеки. Каталоги, заданные опцией `-I` учитываются после текущего, в том же порядке, как они заданы в командной строке, но перед стандартной библиотекой.

  Если каталог начиначается со знака `+`, то он считается заданным относительно каталога стандартной библиотеки. Например, `-I +labltk` добавляет к списку поиска подкаталог `labltk` стандартной библиотеки.

-inline n

  Устанавливает агрессивность встраиваемости в значение n, где n - положительное целое число. Значение `-inline 0` приводит к тому, что функции вовсе не становятся встраиваемыми, за исключением тех, тело которых меньше точки вызова. Таким образом, встраиваемость не приводит к увеличению объема кода. Агрессивность по умолчанию (`-inline 1` предусматривает встраиваемость чуть больших по размеру функций, что дает небольшой прирост кода. Дальнейнее увеличение значения этого параметра приводит к том, что все более и более объемные функции становятся кандидатами на встраивание, но и код от этого может значительно вырасти.

-linkall

  Форсирует компоновку всех модулей библиотеки. Без этого параметра модули, на которых ссылок нет, не компонуются. При построении библиотеки (флаг `-a`) установка флага `-linkall` приводит к тому, что в последующем при компиляции программ, использующих эту библиотеку, все модули библиотеки будут компоноваться заново.

-noassert

  Отключает проверку утверждений, и утверждения не компилируются. При компоновке ранее скомпилированных файлов этот флаг не работает.

-noautolink

  При компоновке библиотек `.cmxa` опции `-custom`, `-cclib` и `-ccopt`, вероятно хранящиеся в библиотеках (если они были указаны при построении библиотек), игнорируются. Этот флаг полезен, если библиотека содержит неправильные спецификации библиотек или опций С. В этом случае надо указать в командной строке правильные библиотеки и опции С.

-nolabels

  Игнорировать неопциональные метки в типах. В этом случае метки не могут использоваться в приложениях, и порядок аргументов становится строгим.

-o exec-file

  Имя файла, создаваемого компоновщиком. По умолчанию по традициям Unix создается файл `a.out`. Если используется опция `-a`, укажите имя библиотеки. Если используется опция `-output-obj`, укажите имя объектного файла.

-output-obj

  Вместо исполняемого файла компоновщик создает объектный файл С. Это позволяет при необходимости оформить код Caml как библиотеку С, которую можно вызывать из любой программы С. Более подробно см. раздел 18.7.5. По умолчанию создается файл `camlprog.o`, но имя может быть задано с помощью опции `-o`.

-p

  Генерировать дополнительный код для записи профилировочной информации при исполнении программы. Такую информацию можно изучить с помощью программы `gprof` (см. подробнее гл. 17). Параметр должен быть задан как во время компиляции, так и во время компоновки. Можно компоновать объектные файлы, скомпилированные в обычном режиме, но профилирование в этом случае будет менее точным.

  Unix

    Подробную информацию о профайлах см. в страницах руководства Unix `gprof(1)`.

    Полная поддержка `gprof` есть только на некоторых платформах (в настоящее время - Intel x86/Linux и Alpha/Digital Unix). На других платформах профайлы менее точны (нет графов вызова, только профайл по времени).

  Windows

    Этот параметр не работает под Windows.

-pack

  Создает объектный файл (`.cmx/.o`) и связанный с с ним скомпилированный интерфейс (`.cmi`), которые включают все объектные файлы `.cmx`, перечисленные в командной строке. Эти файлы становятся субмодулями результирующего файла `.cmx`. Имя последнего задается опцией `-o`. Например,

      ocamlopt -pack -o p.cmx a.cmx b.cmx c.cmx

  создает компилированные файлы `p.cmx` и `p.cmi`, описывающие единицу компиляции, включающую субмодули A, B и C, соотвествующие содержанию объектных файлов `a.cmx`, `b.cmx` и `c.cmx`. В дальнейшем на них можно ссылаться как `P.A`, `P.B` и `P.C`.

  Unix

    Параметр `-pack` доступен лишь на тех платформах, для которых существуют инструменты GNU `binutils` `nm` и `objcopy`.

-pp command

  Компилятр вызывает заданную команду `command` как препроцессор для каждого исходного файла. Вывод команды перенаправляется в промежуточный файл, который и компилируется. Если компиляция проходит без ошибок, по ее завершении промежуточный файл удаляется. Имя файла конструируется на основе имени исходного файла и получает расширение `.ppi` для интерфейса (`.mli`) или `.ppo` для реализации (`.ml`).

-principal

  Во время проверки типов компилятор проверяет также информацию о путях, следя чтобы все типы выводились приниципально. Программы, допустимые в режиме `-principal`, допустимы и в режиме по умолчанию с эквивалентными типами, однако бинарные сигнатуры в этом случае другие.

-rectypes

  Разрешает во время проверки типа произвольные рекурсивные типы. По умолчанию поддерживаются только рекурсивные типы с рекурсией по типу объекта.

-S

  Сохранять код ассемблера, полученный во время компиляции. Код для файла `X.ml` сохраняется как `X.s`.

-thread

  Компилирует или компонует многопоточные программы с использованием библиотеки `thread`, описанной в главе 24. На самом деле, эта опция просто подключает особую версию стандартной библиотеки, безопасную по потокам.

-unsafe

  Отключает проверку границ на массивах и обращении к строкам (конструкции `v.(i)` и `s.[i]`). Программы, собранные с этой опцией несколько быстрее, но не являются безопасными: при обращении к элементу за пределами массива или границы строки может произойти все, что угодно.

-v

  Выводит номер версии компилятора и путь к стандартной библиотеке, после чего прекращает работу.

-verbose

  Выводит все внешние команды перед их выполнением. В частности это касается ассемблера, компилятора и компоновщика C.

-version

  Выводит номер версии компилятора в краткой форме (например `3.06`) и прекращает работу.

-w warning-list

  Включает или выключает предупреждения согласно аргументу `warning-list`. Аргумент является строкой из одной или нескольких букв следующего значения:

  A/a

    включить/выключить все предупреждения.

  C/c

    включить/выключить предупреждения о подозрительных комментариях.

  D/d

    Включить/выключить предупреждения об использовании устаревших функций.

  F/f

    Включить/выключить предупреждения о неполном применении функций (например, `f x; expr` при том, что вызов `f x` имеет тип функции).

  L/l

    Включить/выключить предупреждения о пропуске меток в вызовах.

  M/m

    Включить/выключить предупреждения о переопределенных методах.

  P/p

    Включить/выключить предупреждения о неполных совпадениях (пропущенные ветки в поиске по образцу).

  S/s

    Включить/выключить предупреждения о предложениях с типом, отличным от `unit` (например, `expr1; expr2`, когда `expr1` не имеет тип `unit`.

  U/u

    Включить/выключить предупреждения о неиспользуемых (избыточных) совпадениях с образцом.

  V/v

    Включить/выключить предупреждения о скрытых переменных экземпляра.

  X/x

    Включить/выключить все остальные предупреждения.

  По умолчанию используется установка `-w Al` (все предупреждения, кроме меток).

-warn-error

  Все предупреждения, включенные в аргумент `warning-list` считаются ошибками. Компилятор останавливается на ошибке при появлении одного из предупреждений, вместо того, чтобы продолжать выполнение. `warning-list` является строкой из одной или нескольких букв того же значения, что и в опции `w`: прописная буква превращает предупреждение в ошибку, строчная оставляет ее предупреждением. Значение по умолчанию - `-warn-error a` (все предупреждения не считаются ошибками).

-where

  Выводит путь к стандартной библиотеке и прекращает работу.

## 11.3 Распространенные ошибки

Сообщения об ошибказ практически идентичны сообщениям `ocamlc`. См. раздел 8.4.

## 11.4 Совместимость с компилятором байткода

В этом разделе перечислены известные случаи несовместимости компилятора байткода и компилятора во внутренний код. В остальном код, созданный этими компиляторами, должен работать одинаково.

- Следующие операции приводят к остановке программы через системное прерывание или аварийный сигнал Unix (байткод вызывает исключение):

  - Целочисленное деление или деление по модулю на ноль.

  - Переполнение стека.

  - (Только на процессорах Alpha). Операции над числами с плавающей точкой, в которых участвуют бесконечные или денормализованные числа (все остальные процессоры, поддерживаемые `ocamlopt` работают с такими числами правильно, по стандарту IEEE 754).

  В частности, переполнение стека из-за глубокой рекурсии на большинстве ядер Unix приводит к сигналу "нарушение сегментации".

- Сигналы отмечены лишь по время выделения кучи программой. Иными словами, если сигнал возник в части кода, который не смог выделить память, его обработчик будет вызван лишь в следующий момент выделения кучи.

Лучший способ избежать таких несовместимостей - это никогда не перехватывать исключения `Division_by_zero` и `Stack_overflow`, то есть независимо от компилятора считать такие ситуаци ошибками. Зачастую лучше заранее проверить делитель, чем ловить исключение.