//===- ModuleTranslation.h - MLIR to LLVM conversion ------------*- 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 implements the translation between an MLIR LLVM dialect module and
// the corresponding LLVMIR module. It only handles core LLVM IR operations.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_TARGET_LLVMIR_MODULETRANSLATION_H
#define MLIR_TARGET_LLVMIR_MODULETRANSLATION_H

#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/LLVMIR/Transforms/LegalizeForExport.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Value.h"
#include "mlir/Target/LLVMIR/TypeTranslation.h"

#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/MatrixBuilder.h"
#include "llvm/IR/Value.h"

namespace mlir {
class Attribute;
class Location;
class ModuleOp;
class Operation;

namespace LLVM {

namespace detail {
class DebugTranslation;
} // end namespace detail

class LLVMFuncOp;

/// Implementation class for module translation. Holds a reference to the module
/// being translated, and the mappings between the original and the translated
/// functions, basic blocks and values. It is practically easier to hold these
/// mappings in one class since the conversion of control flow operations
/// needs to look up block and function mappings.
class ModuleTranslation {
public:
  template <typename T = ModuleTranslation>
  static std::unique_ptr<llvm::Module>
  translateModule(Operation *m, llvm::LLVMContext &llvmContext,
                  StringRef name = "LLVMDialectModule") {
    if (!satisfiesLLVMModule(m))
      return nullptr;
    if (failed(checkSupportedModuleOps(m)))
      return nullptr;
    std::unique_ptr<llvm::Module> llvmModule =
        prepareLLVMModule(m, llvmContext, name);

    LLVM::ensureDistinctSuccessors(m);

    T translator(m, std::move(llvmModule));
    if (failed(translator.convertFunctionSignatures()))
      return nullptr;
    if (failed(translator.convertGlobals()))
      return nullptr;
    if (failed(translator.convertFunctions()))
      return nullptr;

    return std::move(translator.llvmModule);
  }

  /// A helper method to get the single Block in an operation honoring LLVM's
  /// module requirements.
  static Block &getModuleBody(Operation *m) { return m->getRegion(0).front(); }

protected:
  /// Translate the given MLIR module expressed in MLIR LLVM IR dialect into an
  /// LLVM IR module. The MLIR LLVM IR dialect holds a pointer to an
  /// LLVMContext, the LLVM IR module will be created in that context.
  ModuleTranslation(Operation *module,
                    std::unique_ptr<llvm::Module> llvmModule);
  virtual ~ModuleTranslation();

  virtual LogicalResult convertOperation(Operation &op,
                                         llvm::IRBuilder<> &builder);
  virtual LogicalResult convertOmpOperation(Operation &op,
                                            llvm::IRBuilder<> &builder);
  virtual LogicalResult convertOmpParallel(Operation &op,
                                           llvm::IRBuilder<> &builder);
  virtual LogicalResult convertOmpMaster(Operation &op,
                                         llvm::IRBuilder<> &builder);
  void convertOmpOpRegions(Region &region, StringRef blockName,
                           DenseMap<Value, llvm::Value *> &valueMapping,
                           DenseMap<Block *, llvm::BasicBlock *> &blockMapping,
                           llvm::BasicBlock &sourceBlock,
                           llvm::BasicBlock &continuationBlock,
                           llvm::IRBuilder<> &builder,
                           LogicalResult &bodyGenStatus);
  virtual LogicalResult convertOmpWsLoop(Operation &opInst,
                                         llvm::IRBuilder<> &builder);

  /// Converts the type from MLIR LLVM dialect to LLVM.
  llvm::Type *convertType(Type type);

  static std::unique_ptr<llvm::Module>
  prepareLLVMModule(Operation *m, llvm::LLVMContext &llvmContext,
                    StringRef name);

  /// A helper to look up remapped operands in the value remapping table.
  SmallVector<llvm::Value *, 8> lookupValues(ValueRange values);

private:
  /// Check whether the module contains only supported ops directly in its body.
  static LogicalResult checkSupportedModuleOps(Operation *m);

  LogicalResult convertFunctionSignatures();
  LogicalResult convertFunctions();
  LogicalResult convertGlobals();
  LogicalResult convertOneFunction(LLVMFuncOp func);
  LogicalResult convertBlock(Block &bb, bool ignoreArguments,
                             llvm::IRBuilder<> &builder);

  llvm::Constant *getLLVMConstant(llvm::Type *llvmType, Attribute attr,
                                  Location loc);

  /// Original and translated module.
  Operation *mlirModule;
  std::unique_ptr<llvm::Module> llvmModule;
  /// A converter for translating debug information.
  std::unique_ptr<detail::DebugTranslation> debugTranslation;

  /// Builder for LLVM IR generation of OpenMP constructs.
  std::unique_ptr<llvm::OpenMPIRBuilder> ompBuilder;

  /// Precomputed pointer to OpenMP dialect. Note this can be nullptr if the
  /// OpenMP dialect hasn't been loaded (it is always loaded if there are OpenMP
  /// operations in the module though).
  const Dialect *ompDialect;

  /// Mappings between llvm.mlir.global definitions and corresponding globals.
  DenseMap<Operation *, llvm::GlobalValue *> globalsMapping;

  /// A stateful object used to translate types.
  TypeToLLVMIRTranslator typeTranslator;

protected:
  /// Mappings between original and translated values, used for lookups.
  llvm::StringMap<llvm::Function *> functionMapping;
  DenseMap<Value, llvm::Value *> valueMapping;
  DenseMap<Block *, llvm::BasicBlock *> blockMapping;

  /// A mapping between MLIR LLVM dialect terminators and LLVM IR terminators
  /// they are converted to. This allows for connecting PHI nodes to the source
  /// values after all operations are converted.
  DenseMap<Operation *, llvm::Instruction *> branchMapping;
};

} // namespace LLVM
} // namespace mlir

#endif // MLIR_TARGET_LLVMIR_MODULETRANSLATION_H
