/*========================== begin_copyright_notice ============================

Copyright (C) 2017-2021 Intel Corporation

SPDX-License-Identifier: MIT

============================= end_copyright_notice ===========================*/

#pragma once
#include "Compiler/CodeGenContextWrapper.hpp"
#include "Compiler/MetaDataUtilsWrapper.h"
#include <llvm/IR/DataLayout.h>
#include "Compiler/Optimizer/OCLBIUtils.h"
#include "common/LLVMWarningsPush.hpp"
#include <llvm/Pass.h>
#include <llvm/IR/InstVisitor.h>
#include <llvm/IR/PassManager.h>
#include "common/LLVMWarningsPop.hpp"
#include "IGC/common/Types.hpp"

namespace IGC {

/// @brief  SubGroupFuncsResolution pass used for resolving OpenCL Sub Group functions.
// Shared implementation. Holds the logic and is used by both the legacy and the
// new-pass-manager wrappers below; it is not itself an llvm::Pass.
class SubGroupFuncsResolution : public llvm::InstVisitor<SubGroupFuncsResolution, void> {
public:
  /// @brief  Constructor
  SubGroupFuncsResolution() {}

  /// @brief  Destructor
  ~SubGroupFuncsResolution() {}

  /// @brief  Provides name of pass
  static llvm::StringRef getPassName() { return "SubGroupFuncsResolution"; }

  /// @brief  Main entry point.
  ///         Finds all OpenCL Sub Group function calls and resolve them into an llvm sequence
  /// @param  F The destination function.
  bool runOnFunction(llvm::Function &F, IGC::IGCMD::MetaDataUtils *pMdUtils, IGC::ModuleMetaData *pModMD,
                     IGC::CodeGenContext *pCtx);

  /// @brief  Call instructions visitor.
  ///         Checks for OpenCL Sub Group  functions and resolves them into appropriate sequence of code
  /// @param  CI The call instruction.
  void visitCallInst(llvm::CallInst &CI);

  void BTIHelper(llvm::CallInst &CI);

  void mediaBlockRead(llvm::CallInst &CI);
  void mediaBlockWrite(llvm::CallInst &CI);

  void simdBlockRead(llvm::CallInst &CI, bool hasCacheControls = false);
  void simdBlockWrite(llvm::CallInst &CI, bool hasCacheControls = false);

  void pushMediaBlockArgs(llvm::SmallVector<llvm::Value *, 5> &args, llvm::CallInst &CI);

  void CheckMediaBlockInstError(llvm::GenIntrinsicInst *inst, bool isRead);

  void subGroupArithmetic(llvm::CallInst &CI, WaveOps op, GroupOpType groupType);

  static const llvm::StringRef SUB_GROUP_BARRIER;
  static const llvm::StringRef GET_MAX_SUB_GROUP_SIZE;
  static const llvm::StringRef GET_SUB_GROUP_LOCAL_ID;
  static const llvm::StringRef SUB_GROUP_SHUFFLE;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_US;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_F;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_H;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_C;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_B;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_DF;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_DOWN;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_DOWN_US;
  static const llvm::StringRef SUB_GROUP_SHUFFLE_DOWN_UC;
  static const llvm::StringRef SUB_GROUP_BROADCAST;
  static const llvm::StringRef SUB_GROUP_BROADCAST_US;
  static const llvm::StringRef SUB_GROUP_BROADCAST_F;
  static const llvm::StringRef SUB_GROUP_BROADCAST_H;
  static const llvm::StringRef SUB_GROUP_BROADCAST_C;
  static const llvm::StringRef SUB_GROUP_BROADCAST_B;
  static const llvm::StringRef SUB_GROUP_BROADCAST_DF;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_US;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_F;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_H;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_C;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_B;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_BROADCAST_DF;

  static const llvm::StringRef SIMD_BLOCK_READ_1_GBL;
  static const llvm::StringRef SIMD_BLOCK_READ_2_GBL;
  static const llvm::StringRef SIMD_BLOCK_READ_4_GBL;
  static const llvm::StringRef SIMD_BLOCK_READ_8_GBL;
  static const llvm::StringRef SIMD_BLOCK_READ_1_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_2_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_4_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_8_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_16_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_1_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_2_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_4_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_8_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_16_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_1_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_2_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_4_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_8_GBL_L;

  static const llvm::StringRef SIMD_BLOCK_WRITE_1_GBL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_GBL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_GBL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_GBL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_16_GBL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_16_GBL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_GBL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_GBL_L;

  static const llvm::StringRef SIMD_BLOCK_READ_1_LCL;
  static const llvm::StringRef SIMD_BLOCK_READ_2_LCL;
  static const llvm::StringRef SIMD_BLOCK_READ_4_LCL;
  static const llvm::StringRef SIMD_BLOCK_READ_8_LCL;
  static const llvm::StringRef SIMD_BLOCK_READ_1_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_2_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_4_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_8_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_16_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_READ_1_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_2_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_4_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_8_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_16_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_READ_1_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_2_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_4_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_READ_8_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_LCL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_LCL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_LCL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_LCL;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_16_LCL_H;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_16_LCL_B;
  static const llvm::StringRef SIMD_BLOCK_WRITE_1_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_2_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_4_LCL_L;
  static const llvm::StringRef SIMD_BLOCK_WRITE_8_LCL_L;

  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_1;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_2;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_4;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_8;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_1_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_2_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_4_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_8_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_16_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_1_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_2_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_4_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_8_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_16_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_1_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_2_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_4_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_READ_8_L;

  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_1;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_2;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_4;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_8;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_1_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_2_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_4_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_8_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_16_H;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_1_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_2_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_4_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_8_L;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_1_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_2_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_4_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_8_B;
  static const llvm::StringRef SIMD_MEDIA_BLOCK_WRITE_16_B;

  static const llvm::StringRef MEDIA_BLOCK_READ;
  static const llvm::StringRef MEDIA_BLOCK_WRITE;

  static const llvm::StringRef MEDIA_BLOCK_RECTANGLE_READ;

  static const llvm::StringRef GET_IMAGE_BTI;

  static const llvm::StringRef SUB_GROUP_REDUCE;
  static const llvm::StringRef SUB_GROUP_SCAN;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_REDUCE;
  static const llvm::StringRef SUB_GROUP_CLUSTERED_SCAN;

private:
  /// @brief  Container for instructions to be deleted after visiting a function.
  llvm::SmallVector<llvm::Instruction *, 16> m_instsToDelete;

  /// @brief - maps image and sampler kernel parameters to BTIs
  CImagesBI::ParamMap m_argIndexMap;

  // @brief - maps SPIR-V operation to vISA operation type
  static const std::array<std::pair<std::string, WaveOps>, 13> m_spvOpToWaveOpMap;

  /// @brief  Indicates if the pass changed the processed function
  bool m_changed{};

  CodeGenContext *m_pCtx = nullptr;

  /// @brief  MetaData utils, cached at runOnFunction() entry.
  IGC::IGCMD::MetaDataUtils *m_pMdUtils = nullptr;

  /// @brief  Module metadata, cached at runOnFunction() entry.
  IGC::ModuleMetaData *m_modMD = nullptr;

  /// @brief examine metadata for intel_reqd_sub_group_size
  int32_t GetSIMDSize(llvm::Function *F);

  /// @brief emits the given error message in SIMD32.  Used on subgroup functions
  // that aren't currently supported in SIMD32.
  void CheckSIMDSize(llvm::Instruction &I, llvm::StringRef msg);

  // @brief parse function name to extract spir-v operation type
  //        and map it to vISA operation type
  WaveOps GetWaveOp(llvm::StringRef funcName);
};

// Legacy Pass Manager wrapper.
class SubGroupFuncsResolutionLPM : public llvm::FunctionPass {
public:
  static char ID;

  SubGroupFuncsResolutionLPM();
  ~SubGroupFuncsResolutionLPM() {}

  virtual llvm::StringRef getPassName() const override { return SubGroupFuncsResolution::getPassName(); }

  void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
    AU.addRequired<CodeGenContextWrapper>();
    AU.addRequired<MetaDataUtilsWrapper>();
  }

  virtual bool runOnFunction(llvm::Function &F) override {
    return m_impl.runOnFunction(F, getAnalysis<MetaDataUtilsWrapper>().getMetaDataUtils(),
                                getAnalysis<MetaDataUtilsWrapper>().getModuleMetaData(),
                                getAnalysis<CodeGenContextWrapper>().getCodeGenContext());
  }

private:
  SubGroupFuncsResolution m_impl;
};

#if LLVM_VERSION_MAJOR >= 16
// New Pass Manager wrapper. Modeled as a module pass that loops over the defined
// functions (the seeded context analyses are module-level; IGC passes never use
// skipFunction). name() returns the legacy pass argument so PrintBefore/PrintAfter
// matches under the new pass manager.
class SubGroupFuncsResolutionNPM : public llvm::PassInfoMixin<SubGroupFuncsResolutionNPM> {
public:
  llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
  static llvm::StringRef name() { return "igc-sub-group-func-resolution"; }
  static bool isRequired() { return true; }
};
#endif // LLVM_VERSION_MAJOR >= 16

} // namespace IGC
