Спустя три месяца непрерывной и местами безумной работы с момента последнего релиза, мы готовы представить технологию, которая меняет правила игры в системном программировании. Мы создали AsmX Raptor — новое поколение движка, которое впервые позволяет писать высокоуровневый код и ассемблерные инструкции в едином, неразрывном потоке выполнения.
Это не попытка заменить C++ или Rust. Это манифест эффективности и попытка исправить историческую несправедливость, когда разработчик ядер, драйверов или гипервизоров вынужден выбирать: удобство абстракций или абсолютный контроль над железом.
Мы хотим чувствовать ток в транзисторах, точно знать, где находится каждый байт, и при этом пользоваться мощью строгой типизации. И вот как мы это сделали.
Иллюзия контроля: почему индустрия застряла в компромиссах?
Когда вы пишете критичный системный код (ОС, криптографию, кастомные аллокаторы), вам необходим прямой доступ к регистрам CPU, специфическим инструкциям (cpuid, rdtsc, wrmsr) и точное управление стеком. Что индустрия предлагает инженеру сегодня?
Чистый ассемблер (NASM/GAS): Вы получаете 100% контроль, но теряете систему типов, структуры, области видимости и удобство поддержки. Написание бизнес-логики превращается в ад ручного управления памятью.
Раздельная компиляция (C/C++ + ASM объектники): Вы пишете
.asmфайл, компилируете его, затем линкуете с высокоуровневым кодом. Возникает проблема накладных расходов: чтобы просто вызвать ассемблерную функцию, вы обязаны строго соблюдать ABI (например, System V AMD64), тратить драгоценные такты на пролог/эпилог и сохранение регистров. Это создает «мусор» в виде лишних переходов и объектов в памяти.C/C++ с
inline assembly(__asm__): Самый популярный, но и самый опасный путь.
Проблема, о которой принято молчать: Боль inline asm
Многие считают asm в GCC или Clang решением всех проблем. На практике это черный ящик, который ломает абстракции.
Строковый ад: Код пишется внутри строковых литералов. Вы лишаетесь автодополнения, статической проверки типов операндов и адекватной подсветки синтаксиса на уровне IDE.
Fragile Constraints (Хрупкие ограничения): Вы пишете
asm volatile ("..." : "=r" (val) : "r" (input) : "memory");. Ошиблись в одной букве в выходных операндах (=rvs+m) — и компилятор тихо сгенерирует некорректный машинный код. Эта ошибка «всплывет» только в рантайме в виде трудноуловимого Heisenbug'а.Разрушение пайплайна оптимизатора: Для AST компилятора ассемблерная вставка — это слепое пятно. Оптимизатор может переставить инструкции до или после блока, нарушив хрупкую логику. Компилятор часто перестраховывается, сохраняя лишние регистры в стек при входе в asm-блок, даже если вы их не трогаете (Overhead).
Привязка к бэкенду: Поведение этих вставок и синтаксис ограничений специфичны для конкретных версий компиляторов.
Путь инженера — это путь итераций и устранения лишних сущностей. Мы поняли, что ассемблер не должен быть «вставкой». Он должен быть полноправным гражданином языка.
Raptor Engine: Ассемблер как нативный токен
AsmX Raptor решает проблему на уровне фундамента. Здесь нет строковых вставок. Есть единый поток выполнения, где машинные инструкции и строгие декларации типов живут в одном синтаксическом дереве (AST).
Взгляните на этот код:
fn foo_int32_t(int32_t, int32_t) -> int32_t { ;; code } fn main { @mov $0, %rdi; @add $10, %rdi; @cmp $0, %rdi; ;; Полноценная поддержка CV-квалификаторов и типизации в том же скоупе! const int32_t* dotw1 = nullptr; const volatile int32_t* const volatile dotw12 = nullptr; int32_t volatile * const dotw18 = nullptr; const char* str = "string"; const int32_t& addrof = ch0; bool b3 = 1 > 3; int32_t (*funcPtr)(int32_t, int32_t) = foo_int32_t; int16_t casted = reinterpret_cast<int16_t>(43); @syscall($1, $1, &message, $13); @call somefunc @mov $60, %rax @mov $0, %rdi @syscall }
Здесь @mov и const int32_t* парсятся одним и тем же ядром. Компилятор понимает семантику ваших действий с регистрами и типами одновременно.
Архитектурный сдвиг: От плоского списка к Frontend-Backend Split
Переход на Raptor Engine — это фундаментальная смена парадигмы для AsmX. В старом ядре (Legacy Driver) лексер передавал токены базовому парсеру рекурсивного спуска (recursive descent), который почти мгновенно транслировал их в плоский список инструкций для железа. Это делало невозможным сложный статический анализ и многопроходные оптимизации.
В Raptor мы внедрили строгую многоуровневую архитектуру компиляции:
Transformer V2 (Умный препроцессинг): Пре-нормализация токенов и разрешение сложных лексических конструкций с помощью логики lookahead (заглядывания вперед).
Pratt Parser: Мы объединили рекурсивный спуск с алгоритмом сортировочной станции (Precedence Climbing) для построения строго типизированного абстрактного синтаксического дерева (AST).
Semantic Analyzer: Проактивная валидация. Анализатор обходит AST, выполняет проверку типов (
QualType), управляет областями видимости (Scope) и разрешает символы до того, как будет сгенерирован хоть один байт машинного кода.Compiler Driver (Raptor): Мост абстракций. Он связывает высокоуровневое AST с низкоуровневым аппаратным представлением через паттерн "Operand Bridge".
Благодаря этому конвейеру, AsmX Raptor теперь не просто транслирует мнемоники, он понимает ваш код.
Эволюция системы типов: от хардкода к полноправным абстракциям
Давайте будем честны: в старой версии AsmX система типов существовала скорее для галочки. Был жестко задан один-единственный тип str, и на этом статический анализ заканчивался. Но чтобы язык мог претендовать на системный уровень, он обязан понимать, с какими данными работает железо.
В релизе AsmX Raptor мы с нуля переписали систему типов, вдохновляясь мощью C++, но отбрасывая его исторический багаж. В движок встроена таблица базовых типов, которая «из коробки» понимает int16_t, int32_t, bool, char и указатели. Что мы имеем под капотом теперь:
Истинный nullptr, а не макрос-костыль
В C и раннем C++ NULL был просто макросом, разворачивающимся в 0, что приводило к диким ошибкам при перегрузке функций. В Raptor nullptr — это самостоятельный примитивный тип (nullptr). Анализатор гарантирует, что nullptr может инициализировать только указатели.
const int32_t* p = nullptr; ;; OK: указатель принимает nullptr const int32_t p = nullptr; ;; Ошибка компиляции
[ExpressionException]: [type error] cannot initialize 'const int32_t' with nullptr: not a pointer type 95 | 96 | const int32_t p = nullptr; 97 | ^-------------------------
Полноценные CV-квалификаторы и защита LValue
Язык нативно понимает const и volatile, храня их как свойства внутри обертки QualType. Семантический анализатор ударит вас по рукам, если вы попытаетесь присвоить значение read-only lvalue (константе).
const char char_a = 'a'; char_a = 'd';
[ExpressionException]: [type error] cannot assign to a const-qualified lvalue 85 | 86 | char_a = 'd'; 87 | ^------------
Reference Collapsing и строгие касты
Мы реализовали правила схлопывания для ссылочных типов (Reference Collapsing), чтобы в будущем гладко интегрировать продвинутое управление памятью и семантику перемещения.
Reference Collapsing (Схлопывание ссылок): Реализованы строгие правила для ссылочных типов (LValue/RValue). Попытка привязать неконстантную ссылку к временному объекту или другому типу вызовет контекстную ошибку:
[ExpressionException]: [type error] cannot bind non-const lvalue reference 'const int32_t&' to a value of type 'const char*'; a temporary cannot be created for a non-const reference 95 | 96 | const int32_t& ref = str; 97 | ^------------------------
Никакого неявного превращения указателей в целые числа. Как и в C++, мы ввели явные намерения через узлы ImplicitCastExpression. Вы используете reinterpret_cast<T>, чтобы компилятор (и другие программисты) четко видели: здесь происходит низкоуровневая магия с памятью, и система типов временно отключается для конкретного блока.
Механика продвинутых кастов (Advanced Casting)
Приведения типов в Raptor — это не просто смена тега компилятора. Это полноценные AST-узлы (ImplicitCastExpression), которые проходят семантический анализ. Мы строго соблюдаем правила Value Categories, где касты порождают PRValue (Pure R-Value), за исключением ссылочных кастов, сохраняющих статус LValue.
const_cast<T>: Работает только с указателями и ссылками для снятия/добавления CV-квалификаторов.[ExpressionException]: [type error] invalid const_cast: const_cast can only be used with pointers or references 72 | int16_t casted = const_cast<int32_t>(2026); 73 | ^-------------------------static_cast<T>: Выполняет стандартные конверсии с проверкой размеров черезAnyType.size().int32_t casted = static_cast<int16_t>(43); ;; OK int16_t casted = static_cast<int16_t>(43); ;; OK[ExpressionException]: [type error] cannot initialize 'int16_t' with 'int32_t' 72 | int16_t casted = static_cast<int32_t>(2026); 73 | ^-------------------------reinterpret_cast<T>: Низкоуровневый спасательный люк. Позволяет сырое приведение памяти (pointer punning), отключая систему типов для конкретного блока.
Инициализация данных и секции
Системное программирование требует точного размещения данных в бинарном файле. Мы полностью переработали синтаксис работы с секциями. Теперь инициализация переменных в .rodata или .data проходит через строгую проверку тайпчекером:
@section rodata { integer: int32_t(1); message: const_cast<const char*>("Hello World!\n"); } @section data { call: const_cast<char*>("call from the somefunc\n"); }
Если вы попытаетесь использовать тип, который еще не зарегистрирован в ядре компилятора, AST-парсер мгновенно остановит сборку. Мы отказались от нечитаемых логов в стиле старого GCC и сделали систему ошибок человечной. Компилятор показывает конкретную строку и указывает на проблемный токен:
[ExpressionException]: Unknown type name 'int8_t' 4 | 5 | some_int: int8_t(1); 6 | ^---------
Функции и тайпчекер: контроль на входе и выходе
Внедрение строгой типизации полностью изменило то, как мы объявляем функции. AST-дерево валидирует типы аргументов и возвращаемое значение.
Взгляните на реализацию системного вызова:
fn syscall_write(int32_t fd, const char* buf, int32_t count) -> int32_t { @mov $1, %rax; @syscall; }
Здесь int32_t и const char* — это не просто текст. На данном этапе архитектура AST уже позволяет полноценно декларировать функции с типизированными параметрами и возвращаемыми значениями.
Примечание: Разрешение имен (Name Resolution) для перегрузок функций (overloads) и строгая валидация аргументов непосредственно при генерации вызова — это следующие этапы. Реализация перегрузок требует сложного манглирования (name mangling) по стандарту Itanium и скоринга типов. Сейчас мы заложили под это железобетонный фундамент.
Сборка модулей ядра Linux (.ko) без привязки к версии
Возможно, самое революционное системное обновление AsmX-G3 — это синтез динамических модулей ядра.
Legacy-драйвер имел захардкоженные смещения структур ядра (lock на версию 6.17.9-arch1-1). Это делало компилятор хрупким. Raptor теперь поддерживает любую версию Linux Kernel (Version-Agnostic).
Используя команду синхронизации --sync (asmx -S linux-headers), компилятор запускает интеллектуальный Makefile:
Компилирует временный probe-модуль ядра.
Извлекает метаданные и отступы
struct moduleпрямо из работающей системы черезdmesg.Кэширует их в
.cache/asmx/linux-headers_$(uname -r).json.
При сборке Raptor автоматически синтезирует обязательную секцию .gnu.linkonce.this_module и валидирует метаданные (например, vermagic) против Module.symvers хоста. Это гарантирует, что при выполнении insmod ваше ядро не упадет в Kernel Panic из-за несовпадения структур.
Инструментарий (Dev Tooling): Заглянуть под капот
Разработка системного языка невозможна без глубокой интроспекции. Мы добавили интерактивные REPL-инструменты для проверки работы компилятора в реальном времени:
--debug-only-lexer-repl: Интерактивная оболочка (REPL), где вы можете вводить токены и наблюдать, какTransformer V2их сплавляет.--debug-ast-dump-repl: Мощнейший инструмент для отладки. Вводите C++-подобный синтаксис (например, сложный указатель на функцию) и мгновенно получайте визуализацию дерева AST.
Функция PrintTree теперь выводит не просто плоский лог, а иерархию с указанием Kind, Type и точных координат <line:col> каждого узла, взятых из объекта StructToken.
Legacy vs. Raptor: Итоговое сравнение
Особенность | Legacy Driver | Raptor Driver (G4 Evolution) |
Парсинг | Top-down Recursive Descent | Pratt (Precedence Climbing) + Recursive Descent |
Валидация | Реактивная (ошибка при эмиссии кода) | Проактивная (Semantic Analyzer Pass) |
Типобезопасность | Неявная (String-based) | Строгая |
Kernel Support | Привязка к конкретной версии | Агностичная (Dynamic Sync) через |
Доступ к памяти | Базовый | Интеллектуальный |
Кодогенерация | Прямые вызовы HWM | Абстракция "Operand Bridge" |
Приведение типов | Сырое (None) | Поддержка |
Roadmap: Почему мы не даем точных сроков
Сейчас архитектура фронтенда (лексер, парсер алгоритма сортировочной станции, семантический анализатор) полностью завершена. Вы уже можете писать сложные типизированные структуры, касты и функции — движок успешно их парсит и строит корректное AST.
Следующий логичный шаг — кодогенерация (бэкенд) для локальных переменных, чтобы они превращались в реальные аллокации на стеке или в регистрах. И здесь нас часто спрашивают: "Когда точный релиз?"
Мы не даем жестких roadmap-ов по одной простой причине. Разработка компилятора — это классический «эффект домино». Натыкаясь на необходимость реализовать выделение памяти под переменную, мы понимаем, что нам нужно усложнить систему областей видимости (Scope). Расширение скоупов тянет за собой изменения в таблице символов (SymbolTable.Environment), а это заставляет переосмыслить фазу линковки. Внедрение новых конструкций, вроде IfStatement, потребует модификации конвейера парсера.
Мы предпочитаем выпускать фундаментально правильные решения, а не резать архитектуру ради дедлайнов. AsmX Raptor уже сейчас дает беспрецедентный контроль над инструкциями в рамках единого синтаксиса. И это только начало.
Ссылки
Git diff: 66 files changed, +9 707 insertions(+), -155 deletions(-)
Репозиторий проекта: https://github.com/AsmXFoundation/AsmX-G3
