Компиляторы

LLVM: инфраструктура компиляторов

В 2000 году каждый компилятор писал свой backend с нуля: оптимизации, register allocator, code generator для каждой архитектуры. Сегодня Rust, Swift, Kotlin Native, Julia и ещё десяток языков используют один и тот же backend - LLVM. Написать новый язык теперь означает написать frontend, генерирующий LLVM IR - и получить нативный код для x86, ARM, RISC-V, WebAssembly без написания ни строчки кода под каждую платформу.

  • **rustc** компилирует весь Rust через LLVM backend: `cargo build --release` вызывает LLVM оптимизации (-O2 эквивалент) и генерирует нативный код для target архитектуры
  • **Apple Silicon toolchain**: swiftc, Metal shader compiler, Xcode instruments - все используют LLVM; переход на ARM в 2020 занял меньше года именно потому что LLVM уже поддерживал AArch64
  • **Google BOLT**: post-link binary optimizer на основе LLVM - применяется к production бинарникам Chrome и даёт 5-10% speedup без изменения исходного кода

LLVM IR

LLVM IR (Intermediate Representation) - типизированный RISC-подобный язык в SSA-форме. Это связующее звено: frontend (Clang, rustc, swiftc) генерирует IR, backend (x86, ARM, RISC-V, WebAssembly) компилирует IR в машинный код. IR существует в трёх форматах: human-readable .ll текст, бинарный .bc bitcode, in-memory объекты.

LLVM используют: Clang (C/C++/ObjC), rustc (Rust), swiftc (Swift), kotlinc-native (Kotlin Native), Julia JIT, Emscripten (Wasm), XLA (TensorFlow/JAX ML compiler). Общий IR позволяет всем этим языкам получать одни и те же оптимизации бесплатно. LLVM 18 (2024) поддерживает 20+ target архитектур.

Для чего нужна SSA (Static Single Assignment) форма в LLVM IR?

LLVM Pass Infrastructure

LLVM pass - это трансформация или анализ IR. New Pass Manager (NPM, LLVM 13+) организует passes в pipeline: FunctionPass работает над одной функцией, ModulePass - над всем модулем, LoopPass - над циклами. Passes комбинируются в PassManager который применяет их в правильном порядке с кешированием результатов анализа.

LLVM pass API позволяет писать собственные оптимизации как плагины. Clang-Tidy и многие статические анализаторы реализованы как LLVM passes. Intel Embree (ray tracing), Apple Metal compiler и NVIDIA PTX backend - всё это custom LLVM passes. Написать свой pass: ~200 строк C++ + CMake для простого анализа.

Что делает pass mem2reg в LLVM?

LLVM Module System и LTO

LLVM Module - единица компиляции: содержит функции, глобальные переменные, метаданные, target triple. Link Time Optimization (LTO) объединяет модули перед оптимизацией: функции из разных .cpp файлов инлайнятся друг в друга. ThinLTO - распределённый вариант: модули оптимизируются параллельно с summary-based cross-module анализом.

Google Chrome с ThinLTO (2020): бинарник на 10-15% меньше, запуск быстрее на 2-5%. Firefox с LTO: на ARM Android сохранил 10MB от APK. Rust cargo release profile по умолчанию использует ThinLTO через LLVM. LTO особенно выгоден для функций-однострочников в разных .cpp файлах, которые без LTO остаются function calls.

В чём преимущество ThinLTO перед полным LTO?

LLVM Toolchain

LLVM - это не только компилятор, но и полный toolchain: Clang (C/C++), clang-format/clang-tidy (linting), lld (linker, в 5x быстрее GNU ld), lldb (debugger), AddressSanitizer, MemorySanitizer, UBSan, libfuzzer, clang-query, clangd (Language Server).

LLVM 16+ включает Polly (loop optimization для HPC), Flang (Fortran frontend), BOLT (post-link binary optimizer). BOLT использует профиль PGO для реорганизации кода: Google использует BOLT для production бинарников и достигает 5-15% speedup без изменения исходного кода. Apple использует LLVM как основу для всей своей компиляторной инфраструктуры (Xcode).

LLVM - это только Clang (C/C++ компилятор)

LLVM - это компиляторная инфраструктура: IR, pass framework, backend для 20+ архитектур, linker (lld), debugger (lldb), sanitizers, fuzzer и Language Server (clangd)

Clang - лишь один из frontends LLVM. Rust, Swift, Julia, Kotlin Native, Emscripten, XLA и десятки других проектов используют LLVM IR и backend не трогая Clang

Что делает AddressSanitizer (ASan) и каков его overhead?

Итоги

  • LLVM IR - типизированный SSA язык, связывающий frontends (Clang/rustc/swiftc) с backends (x86/ARM/Wasm); 20+ target архитектур из одного IR
  • Pass infrastructure: ~150 passes в -O2 pipeline; mem2reg строит SSA, instcombine упрощает алгебру, inline встраивает функции
  • Полный toolchain: lld (5x быстрее GNU ld), ASan/TSan/UBSan sanitizers, clangd LSP, libFuzzer, BOLT post-link optimizer

Связанные темы

LLVM - основа для многих компиляторных проектов:

  • LLVM Passes — Детальное рассмотрение написания собственных function/module passes
  • MLIR — MLIR расширяет идеи LLVM IR для ML компиляторов с dialects и progressive lowering
  • WebAssembly — Emscripten и wasm32 backend LLVM компилируют C/C++ в .wasm через LLVM IR

Вопросы для размышления

  • LLVM IR - это хорошая абстракция для большинства языков, но Julia и Haskell GHC имеют собственные IR (Julia IR, STG). Почему они не используют LLVM IR как единственное представление?
  • ThinLTO ускоряет LTO за счёт параллелизации. Какие категории межпроцедурных оптимизаций ThinLTO не может сделать по сравнению с полным LTO?
  • BOLT реорганизует бинарный код на основе профиля. Почему компилятор не может сделать то же самое на этапе компиляции без отдельного post-link шага?

Связанные уроки

  • arch-04-cpu
LLVM: инфраструктура компиляторов

0

1

Войти