- PVSM.RU - https://www.pvsm.ru -

Путеводитель по исходникам LLVM

В моём углубленном курсе компиляторов прошлой осенью мы провели некоторое время, изучая дерево исходников LLVM. Миллион строк кода на C++ выглядят пугающе, но я нахожу это интересным упражнением, и, по крайней мере, некоторые студенты с этим согласны, и я подумал, что я попытаюсь написать что-то подобное. Мы будем использовать LLVM 3.9, но предыдущие (и, возможно, будущие) релизы не сильно отличаются.
Путеводитель по исходникам LLVM - 1

Я не хочу тратить много времени на теоретические основы LLVM, но есть несколько вещей, которые вы должны знать.

Ядро LLVM не содержит фронтенды, только миддленд-оптимизаторы, несколько бэкендов, документацию, и много вспомогательного кода. Фронтенды, такие, как Clang, живут в отдельных проектах.

Промежуточное представление кода в ядре LLVM живёт в ОЗУ и с ним можно производить манипуляции с использованием большого C++ API. Это представление может быть сохранено в виде читаемого текста и распарсено обратно в память, но только для удобства отладки: при нормальной компиляции с использованием LLVM, текстовый IR никогда не генерируется. Обычно фронтенд строит IR с помощью вызовов LLVM API, затем запускает некоторые проходы оптимизации, и после вызывает бэкенд, который генерирует ассемблер или машинный код. Когда код LLVM записывается на диск (чего не происходит при нормальной компиляции проектов C и C++ с использованием Clang), он сохраняется как «биткод», компактное бинарное представление.

Основная документация на LLVM API генерируется в doxygen, и может быть найдена здесь [1]. Эту информацию сложно использовать, если вы уже не знаете в точности, что вам нужно делать, и что искать. Руководства, ссылки на которые даны ниже, это стартовая точка для изучения LLVM API.

Обратимся к коду. Корневая директория [2] содержит:

bindings [3] — «связки», позволяют использовать LLVM API из языков, отличных от C++. Существуют также и другие связки, с языком С (о котором речь будет ниже), и Haskell (его нет в этом дереве).

cmake [4] — LLVM использует CMake, а не autoconf. Просто скажите спасибо тем, кто сделал это за вас.

docs [5] — документация в формате ReStructuredText. Смотрите пример руководства по языку [6], которое определяет смысл каждой инструкции LLVM (GitHub отображает .rst файлы как HTML по умолчанию, вы можете посмотреть «сырой» файл здесь [7]). Материал в поддиректории с руководством [8] особенно интересен, но не смотрите на него там, лучше зайдите сюда [9]. Это лучший способ изучить LLVM!

examples [10]: Это исходники, которые прилагаются к руководству. Как LLVM-хакерам, вам следует брать отсюда код, CMakeLists.txt, и т.п. отсюда каждый раз, когда это возможно.

include [11]: Первая поддиректория, llvm-c [12], содержит связки для языка С, которые я не использовал, но которые выглядит довольно разумно. Важно то, что разработчики LLVM стараются сохранять эти связки стабильными, в то время, как С++ API изменяется с каждым релизом, хотя скорость изменений, кажется, замедляется последние несколько лет.

Вторая поддиректория, llvm [13], большая: она содержит 878 заголовочных файлов, определяющих LLVM API. В общем случае, проще использовать doxygen-версии этих файлов, чем читать их напрямую, но часто приходится грепать эти файлы в поисках какой-либо функции.

lib [14] содержит действительно полезные вещи, мы рассмотрим их ниже отдельно.

projects [15] не содержит ничего по умолчанию, однако сюда копируются компоненты LLVM, такие, как compiler-rt (рантайм-библиотеки для таких вещей, как санитайзеры), поддержка OpenMP и библиотеки LLVM C++, живущие в других репозиториях.

resources [16]: что-то для Visual C++, что не нужно ни вам, ни мне, (подробнее здесь [17])

runtimes: [18] ещё один плейсхолдер для внешних проектов, добавленный только прошлым летом (2016 года. прим. перев.), и я не знаю, на самом деле, для чего это.

test: [19]: большая директория, содержит тысячи юнит-тестов LLVM, они запускаются, когда вы собираете цель check (make check-all, прим. перев.). Большая часть, это файлы .ll, содержащие LLVM IR в текстовой форме. Они тестируют разные вещи, например, что проход оптимизации приводит к ожидаемому результату. Я рассмотрю тесты LLVM в подробностях в будущем посте.

tools: [20] сам по себе LLVM, это просто коллекция библиотек, и в нём нет выделенной функции main. Большинство поддиректорий в директории tools содержат исполняемые инструменты, которые линкуются с библиотеками LLVM. Например, llvm-dis, это дизассемблер, переводящий биткод в формат текстового ассемблера.

unittests: [21] больше юнит-тестов, также запускаются при сборке цели check. Это файлы C++, которые используют фреймворк Google Test [22] для вызова API напрямую, в отличие от тестов в директории «tests», которые запускают функции LLVM не напрямую, а через запуск ассемблера, дизассемблера или оптимизатора.

utils: [23] моды emacs и vim для соблюдения стиля кодинга LLVM, файл Valgrind для подавления ложноположительных срабатываний, инструменты lit и FileCheck для поддержки юнит-тестирования, и много других разных вещей. Возможно, большая часть из них вам не нужны.

OK, пока всё было довольно просто. Мы пропустили директорию lib [14], в которой содержится практически всё важное. Посмотрим на поддиректории:

Analysis [24] директория содержит множество статических анализаторов, таких, как анализ алиасов и глобальных значений. Некоторые анализаторы имеют структуру проходов LLVM и должны запускаться менеджером проходов, другие представляют собой библиотеки, и могут быть вызваны напрямую. Странный член семейства анализаторов, это InstructionSimplify.cpp, это фактически преобразование, а не анализ. Я уверен, что многие не заметят комментарий, поясняющий, что этот проход делает здесь.

вот этот комментарий

Этот проход не изменяет сам по себе IR. Правило состоит в том, что llvm::SimplifyInstruction может возвращать только константы и существующие объекты Value, что удовлетворяет требованиям к анализатору. Проход, вызывающий SimplifyInstruction для каждой инструкции, это проход преобразования (lib/Transforms/Utils/SimplifyInstructions.cpp.).

AsmParser [25]: парсит текстовый IR в память.

Bitcode [26]: сериализация IR в компактный формат и чтение из компактного формата в RAM.

CodeGen: [27] генератор аппаратно-независимого кода LLVM, фреймворк, на котором написаны бэкенды LLVM, и набор библиотек, которые эти бэкенды могут использовать. Там много кода (>100 KLOC), и, к сожалению, я мало о нём знаю.

DebugInfo [28], это библиотека для поддержки отображения между инструкциями LLVM и локациями исходного кода. Много хорошей информации на этих слайдах [29]с выступления на 2014 LLVM Developers’ Meeting.

ExecutionEngine: [30] Хотя LLVM обычно транслируется в машинный код или в ассемблер, он может быть выполнен интерпретатором. Не-JIT интерпретатор не работал как следует в последний раз, когда я его пытался использовать, но в любом случае, он работает медленнее, чем JIT. Последнее JIT API, Orc [31], находится здесь.

Fuzzer: [32] это libFuzzer [33], аналогичный AFL [34] (фаззинг [35]). Он использует функциональность LLVM для фаззинга программ, компилируемых с помощью LLVM.

IR [36]: различный код, относящийся к IR. Код для вывода IR-кода в текстовом формате, для апгрейда файлов биткода, созданных в ранних версиях LLVM, для сворачивания констант в процессе создания IR-узлов и т.п.

IRReader [37], LibDriver [38], LineEditor [39]: почти никому неинтересно, что здесь находится, и вряд ли там есть какой-то полезный код вообще.

Linker: [40] Модуль LLVM, как и единица компиляции С и С++, содержит функции и переменные. Линковщик объединяет множество модулей в один большой модуль.

LTO: [41] Оптимизация времени компоновки, предмет множества постов и научных статей, позволяет оптимизатору видеть за пределами отдельных скомпилированных модулей. LLVM делает оптимизацию при компоновке «бесплатно», используя линковщик для создания большого модуля и затем оптимизируя его обычными проходами оптимизации. Это хороший подход, но он не масштабируется для очень больших проектов. Современный подход — ThinLTO [42], который позволяет получить большую часть преимуществ за маленькую часть цены.

MC: [43] компилятор обычно генерирует ассемблерный код и позволяет ассемблеру создать машинный код. Подсистема MC в LLVM устраняет промежуточное звено и позволяет генерировать машинный код напрямую. Это ускоряет компиляцию и особенно полезно когда LLVM используется как JIT-компилятор.

Object [44]: Реализация деталей форматов объектных файлов, таких, как ELF.

ObjectYAML [45] — поддерживает кодирование объектных файлов в YAML [46]. Я не знаю, зачем это нужно.

Option: [47] — парсинг командной строки.

Passes: [48] часть менеджера проходов, который управляет запуском проходов LLVM, принимая во внимание зависимости.

ProfileData: [49] — читает и пишет данные профилирования для поддержки оптимизаций, основанных на профилировании.

Support: [50] Поддержка различного кода, включая APInts (целые числа с произвольной точностью, широко используемые в LLVM) и т.п.

TableGen: [51] своего рода швейцарский нож [52], инструмент, который получает на входе .td-файлы (которых в LLVM больше 200), содержащие структурированные данные, и генерирующий код С++, который компилируется в LLVM. TableGen используется, например, для реализации ассемблера и дизассемблера.

Target: [53] здесь живут бэкенды для различных процессоров. Здесь много TableGen-файлов. Вы можете создать новый бэкенд, сделав клон одного из них, чья архитектура наиболее близка к вашей, и затем проведя в его разработке пару лет.

Transforms: [54] это моя любимая директория, здесь живут миддленд-оптимизаторы. IPO содержит межпроцедурные оптимизации, работающие между границами функций, они обычно не очень агрессивны, но видят сразу много кода. InstCombine — это peephole-оптимизатор. Instrumentation — поддержка санитайзеров. ObjCARC поддерживает вот это [55]. Scalar содержит оптимизации «из учебника» по компиляторам, я постараюсь написать более подробный пост о содержимом этой директории. Utils — вспомогательный код. Vectorize — автовекторизатор LLVM, предмет большой работы последних лет.

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

Автор: 32bit_me

Источник [56]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/269483

Ссылки в тексте:

[1] здесь: http://llvm.org/doxygen/index.html

[2] Корневая директория: https://github.com/llvm-mirror/llvm/tree/release_39

[3] bindings: https://github.com/llvm-mirror/llvm/tree/release_39/bindings

[4] cmake : https://github.com/llvm-mirror/llvm/tree/release_39/cmake

[5] docs: https://github.com/llvm-mirror/llvm/tree/release_39/docs

[6] руководства по языку: https://github.com/llvm-mirror/llvm/blob/release_39/docs/LangRef.rst

[7] здесь: https://raw.githubusercontent.com/llvm-mirror/llvm/release_39/docs/LangRef.rst

[8] руководством: https://github.com/llvm-mirror/llvm/tree/release_39/docs/tutorial

[9] сюда: http://llvm.org/docs/tutorial/

[10] examples: https://github.com/llvm-mirror/llvm/tree/release_39/examples

[11] include: https://github.com/llvm-mirror/llvm/tree/release_39/include

[12] llvm-c: https://github.com/llvm-mirror/llvm/tree/release_39/include/llvm-c

[13] llvm: https://github.com/llvm-mirror/llvm/tree/release_39/include/llvm

[14] lib: https://github.com/llvm-mirror/llvm/tree/release_39/lib

[15] projects: https://github.com/llvm-mirror/llvm/tree/release_39/projects

[16] resources: https://github.com/llvm-mirror/llvm/tree/release_39/resources

[17] здесь: https://blog.regehr.org/archives/1453#comment-19007

[18] runtimes: : https://github.com/llvm-mirror/llvm/tree/release_39/runtimes

[19] test:: https://github.com/llvm-mirror/llvm/tree/release_39/test

[20] tools:: https://github.com/llvm-mirror/llvm/tree/release_39/tools

[21] unittests: : https://github.com/llvm-mirror/llvm/tree/release_39/unittests

[22] Google Test: https://github.com/google/googletest

[23] utils:: https://github.com/llvm-mirror/llvm/tree/release_39/utils

[24] Analysis: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Analysis

[25] AsmParser: https://github.com/llvm-mirror/llvm/tree/release_39/lib/AsmParser

[26] Bitcode: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Bitcode

[27] CodeGen:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/CodeGen

[28] DebugInfo: https://github.com/llvm-mirror/llvm/tree/release_39/lib/DebugInfo

[29] этих слайдах : http://llvm.org/devmtg/2014-10/Slides/Christopher-DebugInfoTutorial.pdf

[30] ExecutionEngine:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/ExecutionEngine

[31] JIT API, Orc: http://llvm.org/devmtg/2016-11/#talk1

[32] Fuzzer:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Fuzzer

[33] libFuzzer: http://llvm.org/docs/LibFuzzer.html

[34] AFL: http://lcamtuf.coredump.cx/afl/

[35] фаззинг: https://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%B7%D0%B7%D0%B8%D0%BD%D0%B3

[36] IR: https://github.com/llvm-mirror/llvm/tree/release_39/lib/IR

[37] IRReader: https://github.com/llvm-mirror/llvm/tree/release_39/lib/IRReader

[38] LibDriver: https://github.com/llvm-mirror/llvm/tree/release_39/lib/LibDriver

[39] LineEditor: https://github.com/llvm-mirror/llvm/tree/release_39/lib/LineEditor

[40] Linker:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Linker

[41] LTO:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/LTO

[42] ThinLTO: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

[43] MC:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/MC

[44] Object: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Object

[45] ObjectYAML: https://github.com/llvm-mirror/llvm/tree/release_39/lib/ObjectYAML

[46] YAML: http://llvm.org/docs/YamlIO.html

[47] Option:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Option

[48] Passes:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Passes

[49] ProfileData:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/ProfileData

[50] Support: : https://github.com/llvm-mirror/llvm/tree/release_39/lib/Support

[51] TableGen: : https://github.com/llvm-mirror/llvm/tree/release_39/lib/TableGen

[52] швейцарский нож: http://llvm.org/docs/TableGen/

[53] Target:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Target

[54] Transforms:: https://github.com/llvm-mirror/llvm/tree/release_39/lib/Transforms

[55] вот это: http://clang.llvm.org/docs/AutomaticReferenceCounting.html

[56] Источник: https://habrahabr.ru/post/343344/