//===- ModuleCombiner.h - MLIR SPIR-V Module Combiner -----------*- C++ -*-===//
//
// Part of the LLVM Project, 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
//
//===----------------------------------------------------------------------===//
//
// This file declares the entry point to the SPIR-V module combiner library.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_DIALECT_SPIRV_LINKING_MODULECOMBINER_H_
#define MLIR_DIALECT_SPIRV_LINKING_MODULECOMBINER_H_

#include "mlir/Dialect/SPIRV/IR/SPIRVModule.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"

namespace mlir {
class OpBuilder;

namespace spirv {
class ModuleOp;

/// To combine a number of MLIR SPIR-V modules, we move all the module-level ops
/// from all the input modules into one big combined module. To that end, the
/// combination process proceeds in 2 phases:
///
///   (1) resolve conflicts between pairs of ops from different modules
///   (2) deduplicate equivalent ops/sub-ops in the merged module.
///
/// For the conflict resolution phase, the following rules are employed to
/// resolve such conflicts:
///
///   - If 2 spv.func's have the same symbol name, then rename one of the
///   functions.
///   - If an spv.func and another op have the same symbol name, then rename the
///   other symbol.
///   - If none of the 2 conflicting ops are spv.func, then rename either.
///
/// For deduplication, the following 3 cases are taken into consideration:
///
///   - If 2 spv.globalVariable's have either the same descriptor set + binding
///   or the same build_in attribute value, then replace one of them using the
///   other.
///   - If 2 spv.specConstant's have the same spec_id attribute value, then
///   replace one of them using the other.
///   - If 2 spv.func's are identical replace one of them using the other.
///
/// In all cases, the references to the updated symbol (whether renamed or
/// deduplicated) are also updated to reflect the change.
///
/// \param modules the list of modules to combine. Input modules are not
/// modified.
/// \param combinedMdouleBuilder an OpBuilder to be used for
//                               building up the combined module.
/// \param symbRenameListener a listener that gets called everytime a symbol in
///                           one of the input modules is renamed. The arguments
///                           passed to the listener are: the input
///                           spirv::ModuleOp that contains the renamed symbol,
///                           a StringRef to the old symbol name, and a
///                           StringRef to the new symbol name. Note that it is
///                           the responsibility of the caller to properly
///                           retain the storage underlying the passed
///                           StringRefs if the listener callback outlives this
///                           function call.
///
/// \return the combined module.
OwningSPIRVModuleRef
combine(llvm::MutableArrayRef<ModuleOp> modules,
        OpBuilder &combinedModuleBuilder,
        llvm::function_ref<void(ModuleOp, StringRef, StringRef)>
            symbRenameListener);
} // namespace spirv
} // namespace mlir

#endif // MLIR_DIALECT_SPIRV_LINKING_MODULECOMBINER_H_
