библиотечная архитектура с самого началаa library-first architecture from day one
Фронтенд C-семейства языков поверх LLVM — инфраструктуры компиляции, спроектированной как набор переиспользуемых библиотек, а не монолитная программа. Именно эта архитектурная ставка (не производительность, не диагностика) определила, почему вокруг Clang выросла целая экосистема IDE-инструментов, которой у GCC нет.The C-family frontend built on top of LLVM — a compiler infrastructure designed as a set of reusable libraries rather than a monolithic program. That architectural bet (not performance, not diagnostics) is what determined why an entire ecosystem of IDE tooling grew up around Clang that GCC never got.
LLVM (изначально — Low Level Virtual Machine, сегодня само название используется без расшифровки, так как проект давно перерос идею «виртуальной машины») начат Крисом Латтнером в 2000 году как исследовательский проект его магистерской диссертации в University of Illinois под руководством Вихрама Адве. Ключевая идея диссертации: промежуточное представление, спроектированное для пожизненного (lifelong) анализа программы — не только во время компиляции, но и во время выполнения и после (link-time, install-time оптимизации) — то, что классические компиляторы вроде GCC не закладывали архитектурно с самого начала.LLVM (originally "Low Level Virtual Machine," though today the name itself is used without expansion since the project long outgrew the "virtual machine" idea) was started by Chris Lattner in 2000 as a research project for his master's thesis at the University of Illinois, advised by Vikram Adve. The thesis's key idea: an intermediate representation designed for lifelong program analysis — not just at compile time, but at run time and beyond (link-time, install-time optimizations) — something classic compilers like GCC never architected for from the start.
Apple наняла Латтнера в 2005 году и стала основным спонсором развития — компании требовался компилятор с permissive-лицензией (без обязательств GPL раскрывать исходный код проприетарных расширений) и с API, который можно встроить прямо в Xcode для автодополнения и рефакторинга. Так родился Clang (2007) — фронтенд C/C++/Objective-C, транслирующий код в LLVM IR. Впервые Clang стал компилятором по умолчанию для iOS в 2011 году, для macOS — вскоре после.Apple hired Lattner in 2005 and became the primary sponsor of development — the company needed a permissively-licensed compiler (no GPL obligation to disclose the source of proprietary extensions) with an API that could be embedded directly into Xcode for autocomplete and refactoring. That's how Clang (2007) was born — the C/C++/Objective-C frontend that translates code into LLVM IR. Clang first became the default compiler for iOS in 2011, and for macOS shortly after.
LLVM сегодня — гораздо больше, чем C++-компилятор: это общая инфраструктура, которую используют фронтенды для совершенно разных языков — Rust (rustc компилирует в LLVM IR), Swift, Julia, Zig (частично, через собственный самостоятельный кодогенератор, но исторически и опционально через LLVM backend), а также нестандартные применения вроде JIT-компиляции в базах данных и графических шейдерных компиляторах. Latner позже (2017) покинул Apple ради Tesla, затем Google (проект MLIR — расширение идей LLVM на компиляцию ML-графов), затем основал собственную компанию Modular AI.LLVM today is far more than a C++ compiler: it's a shared infrastructure used by frontends for entirely different languages — Rust (rustc compiles to LLVM IR), Swift, Julia, Zig (partially, through its own standalone codegen, but historically and optionally via the LLVM backend too), plus non-standard uses like JIT compilation in databases and graphics shader compilers. Lattner later (2017) left Apple for Tesla, then Google (the MLIR project — extending LLVM's ideas to compiling ML graphs), then founded his own company, Modular AI.
Структурно похоже на GCC (координатор + внутренние инструменты), но реализовано как единый исполняемый файл clang, умеющий выступать в разных ролях в зависимости от аргументов — в отличие от отдельных cc1plus/as/collect2 у GCC.Structurally similar to GCC (a coordinator plus internal tools), but implemented as a single clang executable that can act in different roles depending on its arguments — unlike GCC's separate cc1plus/as/collect2 binaries.
clang/clang++ — driverТот же бинарник clang, вызванный с флагом -cc1, превращается в собственно компилятор — driver внутренне порождает подпроцесс clang -cc1 ... с расширенным, «сырым» набором флагов. clang -### main.cpp, аналогично GCC, печатает реальный план команд.The same clang binary, invoked with the -cc1 flag, turns into the actual compiler — the driver internally spawns a clang -cc1 ... subprocess with an expanded, "raw" set of flags. clang -### main.cpp, like GCC's equivalent, prints the real command plan.
clang -cc1 — фронтенд + генерация IRfrontend + IR generationРазбирает исходник (препроцессор → лексер → парсер → AST → семантика), затем сразу транслирует AST в LLVM IR через слой CodeGen — в отличие от GCC, где GENERIC/GIMPLE/RTL остаются внутренними, LLVM IR на этом шаге уже можно сохранить в текстовом виде (clang -S -emit-llvm) и изучать напрямую.Parses the source (preprocessor → lexer → parser → AST → semantics), then immediately translates the AST into LLVM IR via the CodeGen layer — unlike GCC, where GENERIC/GIMPLE/RTL stay internal, LLVM IR at this step can already be dumped as text (clang -S -emit-llvm) and inspected directly.
Над IR выполняется настраиваемый пайплайн проходов (passes) — их можно перечислить явно через opt (отдельная утилита LLVM для запуска произвольного набора проходов вручную, вне полного драйвера) или посмотреть весь список флагом -mllvm -print-pipeline-passes. Затем LLVM backend (target-specific часть) генерирует машинный код под выбранную архитектуру.A configurable pipeline of passes runs over the IR — they can be listed explicitly via opt (a standalone LLVM utility for running an arbitrary set of passes by hand, outside the full driver) or the whole list can be dumped with -mllvm -print-pipeline-passes. The LLVM backend (the target-specific part) then generates machine code for the chosen architecture.
lld — собственный линковщик LLVMLLVM's own linkerClang по умолчанию на большинстве платформ всё ещё использует системный линковщик (GNU ld на Linux), но может явно вызвать собственный lld (-fuse-ld=lld) — написанный с нуля, значительно быстрее GNU ld на больших проектах за счёт лучшего параллелизма и более простой внутренней модели.By default on most platforms, Clang still uses the system linker (GNU ld on Linux), but can explicitly invoke its own lld (-fuse-ld=lld) — written from scratch, noticeably faster than GNU ld on large projects thanks to better parallelism and a simpler internal model.
Прямая противоположность подходу GCC-плагинов: Clang с самого начала спроектирован как библиотека (libclang — стабильный C API, LibTooling — более полный, но менее стабильный C++ API), а не как монолит с точечными callback-хуками. Сторонний инструмент линкуется напрямую против библиотек Clang и получает полный доступ к AST, а не к урезанному представлению.The direct opposite of GCC's plugin approach: Clang was designed as a library from the very start (libclang — a stable C API, LibTooling — a more complete but less stable C++ API), not as a monolith with point-in-time callback hooks. A third-party tool links directly against Clang's libraries and gets full access to the AST, not a stripped-down view of it.
Библиотека для декларативного описания паттернов в AST (например, «найти все вызовы конструктора с ровно одним аргументом того же типа, что и класс») без ручного обхода дерева — на этом фундаменте построены clang-tidy и большинство инструментов автоматического рефакторинга C++ кода.A library for declaratively describing patterns in the AST (for example, "find every constructor call with exactly one argument of the same type as the class") without manually walking the tree — clang-tidy and most automated C++ refactoring tools are built on this foundation.
// пример matcher-выражения (упрощённо)an example matcher expression (simplified):
cxxConstructorDecl(
hasParameter(0, hasType(isSameOrDerivedFrom("MyClass")))
)
clangd (language server) читает файл compile_commands.json (Compilation Database — стандарт, который сам Clang умеет генерировать флагом -MJ или через CMake CMAKE_EXPORT_COMPILE_COMMANDS=ON), чтобы знать точные флаги компиляции каждого файла проекта — именно так IDE получает точное автодополнение без повторной настройки сборки внутри редактора.clangd (the language server) reads a compile_commands.json file (the Compilation Database — a standard Clang itself can generate via the -MJ flag, or through CMake's CMAKE_EXPORT_COMPILE_COMMANDS=ON) to know the exact compile flags for every file in the project — this is how an IDE gets accurate autocomplete without re-configuring the build inside the editor.
Clang — родной дом для всего семейства LLVM-санитайзеров, включая те, которых нет у GCC (см. санитайзеры GCC для базового набора, общего для обоих).Clang is the native home of the full LLVM sanitizer family, including ones GCC lacks (see GCC's sanitizers for the baseline set shared by both).
| флагflag | ловитcatches | только в Clang?Clang-only? |
|---|---|---|
-fsanitize=memory | чтение неинициализированной памятиreads of uninitialized memory | даyes |
-fsanitize=cfi | перехват косвенных вызовов (Control Flow Integrity)indirect-call hijacking (Control Flow Integrity) | да, требует LTOyes, requires LTO |
-fsanitize=safe-stack | защита от stack buffer overflow отдельным «безопасным» стекомstack buffer overflow protection via a separate "safe" stack | даyes |
-fsanitize=fuzzer | встроенная интеграция с libFuzzer для coverage-guided фаззингаbuilt-in integration with libFuzzer for coverage-guided fuzzing | даyes |
-fsanitize=address,undefined,thread | ASan/UBSan/TSan — общий базовый наборASan/UBSan/TSan — the shared baseline set | нет, есть и в GCCno, also in GCC |
MemorySanitizer требует пересборки всех зависимостей (включая стандартную библиотеку) с MSan для отсутствия ложных срабатываний — на практике это самый дорогой в настройке, но и самый чувствительный из санитайзеров. Новые санитайзеры также обычно появляются в Clang первыми — экспериментальная численная нестабильность floating-point (-fsanitize=numerical) на практике часто обкатывается здесь раньше остальных.MemorySanitizer requires rebuilding all dependencies (including the standard library) with MSan to avoid false positives — in practice it's the most expensive to set up but also the most sensitive of the sanitizers. New sanitizers also tend to land in Clang first — experimental floating-point instability detection (-fsanitize=numerical) is in practice usually field-tested here before anywhere else.
availability — проверка API конкретной версии платформы— checking a specific platform version's API__attribute__((availability(macos,introduced=10.15)))
void newApi();
// вызов newApi() при таргете macOS 10.14 — ошибка компиляции, не рантайма// calling newApi() while targeting macOS 10.14 is a compile error, not a runtime one
Статическая проверка соблюдения дисциплины блокировок через аннотации __attribute__((guarded_by(...)))/__attribute__((requires_capability(...))) — компилятор доказывает, что доступ к полю, помеченному guarded_by(mu), никогда не происходит без удержания мьютекса mu, обнаруживая часть гонок данных ещё до запуска TSan, прямо на этапе компиляции.Static verification of locking discipline via __attribute__((guarded_by(...)))/__attribute__((requires_capability(...))) annotations — the compiler proves that access to a field marked guarded_by(mu) never happens without holding the mutex mu, catching some data races before TSan ever runs, right at compile time.
class Counter {
Mutex mu;
int value __attribute__((guarded_by(mu)));
public:
void increment() { MutexLock l(&mu); value++; } // ok// ok
void bad() { value++; } // предупреждение на этапе компиляции// a compile-time warning
};
Синтаксис ^{ ... } для замыканий в чистом C — появился до лямбд C++11 и по духу очень похож, но с собственной моделью управления памятью (Block_copy/Block_release), интегрированной с Objective-C ARC. Практическая ниша сегодня — GCD (Grand Central Dispatch) в коде на Objective-C/Swift-interop уровне.The ^{ ... } syntax for closures in plain C — predates C++11 lambdas and is spiritually very similar, but with its own memory-management model (Block_copy/Block_release) integrated with Objective-C ARC. Its practical niche today is GCD (Grand Central Dispatch) code at the Objective-C/Swift-interop level.
Уровни -O0…-O3/-Ofast/-Os/-Og совпадают по названию и общему смыслу с GCC (см. таблицу GCC) — но конкретный набор проходов, которые каждый уровень включает, различается, так как это два независимых компилятора со своими наборами оптимизационных проходов.The -O0…-O3/-Ofast/-Os/-Og levels match GCC's names and general intent (see GCC's table) — but the specific set of passes each level enables differs, since these are two independent compilers with their own sets of optimization passes.
| флагflag | что делаетwhat it does |
|---|---|
-flto=thin | ThinLTO — облегчённая версия LTO, компилирующая модули параллельно с ограниченным межмодульным анализом вместо полного слияния всего IR в один блок — заметно быстрее классического -flto=full на больших проектахThinLTO — a lightweight version of LTO that compiles modules in parallel with limited cross-module analysis instead of merging all IR into one block — noticeably faster than classic -flto=full on large projects |
-fprofile-instr-generate / -fprofile-instr-use | PGO в терминологии Clang — аналог -fprofile-generate/-use у GCCin Clang's terminology — the analogue of GCC's -fprofile-generate/-use |
-fsanitize=cfi + -flto | Control Flow Integrity требует LTO для построения глобального графа допустимых косвенных вызововControl Flow Integrity requires LTO to build a global graph of permitted indirect calls |
-mllvm -print-pipeline-passes | напечатать полный список LLVM-проходов для выбранного уровня оптимизацииprint the full list of LLVM passes for the chosen optimization level |
opt -passes=... | запустить произвольный явный набор проходов над .ll файлом вручную, вне полного драйвера — инструмент для экспериментов с отдельными оптимизациямиrun an arbitrary explicit set of passes over a .ll file by hand, outside the full driver — a tool for experimenting with individual optimizations |
Форматирование по конфигурируемому стилю (.clang-format, YAML-конфиг с готовыми базовыми стилями LLVM/Google/Chromium/Mozilla) — стандарт индустрии для автоматических CI-проверок стиля кода без споров в code review.Formatting to a configurable style (.clang-format, a YAML config with ready-made base styles for LLVM/Google/Chromium/Mozilla) — the industry standard for automated CI style checks with no code-review style arguments.
Statically-linted проверки идиом на базе ASTMatchers: modernize-* чеки для авто-миграции на новые стандарты (замена NULL на nullptr, циклов на range-based for), bugprone-*, performance-*, cppcoreguidelines-* — многие чеки умеют автоматически применять исправление (-fix), а не только сообщать о проблеме.Statically-linted idiom checks built on ASTMatchers: modernize-* checks for auto-migrating to newer standards (replacing NULL with nullptr, loops with range-based for), bugprone-*, performance-*, cppcoreguidelines-* — many checks can automatically apply a fix (-fix), not just report the problem.
Language server — основа автодополнения, go-to-definition, inline-диагностики в VS Code, Neovim, CLion, Sublime Text; работает поверх Compilation Database (см. выше).The language server — the backbone of autocomplete, go-to-definition, and inline diagnostics in VS Code, Neovim, CLion, and Sublime Text; operates on top of the Compilation Database (see above).
lldb — отладчик, часть проекта LLVM, API которого используется в отладчике Xcode; lld — собственный линковщик LLVM (см. раздел про пайплайн выше).lldb is a debugger, part of the LLVM project, whose API is used inside Xcode's debugger; lld is LLVM's own linker (see the pipeline section above).
плюсы:pros: лучшая в индустрии диагностика ошибок, полный набор санитайзеров включая MSan/CFI/фаззинг, переиспользуемая архитектура (libclang/LibTooling/LLVM) — фундамент огромной экосистемы инструментов, permissive-лицензия удобна для встраивания в проприетарные продукты.industry-best error diagnostics, the full sanitizer set including MSan/CFI/fuzzing, a reusable architecture (libclang/LibTooling/LLVM) underpinning a huge tooling ecosystem, a permissive license convenient for embedding in proprietary products.
минусы:cons: меньше целевых архитектур из коробки, чем у GCC (хотя разрыв сокращается — см. обзор таргетов), генерируемый код на некоторых числодробительных бенчмарках иногда уступает GCC, крупная кодовая база LLVM сама по себе требует много ресурсов для полной пересборки из исходников.fewer out-of-the-box target architectures than GCC (though the gap is narrowing — see the target overview), generated code on some number-crunching benchmarks occasionally trails GCC, the large LLVM codebase itself demands substantial resources to rebuild fully from source.