-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Компоновка единиц трансляции на Си #324
Comments
Два соображенияОшибка компоновки для повторяющихся entry-функцийКомпилятор должен выдавать ошибку, если в различных единицах трансляции находятся одноимённые entry-функции (#255, #265). В текущем варианте с нативными функциями это работает — синхронно создаётся Предлагаемый выше подход подразумевает написание одного сишного файла, у которого компилятор Рефала интерпретирует только первую строку — извлекает из неё имя таблицы определений. Т.е. почти полностью сишный файл для компоновщика является чёрным ящиком. Поэтому компилятор не знает, какие entry-функции в этом файле определены. Если сишный файл создаётся при использовании оптимизации прямой кодогенерации, компилятор мог бы генерировать синхронный с ним Вариант решения проблемы — добавлять в исходник комментарий вида /*
$ENTRY Foo
$ENTRY Bar
$ENTRY Baz
*/ содержимое которого будет анализироваться на стадии компоновки. Недостаток подхода: комментарий пишет сам программист и вынужден согласовывать его содержимое вручную. Альтернативный вариант — парсить сам файл, ища в нём макросы Поэтому скорее всего придётся остановиться на подходе с комментарием.
|
Рабочий пример #include <stdio.h>
#define TABLE \
UNIT(foo, 10) \
UNIT(bar, 20) \
UNIT(baz, 5)
/* START */
#define UNIT(name, val) \
name_ ## name,
enum {
TABLE
};
#undef UNIT
#define UNIT(name, val) \
val,
int values [] = {
TABLE
};
#undef UNIT
/* END */
int main() {
printf("foo %d\n", values[name_foo]);
printf("bar %d\n", values[name_bar]);
printf("baz %d\n", values[name_baz]);
return 0;
} Текст между |
Эта задача — подзадача #197 и #313.
Цель
Нужно разработать API и механизм компоновки единиц трансляции на Си. При этом механизм должен быть
.rasl
-файла — сейчас он требуется, файлы рантайма имеют пустой.rasl
-файл.Текущая ситуация
На данный момент единственный способ написать примитивную функцию на C++ — написать исходник на Рефале и написать в нём функцию с нативной вставкой. В результате будет скомпилирована пара файлов
.rasl
+.cpp
.В первом файле будет находиться список «нативных» функций, которые нужно будет найти и подгрузить, список идентификаторов, которые нужно будет аллоцировать, и список пустых функций, функций-условий и swap’ов, которые аллоцируются кодом на Рефале. Второй файл будет содержать, помимо кода нативной вставки, enum’ы с номерами функций (включая внешние) и идентификаторов из списков. Для нативных функций будут написаны конструкторы глобальных объектов, которые регистрируют их в односвязном списке.
Рантайм при загрузке сначала прочитает модуль на RASL, аллоцирует всё, что там необходимо (идентификаторы, пустые функции, статические ящики, дескрипторы функций для нативных функций). Затем произведёт разрешение ссылок на нативные функции.
Таким образом, для написания внешних функций нужно писать исходник на Рефале с нативными вставками, скомпилированная единица трансляции будет представлять собой пару
.rasl
+.cpp
.Из-за этого файлы рантайма приходится создавать пустые файлы
.rasl
, иначе файлы не прикомпонуются.Реализация привязана к C++, поскольку регистрация нативных функций выполняется конструкторами глобальных переменных.
Подход уродливый, нативные вставки уродливые (см. #318), привязка к C++.
Цель (развёрнуто)
Надо сделать так, чтобы исходники можно было писать на Си (C89) и подключать к программе естественным образом — указывая в командной строке компилятора. Без дополнительных файлов
.rasl
и прочей обработки.Нужно сделать удобный и красивый интерфейс. Идеал — Рефал-05, но отличие в том, что Рефал-05 поддерживает только компиляцию в Си и компоновка программ у него статическая сишная. У Рефала-5λ компоновка динамическая, поскольку нужно подгружать файлы с байткодом и их интерпретировать.
Интерфейс должен упрощать написание пользователем «обвязки» — минимизировать количество и упростить написание вспомогательного кода, не относящегося к задаче.
Конкретика
Предлагается развитие идеи, описанной в #197. В файле создаётся дескриптор с уникальным именем, содержащий ссылки на все сущности программы (массивы идентификаторов, функций и т.д.). При компоновке создаётся временный файл
.c
, содержащий массив указателей на все дескрипторы исходных файлов. Рантайм проходит по этому списку и сканирует дескрипторы единиц трансляции, создавая идентификаторы, нативные функции и прочее.Обвязка в данном случае — это объект дескриптора и всё с этим связанное — весь код, кроме тел функций на Си. Обвязка минимизируется путём написания макросов по определённым соглашениям.
Исходный файл на Си выглядит таким образом:
Содержимое
my-unit-name.table
:Первое вхождение
my-unit-table.table
превращается в enum:rl
означает Рефал-5λ,e
— enum,id
,fn
,var
— идентификатор, функция, переменная.Далее, дескрипторы объектов, создаваемые макросами
RL_…_DEF
, описываются структурами видаДля идентификаторов инициализируется первая структура, для функций и переменных — вторая и третья. Флаги определяют тип сущности и область видимости функций. Идентификаторы компилируются примерно так:
Функции — так:
Добавление сдвинутого идентификатора ко флагам технически не обязательно, но нужно для статического контроля. Если пользователь забудет подключить
rl-table-defs.h
или забудет в ней одну строчку, то выскочит ошибка компиляции. Также рантайм может контролировать правильность идентификатора.Макросы объявления переменных раскрываются аналогично, разве что для типа переменной вводится синоним при помощи
typedef
.Обращения к функциям, переменным и идентификаторам раскрываются примерно также, как сейчас раскрывается
USE_IDENT
вLibrary
с той разницей, что макрос этот будет в API:refal-5-lambda/src/lib/Library.ref
Line 847 in 3cd08ed
Макрос для переменной раскрывается с участием typedef-синонима, поэтому дополнительное приведение типов не нужно.
Второе вхождение таблицы раскрывается так:
Поэтому, если пользователь забудет определить объект из таблицы, то тоже получим ошибку компиляции.
Можно также придумать фокус, чтобы мы получали ошибку компиляции, если второго вхождения таблицы нет. Можно в
rl-table-decl.h
объявить, но не определить статический объект, и использовать его, а вrl-table-def.h
его определить.Строка
всегда должна располагаться в начале файла — она распознаётся компилятором Рефала и из неё берётся идентификатор для списка инициализации.
Для генерации имени точки входа можно написать утилиту, которая вычисляет хеш от содержимого файла (начиная со второй строчки) и файла
.table
(оба имени задаются в командной строке) и на основе этого хеша генерировала случайное имя. Например, хеш в системе счисления по основанию 52 (буквы обоих регистров) или 62 (буквы обоих регистров + цифры), но при этом нужно гарантировать, что имя не начинается на цифру.Очевидно, что когда компилятор генерирует код на Си, он его компилирует по той же схеме, разве что файлы
.table
не создаёт — текст таблицы дважды помещает в исходник.Если файл не начинается на определение макроса
RL_UNIT_ENTRY_POINT
, то это файл рантайма, он просто передаётся компилятору Си. Можно сканировать файл целиком и выдавать предупреждение, если макрос объявлен, но не располагается на первой строчке.В целях отладки для единицы трансляции нужно хранить имя файла единицы трансляции. Это несложно сделать, добавив ещё одну сущность, заносимую в таблицу, но пример я решил не перегружать. Также данный подход очевидно расширяется на поддержку метафункций.
Сквозная нумерация функций, переменных и идентификаторов сделана для упрощения работы программиста — нужно объявлять не несколько отдельных таблиц, а только одну. Различные внутренние имена для идентификаторов, функций и переменных (
rleid_
,rlefn_
,rlevar_
, соответственно) не дадут пользователю перепутать разные сущности, например, указав имя функции вRL_IDENT
илиRL_VAR
.Вывод. Предложенный способ работоспособен и не сильно напрягает с написанием связующего кода. А тот код, который написать требуется (таблица + определения используемых сущностей), может быть статически проверен компилятором языка Си на согласованность.
The text was updated successfully, but these errors were encountered: