Хабрахабр

[Перевод] Сколько стоит компилятор?

Компилирующий тулчейн является одним из самых больших и самых сложных компонентов любой системы, и, как правило, основан на опенсорсном коде, либо GCC, либо LLVM. На Linux-системе, только ядро операционной системы и браузер имеют больше строк кода. Для коммерческих систем, компилятор должен быть абсолютно надёжным, каким бы ни был исходный код, он должен генерировать надёжный, высокопроизводительный бинарный код.

Благодаря опенсорсу, не так много, как вы можете подумать. Сколько стоит такой большой, сложный и важный компонент системы? В этом посте, я приведу реальный пример, который показывает нам, что построение нового коммерческого компилирующего тулчейна возможно без огромных затрат.

Сколько нужно кода?

Анализ, проделанный с помощью (SLOCCount, автор David A Wheeler) показывает, что GCC содержит больше 5 миллионов строк. LLVM меньше, всего 1.6миллионов строк, но он более молодой, поддерживает по умолчанию только C и C++ и имеет в три раза меньше целевых архитектур. Однако полезный тулчейен имеет больше компонентов.

  • Отладчик: либо GDB (800К строк), либо LLDB (600К строк)
  • Линкер: GNU ld (160К строк), gold (140К строк), либо lld (60К строк)
  • Ассемблер/дизассемблер: GNU gas (850К строк), либо ассемблер LLVM
  • Binary utilities: GNU (90К строк) и/или LLVM (включено в исходники LLVM)
  • Emulation library: libgcc (входит в исходники GCC) или CompilerRT (340К строк)
  • Стандартная библиотека C: newlib (850К строк), glibc (1.2M строк), musl (82К строк) или uClibC-ng (251К строк)

В большинстве инструментов GNU, регрессионные тесты встроены в исходники. Кроме этого, тулчейн нуждается в тестировании. Плюс к этому, во встраиваемых системах нужен отладочный сервер для взаимодействия с отладчиком при тестировании. В LLVM, регрессионные тесты имеют отдельную кодовую базу, 500К строк.

Что нужно для портирования компилятора?

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

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

При разработке всех основных компиляторов были приложены значительные усилия для хорошего отделения кода, зависящего от целевой платформы, и портирование компиляторного тулчейна на новую архитектуру является посильной задачей. К счастью, большая часть огромной кодовой базы является обобщённой. Эта задача включает в себя пять стадий.

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

Реализация полной функциональности
Завершение всех функций компилятора и других инструментов. 2. Должен работать линкер, написан BSP, и, при необходимости, добавлены кастомные опции. Атрибуты, встроенные/intrinsic — функции, а также эмуляция недостающей функциональности должны быть завершены. Самое важное, полный набор регрессионных тестов должен проходить. В конце этого процесса пользователь получает полнофункциональный тулчейн.

Тестирование.
Самая большая часть проекта. 3. Тестирование должно покрывать три области:
— регрессионное тестирование, показывающее, что тулчейн не сломан и может работать для разных архитектур.
-Аттестационное тестирование (compliance testing), часто используются тесты заказчика, показывающие, что вся требуемая функциональность присутствует.
-бенчмарки, показывающие, что сгенерированный тулчейном код удовлетворяет критериям требуемой скорости выполнения кода, размера кода и энергоэффективности.

Развёртывание.
На этой стадии вам нужно помочь пользователям изучить новый компилятор и то, насколько он отличается от предыдущих инструментов, и обычно для этого нужны руководства в письменной форме и на видео. 4. Так бывает, когда LLVM и GCC заменяют старые проприетарные компиляторы, в связи с тем, что они гораздо более развиты в плане функциональности. Будут обнаруживаться новые баги, и также будет много сообщений о багах, вызванных различиями между старым и новым компилятором. Если пользовательская база велика, фаза развёртывания становится очень существенной.

Сопровождение
LLVM и GCC очень активно развиваются, и новая функциональность добавляется постоянно, как для поддержки новых языковых стандартов во фронтенд, так и для добавления новых оптимизаций в бэкенд. 5. Плюс к этому, конечно, у вас будет новая функциональность, специфичная для целевой архитектуры, и багрепорты от пользователей. Вы должны поддерживать свой компилятор в актуальном состоянии при всех этих изменениях.

Сколько нужно усилий в общем случае

Рассмотрим общий случай. Новая архитектура с большой пользовательской базой, должна поддерживать C и C++, и для bare metal, и для Linux. В этом случае, вероятно, архитектура поддерживает различные реализации, от маленьких процессоров, используемых для bare metal приложений или RTOS во встраиваемых системах, до больших процессоров, способных выполнять полноценное Linux-окружение.

Начальная версия тулчейна (proof of concept) должна быть реализована в течение 3-х месяцев. Полный релиз такого тулчейна занимает 1-3 человеко-года. Реализация всей функциональности должна занять 6-9 месяцев, и ещё 3 месяца, если требуется поддержка bare metal и Linux.

Начальное развёртывание занимает 3 месяца, но с большой пользовательской базой, может занять на 9 месяцев больше. Тестирование занимает как минимум 6 месяцев, но с большим количеством специфических тестов может занять до 12 месяцев.

Эти усилия могут отнимать от 0,5 человеко-месяца в месяц до, что более вероятно, 1 человеко-месяца в месяц. Усилия по поддержке сильно зависят от количества репортов от пользователей и количества новых функций.

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

Сколько нужно усилий в простейшем случае

Не всегда нужен компилятор, который будет использоваться большим количеством пользователей. Существуют различные специфичные процессоры, особенно DSP, которые разработаны в маленьких компаниях, состоящих из одного инженера. Там, где такие процессоры доказали свою коммерческую успешность, они начинают развиваться, и вместо крошечного ядра, программируемого на ассемблере инженером-одиночкой, становятся гораздо более мощными процессорами с большой командой программистов на ассемблере. В этом случае переход на компиляцию языка С будет означать огромный прирост продуктивности и снижение стоимости разработки.

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

Тестирование по-прежнему занимает больше всего усилий, и продолжается от 3 до 6 месяцев, но при маленькой пользовательской базе 3 месяца более чем достаточно. Стадия proof-of-concept по-прежнему занимает 3 месяца, но затем доводится до полнофункциональной версии ещё за 3 месяца.

Поддержка также необходима, но для маленькой системы с небольшой пользовательской базой будет достаточно 0,25 человеко-месяца в месяц.

Если небольшая горстка стандартных программ компилируется, этого может быть достаточно для демонстрации эффективности компилятора, без прохождения полного набора тестов. Для маленьких заказчиков, может бвть важно остановиться после реализации всей функциональности.

Исследование темы

В 2016 году в Embecosm обратилась компания, занимающаяся разработкой микроэлектроники, которая много лет использовала свой собственный DSP c 16-битным адресным пространством, разработнный специально для их узкой области. Они использовали уже третье поколение своего процессора, когда осознали, что тратят слишком большие усилия на программирование на ассемблере. Ситуация усугублялась тем, что стандартные кодеки, которые они использовали, имели референсную реализацию на С. У них был компилятор, но очень старый, и портирование кода на новое поколение DSP было невозможно.

Предполагалось, сто код при необходимости будет дорабатываться вручную. Embecosm был заказан тулчейн на основе LLVM, способный компилировать кодеки LLVM и производить высококачественный код. Заказчик также хотел получить опыт построения компиляторов, поэтому один из инженеров заказчика присоединился к команде Embecosm и занимался поддержкой компилятора, когда проект был закончен. У заказчика был готовый ассеблер и линковщик, который комбинировал все ассемблерные файлы в один, связывал все ссылки, и генерировал бинарный файл, загружаемый в DSP.

Для того, чтобы использовать newlib, мы создали псевдолинкер, который извлекает требуемые файлы из newlib в виде ассемблерных исходников, и объединяет их с тестовой программой. В первые три месяца мы разработали тулчейн на основе существующего ассемблера и дизассемблера. процессор в кремнии был недоступен, мы проводили тестирование на Verilator-модели чипа. Т.к. При отсутствии ELF, отладка на уровне исходника невозможна, но GDB способен загрузить программу и получить результат, что достаточно для тестирования. Для этого мы написали gdb-сервер, позволяющий GDB взаимодействовать с моделью.

Стало ясно, что есть два препятствия для достижения полной функциональности: 1) отсутствие поддержки бинарных ELF-файлов и 2) отсутствие поддержки 16-битных char. Это позволило нам скомпилировать тестовую программу и продемонстрировать, что компилирующий тулчейн работает.

Мы также реализовали поддержку 16-битных char в LLVM, как описано в этом посте. На второй фазе мы реализовали GNU-ассемблер/дизассемблер, используя CGEN, на это ушло около 10 дней. DSP имеет ряд специальных режимов поддержки арифметики с фиксированной точкой. С этими двумя вещами завершение полнофункционального тулчейна стало более простым, и мы смогли запускать стандартные LLVM lit и регрессионные тесты GCC для тулчейна, большинство из которых прошли успешно. Для их поддержки мы реализовали специальные встроенные и intrinsic-функции.

Поддержка ELF означает, что возможны такие техники, как link-time optimization (LTO) и garbage collection для секций, что приводит к успешной оптимизации кода, и это соответствует требованиям заказчика при ограниченном объёме памяти. На этой стадии у нас получился компилятор, который корректно компилирует код заказчика. При затратах в 120 человеко-дней, была достигнута цель компиляции С-кода для нового DSP.

Если они решат, что компилятор будет доступен широкому потребителю, они могут продолжить работу с полным тестированием тулчейна. Заказчик решил, что на этой стадии функциональность его устраивает, и продолжения работы не требуется.

Выводы

Два фактора сделали возможным построение полнофункционального компилирующего тулчейна за 120 человеко-дней.

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

Ни один челове не может знать всё про эмуляцию, GDB, CGEN-ассемблеры, GNU-линкер и компилятор LLVM. Команда экспертов
Хотя это был проект на 120 дней, над ним работала команда из пяти человек, каждый из которых имеет многолетний опыт.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть