-
Notifications
You must be signed in to change notification settings - Fork 18
Function Pass Provider
В LLVM для анализа и преобразования программы используются проходы (llvm::Pass
) разных типов, позволяющих независимо обрабатывать разные участки программы. Конкретная реализация каждого прохода наследует один из заранее определенных базовых классов (llvm::ModulePass
, llvm::FunctionPass
и др.).
В системе SAPFOR наиболее часто используются проходы следующих типов:
-
llvm::ModulePass
- исполняется над всей программой, -
llvm::FunctionPass
- исполняется над каждой функцией, -
llvm::ImmutablePass
- никогда не запускается, не меняет состоние; этот проход только предоставляет информацию.
Замечание. Подробнее про типы проходов можно прочитать здесь.
Один проход может использовать результаты анализов из других проходов. Данные проходы будут автоматически добавлены в очередь выполнения и выполнены в правильном порядке менеджером проходов (llvm::legacy::PassManager
).
При этом перенос результатов анализа между функциональными и модульными проходами может вызывать некоторые трудности.
Чтобы получить результаты анализа некоторой функции из модульного прохода, соответствующий функциональный проход должен быть выполнен "на лету" для данной функции. Для доступа к результатам анализа функции F
можно воспользоваться методом getAnalisys<...>(F)
. При этом каждый раз, когда вызывается этот метод, будет выполнена вся последовательность проходов, необходимых для анализа функции F
.
Если модульный проход M
запрашивает результаты анализа у функциональных проходов P1
и P2
, то данные проходы, а также необходимые для их выполнения проходы будут выполнены независимо друг от друга. Может возникнуть ситуация, когда P1
и P2
зависят друг от друга. Например, если для выполнения прохода P2
требуются результаты выполнения прохода P1
, проход P1
будет выполнен дважды. При этом результаты выполнения прохода P1
, используемые проходом P2
, могут отличаться от результатов выполнения прохода P1
, полученных модульным проходом M
. Таким образом модульный проход не сможет сопоставить имеющиеся у него результаты проходов P1
и P2
.
Некоторый модульный проход, выполняющий преобразование исходного кода программы, запрашивает результат анализа зависимостей по данным для циклов некоторой функции (DIDependenceAnalysisPass
). Данный функциональный проход описывает зависимости по данным не в терминах LLVM IR, а в терминах дерева псевдонимов (DIEstimateMemoryPass
), которое система SAPFOR строит для описания памяти, используемой в данной функции. Следовательно, дерево псевдонимов будет построено во время анализа зависимостей по данным. Чтобы модульный проход мог понять, какая память вызывает ту или иную зависимость по данным, ему также требуется доступ к дереву псевдонимов для анализируемой функции. При этом при использовании метода getAnalysis<...>()
дерево псевдонимов будет построено заново и не будет связано с результатами анализа зависимостей.
using namespace llvm;
bool SomeModulePass::runOnModule(Module &M) {
// Получаем результаты анализа зависимостей для заданной функции.
// При этом неявно будет построено дерево псевдонимов,
// к вершинам которого будут привязаны результаты анализа зависимостей.
auto &DIDepInfo = getAnalysis<DIDependenceAnalysisPass>(F).getDependencies();
// Получаем дерево псевдонимов.
// При этом построенное ранее дерево псевдонимов будет перестроено и
// полученные выше результаты анализа зависимостей по данным потеряют связь
// с описанием участков памяти, вызывавших эти зависимости.
auto &DIAT = getAnalisis<DIEstimateMemoryPass>(F).getAliasTree();
}
Замечание. Фактически вершины дерева псевдонимов выделяются в динамической памяти и связывание с результатами анализа зависимостей по данным осуществляется за счет указателей на объекты, описывающие вершины дерева псевдонимов. При перестроении дерева будут созданы новые вершины, и используемые в описании результатов анализа зависимостей указатели на вершины дерева перестанут быть корректными.
Еще одна проблема - это то, что в менеджере проходов для создания запускаемых проходов используется конструктор умолчания этих проходов, поэтому невозможно уточнить дополнительные параметры для инициализации таких проходов.
Для решения этих проблем в SAPFOR реализован класс FunctionPassProvider
. Данный класс является функциональным проходом и при выполнении "на лету" сохраняет результаты выполнения проходов анализа, которые были явно указаны пользователем при его описании. Все указанные проходы будут выполнены в рамках единой последовательности проходов анализа заданной функции и их результаты затем могут быть корректно сопоставлены в модульном проходе. Кроме того данный класс позволяет инициализировать выполнение любого из выполняемых проходов (даже если он не был явно запрошен пользователем) до начала его исполнения.
#include "tsar/Support/PassProvider.h"
using namespace llvm;
namespace {
using DependenceProvider =
FunctionPassProvider<DIDependenceAnalysisPass, DIEstimateMemoryPass>;
}
INITIALIZE_PROVIDER(DependenceProvider, "dep-provider", "Dependence Provider")
bool SomeModulePass::runOnModule(Module &M) {
// Перед исполнением всех проходов, требующихся для корректного выполнения
// проходов, заданных при описании провайдера, может быть выполнена
// дополнительная инициализация любого прохода (даже если проход не был
// явно указан при описании провайдера).
auto &GlobalOpts = getAnalysis<GlobalOptionsImmutableWrapper>().getOptions();
DependenceProvider::initialize<GlobalOptionsImmutableWrapper>(
[&GlobalOpts](GlobalOptionsImmutableWrapper &Wrapper) {
Wrapper.setOptions(&GlobalOpts);
});
// Запускаем все проходы провайдера в правильном порядке.
auto &Provider = getAnalysis<DependenceProvider>(F);
// Провайдер сохраняет результаты выполнения проходов, указанных при его
// описании, и позволяет получить к ним доступ из модульного прохода.
auto &DIDepInfo = Provider.get<DIEstimateMemoryPass>().getAliasTree();
auto &DIAT = Provider.get<DIDependenceAnalysisPass>().getDependencies();
...
}
Класс FunctionPassProvider
- это шаблонный класс, принимающий переменное количество параметров шаблона, котоые указывают функциональные проходы, к результатам которых необходимо получить доступ.
Данный класс определен в заголовочном файле:
#include "tsar/Support/PassProvider.h"
Если функциональные проходы используют результаты анализа псевдонимов (alias analysis), то рекомендуется использовать FunctionPassAAProvider
, которые предоставляет более полный доступ к результатом анализа псевдонимов проходам запускаемым "на лету". Данные класс наследует FunctionPassProvider
и его использование аналогично использованию базового класса, за исключением дополнительных инициализаций, необходимых для выполнения анализа псевдонимов. Требующиеся инициализации описаны ниже. Класс FunctionPassAAProvider
определен в заголовочном файле:
#include "tsar/Support/PassAAProvider.h"
Замечание. Если функциональные проходы, запускаемые "на лету", исследуют свойства памяти программы, то рекомендуется всегда использовать
FunctionPassAAProvider
.
Итак, чтобы получить доступ к проходам P1
и P2
из модульного прохода P
нужно выполнить следующие шаги.
using SimplePassProvider = FunctionPassProvider<P1,P2>;
Если требуется явно указать проходы, которые должны быть выполнены перед выполнением класса-провайдера, то можно переопределить метод getAnalysisUsage(...)
в унаследованном от него классе:
#include "tsar/Support/Utils.h"
namespace {
class SimplePassProvider: public FunctionPassProvider<P1, P2> {
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
AU.addRequiredID(tsar::getPassIDAndErase(createSomePass());
FunctionPassProvider::getAnalysisUsage(AU);
}
};
}
INITIALIZE_PROVIDER_BEGIN(SimplePassProvider, "simple-p", "Simple Provider")
INITIALIZE_PASS_DEPENDENCY(P1)
INITIALIZE_PASS_DEPENDENCY(P2)
INITIALIZE_PROVIDER_END(SimplePassProvider, "simple-p", "Simple Provider")
Если известно, что запрашиваемые проходы будут уже инициализированны на момент инициализации класса-провайдера, то можно использовать упрощенную форму инициализации:
INITIALIZE_PROVIDER(SimplePassProvider, "simple-p", "Simple Provider")
INTIALIZE_PASS_DEPENDENCY(SimplePassProvider)
void P::getAnalysisUsage() const {
AU.addRequired<SimplePassProvider>();
}
Для некоторых проходов требуется произвести дополнительную инициализацию. Обычно это нужно для llvm::ImmutablePass
проходов, которые содержат информацию, необходимую для выполнения других проходов. Так как проходы данного типа не являются исполняемыми и только предоставляют доступ к ранее полученной информации, то в соответсвующих версиях этих проходов вызываемых из проходов "на лету" внутри класса-провайдера этой информации не будет. Таким образом, нужно вызвать функцию FunctionPassProvider::initialize()
и установить ссылки/указатели на объекты, полученные из аналогичных проходов в модульном проходе.
Используемые проходы должны быть инициализированы до запуска на выполнение класса-провайдера. Это можно сделать внутри модульного прохода, использующего класс-провайдер, или перед выполнением модульного прохода, создав отдельный проход, выполняющий инициализацию. Ниже приведен пример инициализации внутри модульного прохода, которому требуются результаты провайдера.
Предположим, что для выполнения проходов P1
и P2
требуются результаты проходов MemoryMatcherImmutableWrapper
и GlobalOptionsImmutableWrapper
, которые были посчитаны в основной последовательности проходов анализа и преобразвоания и не должны (не могут) быть пересчитаны во время выполнения проходов P1
и P2
"на лету". Тогда модульный проход перед запуском на выполнение прохода, соответсвующего классу-провайдеру, должен получить значнеия данных llvm::ImmutablePass
проходов и передать их в объект класса-провайдера.
bool P::runOnModule(llvm::Module &M) {
...
auto& MMWrapper = getAnalysis<MemoryMatcherImmutableWrapper>();
SimplePassProvider::initialize<MemoryMatcherImmutableWrapper>(
[&MMWrapper](MemoryMatcherImmutableWrapper& Wrapper) {
Wrapper.set(*MMWrapper);
});
auto& mGlobalOpts = getAnalysis<GlobalOptionsImmutableWrapper>().getOptions();
SimplePassProvider::initialize<GlobalOptionsImmutableWrapper>(
[&mGlobalOpts](GlobalOptionsImmutableWrapper& Wrapper) {
Wrapper.setOptions(&mGlobalOpts);
});
...
}
Если в качестве класса-провайдера используется FunctionPassAAProvider
, то дополнительно должен быть инициализирован GlobalAAResultImmutableWrapper
:
bool P::runOnModule(llvm::Module &M) {
...
auto &GlobalsAA = getAnalysis<GlobalsAAWrapperPass>().getResult();
SimplePassProvider::initialize<GlobalsAAResultImmutableWrapper>(
[&GlobalsAA](GlobalsAAResultImutableWrapper &Wrapper) {
Wrapper.set(GlobalsAA);
});
...
}
Предупрежедние. Многие проходы, требующие инициализации хранимых данных, будут неявно запущены на выполнение классом-провайдером. Например, результаты таких проходов могут использоваться в проходах, необходимых для выполнения проходов, явно указанных в описании класса-провайдера. Необходимо аккуратно инициализировать все требующиеся проходы: отсутствие инициализации может приводить к значительному снижению точности анализа или к некорректной работе анализатора (в зависимости от реализации неиницилаизрованных проходов). В случае сборки и запуска анализатора в отладочном режиме и отстутсвия при этом обязательной инициализации таких проходов можно увидеть соответсвующие диагностические сообщения, связанные с аварийным заверешние работы анализатора.
bool runOnModule(Module &M) {
...
auto &Provider = getAnalysis<P>(F); //запуск всех необходимых проходов "на лету"
auto &Result1 = Provider.get<P1>(); //доступ к результатам прохода P1
auto &Result2 = Provider.get<P2>(); //доступ к результатам прохода P2
...
}
Замечание. Каждый вызов метода
getAnalysis()
запускает выполнение всех проходов провайдера заново.
Написать нам можно через форму связи на сайте проекта DVM-системы.