Привет всем! Прежде чем мы погрузимся в глубокий анализ архитектуры компилятора ZGEN, я хочу приоткрыть завесу тайны и рассказать о том, что прямо сейчас кипит в нашей "лаборатории". Это те фичи и улучшения, которые проходят активное тестирование и, возможно, скоро станут частью следующего релиза. Ваше мнение по этим вопросам поможет нам двигаться в правильном направлении.
Warnings, Errors: На пути к умной диагностике
Хороший компилятор — это не тот, который просто работает, а тот, который помогает вам писать код лучше. Мы уделяем огромное внимание качеству диагностики. Сейчас компилятор уже выдает осмысленные предупреждения и ошибки, указывая на конкретное место в коде:
[zcc::driver<impl AssemblyDriver>]: Compilation of user-defined module is not supported
2 |
3 | @include std::fs::read_to_string;
4 | ^--------------------------------
[zcc::driver<impl AssemblyDriver>]: Compilation of user-defined module is not supported
4 |
5 | @include ownlib("./mylibs/ownlib.asmx");
6 | ^------------------------------------
[zcc::driver<impl AssemblyDriver>]: Compilation of user-defined fn functions is not supported
16 |
17 | @fn pub assume {}
18 | ^--------
[zcc::driver<impl AssemblyDriver>]: Compilation of user-defined fn functions is not supported
18 |
19 | @fn pub consume_message_broker(str message, uint64 len) {
20 | ^------------------------------------------------
Но этого мало. Сейчас мы тестируем две новые возможности:
-
Счетчик предупреждений и ошибок. По завершении компиляции вы будете видеть четкую сводку, которая сразу даст понять масштаб проблем:
i 4 Warnings, 0 Errors.
Это стандарт для серьезных инструментов, и мы его внедряем.
Флаг
-W
для подавления предупреждений. Иногда предупреждения могут мешать, особенно если вы уверены в своем коде. Мы тестируем флаг-W
, который полностью отключит вывод всехwarnings
, оставляя только критические ошибки.
И здесь нам нужен ваш совет. Мы рассматриваем введение более гранулярных флагов, по аналогии с GCC/Clang. Напишите в комментариях, хотели бы вы видеть, например, флаг -Wno-unused-variable
, который бы отключал предупреждения только о неиспользуемых переменных?
T-API: Улучшение интерфейса
T-API
— это наш парсер аргументов командной строки. Он должен быть гибким и интуитивно понятным. Мы обнаружили недоработку в текущей версии: флаги вроде --llvm@version
работают корректно, но более привычный формат --llvm-version
интерпретируется как два отдельных флага: --llvm
и --version
. Это контринтуитивно. Сейчас в лаборатории находится патч, который научит tapi
правильно обрабатывать "длинные" флаги с дефисами, чтобы вы могли использовать тот стиль, который вам удобнее.
Call: Связь с внешним миром
Это, пожалуй, самое горячее направление наших исследований. Мы работаем над полноценной интеграцией с системными библиотеками, такими как libc
.
В идеале, к которому мы стремимся, код для вывода строки будет выглядеть так:
@include libc;
@section rodata {
message: str("Hello World")
}
@fn main {
@call libc::printf("sent %s", $message);
}
Это интуитивно, чисто и эффективно. Но чтобы достичь этой простоты, под капотом должна работать невероятно сложная система. На данный момент наша тестовая реализация выглядит более "низкоуровневой":
@include libc;
@section rodata {
format: str("sent %s")
message: str("Hello World")
}
@fn main {
;; программист сам кладет аргументы в регистры по ABI
@call printf;
}
Почему существует эта разница и что нужно для перехода?
Разбор сложных выражений:
zcc_parser
должен научиться понимать конструкциюlibc::printf
, распознавать ее как вызов функции из внешнего модуля и передавать эту информацию вhwc
.Генерация PLT/GOT: Когда
hwc
видит вызов внешней функции (printf
), он не знает ее точный адрес. Поэтому он генерирует специальный "трамплин" в секцииPLT
(Procedure Linkage Table). Первый вызовprintf
перенаправляется на динамический загрузчик системы, который находит реальный адрес функции вlibc.so
и записывает его в таблицуGOT
(Global Offset Table). Все последующие вызовыprintf
уже будут идти напрямую по адресу из GOT, что практически не сказывается на производительности.hwc
полностью управляет созданием этих таблиц.Сборка вызовов по ABI: Компонент
assembly
должен быть обучен правиламABI
(Application Binary Interface) дляx86_64
. Он должен знать, что первые шесть целочисленных аргументов функции передаются через регистры%rdi
,%rsi
,%rdx
,%rcx
,%r8
,%r9
. Именноassembly
должен будет генерироватьmov
инструкции для загрузки аргументов в правильные регистры перед инструкциейcall
.
Этот функционал находится в активной разработке. Ваше мнение здесь критически важно: стоит ли нам выпустить текущую "ручную" реализацию или подождать полностью автоматизированной? Напишите в комментариях, это поможет нам определить приоритеты.
Section: Работа с данными
На данный момент в ZGEN стабильно работает секция .rodata
для констант. Но для полноценных программ необходима возможность работать с изменяемыми глобальными данными. Прямо сейчас мы проводим тестирование и отладку секции .data
:
@section data {
message: str("This is a data section")
}
Это позволит создавать глобальные переменные, которые можно будет изменять в ходе выполнения программы. Пока что, как и в rodata
, мы поддерживаем только строковый тип (str
), но с расширением системы типов появятся и другие.
И раз уж мы заговорили о типах...
Голосуйте в комментариях!
Хочу сразу отметить: типы для чисел с плавающей точкой (float
, double
) появятся не скоро. Их реализация требует значительных доработок в FPU/SSE-части компилятора и отдельного масштабного тестирования. Мы доберемся до них, но постепенно. Двигаемся итерационно.
Спасибо, что заглянули в нашу лабораторию! Ваша обратная связь помогает нам строить лучший инструмент. В следующей статье мы подробно разберем, как все эти компоненты работают вместе в уже релизной версии компилятора.