///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// DxilMetadataHelper.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.                                     //
//                                                                           //
// Helper to serialize/desialize metadata for DxilModule.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#pragma once

#include "dxc/DXIL/DxilConstants.h"
#include "dxc/DXIL/DxilFunctionProps.h"
#include "llvm/ADT/ArrayRef.h"
#include <memory>
#include <string>
#include <vector>

namespace llvm {
class LLVMContext;
class Module;
class Function;
class Instruction;
class DbgDeclareInst;
class Value;
class MDOperand;
class Metadata;
class ConstantAsMetadata;
class MDTuple;
class MDNode;
class NamedMDNode;
class GlobalVariable;
class StringRef;
class Type;
} // namespace llvm

namespace hlsl {

class ShaderModel;
class DxilSignature;
struct DxilEntrySignature;
class DxilSignatureElement;
class DxilModule;
class DxilResourceBase;
class DxilCBuffer;
class DxilResource;
class DxilSampler;
class DxilTypeSystem;
class DxilStructAnnotation;
class DxilFieldAnnotation;
class DxilPayloadAnnotation;
class DxilPayloadFieldAnnotation;
class DxilTemplateArgAnnotation;
class DxilFunctionAnnotation;
class DxilParameterAnnotation;
class RootSignatureHandle;
struct DxilFunctionProps;
class DxilSubobjects;
class DxilSubobject;
struct DxilCounters;

// Additional debug information for SROA'ed array variables,
// where adjacent elements in DXIL might not have been adjacent
// in the original user variable.
struct DxilDIArrayDim {
  unsigned StrideInBits;
  unsigned NumElements;
};

/// Use this class to manipulate DXIL-spcific metadata.
// In our code, only DxilModule and HLModule should use this class.
class DxilMDHelper {
public:
  //
  // Constants for metadata names and field positions.
  //

  // Dxil version.
  static const char kDxilVersionMDName[];
  static const unsigned kDxilVersionNumFields = 2;
  static const unsigned kDxilVersionMajorIdx = 0; // DXIL version major.
  static const unsigned kDxilVersionMinorIdx = 1; // DXIL version minor.

  // Shader model.
  static const char kDxilShaderModelMDName[];
  static const unsigned kDxilShaderModelNumFields = 3;
  static const unsigned kDxilShaderModelTypeIdx =
      0; // Shader type (vs,ps,cs,gs,ds,hs).
  static const unsigned kDxilShaderModelMajorIdx = 1; // Shader model major.
  static const unsigned kDxilShaderModelMinorIdx = 2; // Shader model minor.

  // Intermediate codegen/optimizer options, not valid in final DXIL module.
  static const char kDxilIntermediateOptionsMDName[];
  static const unsigned kDxilIntermediateOptionsFlags = 0; // Unique element ID.

  // DxilCounters
  static const char kDxilCountersMDName[];
  // !{!"<counter>", i32 <count>, !"<counter>", i32 <count>, ...}

  // Entry points.
  static const char kDxilEntryPointsMDName[];

  // Root Signature, for intermediate use, not valid in final DXIL module.
  static const char kDxilRootSignatureMDName[];

  // ViewId state.
  static const char kDxilViewIdStateMDName[];

  // Subobjects
  static const char kDxilSubobjectsMDName[];

  // Source info.
  static const char kDxilSourceContentsMDName[];
  static const char kDxilSourceDefinesMDName[];
  static const char kDxilSourceMainFileNameMDName[];
  static const char kDxilSourceArgsMDName[];

  // Resource binding data
  static const char kDxilDxcBindingTableMDName[];
  static const unsigned kDxilDxcBindingTableResourceName = 0;
  static const unsigned kDxilDxcBindingTableResourceClass = 1;
  static const unsigned kDxilDxcBindingTableResourceIndex = 2;
  static const unsigned kDxilDxcBindingTableResourceSpace = 3;

  // Old source info.
  static const char kDxilSourceContentsOldMDName[];
  static const char kDxilSourceDefinesOldMDName[];
  static const char kDxilSourceMainFileNameOldMDName[];
  static const char kDxilSourceArgsOldMDName[];

  static const unsigned kDxilEntryPointNumFields = 5;
  static const unsigned kDxilEntryPointFunction =
      0;                                         // Entry point function symbol.
  static const unsigned kDxilEntryPointName = 1; // Entry point unmangled name.
  static const unsigned kDxilEntryPointSignatures =
      2; // Entry point signature tuple.
  static const unsigned kDxilEntryPointResources =
      3; // Entry point resource tuple.
  static const unsigned kDxilEntryPointProperties =
      4; // Entry point properties tuple.

  // Signatures.
  static const unsigned kDxilNumSignatureFields = 3;
  static const unsigned kDxilInputSignature = 0;  // Shader input signature.
  static const unsigned kDxilOutputSignature = 1; // Shader output signature.
  static const unsigned kDxilPatchConstantSignature =
      2; // Shader patch constant (PC) signature.

  // Signature Element.
  static const unsigned kDxilSignatureElementNumFields = 11;
  static const unsigned kDxilSignatureElementID = 0;   // Unique element ID.
  static const unsigned kDxilSignatureElementName = 1; // Element name.
  static const unsigned kDxilSignatureElementType = 2; // Element type.
  static const unsigned kDxilSignatureElementSystemValue =
      3; // Effective system value.
  static const unsigned kDxilSignatureElementIndexVector =
      4; // Semantic index vector.
  static const unsigned kDxilSignatureElementInterpMode =
      5;                                               // Interpolation mode.
  static const unsigned kDxilSignatureElementRows = 6; // Number of rows.
  static const unsigned kDxilSignatureElementCols = 7; // Number of columns.
  static const unsigned kDxilSignatureElementStartRow =
      8; // Element packing start row.
  static const unsigned kDxilSignatureElementStartCol =
      9; // Element packing start column.
  static const unsigned kDxilSignatureElementNameValueList =
      10; // Name-value list for extended properties.

  // Signature Element Extended Properties.
  static const unsigned kDxilSignatureElementOutputStreamTag = 0;
  static const unsigned kHLSignatureElementGlobalSymbolTag = 1;
  static const unsigned kDxilSignatureElementDynIdxCompMaskTag = 2;
  static const unsigned kDxilSignatureElementUsageCompMaskTag = 3;

  // Resources.
  static const char kDxilResourcesMDName[];
  static const unsigned kDxilNumResourceFields = 4;
  static const unsigned kDxilResourceSRVs = 0;
  static const unsigned kDxilResourceUAVs = 1;
  static const unsigned kDxilResourceCBuffers = 2;
  static const unsigned kDxilResourceSamplers = 3;

  // ResourceBase.
  static const unsigned kDxilResourceBaseNumFields = 6;
  static const unsigned kDxilResourceBaseID =
      0; // Unique (per type) resource ID.
  static const unsigned kDxilResourceBaseVariable =
      1; // Resource global variable.
  static const unsigned kDxilResourceBaseName =
      2; // Original (HLSL) name of the resource.
  static const unsigned kDxilResourceBaseSpaceID =
      3; // Resource range space ID.
  static const unsigned kDxilResourceBaseLowerBound =
      4; // Resource range lower bound.
  static const unsigned kDxilResourceBaseRangeSize = 5; // Resource range size.

  // SRV-specific.
  static const unsigned kDxilSRVNumFields = 9;
  static const unsigned kDxilSRVShape = 6;       // SRV shape.
  static const unsigned kDxilSRVSampleCount = 7; // SRV sample count.
  static const unsigned kDxilSRVNameValueList =
      8; // Name-value list for extended properties.

  // UAV-specific.
  static const unsigned kDxilUAVNumFields = 11;
  static const unsigned kDxilUAVShape = 6;            // UAV shape.
  static const unsigned kDxilUAVGloballyCoherent = 7; // Globally-coherent UAV.
  static const unsigned kDxilUAVCounter = 8;          // UAV with a counter.
  static const unsigned kDxilUAVRasterizerOrderedView = 9; // UAV that is a ROV.
  static const unsigned kDxilUAVNameValueList =
      10; // Name-value list for extended properties.

  // CBuffer-specific.
  static const unsigned kDxilCBufferNumFields = 8;
  static const unsigned kDxilCBufferSizeInBytes = 6; // CBuffer size in bytes.
  static const unsigned kDxilCBufferNameValueList =
      7; // Name-value list for extended properties.

  // CBuffer extended properties
  static const unsigned kHLCBufferIsTBufferTag =
      0; // CBuffer is actually TBuffer, not yet converted to SRV.

  // Sampler-specific.
  static const unsigned kDxilSamplerNumFields = 8;
  static const unsigned kDxilSamplerType = 6; // Sampler type.
  static const unsigned kDxilSamplerNameValueList =
      7; // Name-value list for extended properties.

  // Resource extended property tags.
  static const unsigned kDxilTypedBufferElementTypeTag = 0;
  static const unsigned kDxilStructuredBufferElementStrideTag = 1;
  static const unsigned kDxilSamplerFeedbackKindTag = 2;
  static const unsigned kDxilAtomic64UseTag = 3;

  // Type system.
  static const char kDxilTypeSystemMDName[];
  static const char kDxilTypeSystemHelperVariablePrefix[];
  static const unsigned kDxilTypeSystemStructTag = 0;
  static const unsigned kDxilTypeSystemFunctionTag = 1;
  static const unsigned kDxilFieldAnnotationSNormTag = 0;
  static const unsigned kDxilFieldAnnotationUNormTag = 1;
  static const unsigned kDxilFieldAnnotationMatrixTag = 2;
  static const unsigned kDxilFieldAnnotationCBufferOffsetTag = 3;
  static const unsigned kDxilFieldAnnotationSemanticStringTag = 4;
  static const unsigned kDxilFieldAnnotationInterpolationModeTag = 5;
  static const unsigned kDxilFieldAnnotationFieldNameTag = 6;
  static const unsigned kDxilFieldAnnotationCompTypeTag = 7;
  static const unsigned kDxilFieldAnnotationPreciseTag = 8;
  static const unsigned kDxilFieldAnnotationCBUsedTag = 9;
  static const unsigned kDxilFieldAnnotationResPropTag = 10;
  static const unsigned kDxilFieldAnnotationBitFieldsTag = 11;
  static const unsigned kDxilFieldAnnotationBitFieldWidthTag = 12;
  static const unsigned kDxilFieldAnnotationVectorSizeTag = 13;

  // DXR Payload Annotations
  static const unsigned kDxilPayloadAnnotationStructTag = 0;
  static const unsigned kDxilPayloadFieldAnnotationAccessTag = 0;

  // StructAnnotation extended property tags (DXIL 1.5+ only, appended)
  static const unsigned kDxilTemplateArgumentsTag =
      0; // Name for name-value list of extended struct properties
  // TemplateArgument tags
  static const unsigned kDxilTemplateArgTypeTag =
      0; // Type template argument, followed by undef of type
  static const unsigned kDxilTemplateArgIntegralTag =
      1; // Integral template argument, followed by i64 value
  static const unsigned kDxilTemplateArgValue =
      1; // Position of template arg value (type or int)

  // Control flow hint.
  static const char kDxilControlFlowHintMDName[];

  // Precise attribute.
  static const char kDxilPreciseAttributeMDName[];

  // NonUniform attribute.
  static const char kDxilNonUniformAttributeMDName[];

  // Variable debug layout metadata.
  static const char kDxilVariableDebugLayoutMDName[];

  // Indication of temporary storage metadata.
  static const char kDxilTempAllocaMDName[];

  // Validator version.
  static const char kDxilValidatorVersionMDName[];
  // Validator version uses the same constants for fields as kDxilVersion*

  // DXR Payload Annotations metadata.
  static const char kDxilDxrPayloadAnnotationsMDName[];

  // Extended shader property tags.
  static const unsigned kDxilShaderFlagsTag = 0;
  static const unsigned kDxilGSStateTag = 1;
  static const unsigned kDxilDSStateTag = 2;
  static const unsigned kDxilHSStateTag = 3;
  static const unsigned kDxilNumThreadsTag = 4;
  static const unsigned kDxilAutoBindingSpaceTag = 5;
  static const unsigned kDxilRayPayloadSizeTag = 6;
  static const unsigned kDxilRayAttribSizeTag = 7;
  static const unsigned kDxilShaderKindTag = 8;
  static const unsigned kDxilMSStateTag = 9;
  static const unsigned kDxilASStateTag = 10;
  static const unsigned kDxilWaveSizeTag = 11;
  static const unsigned kDxilEntryRootSigTag = 12;

  // Node Tags ( extension of shader property tags)

  static const unsigned kDxilNodeLaunchTypeTag = 13;
  static const unsigned kDxilNodeIsProgramEntryTag = 14;
  static const unsigned kDxilNodeIdTag = 15;
  static const unsigned kDxilNodeLocalRootArgumentsTableIndexTag = 16;
  static const unsigned kDxilShareInputOfTag = 17;
  static const unsigned kDxilNodeDispatchGridTag = 18;
  static const unsigned kDxilNodeMaxRecursionDepthTag = 19;
  static const unsigned kDxilNodeInputsTag = 20;
  static const unsigned kDxilNodeOutputsTag = 21;
  static const unsigned kDxilNodeMaxDispatchGridTag = 22;
  static const unsigned kDxilRangedWaveSizeTag = 23;

  // Node Input/Output State.
  static const unsigned kDxilNodeOutputIDTag = 0;
  static const unsigned kDxilNodeIOFlagsTag = 1;
  static const unsigned kDxilNodeRecordTypeTag = 2;
  static const unsigned kDxilNodeMaxRecordsTag = 3;
  static const unsigned kDxilNodeMaxRecordsSharedWithTag = 4;
  static const unsigned kDxilNodeOutputArraySizeTag = 5;
  static const unsigned kDxilNodeAllowSparseNodesTag = 6;

  // Node Record Type
  static const unsigned kDxilNodeRecordSizeTag = 0;
  static const unsigned kDxilNodeSVDispatchGridTag = 1;
  static const unsigned kDxilNodeRecordAlignmentTag = 2;

  // GSState.
  static const unsigned kDxilGSStateNumFields = 5;
  static const unsigned kDxilGSStateInputPrimitive = 0;
  static const unsigned kDxilGSStateMaxVertexCount = 1;
  static const unsigned kDxilGSStateActiveStreamMask = 2;
  static const unsigned kDxilGSStateOutputStreamTopology = 3;
  static const unsigned kDxilGSStateGSInstanceCount = 4;

  // DSState.
  static const unsigned kDxilDSStateNumFields = 2;
  static const unsigned kDxilDSStateTessellatorDomain = 0;
  static const unsigned kDxilDSStateInputControlPointCount = 1;

  // HSState.
  static const unsigned kDxilHSStateNumFields = 7;
  static const unsigned kDxilHSStatePatchConstantFunction = 0;
  static const unsigned kDxilHSStateInputControlPointCount = 1;
  static const unsigned kDxilHSStateOutputControlPointCount = 2;
  static const unsigned kDxilHSStateTessellatorDomain = 3;
  static const unsigned kDxilHSStateTessellatorPartitioning = 4;
  static const unsigned kDxilHSStateTessellatorOutputPrimitive = 5;
  static const unsigned kDxilHSStateMaxTessellationFactor = 6;

  // MSState.
  static const unsigned kDxilMSStateNumFields = 5;
  static const unsigned kDxilMSStateNumThreads = 0;
  static const unsigned kDxilMSStateMaxVertexCount = 1;
  static const unsigned kDxilMSStateMaxPrimitiveCount = 2;
  static const unsigned kDxilMSStateOutputTopology = 3;
  static const unsigned kDxilMSStatePayloadSizeInBytes = 4;

  // ASState.
  static const unsigned kDxilASStateNumFields = 2;
  static const unsigned kDxilASStateNumThreads = 0;
  static const unsigned kDxilASStatePayloadSizeInBytes = 1;

public:
  /// Use this class to manipulate metadata of DXIL or high-level DX IR specific
  /// fields in the record.
  class ExtraPropertyHelper {
  public:
    ExtraPropertyHelper(llvm::Module *pModule);
    virtual ~ExtraPropertyHelper() {}

    virtual void EmitSRVProperties(const DxilResource &SRV,
                                   std::vector<llvm::Metadata *> &MDVals) = 0;
    virtual void LoadSRVProperties(const llvm::MDOperand &MDO,
                                   DxilResource &SRV) = 0;

    virtual void EmitUAVProperties(const DxilResource &UAV,
                                   std::vector<llvm::Metadata *> &MDVals) = 0;
    virtual void LoadUAVProperties(const llvm::MDOperand &MDO,
                                   DxilResource &UAV) = 0;

    virtual void
    EmitCBufferProperties(const DxilCBuffer &CB,
                          std::vector<llvm::Metadata *> &MDVals) = 0;
    virtual void LoadCBufferProperties(const llvm::MDOperand &MDO,
                                       DxilCBuffer &CB) = 0;

    virtual void
    EmitSamplerProperties(const DxilSampler &S,
                          std::vector<llvm::Metadata *> &MDVals) = 0;
    virtual void LoadSamplerProperties(const llvm::MDOperand &MDO,
                                       DxilSampler &S) = 0;

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

  protected:
    llvm::LLVMContext &m_Ctx;
    llvm::Module *m_pModule;

  public:
    unsigned m_ValMajor, m_ValMinor; // Reported validation version in DXIL
    unsigned m_MinValMajor,
        m_MinValMinor; // Minimum validation version dictated by shader model
    bool m_bExtraMetadata;
  };

public:
  DxilMDHelper(llvm::Module *pModule, std::unique_ptr<ExtraPropertyHelper> EPH);
  ~DxilMDHelper();

  void SetShaderModel(const ShaderModel *pSM);
  const ShaderModel *GetShaderModel() const;

  // Dxil version.
  void EmitDxilVersion(unsigned Major, unsigned Minor);
  void LoadDxilVersion(unsigned &Major, unsigned &Minor);

  // Validator version.
  void EmitValidatorVersion(unsigned Major, unsigned Minor);
  void LoadValidatorVersion(unsigned &Major, unsigned &Minor);

  // Shader model.
  void EmitDxilShaderModel(const ShaderModel *pSM);
  void LoadDxilShaderModel(const ShaderModel *&pSM);

  // Intermediate flags
  void EmitDxilIntermediateOptions(uint32_t flags);
  void LoadDxilIntermediateOptions(uint32_t &flags);

  // Entry points.
  void EmitDxilEntryPoints(std::vector<llvm::MDNode *> &MDEntries);
  void UpdateDxilEntryPoints(std::vector<llvm::MDNode *> &MDEntries);
  const llvm::NamedMDNode *GetDxilEntryPoints();
  llvm::MDTuple *EmitDxilEntryPointTuple(llvm::Function *pFunc,
                                         const std::string &Name,
                                         llvm::MDTuple *pSignatures,
                                         llvm::MDTuple *pResources,
                                         llvm::MDTuple *pProperties);
  void GetDxilEntryPoint(const llvm::MDNode *MDO, llvm::Function *&pFunc,
                         std::string &Name, const llvm::MDOperand *&pSignatures,
                         const llvm::MDOperand *&pResources,
                         const llvm::MDOperand *&pProperties);

  // Signatures.
  llvm::MDTuple *EmitDxilSignatures(const DxilEntrySignature &EntrySig);
  void LoadDxilSignatures(const llvm::MDOperand &MDO,
                          DxilEntrySignature &EntrySig);
  llvm::MDTuple *EmitSignatureMetadata(const DxilSignature &Sig);
  void EmitRootSignature(std::vector<uint8_t> &SerializedRootSignature);
  void LoadSignatureMetadata(const llvm::MDOperand &MDO, DxilSignature &Sig);
  llvm::MDTuple *EmitSignatureElement(const DxilSignatureElement &SE);
  void LoadSignatureElement(const llvm::MDOperand &MDO,
                            DxilSignatureElement &SE);
  void LoadRootSignature(std::vector<uint8_t> &SerializedRootSignature);

  // Resources.
  llvm::MDTuple *EmitDxilResourceTuple(llvm::MDTuple *pSRVs,
                                       llvm::MDTuple *pUAVs,
                                       llvm::MDTuple *pCBuffers,
                                       llvm::MDTuple *pSamplers);
  void EmitDxilResources(llvm::MDTuple *pDxilResourceTuple);
  void UpdateDxilResources(llvm::MDTuple *pDxilResourceTuple);
  void GetDxilResources(const llvm::MDOperand &MDO, const llvm::MDTuple *&pSRVs,
                        const llvm::MDTuple *&pUAVs,
                        const llvm::MDTuple *&pCBuffers,
                        const llvm::MDTuple *&pSamplers);
  void EmitDxilResourceBase(const DxilResourceBase &R,
                            llvm::Metadata *ppMDVals[]);
  void LoadDxilResourceBase(const llvm::MDOperand &MDO, DxilResourceBase &R);
  llvm::MDTuple *EmitDxilSRV(const DxilResource &SRV);
  void LoadDxilSRV(const llvm::MDOperand &MDO, DxilResource &SRV);
  llvm::MDTuple *EmitDxilUAV(const DxilResource &UAV);
  void LoadDxilUAV(const llvm::MDOperand &MDO, DxilResource &UAV);
  llvm::MDTuple *EmitDxilCBuffer(const DxilCBuffer &CB);
  void LoadDxilCBuffer(const llvm::MDOperand &MDO, DxilCBuffer &CB);
  llvm::MDTuple *EmitDxilSampler(const DxilSampler &S);
  void LoadDxilSampler(const llvm::MDOperand &MDO, DxilSampler &S);
  const llvm::MDOperand &GetResourceClass(llvm::MDNode *MD,
                                          DXIL::ResourceClass &RC);

  // Type system.
  void EmitDxilTypeSystem(DxilTypeSystem &TypeSystem,
                          std::vector<llvm::GlobalVariable *> &LLVMUsed);
  void LoadDxilTypeSystemNode(const llvm::MDTuple &MDT,
                              DxilTypeSystem &TypeSystem);
  void LoadDxilTypeSystem(DxilTypeSystem &TypeSystem);
  llvm::Metadata *EmitDxilStructAnnotation(const DxilStructAnnotation &SA);
  void LoadDxilStructAnnotation(const llvm::MDOperand &MDO,
                                DxilStructAnnotation &SA);
  llvm::Metadata *EmitDxilFieldAnnotation(const DxilFieldAnnotation &FA);
  void LoadDxilFieldAnnotation(const llvm::MDOperand &MDO,
                               DxilFieldAnnotation &FA);
  llvm::Metadata *EmitDxilFunctionAnnotation(const DxilFunctionAnnotation &FA);
  void LoadDxilFunctionAnnotation(const llvm::MDOperand &MDO,
                                  DxilFunctionAnnotation &FA);
  llvm::Metadata *EmitDxilParamAnnotation(const DxilParameterAnnotation &PA);
  void LoadDxilParamAnnotation(const llvm::MDOperand &MDO,
                               DxilParameterAnnotation &PA);
  llvm::Metadata *EmitDxilParamAnnotations(const DxilFunctionAnnotation &FA);
  void LoadDxilParamAnnotations(const llvm::MDOperand &MDO,
                                DxilFunctionAnnotation &FA);
  llvm::Metadata *
  EmitDxilTemplateArgAnnotation(const DxilTemplateArgAnnotation &annotation);
  void LoadDxilTemplateArgAnnotation(const llvm::MDOperand &MDO,
                                     DxilTemplateArgAnnotation &annotation);

  // DXR Payload Annotations
  void EmitDxrPayloadAnnotations(DxilTypeSystem &TypeSystem);
  llvm::Metadata *
  EmitDxrPayloadStructAnnotation(const DxilPayloadAnnotation &SA);
  llvm::Metadata *
  EmitDxrPayloadFieldAnnotation(const DxilPayloadFieldAnnotation &FA,
                                llvm::Type *fieldType);
  void LoadDxrPayloadAnnotationNode(const llvm::MDTuple &MDT,
                                    DxilTypeSystem &TypeSystem);
  void LoadDxrPayloadAnnotations(DxilTypeSystem &TypeSystem);
  void LoadDxrPayloadFieldAnnoations(const llvm::MDOperand &MDO,
                                     DxilPayloadAnnotation &SA);
  void LoadDxrPayloadFieldAnnoation(const llvm::MDOperand &MDO,
                                    DxilPayloadFieldAnnotation &FA);
  void LoadDxrPayloadAccessQualifiers(const llvm::MDOperand &MDO,
                                      DxilPayloadFieldAnnotation &FA);

  // Function props.
  void SerializeNodeProps(llvm::SmallVectorImpl<llvm::Metadata *> &MDVals,
                          unsigned &valIdx,
                          const hlsl::DxilFunctionProps *props);
  void DeserializeNodeProps(const llvm::MDTuple *pProps, unsigned &idx,
                            hlsl::DxilFunctionProps *props);
  llvm::MDTuple *EmitDxilFunctionProps(const hlsl::DxilFunctionProps *props,
                                       const llvm::Function *F);
  const llvm::Function *LoadDxilFunctionProps(const llvm::MDTuple *pProps,
                                              hlsl::DxilFunctionProps *props);
  llvm::MDTuple *EmitDxilEntryProperties(uint64_t rawShaderFlag,
                                         const hlsl::DxilFunctionProps &props,
                                         uint32_t autoBindingSpace);
  void LoadDxilEntryProperties(const llvm::MDOperand &MDO,
                               uint64_t &rawShaderFlag,
                               hlsl::DxilFunctionProps &props,
                               uint32_t &autoBindingSpace);

  // ViewId state.
  void EmitDxilViewIdState(std::vector<unsigned> &SerializedState);
  void LoadDxilViewIdState(std::vector<unsigned> &SerializedState);
  // Control flow hints.
  static llvm::MDNode *
  EmitControlFlowHints(llvm::LLVMContext &Ctx,
                       std::vector<DXIL::ControlFlowHint> &hints);
  static unsigned GetControlFlowHintMask(const llvm::Instruction *I);
  static bool HasControlFlowHintToPreventFlatten(const llvm::Instruction *I);

  // Subobjects
  void EmitSubobjects(const DxilSubobjects &Subobjects);
  void LoadSubobjects(DxilSubobjects &Subobjects);
  llvm::Metadata *EmitSubobject(const DxilSubobject &obj);
  void LoadSubobject(const llvm::MDNode &MDO, DxilSubobjects &Subobjects);

  // Extra metadata present
  bool HasExtraMetadata() { return m_bExtraMetadata; }

  // Instruction Counters
  void EmitDxilCounters(const DxilCounters &counters);
  void LoadDxilCounters(DxilCounters &counters) const;

  // Shader specific.
private:
  llvm::MDTuple *
  EmitDxilGSState(DXIL::InputPrimitive Primitive, unsigned MaxVertexCount,
                  unsigned ActiveStreamMask,
                  DXIL::PrimitiveTopology StreamPrimitiveTopology,
                  unsigned GSInstanceCount);
  void LoadDxilGSState(const llvm::MDOperand &MDO,
                       DXIL::InputPrimitive &Primitive,
                       unsigned &MaxVertexCount, unsigned &ActiveStreamMask,
                       DXIL::PrimitiveTopology &StreamPrimitiveTopology,
                       unsigned &GSInstanceCount);

  llvm::MDTuple *EmitDxilDSState(DXIL::TessellatorDomain Domain,
                                 unsigned InputControlPointCount);
  void LoadDxilDSState(const llvm::MDOperand &MDO,
                       DXIL::TessellatorDomain &Domain,
                       unsigned &InputControlPointCount);

  llvm::MDTuple *EmitDxilHSState(
      llvm::Function *pPatchConstantFunction, unsigned InputControlPointCount,
      unsigned OutputControlPointCount, DXIL::TessellatorDomain TessDomain,
      DXIL::TessellatorPartitioning TessPartitioning,
      DXIL::TessellatorOutputPrimitive TessOutputPrimitive,
      float MaxTessFactor);
  void LoadDxilHSState(const llvm::MDOperand &MDO,
                       llvm::Function *&pPatchConstantFunction,
                       unsigned &InputControlPointCount,
                       unsigned &OutputControlPointCount,
                       DXIL::TessellatorDomain &TessDomain,
                       DXIL::TessellatorPartitioning &TessPartitioning,
                       DXIL::TessellatorOutputPrimitive &TessOutputPrimitive,
                       float &MaxTessFactor);

  llvm::MDTuple *EmitDxilMSState(const unsigned *NumThreads,
                                 unsigned MaxVertexCount,
                                 unsigned MaxPrimitiveCount,
                                 DXIL::MeshOutputTopology OutputTopology,
                                 unsigned payloadSizeInBytes);
  void LoadDxilMSState(const llvm::MDOperand &MDO, unsigned *NumThreads,
                       unsigned &MaxVertexCount, unsigned &MaxPrimitiveCount,
                       DXIL::MeshOutputTopology &OutputTopology,
                       unsigned &payloadSizeInBytes);

  llvm::MDTuple *EmitDxilASState(const unsigned *NumThreads,
                                 unsigned payloadSizeInBytes);
  void LoadDxilASState(const llvm::MDOperand &MDO, unsigned *NumThreads,
                       unsigned &payloadSizeInBytes);

  llvm::MDTuple *EmitDxilNodeIOState(const NodeIOProperties &Node);
  llvm::MDTuple *EmitDxilNodeRecordType(const NodeRecordType &RecordType);
  hlsl::NodeIOProperties LoadDxilNodeIOState(const llvm::MDOperand &MDO);
  hlsl::NodeRecordType LoadDxilNodeRecordType(const llvm::MDOperand &MDO);

  void EmitDxilNodeState(std::vector<llvm::Metadata *> &MDVals,
                         const DxilFunctionProps &props);

  void AddCounterIfNonZero(uint32_t value, llvm::StringRef name,
                           std::vector<llvm::Metadata *> &MDVals);
  void LoadCounterMD(const llvm::MDOperand &MDName,
                     const llvm::MDOperand &MDValue,
                     DxilCounters &counters) const;

public:
  // Utility functions.
  static bool IsKnownNamedMetaData(const llvm::NamedMDNode &Node);
  static bool IsKnownMetadataID(llvm::LLVMContext &Ctx, unsigned ID);
  static void GetKnownMetadataIDs(llvm::LLVMContext &Ctx,
                                  llvm::SmallVectorImpl<unsigned> *pIDs);
  static void combineDxilMetadata(llvm::Instruction *K,
                                  const llvm::Instruction *J);
  static llvm::ConstantAsMetadata *Int32ToConstMD(int32_t v,
                                                  llvm::LLVMContext &Ctx);
  llvm::ConstantAsMetadata *Int32ToConstMD(int32_t v);
  static llvm::ConstantAsMetadata *Uint32ToConstMD(unsigned v,
                                                   llvm::LLVMContext &Ctx);
  llvm::ConstantAsMetadata *Uint32ToConstMD(unsigned v);
  static llvm::ConstantAsMetadata *Uint64ToConstMD(uint64_t v,
                                                   llvm::LLVMContext &Ctx);
  llvm::ConstantAsMetadata *Uint64ToConstMD(uint64_t v);
  llvm::ConstantAsMetadata *Int8ToConstMD(int8_t v);
  llvm::ConstantAsMetadata *Uint8ToConstMD(uint8_t v);
  static llvm::ConstantAsMetadata *BoolToConstMD(bool v,
                                                 llvm::LLVMContext &Ctx);
  llvm::ConstantAsMetadata *BoolToConstMD(bool v);
  llvm::ConstantAsMetadata *FloatToConstMD(float v);
  static int32_t ConstMDToInt32(const llvm::MDOperand &MDO);
  static unsigned ConstMDToUint32(const llvm::MDOperand &MDO);
  static uint64_t ConstMDToUint64(const llvm::MDOperand &MDO);
  static int8_t ConstMDToInt8(const llvm::MDOperand &MDO);
  static uint8_t ConstMDToUint8(const llvm::MDOperand &MDO);
  static bool ConstMDToBool(const llvm::MDOperand &MDO);
  static float ConstMDToFloat(const llvm::MDOperand &MDO);
  static std::string StringMDToString(const llvm::MDOperand &MDO);
  static llvm::StringRef StringMDToStringRef(const llvm::MDOperand &MDO);
  static llvm::Value *ValueMDToValue(const llvm::MDOperand &MDO);
  llvm::MDTuple *Uint32VectorToConstMDTuple(const std::vector<unsigned> &Vec);
  void ConstMDTupleToUint32Vector(llvm::MDTuple *pTupleMD,
                                  std::vector<unsigned> &Vec);
  static bool IsMarkedPrecise(const llvm::Instruction *inst);
  static void MarkPrecise(llvm::Instruction *inst);
  static bool IsMarkedNonUniform(const llvm::Instruction *inst);
  static void MarkNonUniform(llvm::Instruction *inst);
  static bool GetVariableDebugLayout(llvm::DbgDeclareInst *inst,
                                     unsigned &StartOffsetInBits,
                                     std::vector<DxilDIArrayDim> &ArrayDims);
  static void
  SetVariableDebugLayout(llvm::DbgDeclareInst *inst, unsigned StartOffsetInBits,
                         const std::vector<DxilDIArrayDim> &ArrayDims);
  static void
  CopyMetadata(llvm::Instruction &I, llvm::Instruction &SrcInst,
               llvm::ArrayRef<unsigned> WL = llvm::ArrayRef<unsigned>());

private:
  llvm::LLVMContext &m_Ctx;
  llvm::Module *m_pModule;
  const ShaderModel *m_pSM;
  std::unique_ptr<ExtraPropertyHelper> m_ExtraPropertyHelper;
  unsigned m_ValMajor, m_ValMinor; // Reported validation version in DXIL
  unsigned m_MinValMajor,
      m_MinValMinor; // Minimum validation version dictated by shader model

  // Non-fatal if extra metadata is found, but will fail validation.
  // This is how metadata can be exteneded.
  bool m_bExtraMetadata;
};

/// Use this class to manipulate metadata of extra metadata record properties
/// that are specific to DXIL.
class DxilExtraPropertyHelper : public DxilMDHelper::ExtraPropertyHelper {
public:
  DxilExtraPropertyHelper(llvm::Module *pModule);
  virtual ~DxilExtraPropertyHelper() {}

  virtual void EmitSRVProperties(const DxilResource &SRV,
                                 std::vector<llvm::Metadata *> &MDVals);
  virtual void LoadSRVProperties(const llvm::MDOperand &MDO, DxilResource &SRV);

  virtual void EmitUAVProperties(const DxilResource &UAV,
                                 std::vector<llvm::Metadata *> &MDVals);
  virtual void LoadUAVProperties(const llvm::MDOperand &MDO, DxilResource &UAV);

  virtual void EmitCBufferProperties(const DxilCBuffer &CB,
                                     std::vector<llvm::Metadata *> &MDVals);
  virtual void LoadCBufferProperties(const llvm::MDOperand &MDO,
                                     DxilCBuffer &CB);

  virtual void EmitSamplerProperties(const DxilSampler &S,
                                     std::vector<llvm::Metadata *> &MDVals);
  virtual void LoadSamplerProperties(const llvm::MDOperand &MDO,
                                     DxilSampler &S);

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

} // namespace hlsl
