// Copyright 2021 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef IREE_COMPILER_UTILS_PASSUTILS_H_
#define IREE_COMPILER_UTILS_PASSUTILS_H_

#include <array>

#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"

namespace mlir::iree_compiler {

/// Constructs a pipeline of passes across multiple nested op types.
///
/// Usage:
///   using FunctionLikeNest = MultiOpNest<IREE::Util::InitializerOp,
///                                        IREE::Util::FuncOp>;
///
///   FunctionLikeNest(passManager)
///     .addPass(createMyPass)
///     .addPredicatedPass(enable, createMyOtherPass);
template <typename... OpTys>
struct MultiOpNest {
public:
  MultiOpNest(OpPassManager &parentPm) : parentPm(parentPm) {
    addNest<0, OpTys...>();
  }

  template <typename F>
  MultiOpNest &addPass(F constructor) {
    addPassInternal(constructor);
    return *this;
  }

  // We have an explicit overload for a concrete function to support
  // passing overload constructors (i.e. createCanonicalizerPass).
  MultiOpNest &addPass(std::unique_ptr<Pass> (*constructor)()) {
    addPassInternal(constructor);
    return *this;
  }

  template <typename F>
  MultiOpNest &addPredicatedPass(bool enable, F constructor) {
    if (enable) {
      addPassInternal(constructor);
    }
    return *this;
  }

private:
  // Initialize a nest.
  template <int index, typename T, typename... Rest>
  void addNest() {
    std::get<index>(nestedPassManagers) = &parentPm.nest<T>();
    addNest<index + 1, Rest...>();
  }
  template <int index>
  void addNest() {}

  // Add a pass to all nests by constructor.
  template <typename F>
  void addPassInternal(F constructor) {
    addPassRecurse<F, 0, OpTys...>(constructor);
  }
  template <typename F, int index, typename T, typename... Rest>
  void addPassRecurse(F constructor) {
    std::get<index>(nestedPassManagers)->addPass(constructor());
    addPassRecurse<F, index + 1, Rest...>(constructor);
  }
  template <typename F, int index>
  void addPassRecurse(F constructor) {}

  OpPassManager &parentPm;
  std::array<OpPassManager *, sizeof...(OpTys)> nestedPassManagers;
};

// If running under a FixedPointIterator pass, annotate that a modification
// has been made which requires another iteration. No-op otherwise.
void signalFixedPointModified(Operation *rootOp);

} // namespace mlir::iree_compiler

#endif // IREE_COMPILER_UTILS_PASSUTILS_H_
