Компіляція
clau компілює модулі Erlang/OTP 29 через LLVM у перевірене IR, біткод або
нативні об'єктні файли. -o/--output компонує позиційні вхідні файли (або одну
вибрану ціль проєкту), їхній стартовий об'єкт точки входу і
runtime у виконуваний файл (компонування); збирання
проєктів компонують виконувані цілі в їхні виводи з маніфесту
(проєкти). Згенеровані об'єктні файли також можна
запускати через обв'язку C++, скомпоновану з runtime (див. приклад нижче).
Прийнята підмножина вихідного коду
Іменовані модулі з експортами та впорядкованими клаузами функцій. Голови та
зіставлення в тілі приймають змінні, _, псевдоніми, повторювані імена і
зразки над атомами, довільними цілими числами, скінченними числами з рухомою
комою, кортежами, списками/рядками, map, bitstring і звичайними кортежними
record. Тіла — це послідовності зіставлень, конструкторів, перевірених
операторів/guard BIF, erlang:display/1 (друк),
halt/0,1 (код завершення), функцій, що
викликають винятки, — error/1,2,3, exit/1, throw/1 і erlang:raise/3
(ABI), інших
вбудованих функцій мосту (erlang:function_exported/3) і fun
вбудованих функцій, case/if, catch Expr,
try ... of ... catch Class:Reason:Stack ... after з
трасуваннями стека, maybe ... else ... end,
comprehension для списків, binary і map
(зразки) та прямих локальних або буквальних
віддалених викликів у межах пакета, зокрема власної, взаємної та міжмодульної
рекурсії на явних фреймах процесу з належними хвостовими викликами
(модель виконання); рекурсію в тілі
обмежує стек процесу (за замовчуванням необмежений), а не нативний стек. Guard
підтримують увесь допущений каталог. Див. зразки,
guard і терми.
Функціональні значення fun F/A, fun M:F/A, анонімні та іменовані fun із
захопленими змінними, виклики fun і динамічні виклики (M:F(...),
apply/2,3) виконуються (fun). З діагностикою відхиляються навіть
у невикористаних функціях: receive, fun вбудованих функцій,
процеси та обмін повідомленнями.
Прийняті атрибути: module, export, file, кортежний і нативний record,
export_record, import_record, форми type/spec, doc/moduledoc,
author, vsn, copyright, deprecated, -compile з
{no_auto_import, ...} або параметрами nowarn_*, що стосуються лише
попереджень (наприклад, nowarn_deprecated_catch), і -import guard BIF з
erlang. Інші атрибути (on_load, parse transform, інші параметри
compile, параметризовані модулі) відхиляються.
Вихідні файли, що починаються з #!, дотримуються
правил escript (неявний модуль і експорт main/1,
-mode приймається).
Форми type/spec аналізуються, але ніколи не змінюють згенерований код.
Режими лише для синтаксису (--parse-check, --print-ast, --print-source,
...) приймають повну граматику.
Запуск прикладу зі скомпільованими модулями
client:main/1 робить приклад програмою:
./build/debug/bin/clau -O2 -o build/demo examples/compile/answer.erl examples/compile/client.erl
./build/demo # prints 42, -7 and {record,map,binary,list,integer,other}
Ті самі модулі можна запустити й через обв'язку C++. З Developer PowerShell для Windows x64 і зібраним компілятором:
$tool = './build/debug/bin/clau.exe'
& $tool -O0 --emit obj --artifact-dir build/example-aot examples/compile/answer.erl examples/compile/client.erl
cmake -S examples/compile -B build/example-native -G Ninja -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Debug "-DGENERATED_DIR:PATH=$((Resolve-Path build/example-aot).Path)"
cmake --build build/example-native
./build/example-native/bin/Debug/compiled_modules.exe
Вона друкує 42, -7, record, map, binary, list, integer, other,
по одному на рядок. Обв'язка явно реєструє модулі, створює контекст і декодує
результати; це приклад хоста, а не робоча точка входу. В Unix використовуйте
build/debug/bin/clau, clang++ і -DGENERATED_DIR="$PWD/build/example-aot"
(нативні запуски там ще не перевірено).
Інші дії над тими самими вихідними файлами:
& $tool -O2 --emit llvm-ir --artifact-dir build/example-ir examples/compile/answer.erl examples/compile/client.erl
& $tool --print-types --verbose examples/compile/answer.erl examples/compile/client.erl
& $tool --print-ir --print-optimized-ir examples/compile/answer.erl examples/compile/client.erl
& $tool -O2 --no-type-specialization --verbose examples/compile/answer.erl examples/compile/client.erl
Друк вихідного коду
--print-source (дія фронтенду, як --print-ast) друкує кожен розібраний
модуль як вихідний код Erlang; --print-types друкує той самий текст з
анотаціями типів. Принтер (print_source, expression_source, type_source
у printing.hpp; compiler/src/printing/source_*) придатний для повторного
використання:
- Форми йдуть одна за одною в порядку вихідного коду, з порожнім рядком навколо
кожної функції; форма
-file, яку препроцесор додає перед модулем, пропускається, а ті, що оточують включені файли, зберігаються. - Текст — це розібраний синтаксис: макроси розгорнуто, включення вбудовано, а
коментарі, початкове написання та розміщення втрачено. Клаузи та блокові
вирази (
case,if,receive,try,maybe,begin, fun з більш ніж одним рядком) займають рядки з відступом у чотири колонки; усе інше — в одному рядку. Дужки беруться лише з власних груп вихідного коду. - Надрукований текст розбирається назад у те саме синтаксичне дерево, а
повторний друк дає той самий текст (CTest
printing_sourceна фікстурах, що розбираються). SourceNotesдодає до виразу анотацію, що друкується якExpression :: Text(у дужках, якщо це не цілий вираз тіла; не Erlang), і рядки коментарів над формою. Зразки, guard і ліва частина зіставлення анотацій не мають.
Параметри
| Параметр | Поведінка |
|---|---|
--emit obj|llvm-ir|llvm-bc | Публікує один артефакт на модуль |
--artifact-dir DIR | Корінь артефактів (потребує --emit) |
-o PATH / --output PATH | Компонує виконуваний файл (компонування); конфліктує з --emit |
--linker PATH, --runtime-library PATH | Драйвер Clang і архів runtime для -o |
--entry MODULE[:FUNCTION] | Функція точки входу виконуваного файлу з арністю 1; перевіряється в кожному режимі компіляції й додає стартовий артефакт clausev1_start (виконувані файли) |
--target-triple TRIPLE | Цільова машина; --target — це вибір цілі проєкту |
-O0 / -O2 / -Os | Загальний код за замовчуванням + LLVM O0 / обмежена спеціалізація + LLVM O2 / LLVM Os, без спеціалізації, одна секція на символ і вилучення компонувальником коду та даних, на які немає посилань |
--no-type-specialization | Вимикає варіанти незалежно від порядку параметрів |
--print-ir / --print-optimized-ir | Перевірене IR до/після проходів LLVM, з рядками вихідного коду Erlang як коментарями |
--print-types | Кожен модуль як вихідний код Erlang з анотаціями виведених типів (семантика); зупиняється перед LLVM |
--verbose | Події фаз [pp], [parse] і [comp] у stderr |
--impldebug n[,n...] | Налагоджувальний вивід кроків реалізації в stderr (наприклад, 23: зведення виведення типів) |
- Без
--emitкомпіляція перевіряє об'єктні файли в пам'яті й нічого не записує. - Позиційні вхідні файли утворюють один пакет; кожна ціль проєкту — окремий пакет.
- Корені артефактів:
build/aot(позиційні) абоbuild/aot/<hex-target>у каталозі маніфесту. Явні корені задаються відносно каталогу виклику. - Імена — оборотний hex:
answer→clausev1_616e73776572__0.obj(.oдля ELF/Mach-O,.ll,.bc); стартовий об'єкт —clausev1_start.obj. - Усі пакети компілюються і готуються до публікації. Невдачі нічого не публікують і зберігають попередні виводи; заміна атомарна для кожного файлу, а не для пакета.
--emitконфліктує з-o; перемикачі компіляції конфліктують із діями лише фронтенду та--new-project.--print-typesвідхиляє параметри цілі/ оптимізації.- Знімки IR — це асемблер LLVM; кілька знімків розділяються екранованими
заголовками-коментарями і не є одним модулем, що розбирається. Для вхідних
даних інструментів використовуйте
--emit llvm-ir. Коментарі з рядками вихідного коду показують початковий текст (нерозгорнуті макроси) і переживають оптимізацію завдяки налагоджувальним розташуванням.
Бекенд
- Один контекст LLVM і одна цільова машина на пакет. Триплет хоста використовує процесор і можливості хоста; чужі триплети — загальний процесор. PIC, мала модель коду.
- Бекенди: X86, ARM, AArch64 (перетин із SDK). Невідомі або відсутні бекенди дають помилку; запасного варіанта з хостом немає.
- Перевірка виконується до і після оптимізації; вона перевіряє коректність IR, а не правильність Erlang.
- Бюджети на ціль: 1 024 модулі, 250 000 вузлів AST на модуль, 1 000 000 на пакет. Серіалізований вивід: 64 MiB на модуль, 256 MiB на пакет. Перевищення бюджету — звичайна діагностика ресурсів.
SDK LLVM
Стабільний LLVM 23.1.x, щонайменше 23.1.1. LLVM — залежність хоста: архітектура, стандартна бібліотека C++ і CRT Windows мають відповідати інструменту компілятора. Код проєкту зберігає винятки/RTTI; жоден виняток не може розкручуватися через LLVM. Runtime ніколи не використовує LLVM.
- Пошук перевіряє стандартні префікси (
/usr,/usr/local,/opt/homebrew,/opt/local,/opt/llvm, Linuxbrew,/Library/Developer/Toolchains, Program Files у Windows), зокрема розміщення з версіями. Дерева збирання відхиляються. LLVM_DIRявно вибирає SDK; недійсний вибір дає помилку без запасного варіанта.- Якщо нічого не знайдено, CMake завантажує зафіксований архів 23.1.2
(з перевіркою SHA-256) у
thirdparty/для Windows x64/ARM64, Linux x64/ARM64 або macOS ARM64.CLAUSE_DOWNLOAD_LLVM=OFFвимикає завантаження. - Пробне компонування під час конфігурування перевіряє сумісність ABI.
Еталонне налаштування Windows x64 (2026-09-29): clang-cl 23.1.2 хоста в
C:/Program Files/LLVM/bin, SDK
thirdparty/clang+llvm-23.1.2-x86_64-pc-windows-msvc, інструменти Visual
Studio 18 x64, Windows SDK 10.0.26100.0, Ninja, /MT,
_ITERATOR_DEBUG_LEVEL=0. Автоматичний вибір застосовує налаштування /MT та
ітераторів; явний LLVM_DIR — ні, і тоді пробне компонування завершується
невдачею.
Історичне еталонне налаштування macOS: Homebrew llvm 23.1.1_1 (arm64,
спільна libLLVM.23.1.dylib, перевірки assertions вимкнено) з AppleClang 21.
Інші передумови: CMake ≥ 3.28, компілятор C++23, Boost ≥ 1.90, toml++ 3.4.0 та інструменти перевірки якості. OTP потрібен лише для аудитів за бажанням і регенерації фікстур.
Clause