///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// HLModule.h                                                                //
// Copyright (C) Microsoft Corporation. All rights reserved.                 //
// This file is distributed under the University of Illinois Open Source     //
// License. See LICENSE.TXT for details.                                     //
//                                                                           //
// HighLevel DX IR module.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#pragma once

#include "dxc/DXIL/DxilConstants.h"
#include "dxc/DXIL/DxilFunctionProps.h"
#include "dxc/DXIL/DxilMetadataHelper.h"
#include "dxc/DXIL/DxilResourceProperties.h"
#include "dxc/DXIL/DxilSampler.h"
#include "dxc/DXIL/DxilShaderModel.h"
#include "dxc/DXIL/DxilSignature.h"
#include "dxc/DXIL/DxilSubobject.h"
#include "dxc/HLSL/HLOperations.h"
#include "dxc/HLSL/HLResource.h"
#include "dxc/Support/Global.h"
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace llvm {
template <typename T> class ArrayRef;
class LLVMContext;
class Module;
class Function;
class Instruction;
class CallInst;
class MDTuple;
class MDNode;
class GlobalVariable;
class DIGlobalVariable;
class DebugInfoFinder;
class GetElementPtrInst;
} // namespace llvm

namespace hlsl {

class ShaderModel;
class OP;

struct HLOptions {
  HLOptions()
      : bDefaultRowMajor(false), bIEEEStrict(false), bAllResourcesBound(false),
        bDisableOptimizations(false), PackingStrategy(0),
        bUseMinPrecision(false), bDX9CompatMode(false), bFXCCompatMode(false),
        bLegacyResourceReservation(false), bForceZeroStoreLifetimes(false),
        unused(0) {}
  uint32_t GetHLOptionsRaw() const;
  void SetHLOptionsRaw(uint32_t data);
  unsigned bDefaultRowMajor : 1;
  unsigned bIEEEStrict : 1;
  unsigned bAllResourcesBound : 1;
  unsigned bDisableOptimizations : 1;
  unsigned PackingStrategy : 2;
  static_assert((unsigned)DXIL::PackingStrategy::Invalid < 4,
                "otherwise 2 bits is not enough to store PackingStrategy");
  unsigned bUseMinPrecision : 1;
  unsigned bDX9CompatMode : 1;
  unsigned bFXCCompatMode : 1;
  unsigned bLegacyResourceReservation : 1;
  unsigned bForceZeroStoreLifetimes : 1;
  unsigned bResMayAlias : 1;
  unsigned unused : 19;
};

typedef std::unordered_map<const llvm::Function *,
                           std::unique_ptr<DxilFunctionProps>>
    DxilFunctionPropsMap;

/// Use this class to manipulate HLDXIR of a shader.
class HLModule {
public:
  HLModule(llvm::Module *pModule);
  ~HLModule();
  using Domain = DXIL::TessellatorDomain;
  // Subsystems.
  llvm::LLVMContext &GetCtx() const;
  llvm::Module *GetModule() const;
  OP *GetOP() const;
  void SetShaderModel(const ShaderModel *pSM);
  const ShaderModel *GetShaderModel() const;
  void SetValidatorVersion(unsigned ValMajor, unsigned ValMinor);
  void GetValidatorVersion(unsigned &ValMajor, unsigned &ValMinor) const;
  void SetForceZeroStoreLifetimes(bool ForceZeroStoreLifetimes);
  bool GetForceZeroStoreLifetimes() const;

  // HLOptions
  void SetHLOptions(HLOptions &opts);
  const HLOptions &GetHLOptions() const;

  // AutoBindingSpace also enables automatic binding for libraries if set.
  // UINT_MAX == unset
  void SetAutoBindingSpace(uint32_t Space);
  uint32_t GetAutoBindingSpace() const;

  // Entry function.
  llvm::Function *GetEntryFunction() const;
  void SetEntryFunction(llvm::Function *pEntryFunc);
  const std::string &GetEntryFunctionName() const;
  void SetEntryFunctionName(const std::string &name);
  llvm::Function *GetPatchConstantFunction();

  // Resources.
  unsigned AddCBuffer(std::unique_ptr<DxilCBuffer> pCB);
  DxilCBuffer &GetCBuffer(unsigned idx);
  const DxilCBuffer &GetCBuffer(unsigned idx) const;
  const std::vector<std::unique_ptr<DxilCBuffer>> &GetCBuffers() const;

  unsigned AddSampler(std::unique_ptr<DxilSampler> pSampler);
  DxilSampler &GetSampler(unsigned idx);
  const DxilSampler &GetSampler(unsigned idx) const;
  const std::vector<std::unique_ptr<DxilSampler>> &GetSamplers() const;

  unsigned AddSRV(std::unique_ptr<HLResource> pSRV);
  HLResource &GetSRV(unsigned idx);
  const HLResource &GetSRV(unsigned idx) const;
  const std::vector<std::unique_ptr<HLResource>> &GetSRVs() const;

  unsigned AddUAV(std::unique_ptr<HLResource> pUAV);
  HLResource &GetUAV(unsigned idx);
  const HLResource &GetUAV(unsigned idx) const;
  const std::vector<std::unique_ptr<HLResource>> &GetUAVs() const;

  void RemoveGlobal(llvm::GlobalVariable *GV);
  void RemoveFunction(llvm::Function *F);

  // ThreadGroupSharedMemory.
  typedef std::vector<llvm::GlobalVariable *>::iterator tgsm_iterator;
  tgsm_iterator tgsm_begin();
  tgsm_iterator tgsm_end();
  void AddGroupSharedVariable(llvm::GlobalVariable *GV);

  // Signatures.
  std::vector<uint8_t> &GetSerializedRootSignature();
  void SetSerializedRootSignature(const uint8_t *pData, unsigned size);

  // DxilFunctionProps.
  bool HasDxilFunctionProps(llvm::Function *F);
  DxilFunctionProps &GetDxilFunctionProps(llvm::Function *F);
  void AddDxilFunctionProps(llvm::Function *F,
                            std::unique_ptr<DxilFunctionProps> &info);
  void SetPatchConstantFunctionForHS(llvm::Function *hullShaderFunc,
                                     llvm::Function *patchConstantFunc);
  bool IsGraphicsShader(llvm::Function *F); // vs,hs,ds,gs,ps
  bool IsPatchConstantShader(llvm::Function *F);
  bool IsComputeShader(llvm::Function *F);
  bool IsNodeShader(llvm::Function *F);

  // Is an entry function that uses input/output signature conventions?
  // Includes: vs/hs/ds/gs/ps/cs as well as the patch constant function.
  bool IsEntryThatUsesSignatures(llvm::Function *F);
  // Is F an entry?
  // Includes: IsEntryThatUsesSignatures and all ray tracing shaders.
  bool IsEntry(llvm::Function *F);

  DxilFunctionAnnotation *GetFunctionAnnotation(llvm::Function *F);
  DxilFunctionAnnotation *AddFunctionAnnotation(llvm::Function *F);

  // Float Denorm mode.
  void SetFloat32DenormMode(const DXIL::Float32DenormMode mode);
  DXIL::Float32DenormMode GetFloat32DenormMode() const;

  // Default function linkage for libraries
  DXIL::DefaultLinkage GetDefaultLinkage() const;
  void SetDefaultLinkage(const DXIL::DefaultLinkage linkage);

  // HLDXIR metadata manipulation.
  /// Serialize HLDXIR in-memory form to metadata form.
  void EmitHLMetadata();
  /// Deserialize HLDXIR metadata form into in-memory form.
  void LoadHLMetadata();
  /// Delete any HLDXIR from the specified module.
  static void ClearHLMetadata(llvm::Module &M);

  DxilResourceBase *
  AddResourceWithGlobalVariableAndProps(llvm::Constant *GV,
                                        DxilResourceProperties &RP);
  unsigned GetBindingForResourceInCB(llvm::GetElementPtrInst *CbPtr,
                                     llvm::GlobalVariable *CbGV,
                                     DXIL::ResourceClass RC);

  // Type related methods.
  static bool IsStreamOutputPtrType(llvm::Type *Ty);
  static bool IsStreamOutputType(llvm::Type *Ty);
  static void GetParameterRowsAndCols(llvm::Type *Ty, unsigned &rows,
                                      unsigned &cols,
                                      DxilParameterAnnotation &paramAnnotation);

  // HL code gen.
  static llvm::Function *GetHLOperationFunction(
      HLOpcodeGroup group, unsigned opcode, llvm::Type *RetType,
      llvm::ArrayRef<llvm::Value *> paramList, llvm::Module &M);
  template <class BuilderTy>
  static llvm::CallInst *
  EmitHLOperationCall(BuilderTy &Builder, HLOpcodeGroup group, unsigned opcode,
                      llvm::Type *RetType,
                      llvm::ArrayRef<llvm::Value *> paramList, llvm::Module &M);

  // Caller must handle conversions to bool and no-ops
  static unsigned GetNumericCastOp(llvm::Type *SrcTy, bool SrcIsUnsigned,
                                   llvm::Type *DstTy, bool DstIsUnsigned);

  // Precise attribute.
  // Note: Precise will be marked on alloca inst with metadata in code gen.
  //       But mem2reg will remove alloca inst, so need mark precise with
  //       function call before mem2reg.
  static bool HasPreciseAttributeWithMetadata(llvm::Instruction *I);
  static void MarkPreciseAttributeWithMetadata(llvm::Instruction *I);
  static void ClearPreciseAttributeWithMetadata(llvm::Instruction *I);
  template <class BuilderTy>
  static void MarkPreciseAttributeOnValWithFunctionCall(llvm::Value *V,
                                                        BuilderTy &Builder,
                                                        llvm::Module &M);
  static void MarkPreciseAttributeOnPtrWithFunctionCall(llvm::Value *Ptr,
                                                        llvm::Module &M);
  static bool HasPreciseAttribute(llvm::Function *F);

  // DXIL type system.
  DxilTypeSystem &GetTypeSystem();

  /// Emit llvm.used array to make sure that optimizations do not remove
  /// unreferenced globals.
  void EmitLLVMUsed();
  std::vector<llvm::GlobalVariable *> &GetLLVMUsed();

  // Release functions used to transfer ownership.
  DxilTypeSystem *ReleaseTypeSystem();
  OP *ReleaseOP();
  DxilFunctionPropsMap &&ReleaseFunctionPropsMap();

  llvm::DebugInfoFinder &GetOrCreateDebugInfoFinder();
  // Create global variable debug info for element global variable based on the
  // whole global variable.
  static void CreateElementGlobalVariableDebugInfo(
      llvm::GlobalVariable *GV, llvm::DebugInfoFinder &DbgInfoFinder,
      llvm::GlobalVariable *EltGV, unsigned sizeInBits, unsigned alignInBits,
      unsigned offsetInBits, llvm::StringRef eltName);
  // Replace GV with NewGV in GlobalVariable debug info.
  static void
  UpdateGlobalVariableDebugInfo(llvm::GlobalVariable *GV,
                                llvm::DebugInfoFinder &DbgInfoFinder,
                                llvm::GlobalVariable *NewGV);

  DxilSubobjects *GetSubobjects();
  const DxilSubobjects *GetSubobjects() const;
  DxilSubobjects *ReleaseSubobjects();
  void ResetSubobjects(DxilSubobjects *subobjects);

  // Reg binding for resource in cb.
  void AddRegBinding(unsigned CbID, unsigned ConstantIdx, unsigned Srv,
                     unsigned Uav, unsigned Sampler);

private:
  // Signatures.
  std::vector<uint8_t> m_SerializedRootSignature;

  // Shader resources.
  std::vector<std::unique_ptr<HLResource>> m_SRVs;
  std::vector<std::unique_ptr<HLResource>> m_UAVs;
  std::vector<std::unique_ptr<DxilCBuffer>> m_CBuffers;
  std::vector<std::unique_ptr<DxilSampler>> m_Samplers;

  // ThreadGroupSharedMemory.
  std::vector<llvm::GlobalVariable *> m_TGSMVariables;

  // High level function info.
  std::unordered_map<const llvm::Function *, std::unique_ptr<DxilFunctionProps>>
      m_DxilFunctionPropsMap;
  std::unordered_set<llvm::Function *> m_PatchConstantFunctions;

  // Resource bindings for res in cb.
  // Key = CbID << 32 | ConstantIdx. Val is reg binding.
  std::unordered_map<uint64_t, unsigned> m_SrvBindingInCB;
  std::unordered_map<uint64_t, unsigned> m_UavBindingInCB;
  std::unordered_map<uint64_t, unsigned> m_SamplerBindingInCB;

private:
  llvm::LLVMContext &m_Ctx;
  llvm::Module *m_pModule;
  llvm::Function *m_pEntryFunc;
  std::string m_EntryName;
  std::unique_ptr<DxilMDHelper> m_pMDHelper;
  std::unique_ptr<llvm::DebugInfoFinder> m_pDebugInfoFinder;
  const ShaderModel *m_pSM;
  unsigned m_DxilMajor;
  unsigned m_DxilMinor;
  unsigned m_ValMajor;
  unsigned m_ValMinor;
  DXIL::Float32DenormMode m_Float32DenormMode;
  HLOptions m_Options;
  std::unique_ptr<OP> m_pOP;
  size_t m_pUnused;
  uint32_t m_AutoBindingSpace;
  DXIL::DefaultLinkage m_DefaultLinkage;
  std::unique_ptr<DxilSubobjects> m_pSubobjects;

  // DXIL metadata serialization/deserialization.
  llvm::MDTuple *EmitHLResources();
  void LoadHLResources(const llvm::MDOperand &MDO);
  llvm::MDTuple *EmitHLShaderProperties();
  void LoadHLShaderProperties(const llvm::MDOperand &MDO);
  llvm::MDTuple *EmitDxilShaderProperties();
  // LLVM used.
  std::vector<llvm::GlobalVariable *> m_LLVMUsed;

  // Type annotations.
  std::unique_ptr<DxilTypeSystem> m_pTypeSystem;

  // Helpers.
  template <typename T>
  unsigned AddResource(std::vector<std::unique_ptr<T>> &Vec,
                       std::unique_ptr<T> pRes);
};

/// Use this class to manipulate metadata of extra metadata record properties
/// that are specific to high-level DX IR.
class HLExtraPropertyHelper : public DxilExtraPropertyHelper {
public:
  HLExtraPropertyHelper(llvm::Module *pModule);
  virtual ~HLExtraPropertyHelper() {}

  virtual void
  EmitSignatureElementProperties(const DxilSignatureElement &SE,
                                 std::vector<llvm::Metadata *> &MDVals);
  virtual void LoadSignatureElementProperties(const llvm::MDOperand &MDO,
                                              DxilSignatureElement &SE);
};

} // namespace hlsl
