Компилятор Nimony для Nim 3.0 освоил самокомпиляцию и генерацию кода для JavaScript
Андреас Румпф, автор языка программирования Nim, опубликовал восьмой отчёт о разработке компилятора Nimony, который ляжет в основу будущего релиза Nim 3.0. Главное событие последних месяцев — переход к самокомпиляции: фронтенд, прослойка, оптимизатор и бэкенд теперь могут быть собраны самим Nimony. Это важный шаг, подтверждающий зрелость проекта.
Nimony представляет собой новую реализацию Nim, практически не использующую кодовую базу текущего компилятора. С нуля написаны семантический анализ, обобщённые процедуры, разворачивание шаблонов, управление памятью и генерация кода. Среди целей проекта — инкрементальная перекомпиляция, полностью параллельные сборки, отказ от обязательных предварительных объявлений процедур и типов, типобезопасные дженерики и единая модель конкурентности на основе продолжений.
Этапы компиляции обмениваются данными через текстовый формат NIF. Вместо исполнения макросов в виртуальной машине применяются плагины, компилируемые в нативные исполняемые файлы. Такой подход к расширению функциональности через нативные модули перекликается с архитектурой легковесных сервисов, например Issuerd Вышел Issuerd 0.1.17 — лёгкая альтернатива Keycloak на Rust с проверкой стандартов.
Архитектура перестроена вокруг промежуточного представления NJ («No Jumps»), не содержащего операций break, continue и goto. NJ развивается для использования во фронтендах Nimony и текущей версии Nim 2.x, обеспечивая генерацию кода для C/C++, JavaScript и нативных бэкендов x86-64 и ARM64. Бэкенд nativenif портирован и собирается при помощи Nimony.
Добавлен JavaScript-бэкенд, включаемый подкомандой j. NIFC переименован в Leng, чтобы устранить путаницу между форматами. Обеспечена сборка DLL, позволяющая подключать части программ на Nimony к проектам на Nim 2.x, и добавлена поддержка FreeBSD.
В дополнение к имевшимся режимам управления памятью ARC и atomicArc портированы ORC и YRC. Доступна возможность подключения собственных реализаций через переключатель mm. По умолчанию задействованы типы ref и ptr, не допускающие значения nil; для совместимости со старым кодом предусмотрена прагма {.feature: "lenientnils".}.
Среда выполнения доработана для устойчивости к нехватке памяти. Подобный фокус на надёжности работы с памятью напоминает усилия, предпринятые в утилитах вроде Memtest86+ Memtest86+ 9.0: диагностика памяти выходит на ARM и учится ловить RowHammer. Реализованы суммарные типы и сопоставление с образцом, на основе которых в стандартную библиотеку добавлены типы Opt[T] и Result[T, E] для поддержки функционального программирования.
Проверка диапазонов выполняется полностью на этапе компиляции: если корректность неявного преобразования не удаётся доказать, компиляция завершается ошибкой. Семантика break и block приведена к поведению Nim 3 (RFC 576), а также реализованы явные циклические зависимости между модулями. Классические исключения в виде ref object с подтипами оставлены для совместимости с Nim 2.
Макросы std/macros отображаются на систему плагинов Nimony, что позволяет использовать существующий код метапрограммирования без переписывания. Добавлена параллельная форма цикла for («for i in 0 || 1»), реализованная в виде плагина, интегрированного с пулом потоков и циклом событий. Корректность доступа к результатам проверяется статически.
В стандартную библиотеку добавлены модули httpclient с собственной реализацией TLS, complex, base64, heapqueue и sort. Блокировки переведены на вызовы, не зависящие от libc. Модуль httpclient создан для отработки применения Nim в роли встроенного ассемблера.
Кодовая база компилятора переведена на nifcore — очередную итерацию API для работы с NIF, что снизило потребление памяти. Для повышения безопасности во внутренних структурах NIF задействован подсчёт ссылок и режим copy-on-write. Реализована проверка плагинов перед выполнением, для разработки поставляется линтер.
Добавлены совместимые с Nim прагмы .compile, .link, .intdefine, .booldefine и .strdefine. Прагма .bundle позволяет подключать собственные бэкенды, работающие поверх Leng. При тестировании на системе с чипом Apple M1 самый требовательный к памяти процесс фронтенда занимает 45 МиБ, бэкенда — 111 МиБ, а суммарное потребление при восьми одновременно работающих процессах достигает 171 МиБ.
Сборка тех же компонентов компилятором Nim 2 требует примерно в пять раз больше памяти, что в значительной мере обусловлено применением clang. Целью на текущий год названа готовность компилятора к промышленному использованию для проектов, укладывающихся в задокументированный набор возможностей. В начале года темп разработки снизился из-за параллельной работы над системой инкрементальной компиляции в Nim 2.