Структура оптимизатора
Общая структура класса, рекурсивно совершающего оптимизации (трансформации) над текущим представлением, выглядит следующим образом:
class Optimizer {
std::vector<BaseTransform::Ptr> transforms;
size_t iterLimit;
public:
Optimizer();
void add(const BaseTransform::Ptr &transform);
void process(Program &program) const;
};
Класс оптимизатора содержит:
набор трансформаций transforms, имплементированных и впоследствии зарегистрированных для применения на очередном дереве операций;
метод add для добавления новых трансформаций к списку;
метод process, содержащий реализацию главного цикла оптимизатора с последовательным применением трансформаций к дереву.
Последовательность трансформаций определяется разработчиками компилятора, каждая последующая трансформация может быть добавлена с использованием соответствующих методов. После очередного применения трансформаций из списка добавленных, оптимизатор проверяет, было ли изменено промежуточное представление и достигло ли число итераций максимума. Если одно из этих условий не выполнено, процесс оптимизации останавливается и текущее дерево направляется в следующий компонент компилятора.
Предполагается, что при имплементации трансформаций, в специализации шаблона явно передается список операций, над которыми совершается трансформация. Таким образом, трансформация точно знает, на каких операциях она выполняется и не проверяет это внутри своей реализации.
В свою очередь информация о последовательности трансформаций, применяемых к дереву операций, содержится в объявлении класса Transform. Выглядит класс следующим образом:
template <typename... AdaptorTypes>
struct Transform : public BaseTransform {
Transform() = default;
Transform(const Transform &) = default;
Transform(Transform &&) = default;
~Transform() override = default;
bool canRun(const Operation::Ptr &op) const final {
if constexpr (sizeof...(AdaptorTypes) == 0)
return true;
else
return (op->is<AdaptorTypes>() || ...);
}
};
Трансформация содержит единственный реализованный метод canRun, проверяющий соответствие типов операций, на которых запускается преобразование. Именно этот механизм и обеспечивает применение очередного преобразования только на заданных операциях.