Есть статья под названием Больше не нужно, в которой предлагается алгоритм независимое структурирование потока управления. Это хороший кусок работы для понимания и реализации, но я использую его для своего старшего проекта, и похоже, что он работает.
Моя реализация доступна на GitHub по адресу zneak/fcd. Проект использует LLVM IR в качестве промежуточного представления.
EDIT На очень высоком уровне алгоритм работает следующим образом:
Доминирование
Просто чтобы убедиться, что эти концепции понятны: узел A доминирует над узлом Z, если любой путь от входа в граф до Z должен проходить через A. Точно так же узел Z постдоминирует над узлом Z. node A, если любой путь от A до конца графа должен проходить через Z.
Регионы
Алгоритм структурирует регионы. Регион — это часть графа, которая имеет одно ребро входа и одно ребро выхода. Я лично расширил это определение на базовые блоки (регионы имеют один блок входа и один блок выхода) и сделал так, чтобы блок выхода был исключен из региона.
Я использую следующее определение региона:
- Узел входа доминирует над узлом выхода;
- Узел выхода постдоминирует над узлом входа;
- Как только вы достигнете выхода, любой путь, который приведет вас обратно в регион, проходит через входной узел. (Этот последний бит очень важен и решает проблемы, связанные с циклами.)
Определение подразумевает, что вход доминирует над каждым узлом через выходной узел, а выходной узел доминирует над каждым узлом из входа.
Нормализация циклов
Петля — это область с «обратным краем» (узел с краем, возвращающимся к уже посещенному узлу, если вы прошли граф в глубину).
Убедитесь, что циклы представлены как регионы с одним входом и одним выходом с одним обратным краем. То есть у них должен быть только один входной узел (на который также указывает заднее ребро) и один преемник. Когда это не так, вы можете ввести новый входной блок и сделать так, чтобы все ребра указывали на него, а затем использовать Φ-узел для перенаправления выполнения оттуда (другими словами, ввести переменную, которую вы устанавливаете в конце каждого входящего блока). блок и выполните if (var == 0) { first entry } else if (var == 1) { second entry }
из нового блока).
В моей реализации это происходит на этапе StructurizeCFG в ветке master, на момент написания. Однако он дает плохие результаты, потому что работает гораздо усерднее, чем нужно. Мне это нужно только для структурирования циклов, но оно также структурирует конструкции if-else, и, хотя это не нарушает алгоритм, оно вводит waaaay во многие Φ-узлы, чтобы получить красивый результат. На момент написания также существует ветка под названием seseloop
с пользовательским проходом, чтобы убедиться, что циклы имеют один вход и один выход. Этот проход не затрагивает конструкции if-else, если в этом нет необходимости.
Структурируйте свои функции
Пройдите базовый блок-граф в обратном порядке. Определите регионы, начиная с этого блока. Вы можете использовать дерево пост-доминаторов, чтобы немного ускорить это, так как область должна заканчиваться пост-доминаторами блока (поэтому для каждого блока проверяйте только пост-доминаторы блока).
Если входной блок имеет задний край, указывающий на него, структурируйте его как петлю. Если нет, структурируйте его как регион. Когда регион структурирован, поместите его обратно на график в виде свернутого отдельного узла, который можно включить в другой более крупный структурированный регион.
Это происходит в ast/ast_backend.cpp.
Структурирование регионов
Используйте обход в глубину (пропуская циклы) в области, чтобы определить условие, приводящее к выполнению любого блока. Например:
![График, описывающий последовательность if-then-else. Узел A — входной узел, узел B — истинный узел, узел C — ложный узел, а узел D — выходной узел.](https://i.stack.imgur.com/6lU2F.png)
Узел A не имеет условий. Узел B достигается, если условие в конце узла A истинно. Узел C достигается, если он ложный. Узел D достигается, если он был либо истинным, либо ложным.
(A);
if (a_cond) (B);
if (!a_cond) (C);
if (a_cond || !a_cond) (D);
Затем вам придется упростить эти условия, что, к сожалению, является NP-полной задачей. В общем, не должно быть слишком сложно вернуться к чему-то вроде A; if (a_cond) B; else C; D;
, свернув a_cond || !a_cond
и сравнив условные термины по порядку.
Структурирующие циклы
В основном вы делаете то же самое, как если бы вы структурировали регион, не заботясь о том, что это цикл, но после этого вы добавляете операторы break (с условиями, когда это уместно) в конце блоков, которые могут выйти из цикла, и вы заключаете регион в цикл. while true
блок. Затем авторы определили 6 шаблонов, которые можно заменить более удобочитаемыми шаблонами (например, while true
, начинающийся с if (cond) break
, можно преобразовать в while !cond
).
И это все.
person
zneak
schedule
17.07.2015