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

Copyright (C) 2017-2023 Intel Corporation

SPDX-License-Identifier: MIT

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

//
/// GenXModule
/// ----------
///
/// GenXModule is a module pass whose purpose is to store information
/// about the module being written, such as the built kernels and functions.
///
/// A vISA kernel or function can call a *subroutine*, which can
/// then call further subroutines. All called subroutines are considered part of
/// the kernel or function, which means that a subroutine used by two different
/// kernels needs to have a copy in each. The two copies may be treated
/// differently by the backend passes, so there does actually need to be two
/// copies of the subroutine in the LLVM IR in the backend, one called by each
/// kernel.
///
/// The GenXModule pass performs any necessary copying of subroutines, and
/// populates FunctionGroupAnalysis such that each kernel and its subroutines
/// make one FunctionGroup.
///
/// Subsequent passes are mostly FunctionGroupPasses, so they process one
/// FunctionGroup at a time.
///
/// GenXModule is also an analysis, preserved through subsequent passes to
/// GenXFinalizer at the end, that is used to store each written vISA kernel.
///
/// **IR restriction**: After this pass, the lead function in a FunctionGroup is
/// a kernel (or function in the vISA sense), and other functions in the same
/// FunctionGroup are its subroutines.  A (non-intrinsic) call must be to a
/// function in the same FunctionGroup, and not the lead function.
///
//===----------------------------------------------------------------------===//
#ifndef GENXMODULE_H
#define GENXMODULE_H

#include "GenX.h"
#include "GenXBaling.h"
#include "GenXDebugInfo.h"

#include "vc/Support/BackendConfig.h"

#include "llvm/ADT/Twine.h"
#include "llvm/Pass.h"
#include "llvm/PassRegistry.h"

#include <inc/common/sku_wa.h>

#include <map>
#include <string>
#include <vector>

#include "Probe/Assertion.h"

class VISABuilder;
class VISAKernel;

namespace llvm {
class GenXSubtarget;

//--------------------------------------------------------------------
// GenXModule pass. Stores the information from various parts of the
// GenX writing process
class GenXModule : public ModulePass {
  const GenXSubtarget *ST = nullptr;
  LLVMContext *Ctx = nullptr;
  const GenXBackendConfig *BC = nullptr;

  // Visa option parser contains code that just stores c-strings as
  // pointers without copying. Store all strings here.
  BumpPtrAllocator ArgStorage;

  VISABuilder *CisaBuilder = nullptr;
  void InitCISABuilder();

  VISABuilder *VISAAsmTextReader = nullptr;
  void InitVISAAsmReader();

  bool InlineAsm = false;
  bool CheckForInlineAsm(Module &M) const;

  bool DisableFinalizerOpts = false;
  bool EmitDebugInformation = false;
  bool ImplicitArgsBufferIsUsed = false;
  // represents number of visa instructions in a *kernel*
  std::unordered_map<const Function *, unsigned> VisaCounter;
  // stores vISA mappings for each *function* (including kernel subroutines)
  std::unordered_map<const Function *, genx::di::VisaMapping> VisaMapping;

  bool HasError = false;

private:
  void cleanup() {
    VisaMapping.clear();
    VisaCounter.clear();
    DestroyCISABuilder();
    DestroyVISAAsmReader();
    ArgStorage.Reset();
    HasError = false;
  }

  GenXModule *getGenXModule() { return this; }

public:
  static char ID;

  // Additional info requred to create VISABuilder.
  struct InfoForFinalizer final {
    bool DisableFinalizerOpts = false;
    bool EmitDebugInformation = false;
    bool EmitCrossThreadOffsetRelocation = false;
  };

  explicit GenXModule() : ModulePass(ID) {}
  ~GenXModule() { cleanup(); }
  GenXModule(const GenXModule &) = delete;
  GenXModule &operator=(const GenXModule &) = delete;

  StringRef getPassName() const override { return "GenX module"; }
  void getAnalysisUsage(AnalysisUsage &AU) const override;
  bool runOnModule(Module &M) override;
  void releaseMemory() override { cleanup(); }

  const GenXSubtarget *getSubtarget() const { return ST; }
  bool HasInlineAsm() const { return InlineAsm; }
  VISABuilder *GetCisaBuilder();
  VISABuilder *GetVISAAsmReader();
  void DestroyCISABuilder();
  void DestroyVISAAsmReader();
  LLVMContext &getContext();

  void setHasError() { HasError = true; }
  bool hasError() const { return HasError; }

  bool emitDebugInformation() const { return EmitDebugInformation; }
  void updateVisaMapping(const Function *F, const Instruction *Inst,
                         unsigned VisaIndex, StringRef Reason);
  void updateVisaCountMapping(const Function *F, const Instruction *Inst,
                              unsigned VisaIndex, StringRef Reason);
  const genx::di::VisaMapping *getVisaMapping(const Function *F) const;
  // Returns additional info requred to create VISABuilder.
  // Subtarget must be already initialized before calling this method.
  InfoForFinalizer getInfoForFinalizer() const;
};

void initializeGenXModulePass(PassRegistry &);

} // end namespace llvm
#endif // ndef GENXMODULE_H
