-
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
Компилятор вылетает при самоприменении #319
Comments
Ошибка, выявленная самоприменением, исправлена. Оказалось, что это была простая опечатка.
This comment has been minimized.
This comment has been minimized.
Когда обнаруживалось зацикливание по Хигману-Крускалу, после построения обобщённого вызова не восстанавливались вызовы в нём — оставались заглушки e.Call.
This comment has been minimized.
This comment has been minimized.
Функция DoUnEscapeString-SR содержит аккумулятор, а из-за обобщения снизу этот аккумулятор специализируется для случаев каждого экранированного символа в нём. Это даёт визуальную задержку компиляции на маленьком файле. Поэтому данная функция помечена специализируемой без статических параметров — любой её вызов будет тривиальным.
Теперь компилятор успешно самоприменяется с
Повторный прогон даёт такой результат:
Т.е. ускорение примерно в 2 раза. Но пока ещё рано оценивать быстродействие, впереди ещё много работы (#310). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Обнаружилось несколько новых проблем. Обнаружились и падения, и зацикливания специализатора. Для диагностики были отдельно прокомпилированы все исходники с сохранением дампа и лога: for %r in (*.ref) do call ..\..\bin\rlc -OiADS --opt-tree-cycles=300 --log=__log-%~nr.log %r -C 2>__err-%~nr.txt Проблемы опишу в комментариях ниже. По мере устранения проблем эти комментарии я скрою. |
Слишком много проходов оптимизацииКак видно из команды выше, выполнялось 300 проходов оптимизации. Такой выбор числа проходов позволил оценить, на каком количестве файлов компиляция не завершилась за 100 и за 200 проходов. Более 200 проходов:
Более 100 проходов:
Значит, нужно разбираться, а что так много. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Мне удалось скомпилировать компилятор с ключами
Почему не получалось раньше — не знаю. Вообще, странностей что-то много последнее время… |
Новая, интересная ошибкаИз-за предыдущего коммита перестала работать прогонка. Сейчас заработала. Попробовал собрать компилятор с ключами set RLMAKE_FLAGS=-X-OdiADGPRS -X--opt-tree-cycles=300
set SCRIPT_FLAGS=--scratch --debug Сначала не хватило памяти на самоприменение. Поднял порог: memory-limit = 75000000 Программа откомпилировалась, но компилятор в ней нашёл ошибки. Новые, интересные ошибки: …
static refalrts::FnResult func_gen_Configm_GetCppCompiler_D2(refalrts::VM *vm, refalrts::Iter arg_begin, refalrts::Iter arg_end) {
(void) vm;
refalrts::this_is_generated_function(vm);
refalrts::RefalFunction **functions;
const refalrts::RefalIdentifier *identifiers;
…
static refalrts::NativeReference nat_ref_gen_Configm_GetCppCompiler_D2("Config-GetCppCompiler*2", COOKIE1_, COOKIE2_, func_gen_Configm_GetCppCompiler_D2);
…
static refalrts::FnResult func_gen_Configm_GetCppCompiler_D2(refalrts::VM *vm, refalrts::Iter arg_begin, refalrts::Iter arg_end) {
(void) vm;
refalrts::this_is_generated_function(vm);
refalrts::RefalFunction **functions;
const refalrts::RefalIdentifier *identifiers;
…
static refalrts::NativeReference nat_ref_gen_Configm_GetCppCompiler_D2("Config-GetCppCompiler*2", 0U, 0U, func_gen_Configm_GetCppCompiler_D2); Т.е. функция определена дважды. Один раз как entry, второй — как локальная. И эта функция — функция-хвост. Полный список продублированных функций
Обращает на себя внимание то, что всё это — хвосты, и одно вхождение определено как entry, а второе — как локальное: Выборка только NativeReference
И это странно: функции-хвосты всегда должны быть локальными. |
Ошибка из предыдущего комментария исправлена. Вылезла другая, знакомая ошибка:
|
Формально разбил длинный файл на два куска примерно равной длины (по 600 000 строк), откомпилировалось:
Значит, или компоновщик не осиливает объектник такой длины, или компилятор его некорректно создаёт. Всё-таки надо чистить программу от избыточных функций. |
Программа от избыточных функций чистится: e389bec, dc64eec. Но это не помогло особо:
Длина файла Было сделано два замера:
В обоих случаях измерялся объём файлов Колонка Выводы:
Просмотр файла find "static refalrts::NativeReference" < OptTree-Drive.cpp | sort Много строчек
Похоже, имеет место некая специализация по флагам. Но это нужно лог изучать. Аналогичный просмотр Похоже, опять какая-то переспециализация по аккумулятору
|
Эксперимент показал, что если файл Всего в файле 955 990 строк. Но корректнее считать не строки, а объём объектного файла:
Возможно, можно снизить объём файла, сделав некоторые функции рантайма inline-функциями или макросами. Первыми кандидатами на это являются Вообще, переделывание кодогенератора результатных выражений в стиле Рефала-05 (#196) в этом контексте становится более осмысленным: refalrts::alloc_chars(vm, context[124], context[125], "issed \']\'", 9);
refalrts::alloc_open_bracket(vm, context[126]);
refalrts::alloc_ident(vm, context[127], identifiers[ident_Brackets]);
refalrts::alloc_ident(vm, context[128], identifiers[ident_ADTm_Brackets]);
refalrts::alloc_ident(vm, context[129], identifiers[ident_NoPos]);
refalrts::alloc_open_bracket(vm, context[130]);
refalrts::alloc_close_bracket(vm, context[131]);
refalrts::alloc_close_bracket(vm, context[132]);
refalrts::update_name(context[4], functions[efunc_gen_DoTokenChain_G21]);
refalrts::reinit_ident(context[7], identifiers[ident_Extended]);
refalrts::reinit_ident(context[20], identifiers[ident_Pattern]);
refalrts::reinit_open_bracket(context[80]); При генерации результатных выражений в духе #196 контексты для Ещё мелкая оптимизация для режима res = refalrts::splice_evar( res, context[126], context[126] ); /* ⭐ */
res = refalrts::splice_evar( res, context[113], context[114] );
res = refalrts::splice_evar( res, context[125], context[125] ); /* ⭐ */
res = refalrts::splice_evar( res, context[107], context[108] );
res = refalrts::splice_evar( res, context[8], context[11] );
res = refalrts::splice_evar( res, context[124], context[124] ); /* ⭐ */
res = refalrts::splice_evar( res, context[16], context[19] ); Видно, что инструкции Также можно заменить Объявление
Было:
Неожиданно. Стало быть, встраивание кода не факт что здесь поможет. UPD: замена
context[19] = 0;
context[20] = 0;
context[21] = refalrts::brackets_left( context[19], context[20], context[17], context[18] );
if( ! context[21] )
continue;
refalrts::bracket_pointers(context[21], context[22]); Обнуление диапазона перед присваиванием ему избыточно. Всё равно эти переменные перезаписываются. Это обнуление тянется ещё с первой версии Простого Рефала, где я ещё не был уверен в корректности и делал это на всякий случай. |
Предыдущий коммит оптимизировал перенос плитки из одного элемента: res = refalrts::splice_elem( res, context[126] );
res = refalrts::splice_evar( res, context[113], context[114] );
res = refalrts::splice_elem( res, context[125] );
res = refalrts::splice_evar( res, context[107], context[108] );
res = refalrts::splice_evar( res, context[8], context[11] );
res = refalrts::splice_elem( res, context[124] );
res = refalrts::splice_evar( res, context[16], context[19] ); И, как ни странно, это помогло! Размер объектника:
Отсюда вывод: нужно развивать алгоритмы кодогенерации (#196, #169, #204). |
Обнаружено очередное зацикливание специализатора на аккумуляторе: #332 (comment). |
Оставался без ответа вопрос: почему компилятор делает так много проходов, нет ли здесь ошибки? Ответ: это так и должно быть. Число проходов зависит от глубины дерева функций — чем она больше, тем больше проходов. Т.к. оптимизации фактически реализуют ограниченную суперкомпиляцию, то число проходов в принципе непредсказуемо. Но из общих соображений на каждый «слой» в дереве требуется два прохода прогонки (до неподвижной точки тёплых функций) и проход специализации, в котором появляются новые экземпляры, пригодные для дальнейшей оптимизации. Вот пример: refal-5-lambda/src/compiler/OptTree-Drive.ref Lines 42 to 45 in f5ba6cb
refal-5-lambda/src/compiler/OptTree-Drive.ref Lines 69 to 102 in f5ba6cb
Исходно формируется пустая информация (с пустыми |
В соответствии с замечанием #319 (comment) число проходов увеличено до 300. Опция -OG пока не включена, т.к. возникают проблемы с компоновкой BCC.
При самоприменении в режиме
RLMAKE_FLAGS=-X-OADS
компилятор упал.Выяснилось, что проблема в неверном порождении кода специализированной функции. Запуск
выявил в логе следующее:
В правой части появляются переменные
s.X#0
,e.X#0
иe.X0#0
, которых нет в левой части. При генерации RASL’а для этой функции компилятор падает.Переменные с индексами
X
порождаются в специализаторе:refal-5-lambda/src/compiler/OptTree-Spec.ref
Lines 1510 to 1514 in 3cd08ed
Удалось построить небольшой тест, воспроизводящий проблему:
В логе для теста:
The text was updated successfully, but these errors were encountered: