-
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
Удалять и не порождать неиспользуемые функции #228
Comments
В #239 предложен способ ускорения работы прогонщика, заключающийся в прогонке за один раз сразу нескольких предложений. И предложен именно как оптимизация, минимизирующая число итераций. Но, если подумать, он решает и настоящую проблему. Текущая задача предполагает создавать функции только в тот момент, когда её уже нельзя дальше прогнать: наткнулись на |
Тема задачи уточнилась. Здесь теперь предлагается после выполнения оптимизаций чистить программу от неиспользуемых функций с суффиксами. Чистить от любых неиспользуемых локальных функций нельзя, ибо есть функция При этом программа также может очиститься от функций |
Задача #254 (новая реализация метафункций) ввела понятие метатаблиц — особого рода функций, содержащих отображения имён на все функции без суффиксов текущей области видимости. Поэтому теперь неиспользуемые функции определяются синтаксически — все entry-функции считаем используемыми, все функции с именами |
Помечу как курсовую, хотя она мне кажется слишком простой для курсовой. Берите, пока я не передумал. |
Не, она не подходит на курсовую. Бессодержательная. |
Древесная оптимизация может сделать используемые функции неиспользуемыми. В исходной программе функция При этом, есть проблема: о том, что функция Поэтому предлагается одновременно оптимизировать функции и помечать среди них используемые. Как это сделать? Рассмотрим общий случай включённой прогонки и специализации. Специализация на данный момент имеет оптимизацию: после анализа функции (шагов прогонки вызовов в нём и прохода специализатора) она помечается «замороженной» и на последующих проходах не сканируется. Предлагается сделать зеркальный шаг: «замораживать» (другой заморозкой — блокировать) также функции, которые ещё никем не вызваны. В начале заблокированы только входные точки: entry, Таким образом, будут оптимизированы и разблокированы только те функции, которые вызываются из входных точек. И оптимизатор не делает лишней работы, и одновременно помечаются используемые функции. Функции, оставшиеся заблокированными, из входных точек прямо или косвенно не вызываются, и их можно удалить. Если включена одна только оптимизация прогонки, то добавляются фиктивные проходы специализации, которые только разблокируют функции. Если оптимизация не включена, то программа от избыточных функций не чистится. Либо можно чистить отдельным инструментом — простым обходом графа вызовов в ширину. Цель настоящей заявки — чистить программу от функций, которые не вызываются в результате применения древесных оптимизаций. Формально проходов станет гораздо больше, но сами проходы станут мельче. Вырожденный проход специализатора, разблокирующий используемые функции, и замораживающий проанализированные, может за отдельный проход не считаться и счётчик проходов не декрементировать. А может и считаться — практика покажет. Заметка на поляхЕсли некоторая функция не является входной точкой, обращения к ней отсутствуют в результатных выражениях и в единице трансляции не используются метафункции, но есть указатели на неё в образцах, то она не используется. А образцы с указателями на неё никогда успешно не сопоставляются. Действительно, указатели на такую функцию никогда не смогут появиться в поле зрения, и образцы (с указателями) никогда успешно не сопоставятся. Если образец с указателем на неиспользуемую функцию является первым в предложении, то такое предложение можно удалить. Если образец находится в условии, то это условие будет всегда ложным, но удалять предложение нельзя — в условии может быть побочный эффект. Эти рассуждения актуальны не только и не столько для обычных указателей вида Алгоритм удаления неиспользуемых функций не должен об этом заморачиваться, он будет оставлять функции, на которые есть указатели и в образцах тоже. Заметим ещё, что вызов неиспользуемой функции может появиться и в результате прогонки. Рассмотрим пример:
Указатель на
И попробуй выяви такую неиспользуемую функцию чисто синтаксически. Но вообще невозможно выявить неиспользуемые функции путём анализа кода программы — задача эквивалентна проблеме останова. Поэтому настоящая заявка посвящена выявлению заведомо неиспользуемых функций ради уменьшения объёма компилируемого кода и тем самым ускорения компиляции. Да и вообще некрасиво, что файл заполняется левым мусором. |
Интересный нюанс. Допустим, у нас есть программа
По идее, вызов Но! Получится вот что:
Из-за аварийного предложения функция
Поскольку заранее неизвестно (вернее, заранее трудно сказать), по чему будут специализированы функции, Но что делать в этой ситуации?
В аварийном предложении будет
Если её переименовывать в Другое интересное соображение. Если в предложении где-то есть вызов пустой функции, то все вызовы, записанные справа от него оптимизировать (прогонять/специализировать) нет никакого смысла: они всё равно не вызовутся. Аналогично, все эти вызовы не следует считать и используемыми, инструмент чистки должен их игнорировать. Как это осуществить — я пока не придумал. |
Поддержка была реализована на основе графа вызовов функций, используемом для авторазметки (#252). Реализованный алгоритм обхода графа не только выявлял функции, пригодные и не пригодные для прогонки, но и функции недостижимые. Этим и воспользовались. Граф строится по упрощённой схеме: не выполняются встраивания косвенных вызовов, метатаблиц и inline-функций. Всё это нужно для разметки прогоняемых вершин, но избыточно в рамках настоящей задачи. Корнями графа при обходе считаются все функции без суффиксов — так проще не в ущерб корректности. При построении в граф также добавляются теги АТД-скобок как указатели. На корректность разметки для прогонки это никак не влияет, поскольку теги абстрактных скобок обычно являются пустыми функциями. Но они нужны алгоритму чистки, иначе определения тегов будут удалены и программа станет некорректной.
Рассахариватель почему-то считал их определениями и стирал $EXTERN’ы с соответсвующими именами.
Теперь не только удаляются $EXTERN’ы, соответствующие определённым функциям, но и удаляются повторяющиеся $EXTERN’ы.
Решение одной из проблем, описанных в #228 (comment) Теперь пустая функция Func@0 создаётся для каждой функции и каждого extern’а. А в аварийных предложениях заменяются ссылки на функции ссылками на их пустые сёстры.
Не очень очевидно, что делать, если оптимизация прервалась из-за ограничения на число проходов. Часть функций заблокированы, т.к. до них дело не дошло. Часть функций заморожены. Часть разморожены. Нужно описать разумное поведение для этого случая. |
Разумное поведение такое. В дереве функции могут быть обычными и замороженными, для замороженных указывается причина:
Причины на данный момент две:
В данной схеме пункты 2–3 и 4–5 могут отсутствовать, если соответствующие оптимизации выключены. Если в файле есть нативные вставки, то по умолчанию разморожены все функции без суффиксов. |
UPD: более верная постановка задачи в комментариях (#228 (comment)).
Эта задача — подзадача для #91.
@InfiniteDisorder реализовал оптимизацию встраивания и прогонки #122, однако, реализовал не оптимально. Когда в процессе прогонки в программе формируется вызов «усечённой» функции
<Func*n …>
, тело функцииFunc*n
добавляется к синтаксическому дереву. Добавление это преждевременное, поскольку на следующей итерации оптимизации вызовFunc*n
может прогнаться/встроиться и в конечной программеFunc*n
никогда вызываться не будет.Правильным является вариант, когда функции
Func*n
добавляются в дерево только тогда, когда вызов<Func*n …>
становится холодным, т.е. не подлежащим дальнейшей оптимизации. Но что, если лимит итераций иссяк, но функциюFunc*n
можно ещё прогнать? В этом случае усечённаяFunc*n
должна добавляться вOptTree-Drive-Finalize
.Вообще, привлекательно выглядит вариант вообще не добавлять в дерево тела встраиваемых или прогоняемых функций, если они не вызываются из единицы трансляции или не entry. Но так делать нельзя, поскольку такие функции могут вызываться через
Mu
, т.е. должны быть доступны для рефлексии. ФункцииFunc*n
илиFunc@n
могут пристутствовать в программе или не присутствовать, в зависимости от ключей компиляции, но функцияFunc
без суффиксов присутствовать должна обязательно. В принципе, можно не добавлять в дерево вспомогательные функции вродеFunc\n
,Func=n
,Func:n
и другие (например,Func?m?n
, добавляемыеDesugaring-UnCondition
), если они растворились при оптимизации. Надо оценить затратность реализации и, возможно, так и сделать.The text was updated successfully, but these errors were encountered: