From cc1eaece8b9e47314d0d1082e2a4f6f6adc780a6 Mon Sep 17 00:00:00 2001 From: xnpster Date: Fri, 12 Jan 2024 17:02:39 +0000 Subject: [PATCH] =?UTF-8?q?Add=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=20=D1=82=D0=B8=D0=BF=D0=B0=20=D0=B0=D0=BD=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=D0=B0=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20(=D1=87=D0=B5=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=B8=D0=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...%D1%80%D0%BD%D0%BE%D0%B2%D0%B8%D0%BA%29.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 %D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8-%D1%82%D0%B8%D0%BF%D0%B0-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%28%D1%87%D0%B5%D1%80%D0%BD%D0%BE%D0%B2%D0%B8%D0%BA%29.md diff --git a/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8-%D1%82%D0%B8%D0%BF%D0%B0-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%28%D1%87%D0%B5%D1%80%D0%BD%D0%BE%D0%B2%D0%B8%D0%BA%29.md b/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8-%D1%82%D0%B8%D0%BF%D0%B0-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%28%D1%87%D0%B5%D1%80%D0%BD%D0%BE%D0%B2%D0%B8%D0%BA%29.md new file mode 100644 index 0000000..3f22c60 --- /dev/null +++ b/%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8-%D1%82%D0%B8%D0%BF%D0%B0-%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%28%D1%87%D0%B5%D1%80%D0%BD%D0%BE%D0%B2%D0%B8%D0%BA%29.md @@ -0,0 +1,115 @@ +Так очень часто задачи, связанные с IR, являются в том или ином виде задачей анализа потока данных ([Wikipedia](https://en.wikipedia.org/wiki/Data-flow_analysis)), было принято решение выделить код обхода ГПУ (CFG) таким образом, чтобы его можно было переиспользовать. Эта станица посвящена тому, как его использовать. + +### Введение + +Исходные файлы находятся в папке `CFGraph/DataFlow`. На вершине иерархии находится абстрактный класс `DataFlowAnalysis`. Предполагается, что для каждого анализа будет создаваться наследник (не обязательно непосредственный) и из него будет создаваться (чаще всего один) объект, назовём его объектом анализа. После этого в объект будет загружаться ГПУ, представленный массивом из `SAPFOR::BasicBlock*` вызовом функции `fit` и запускаться анализ вызовом функции `analyze`. + +Теперь подробнее о NodeType. Предполагается, для каждого анализа также будет создаваться наследник класса `DataFlowAnalysisNode`, который будет представлять собой обёртку над `SAPFOR::BasicBlock*`. `DataType `- это тип тех самых данных, которые предпологается распространять по графу. Единственное ограничение - это должна быть итерируемая коллекция. Чаще всего этот тип должен совпадать с типом множеств IN и OUT поставленной задачи потока данных. Этот класс должен реализовывать 5 функций: + +* `DataType getIn()` (пока что это неиспользуемая функция) +* `DataType getOut()` + +* `bool addIn(const DataType& data)` +* `bool addOut(const DataType& data)` (пока что это неиспользуемая функция) + +* `bool forwardData(const DataType& data)` + +Первые две функции должны возвращать для данной вершины множества IN и OUT соответственно (чаще всего это просто геттеры). + +Вторые две функции должны добавлять в множества IN и OUT соотвественно текущей вершины переданные данные и возвращать true, если новый элемент был добавлен. Предполагается, что это "добавление" эквивалентно dest := data U dest, где dest - IN и OUT соотвественно, U - оператор сбора. + +Последняя функция должна выполнять обновление множества OUT по подмножеству множества IN. Предполагается, что это реализация этой функции совпадает с передаточной функцией (**transfer function**, см статью из вики в заголовке). + +### Алгоритм обхода графа. + +Выполнение требуемого анализа начинается с вызова функции `DataFlowAnalysis::analyze()`. В ней выполняется последовательный проход по вектору `nodes` (то есть от первого его элемента к последнему). Для очередного элемента вектора (назовём его `node` = `nodes[i]`) вызывается функция `DataFlowAnalysisNode::doStep()` (о ней будет рассказано далее). Потом для `node` выбирается случайная дуга R из Rollback \ IgnoreRollback (Rollback - множество обратных дуг для данной задачи, то есть дуга, переход по которой повлечёт переход в вершину, которая в массиве вершин имеет меньший индекс (на самом деле меньший или равный), чем текущая; IgnoreRollback - подмножество множества Rollback, изначально пусто). После этого R помещается в node->IgnoreRollback и производится переход по R (в этом случае производится переход в какую-то предыдущую вершину) (На самом деле этот откат производится только если данные текущего блога строго более "свежие", иначе R попадает в IgnoreRollback и выбирается другое R). Если Rollback \ IgnoreRollback пусто, то выполняется переход к следующей за node в nodes вершиной (к `nodes[i+1]`). +Алгоритм завершается, когда весь массив пройден. + +### Анализ отдельной вершины (doStep) + +Начинается всё с того, что каждой вершине приписывается два счётчика: `cnt_in = cnt_out = 0`. Данные блока `A` более свежие чем данные блока `B`, если `A->cnt_out > B->cnt_in`. `A->doStep()` перебирает все блоки `B` такие, что блок `B` имеет более свежие данные чем `A`, то есть `B->cnt_out > A->cnt_in`. Для каждого такого блока `B` он обновляет множество `A->IN` данными из `B->OUT` через `A->addIn(B->getOut())`. И для тех данных, которые действительно обновили `A->IN` он вызывает `A->forwardData()`. (На самом деле берётся не вся коллекция `B->getOut()` сразу целиком, а каждый её элемент сначала попадает в `addIn`, а затем, если `addIn` возвращает `true`, попадает в `forwardData`). При этом в `cnt_in` (`cnt_out`) попадает максимальное значение счётчика `cnt_out` среди блоков, которые добавили в множество `IN` (при помощи `addIn`) хоть один новый элемент (соответственно добавили в `OUT` при помощи `forwardData` хоть один новый элемент). Если данная вершина вызывает `doStep` впервые, то в конце [к `cnt_in` и `cnt_out` прибавляем по 1][**TODO: это не правильно, нужно потом исправить в коде и здесь**] чтобы обозначить, что в `OUT` этой вершины есть данные, которые появляются изначально при создании вершины и которые нужно распространить в остальные вершины. + +Теперь осталось только решить задачу с заполнением `nodes`. Эту задачу берёт на себя метод `fit()`. Реализация этого метода отличается в случаях, когда рассматривается задача прямго и обратного анализа потока данных. До этого повествование шло, будто мы рассматриваем задачу прямого обхода. Поэтому в случае прямого обхода nodes заполняется естественно в топологическом порядке, то есть так, чтобы выполнялось соотношение, что для любых `i < j` дуги из `nodes[i]` в `nodes[j]` либо нет, либо это обратная дуга графа потока управления (см функцию `sortCfgNodes`). Для каждой созданной вершины `node` нужно заполнить + +* `rollback` - множество `i` таких что есть обратная дуга `node -> nodes[i]` +* `prev_blocks` - множество всех вершин, из которых в node могут попасть данные (для задачи прямого обхода - это `nodes[i]` такие что есть дуга `nodes[i] -> nodes`; для задачи обратного прохода - это `nodes[i]`: есть дуга `node -> nodes[i]`). +* `bb` - соответствующий базовый блок. + +На самом деле реальизация `fit` для задач прямого и обратного обхода почти одинаковая, потому что обратная задача отличается только тем, что массив `nodes` развёрнут и `prev_blocks` другой. При этом `IN` `DataFlowAnalysisNode` соотвествует `OUT` для базового блока и наоборот `OUT` `DataFlowAnalysisNode` соотвествует `IN` базового блока. +Поэтому для `DataFlowAnalysis` определены классы `BackwardDataFlowAnalysis` и `ForwardDataFlowAnalysis` (**TODO: ForwardDataFlowAnalysis ещё не определён**). + +На практике требуется при создании экземпляра наследника `DataFlowAnalysisNode` передавать в конструктор какие-то аргументы кроме базового блока, поэтому в `fit` при создании объекта наследника `DataFlowAnalysisNode` используется виртуальная функция `createNode`, которая должна быть реализована в конечном наследнике и выполнять создание объекта типа `NodeType`. Чаще всего это просто вызов конструктора `NodeType` с аргументамы `BasicBlock*` и аргументами-полями класса. + +Итак, если подвести итог, то для создания анализа достаточно: + +* Унаследоваться от `DataFlowAnalysisNode` и реализовать в нём необходимые функции (+ скорее всего понадобится конструктор) (назовём этот класс `NodeImpl`) + +* Унаследоваться от `BackwardDataFlowAnalysis` или `ForwardDataFlowAnalysis` и реализовать в нём `createNode` (+ скорее всего понадобится конструктор) (назовём этот класс `DataFlowImpl`) + +* создать объект `DataFlowImpl analysis_object` +* загрузить в него ГПУ: `analysis_object.fit(CFG)` +* запустить анализ: `analysis_object.analyze()` +* опционально результаты анализа можно помещать в `NodeImpl` и потом проитерировать по всем вершинам с помощью `analysis_object.getNodes()` + +### Пример + +[**Анализ живых переменных**](https://en.wikipedia.org/wiki/Live-variable_analysis) (задача обратного обхода) (см файл `CFGraph/live_variable_analysis.cpp`) + +``` +class LiveVarAnalysisNode : public DataFlowAnalysisNode>> { +private: + set live, dead; +public: + map> getIn() { return getBlock()->getLiveOut(); }; + + map> getOut() { return getBlock()->getLiveIn(); }; + + bool addIn(const map>& data) { return getBlock()->addLiveOut(data); }; + + bool addOut(const map>& data) { return getBlock()->addLiveIn(data); }; + + bool forwardData(const map>& data) + { + bool inserted = false; + + for (const auto& byArg : data) + if (live.find(byArg.first) == live.end() && dead.find(byArg.first) == dead.end()) + inserted |= getBlock()->addLiveIn({ byArg }); + + return inserted; + }; + + LiveVarAnalysisNode(SAPFOR::BasicBlock* block, vector& formal_parameters, + vector& fcalls, const map& funcByName) + { + setBlock(block); + + buildUseDef(getBlock(), live, dead, formal_parameters, fcalls, funcByName, true); + + for (SAPFOR::Argument* arg : live) + getBlock()->addLiveIn({ { arg, { getBlock() } } }); + } +}; + +class LiveVarAnalysis : public BackwardDataFlowAnalysis { +protected: + vector& formal_parameters; + vector& fcalls; + const map& funcByName; + + LiveVarAnalysisNode* createNode(SAPFOR::BasicBlock* block) override { + return new LiveVarAnalysisNode(block, formal_parameters, fcalls, funcByName); + }; +public: + LiveVarAnalysis(vector& formal_parameters, vector& fcalls, + const map& funcByName) : formal_parameters(formal_parameters), fcalls(fcalls), funcByName(funcByName) + { }; +}; + +LiveVarAnalysis* analysis_object = new LiveVarAnalysis(params, curr_fcalls, funcByName); +analysis_object->fit(itCFG->second); +analysis_object->analyze(); +``` + + +Надеюсь, это кому-нибудь поможет, c вопросами и предложениями можно обращаться к @xnpster. \ No newline at end of file