///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// DxilMetadataHelper.cpp                                                    //
// Copyright (C) Microsoft Corporation. All rights reserved.                 //
// This file is distributed under the University of Illinois Open Source     //
// License. See LICENSE.TXT for details.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "dxc/DXIL/DxilMetadataHelper.h"
#include "dxc/DXIL/DxilCBuffer.h"
#include "dxc/DXIL/DxilCounters.h"
#include "dxc/DXIL/DxilFunctionProps.h"
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilResource.h"
#include "dxc/DXIL/DxilSampler.h"
#include "dxc/DXIL/DxilShaderFlags.h"
#include "dxc/DXIL/DxilShaderModel.h"
#include "dxc/DXIL/DxilSignature.h"
#include "dxc/DXIL/DxilSignatureElement.h"
#include "dxc/DXIL/DxilSubobject.h"
#include "dxc/DXIL/DxilTypeSystem.h"
#include "dxc/Support/Global.h"

#include "llvm/ADT/DenseSet.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <array>

#include "dxc/Support/WinFunctions.h"
#include "dxc/Support/WinIncludes.h"

using namespace llvm;
using std::string;
using std::unique_ptr;
using std::vector;

namespace {
void LoadSerializedRootSignature(MDNode *pNode,
                                 std::vector<uint8_t> &SerializedRootSignature,
                                 LLVMContext &Ctx) {
  IFTBOOL(pNode->getNumOperands() == 1, DXC_E_INCORRECT_DXIL_METADATA);
  const MDOperand &MDO = pNode->getOperand(0);

  const ConstantAsMetadata *pMetaData = dyn_cast<ConstantAsMetadata>(MDO.get());
  IFTBOOL(pMetaData != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const ConstantDataArray *pData =
      dyn_cast<ConstantDataArray>(pMetaData->getValue());
  IFTBOOL(pData != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pData->getElementType() == Type::getInt8Ty(Ctx),
          DXC_E_INCORRECT_DXIL_METADATA);

  SerializedRootSignature.assign(pData->getRawDataValues().begin(),
                                 pData->getRawDataValues().end());
}

MDNode *
EmitSerializedRootSignature(const std::vector<uint8_t> &SerializedRootSignature,
                            LLVMContext &Ctx) {
  if (SerializedRootSignature.empty())
    return nullptr;
  Constant *V = llvm::ConstantDataArray::get(
      Ctx, llvm::ArrayRef<uint8_t>(SerializedRootSignature.data(),
                                   SerializedRootSignature.size()));
  return MDNode::get(Ctx, {ConstantAsMetadata::get(V)});
}

} // namespace

namespace hlsl {

const char DxilMDHelper::kDxilVersionMDName[] = "dx.version";
const char DxilMDHelper::kDxilShaderModelMDName[] = "dx.shaderModel";
const char DxilMDHelper::kDxilEntryPointsMDName[] = "dx.entryPoints";
const char DxilMDHelper::kDxilResourcesMDName[] = "dx.resources";
const char DxilMDHelper::kDxilTypeSystemMDName[] = "dx.typeAnnotations";
const char DxilMDHelper::kDxilTypeSystemHelperVariablePrefix[] = "dx.typevar.";
const char DxilMDHelper::kDxilControlFlowHintMDName[] = "dx.controlflow.hints";
const char DxilMDHelper::kDxilPreciseAttributeMDName[] = "dx.precise";
const char DxilMDHelper::kDxilVariableDebugLayoutMDName[] = "dx.dbg.varlayout";
const char DxilMDHelper::kDxilTempAllocaMDName[] = "dx.temp";
const char DxilMDHelper::kDxilNonUniformAttributeMDName[] = "dx.nonuniform";
const char DxilMDHelper::kDxilValidatorVersionMDName[] = "dx.valver";
const char DxilMDHelper::kDxilDxrPayloadAnnotationsMDName[] =
    "dx.dxrPayloadAnnotations";

// This named metadata is not valid in final module (should be moved to
// DxilContainer)
const char DxilMDHelper::kDxilRootSignatureMDName[] = "dx.rootSignature";
const char DxilMDHelper::kDxilIntermediateOptionsMDName[] =
    "dx.intermediateOptions";
const char DxilMDHelper::kDxilViewIdStateMDName[] = "dx.viewIdState";
const char DxilMDHelper::kDxilSubobjectsMDName[] = "dx.subobjects";

const char DxilMDHelper::kDxilSourceContentsMDName[] = "dx.source.contents";
const char DxilMDHelper::kDxilSourceDefinesMDName[] = "dx.source.defines";
const char DxilMDHelper::kDxilSourceMainFileNameMDName[] =
    "dx.source.mainFileName";
const char DxilMDHelper::kDxilSourceArgsMDName[] = "dx.source.args";

const char DxilMDHelper::kDxilDxcBindingTableMDName[] = "dx.binding.table";

const char DxilMDHelper::kDxilSourceContentsOldMDName[] = "llvm.dbg.contents";
const char DxilMDHelper::kDxilSourceDefinesOldMDName[] = "llvm.dbg.defines";
const char DxilMDHelper::kDxilSourceMainFileNameOldMDName[] =
    "llvm.dbg.mainFileName";
const char DxilMDHelper::kDxilSourceArgsOldMDName[] = "llvm.dbg.args";

// This is reflection-only metadata
const char DxilMDHelper::kDxilCountersMDName[] = "dx.counters";

static std::array<const char *, 8> DxilMDNames = {{
    DxilMDHelper::kDxilVersionMDName,
    DxilMDHelper::kDxilShaderModelMDName,
    DxilMDHelper::kDxilEntryPointsMDName,
    DxilMDHelper::kDxilResourcesMDName,
    DxilMDHelper::kDxilTypeSystemMDName,
    DxilMDHelper::kDxilValidatorVersionMDName,
    DxilMDHelper::kDxilViewIdStateMDName,
    DxilMDHelper::kDxilDxrPayloadAnnotationsMDName,
}};

DxilMDHelper::DxilMDHelper(Module *pModule,
                           std::unique_ptr<ExtraPropertyHelper> EPH)
    : m_Ctx(pModule->getContext()), m_pModule(pModule), m_pSM(nullptr),
      m_ExtraPropertyHelper(std::move(EPH)), m_ValMajor(1), m_ValMinor(0),
      m_MinValMajor(1), m_MinValMinor(0), m_bExtraMetadata(false) {}

DxilMDHelper::~DxilMDHelper() {}

void DxilMDHelper::SetShaderModel(const ShaderModel *pSM) {
  m_pSM = pSM;
  m_pSM->GetMinValidatorVersion(m_MinValMajor, m_MinValMinor);
  if (DXIL::CompareVersions(m_ValMajor, m_ValMinor, m_MinValMajor,
                            m_MinValMinor) < 0) {
    m_ValMajor = m_MinValMajor;
    m_ValMinor = m_MinValMinor;
  }
  // Validator version 0.0 is not meant for validation or retail driver
  // consumption, and is used for separate reflection. MinVal version drives
  // metadata decisions for compatilbility, so snap this to latest for
  // reflection/no validation case.
  if (DXIL::CompareVersions(m_ValMajor, m_ValMinor, 0, 0) == 0) {
    m_MinValMajor = 0;
    m_MinValMinor = 0;
  }
  if (m_ExtraPropertyHelper) {
    m_ExtraPropertyHelper->m_ValMajor = m_ValMajor;
    m_ExtraPropertyHelper->m_ValMinor = m_ValMinor;
    m_ExtraPropertyHelper->m_MinValMajor = m_MinValMajor;
    m_ExtraPropertyHelper->m_MinValMinor = m_MinValMinor;
  }
}

const ShaderModel *DxilMDHelper::GetShaderModel() const { return m_pSM; }

//
// DXIL version.
//
void DxilMDHelper::EmitDxilVersion(unsigned Major, unsigned Minor) {
  NamedMDNode *pDxilVersionMD = m_pModule->getNamedMetadata(kDxilVersionMDName);
  IFTBOOL(pDxilVersionMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pDxilVersionMD = m_pModule->getOrInsertNamedMetadata(kDxilVersionMDName);

  Metadata *MDVals[kDxilVersionNumFields];
  MDVals[kDxilVersionMajorIdx] = Uint32ToConstMD(Major);
  MDVals[kDxilVersionMinorIdx] = Uint32ToConstMD(Minor);

  pDxilVersionMD->addOperand(MDNode::get(m_Ctx, MDVals));
}

// Load dxil version from metadata contained in pModule.
// Returns true and passes result through
// the dxil major/minor version params if valid.
// Returns false if metadata is missing or invalid.
bool DxilMDHelper::LoadDxilVersion(const Module *pModule, unsigned &Major,
                                   unsigned &Minor) {
  NamedMDNode *pDxilVersionMD = pModule->getNamedMetadata(kDxilVersionMDName);
  IFRBOOL(pDxilVersionMD != nullptr, false);
  IFRBOOL(pDxilVersionMD->getNumOperands() == 1, false);

  MDNode *pVersionMD = pDxilVersionMD->getOperand(0);
  IFRBOOL(pVersionMD->getNumOperands() == kDxilVersionNumFields, false);

  Major = ConstMDToUint32(pVersionMD->getOperand(kDxilVersionMajorIdx));
  Minor = ConstMDToUint32(pVersionMD->getOperand(kDxilVersionMinorIdx));

  return true;
}

void DxilMDHelper::LoadDxilVersion(unsigned &Major, unsigned &Minor) {
  IFTBOOL(LoadDxilVersion(m_pModule, Major, Minor),
          DXC_E_INCORRECT_DXIL_METADATA);
}

//
// Validator version.
//
void DxilMDHelper::EmitValidatorVersion(unsigned Major, unsigned Minor) {
  NamedMDNode *pDxilValidatorVersionMD =
      m_pModule->getNamedMetadata(kDxilValidatorVersionMDName);

  // Allow re-writing the validator version, since this can be changed at later
  // points.
  if (pDxilValidatorVersionMD)
    m_pModule->eraseNamedMetadata(pDxilValidatorVersionMD);

  pDxilValidatorVersionMD =
      m_pModule->getOrInsertNamedMetadata(kDxilValidatorVersionMDName);

  Metadata *MDVals[kDxilVersionNumFields];
  MDVals[kDxilVersionMajorIdx] = Uint32ToConstMD(Major);
  MDVals[kDxilVersionMinorIdx] = Uint32ToConstMD(Minor);

  pDxilValidatorVersionMD->addOperand(MDNode::get(m_Ctx, MDVals));

  m_ValMajor = Major;
  m_ValMinor = Minor; // Keep these for later use
}

void DxilMDHelper::LoadValidatorVersion(unsigned &Major, unsigned &Minor) {
  NamedMDNode *pDxilValidatorVersionMD =
      m_pModule->getNamedMetadata(kDxilValidatorVersionMDName);

  if (pDxilValidatorVersionMD == nullptr) {
    // If no validator version metadata, assume 1.0
    Major = 1;
    Minor = 0;
    m_ValMajor = Major;
    m_ValMinor = Minor; // Keep these for later use
    return;
  }

  IFTBOOL(pDxilValidatorVersionMD->getNumOperands() == 1,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pVersionMD = pDxilValidatorVersionMD->getOperand(0);
  IFTBOOL(pVersionMD->getNumOperands() == kDxilVersionNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  Major = ConstMDToUint32(pVersionMD->getOperand(kDxilVersionMajorIdx));
  Minor = ConstMDToUint32(pVersionMD->getOperand(kDxilVersionMinorIdx));
  m_ValMajor = Major;
  m_ValMinor = Minor; // Keep these for later use
}

//
// DXIL shader model.
//
void DxilMDHelper::EmitDxilShaderModel(const ShaderModel *pSM) {
  NamedMDNode *pShaderModelNamedMD =
      m_pModule->getNamedMetadata(kDxilShaderModelMDName);
  IFTBOOL(pShaderModelNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pShaderModelNamedMD =
      m_pModule->getOrInsertNamedMetadata(kDxilShaderModelMDName);

  Metadata *MDVals[kDxilShaderModelNumFields];
  MDVals[kDxilShaderModelTypeIdx] = MDString::get(m_Ctx, pSM->GetKindName());
  MDVals[kDxilShaderModelMajorIdx] = Uint32ToConstMD(pSM->GetMajor());
  MDVals[kDxilShaderModelMinorIdx] = Uint32ToConstMD(pSM->GetMinor());

  pShaderModelNamedMD->addOperand(MDNode::get(m_Ctx, MDVals));

  SetShaderModel(pSM);
}

void DxilMDHelper::LoadDxilShaderModel(const ShaderModel *&pSM) {
  NamedMDNode *pShaderModelNamedMD =
      m_pModule->getNamedMetadata(kDxilShaderModelMDName);
  IFTBOOL(pShaderModelNamedMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pShaderModelNamedMD->getNumOperands() == 1,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pShaderModelMD = pShaderModelNamedMD->getOperand(0);
  IFTBOOL(pShaderModelMD->getNumOperands() == kDxilShaderModelNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDString *pShaderTypeMD =
      dyn_cast<MDString>(pShaderModelMD->getOperand(kDxilShaderModelTypeIdx));
  IFTBOOL(pShaderTypeMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  unsigned Major =
      ConstMDToUint32(pShaderModelMD->getOperand(kDxilShaderModelMajorIdx));
  unsigned Minor =
      ConstMDToUint32(pShaderModelMD->getOperand(kDxilShaderModelMinorIdx));
  string ShaderModelName = pShaderTypeMD->getString().str();
  ShaderModelName +=
      "_" + std::to_string(Major) + "_" +
      (Minor == ShaderModel::kOfflineMinor ? "x" : std::to_string(Minor));
  pSM = ShaderModel::GetByName(ShaderModelName.c_str());
  if (!pSM->IsValidForDxil()) {
    char ErrorMsgTxt[40];
    StringCchPrintfA(ErrorMsgTxt, _countof(ErrorMsgTxt),
                     "Unknown shader model '%s'", ShaderModelName.c_str());
    string ErrorMsg(ErrorMsgTxt);
    throw hlsl::Exception(DXC_E_INCORRECT_DXIL_METADATA, ErrorMsg);
  }
  SetShaderModel(pSM);
}

//
// intermediate options.
//
void DxilMDHelper::EmitDxilIntermediateOptions(uint32_t flags) {
  if (flags == 0)
    return;

  NamedMDNode *pIntermediateOptionsNamedMD =
      m_pModule->getNamedMetadata(kDxilIntermediateOptionsMDName);
  IFTBOOL(pIntermediateOptionsNamedMD == nullptr,
          DXC_E_INCORRECT_DXIL_METADATA);
  pIntermediateOptionsNamedMD =
      m_pModule->getOrInsertNamedMetadata(kDxilIntermediateOptionsMDName);

  pIntermediateOptionsNamedMD->addOperand(
      MDNode::get(m_Ctx, {Uint32ToConstMD(kDxilIntermediateOptionsFlags),
                          Uint32ToConstMD(flags)}));
}

void DxilMDHelper::LoadDxilIntermediateOptions(uint32_t &flags) {
  flags = 0;

  NamedMDNode *pIntermediateOptionsNamedMD =
      m_pModule->getNamedMetadata(kDxilIntermediateOptionsMDName);
  if (pIntermediateOptionsNamedMD == nullptr)
    return;

  for (unsigned i = 0; i < pIntermediateOptionsNamedMD->getNumOperands(); i++) {
    MDTuple *pEntry =
        dyn_cast<MDTuple>(pIntermediateOptionsNamedMD->getOperand(i));
    IFTBOOL(pEntry != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    IFTBOOL(pEntry->getNumOperands() >= 1, DXC_E_INCORRECT_DXIL_METADATA);

    uint32_t id = ConstMDToUint32(pEntry->getOperand(0));
    switch (id) {
    case kDxilIntermediateOptionsFlags:
      IFTBOOL(pEntry->getNumOperands() == 2, DXC_E_INCORRECT_DXIL_METADATA);
      flags = ConstMDToUint32(pEntry->getOperand(1));
      break;

    default:
      throw hlsl::Exception(DXC_E_INCORRECT_DXIL_METADATA,
                            "Unrecognized intermediate options metadata");
    }
  }
}

//
// Entry points.
//
void DxilMDHelper::EmitDxilEntryPoints(vector<MDNode *> &MDEntries) {
  DXASSERT(MDEntries.size() == 1 || GetShaderModel()->IsLib(),
           "only one entry point is supported for now");
  NamedMDNode *pEntryPointsNamedMD =
      m_pModule->getNamedMetadata(kDxilEntryPointsMDName);
  IFTBOOL(pEntryPointsNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pEntryPointsNamedMD =
      m_pModule->getOrInsertNamedMetadata(kDxilEntryPointsMDName);

  for (size_t i = 0; i < MDEntries.size(); i++) {
    pEntryPointsNamedMD->addOperand(MDEntries[i]);
  }
}

void DxilMDHelper::UpdateDxilEntryPoints(vector<MDNode *> &MDEntries) {
  DXASSERT(MDEntries.size() == 1, "only one entry point is supported for now");
  NamedMDNode *pEntryPointsNamedMD =
      m_pModule->getNamedMetadata(kDxilEntryPointsMDName);
  IFTBOOL(pEntryPointsNamedMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  for (size_t i = 0; i < MDEntries.size(); i++) {
    pEntryPointsNamedMD->setOperand(i, MDEntries[i]);
  }
}

const NamedMDNode *DxilMDHelper::GetDxilEntryPoints() {
  NamedMDNode *pEntryPointsNamedMD =
      m_pModule->getNamedMetadata(kDxilEntryPointsMDName);
  IFTBOOL(pEntryPointsNamedMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  return pEntryPointsNamedMD;
}

MDTuple *DxilMDHelper::EmitDxilEntryPointTuple(Function *pFunc,
                                               const string &Name,
                                               MDTuple *pSignatures,
                                               MDTuple *pResources,
                                               MDTuple *pProperties) {
  Metadata *MDVals[kDxilEntryPointNumFields];
  MDVals[kDxilEntryPointFunction] =
      pFunc ? ValueAsMetadata::get(pFunc) : nullptr;
  MDVals[kDxilEntryPointName] = MDString::get(m_Ctx, Name.c_str());
  MDVals[kDxilEntryPointSignatures] = pSignatures;
  MDVals[kDxilEntryPointResources] = pResources;
  MDVals[kDxilEntryPointProperties] = pProperties;
  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::GetDxilEntryPoint(const MDNode *MDO, Function *&pFunc,
                                     string &Name,
                                     const MDOperand *&pSignatures,
                                     const MDOperand *&pResources,
                                     const MDOperand *&pProperties) {
  IFTBOOL(MDO != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO);
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilEntryPointNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  // Retrieve entry function symbol.
  const MDOperand &MDOFunc = pTupleMD->getOperand(kDxilEntryPointFunction);
  if (MDOFunc.get() != nullptr) {
    ValueAsMetadata *pValueFunc = dyn_cast<ValueAsMetadata>(MDOFunc.get());
    IFTBOOL(pValueFunc != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    pFunc = dyn_cast<Function>(pValueFunc->getValue());
    IFTBOOL(pFunc != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  } else {
    pFunc = nullptr; // pass-through CP.
  }

  // Retrieve entry function name.
  const MDOperand &MDOName = pTupleMD->getOperand(kDxilEntryPointName);
  IFTBOOL(MDOName.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  MDString *pMDName = dyn_cast<MDString>(MDOName);
  IFTBOOL(pMDName != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  Name = pMDName->getString().str();

  pSignatures = &pTupleMD->getOperand(kDxilEntryPointSignatures);
  pResources = &pTupleMD->getOperand(kDxilEntryPointResources);
  pProperties = &pTupleMD->getOperand(kDxilEntryPointProperties);
}

//
// Signatures.
//
MDTuple *DxilMDHelper::EmitDxilSignatures(const DxilEntrySignature &EntrySig) {
  MDTuple *pSignatureTupleMD = nullptr;

  const DxilSignature &InputSig = EntrySig.InputSignature;
  const DxilSignature &OutputSig = EntrySig.OutputSignature;
  const DxilSignature &PCPSig = EntrySig.PatchConstOrPrimSignature;

  if (!InputSig.GetElements().empty() || !OutputSig.GetElements().empty() ||
      !PCPSig.GetElements().empty()) {
    Metadata *MDVals[kDxilNumSignatureFields];
    MDVals[kDxilInputSignature] = EmitSignatureMetadata(InputSig);
    MDVals[kDxilOutputSignature] = EmitSignatureMetadata(OutputSig);
    MDVals[kDxilPatchConstantSignature] = EmitSignatureMetadata(PCPSig);

    pSignatureTupleMD = MDNode::get(m_Ctx, MDVals);
  }

  return pSignatureTupleMD;
}

void DxilMDHelper::EmitRootSignature(
    std::vector<uint8_t> &SerializedRootSignature) {
  if (SerializedRootSignature.empty()) {
    return;
  }

  MDNode *Node = EmitSerializedRootSignature(SerializedRootSignature, m_Ctx);

  NamedMDNode *pRootSignatureNamedMD =
      m_pModule->getNamedMetadata(kDxilRootSignatureMDName);
  IFTBOOL(pRootSignatureNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pRootSignatureNamedMD =
      m_pModule->getOrInsertNamedMetadata(kDxilRootSignatureMDName);
  pRootSignatureNamedMD->addOperand(Node);
  return;
}

void DxilMDHelper::LoadDxilSignatures(const MDOperand &MDO,
                                      DxilEntrySignature &EntrySig) {
  if (MDO.get() == nullptr)
    return;
  DxilSignature &InputSig = EntrySig.InputSignature;
  DxilSignature &OutputSig = EntrySig.OutputSignature;
  DxilSignature &PCPSig = EntrySig.PatchConstOrPrimSignature;
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilNumSignatureFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  LoadSignatureMetadata(pTupleMD->getOperand(kDxilInputSignature), InputSig);
  LoadSignatureMetadata(pTupleMD->getOperand(kDxilOutputSignature), OutputSig);
  LoadSignatureMetadata(pTupleMD->getOperand(kDxilPatchConstantSignature),
                        PCPSig);
}

MDTuple *DxilMDHelper::EmitSignatureMetadata(const DxilSignature &Sig) {
  auto &Elements = Sig.GetElements();
  if (Elements.empty())
    return nullptr;

  vector<Metadata *> MDVals;
  for (size_t i = 0; i < Elements.size(); i++) {
    MDVals.emplace_back(EmitSignatureElement(*Elements[i]));
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadSignatureMetadata(const MDOperand &MDO,
                                         DxilSignature &Sig) {
  if (MDO.get() == nullptr)
    return;

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i++) {
    unique_ptr<DxilSignatureElement> pSE(Sig.CreateElement());
    LoadSignatureElement(pTupleMD->getOperand(i), *pSE.get());
    Sig.AppendElement(std::move(pSE));
  }
}

void DxilMDHelper::LoadRootSignature(
    std::vector<uint8_t> &SerializedRootSignature) {
  NamedMDNode *pRootSignatureNamedMD =
      m_pModule->getNamedMetadata(kDxilRootSignatureMDName);
  if (!pRootSignatureNamedMD)
    return;

  IFTBOOL(pRootSignatureNamedMD->getNumOperands() == 1,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pNode = pRootSignatureNamedMD->getOperand(0);
  LoadSerializedRootSignature(pNode, SerializedRootSignature, m_Ctx);
}

static const MDTuple *CastToTupleOrNull(const MDOperand &MDO) {
  if (MDO.get() == nullptr)
    return nullptr;

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pTupleMD;
}

MDTuple *DxilMDHelper::EmitSignatureElement(const DxilSignatureElement &SE) {
  Metadata *MDVals[kDxilSignatureElementNumFields];

  MDVals[kDxilSignatureElementID] = Uint32ToConstMD(SE.GetID());
  MDVals[kDxilSignatureElementName] = MDString::get(m_Ctx, SE.GetName());
  MDVals[kDxilSignatureElementType] =
      Uint8ToConstMD((uint8_t)SE.GetCompType().GetKind());
  MDVals[kDxilSignatureElementSystemValue] =
      Uint8ToConstMD((uint8_t)SE.GetKind());
  MDVals[kDxilSignatureElementIndexVector] =
      Uint32VectorToConstMDTuple(SE.GetSemanticIndexVec());
  MDVals[kDxilSignatureElementInterpMode] =
      Uint8ToConstMD((uint8_t)SE.GetInterpolationMode()->GetKind());
  MDVals[kDxilSignatureElementRows] = Uint32ToConstMD(SE.GetRows());
  MDVals[kDxilSignatureElementCols] = Uint8ToConstMD((uint8_t)SE.GetCols());
  MDVals[kDxilSignatureElementStartRow] = Int32ToConstMD(SE.GetStartRow());
  MDVals[kDxilSignatureElementStartCol] =
      Int8ToConstMD((int8_t)SE.GetStartCol());

  // Name-value list of extended properties.
  MDVals[kDxilSignatureElementNameValueList] = nullptr;
  vector<Metadata *> MDExtraVals;
  m_ExtraPropertyHelper->EmitSignatureElementProperties(SE, MDExtraVals);
  if (!MDExtraVals.empty()) {
    MDVals[kDxilSignatureElementNameValueList] =
        MDNode::get(m_Ctx, MDExtraVals);
  }

  // NOTE: when extra properties for signature elements are needed, extend
  // ExtraPropertyHelper.

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadSignatureElement(const MDOperand &MDO,
                                        DxilSignatureElement &SE) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilSignatureElementNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  unsigned ID = ConstMDToUint32(pTupleMD->getOperand(kDxilSignatureElementID));
  MDString *pName =
      dyn_cast<MDString>(pTupleMD->getOperand(kDxilSignatureElementName));
  CompType CT =
      CompType(ConstMDToUint8(pTupleMD->getOperand(kDxilSignatureElementType)));
  DXIL::SemanticKind SemKind = (DXIL::SemanticKind)ConstMDToUint8(
      pTupleMD->getOperand(kDxilSignatureElementSystemValue));
  MDTuple *pSemanticIndexVectorMD =
      dyn_cast<MDTuple>(pTupleMD->getOperand(kDxilSignatureElementIndexVector));
  InterpolationMode IM(
      ConstMDToUint8(pTupleMD->getOperand(kDxilSignatureElementInterpMode)));
  unsigned NumRows =
      ConstMDToUint32(pTupleMD->getOperand(kDxilSignatureElementRows));
  uint8_t NumCols =
      ConstMDToUint8(pTupleMD->getOperand(kDxilSignatureElementCols));
  int32_t StartRow =
      ConstMDToInt32(pTupleMD->getOperand(kDxilSignatureElementStartRow));
  int8_t StartCol =
      ConstMDToInt8(pTupleMD->getOperand(kDxilSignatureElementStartCol));

  IFTBOOL(pName != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pSemanticIndexVectorMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  vector<unsigned> SemanticIndexVector;
  ConstMDTupleToUint32Vector(pSemanticIndexVectorMD, SemanticIndexVector);

  SE.Initialize(pName->getString(), CT, IM, NumRows, NumCols, StartRow,
                StartCol, ID, SemanticIndexVector);
  SE.SetKind(SemKind);

  // For case a system semantic don't have index, add 0 for it.
  if (SemanticIndexVector.empty() && !SE.IsArbitrary()) {
    SE.SetSemanticIndexVec({0});
  }
  // Name-value list of extended properties.
  m_ExtraPropertyHelper->m_bExtraMetadata = false;
  m_ExtraPropertyHelper->LoadSignatureElementProperties(
      pTupleMD->getOperand(kDxilSignatureElementNameValueList), SE);
  m_bExtraMetadata |= m_ExtraPropertyHelper->m_bExtraMetadata;
}

//
// Resources.
//
MDTuple *DxilMDHelper::EmitDxilResourceTuple(MDTuple *pSRVs, MDTuple *pUAVs,
                                             MDTuple *pCBuffers,
                                             MDTuple *pSamplers) {
  DXASSERT(pSRVs != nullptr || pUAVs != nullptr || pCBuffers != nullptr ||
               pSamplers != nullptr,
           "resource tuple should not be emitted if there are no resources");
  Metadata *MDVals[kDxilNumResourceFields];
  MDVals[kDxilResourceSRVs] = pSRVs;
  MDVals[kDxilResourceUAVs] = pUAVs;
  MDVals[kDxilResourceCBuffers] = pCBuffers;
  MDVals[kDxilResourceSamplers] = pSamplers;
  MDTuple *pTupleMD = MDNode::get(m_Ctx, MDVals);

  return pTupleMD;
}

void DxilMDHelper::EmitDxilResources(llvm::MDTuple *pDxilResourceTuple) {
  NamedMDNode *pResourcesNamedMD =
      m_pModule->getNamedMetadata(kDxilResourcesMDName);
  IFTBOOL(pResourcesNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pResourcesNamedMD = m_pModule->getOrInsertNamedMetadata(kDxilResourcesMDName);
  pResourcesNamedMD->addOperand(pDxilResourceTuple);
}

void DxilMDHelper::UpdateDxilResources(llvm::MDTuple *pDxilResourceTuple) {
  NamedMDNode *pResourcesNamedMD =
      m_pModule->getNamedMetadata(kDxilResourcesMDName);
  if (!pResourcesNamedMD) {
    pResourcesNamedMD =
        m_pModule->getOrInsertNamedMetadata(kDxilResourcesMDName);
  }
  if (pDxilResourceTuple) {
    if (pResourcesNamedMD->getNumOperands() != 0) {
      pResourcesNamedMD->setOperand(0, pDxilResourceTuple);
    } else {
      pResourcesNamedMD->addOperand(pDxilResourceTuple);
    }

  } else {
    m_pModule->eraseNamedMetadata(pResourcesNamedMD);
  }
}

void DxilMDHelper::GetDxilResources(const MDOperand &MDO, const MDTuple *&pSRVs,
                                    const MDTuple *&pUAVs,
                                    const MDTuple *&pCBuffers,
                                    const MDTuple *&pSamplers) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilNumResourceFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  pSRVs = CastToTupleOrNull(pTupleMD->getOperand(kDxilResourceSRVs));
  pUAVs = CastToTupleOrNull(pTupleMD->getOperand(kDxilResourceUAVs));
  pCBuffers = CastToTupleOrNull(pTupleMD->getOperand(kDxilResourceCBuffers));
  pSamplers = CastToTupleOrNull(pTupleMD->getOperand(kDxilResourceSamplers));
}

void DxilMDHelper::EmitDxilResourceBase(const DxilResourceBase &R,
                                        Metadata *ppMDVals[]) {
  ppMDVals[kDxilResourceBaseID] = Uint32ToConstMD(R.GetID());
  Constant *GlobalSymbol = R.GetGlobalSymbol();
  // For sm66+, global symbol will be mutated into handle type.
  // Save hlsl type by generate bitcast on global symbol.
  // For sm65-, global symbol will be undef value which saves the HLSLTy
  // directly.
  Type *HLSLTy = R.GetHLSLType();
  if (HLSLTy && HLSLTy != GlobalSymbol->getType()) {
    if (isa<UndefValue>(GlobalSymbol))
      GlobalSymbol = UndefValue::get(HLSLTy);
    else if (m_pSM->IsSM66Plus())
      GlobalSymbol = cast<Constant>(
          ConstantExpr::getCast(Instruction::BitCast, GlobalSymbol, HLSLTy));
  }
  ppMDVals[kDxilResourceBaseVariable] = ValueAsMetadata::get(GlobalSymbol);
  ppMDVals[kDxilResourceBaseName] = MDString::get(m_Ctx, R.GetGlobalName());
  ppMDVals[kDxilResourceBaseSpaceID] = Uint32ToConstMD(R.GetSpaceID());
  ppMDVals[kDxilResourceBaseLowerBound] = Uint32ToConstMD(R.GetLowerBound());
  ppMDVals[kDxilResourceBaseRangeSize] = Uint32ToConstMD(R.GetRangeSize());
}

void DxilMDHelper::LoadDxilResourceBase(const MDOperand &MDO,
                                        DxilResourceBase &R) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() >= kDxilResourceBaseNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  R.SetID(ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseID)));
  Constant *GlobalSymbol = dyn_cast<Constant>(
      ValueMDToValue(pTupleMD->getOperand(kDxilResourceBaseVariable)));
  // For sm66+, global symbol will be mutated into handle type.
  // Read hlsl type and global symbol from bitcast.
  if (m_pSM->IsSM66Plus()) {
    // Before mutate, there's no bitcast. After GlobalSymbol changed into undef,
    // there's no bitcast too. Bitcast will only exist when global symbol is
    // mutated into handle and not changed into undef for lib linking.
    if (BitCastOperator *BCO = dyn_cast<BitCastOperator>(GlobalSymbol)) {
      GlobalSymbol = cast<Constant>(BCO->getOperand(0));
      R.SetHLSLType(BCO->getType());
    }
  }
  R.SetGlobalSymbol(GlobalSymbol);

  R.SetGlobalName(
      StringMDToString(pTupleMD->getOperand(kDxilResourceBaseName)));
  R.SetSpaceID(ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseSpaceID)));
  R.SetLowerBound(
      ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseLowerBound)));
  R.SetRangeSize(
      ConstMDToUint32(pTupleMD->getOperand(kDxilResourceBaseRangeSize)));
}

MDTuple *DxilMDHelper::EmitDxilSRV(const DxilResource &SRV) {
  Metadata *MDVals[kDxilSRVNumFields];

  EmitDxilResourceBase(SRV, &MDVals[0]);

  // SRV-specific fields.
  MDVals[kDxilSRVShape] = Uint32ToConstMD((unsigned)SRV.GetKind());
  MDVals[kDxilSRVSampleCount] = Uint32ToConstMD(SRV.GetSampleCount());

  // Name-value list of extended properties.
  MDVals[kDxilSRVNameValueList] = nullptr;
  vector<Metadata *> MDExtraVals;
  m_ExtraPropertyHelper->EmitSRVProperties(SRV, MDExtraVals);
  if (!MDExtraVals.empty()) {
    MDVals[kDxilSRVNameValueList] = MDNode::get(m_Ctx, MDExtraVals);
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilSRV(const MDOperand &MDO, DxilResource &SRV) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilSRVNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  SRV.SetRW(false);

  LoadDxilResourceBase(MDO, SRV);

  // SRV-specific fields.
  SRV.SetKind(
      (DxilResource::Kind)ConstMDToUint32(pTupleMD->getOperand(kDxilSRVShape)));
  SRV.SetSampleCount(
      ConstMDToUint32(pTupleMD->getOperand(kDxilSRVSampleCount)));

  // Name-value list of extended properties.
  m_ExtraPropertyHelper->m_bExtraMetadata = false;
  m_ExtraPropertyHelper->LoadSRVProperties(
      pTupleMD->getOperand(kDxilSRVNameValueList), SRV);
  m_bExtraMetadata |= m_ExtraPropertyHelper->m_bExtraMetadata;
}

MDTuple *DxilMDHelper::EmitDxilUAV(const DxilResource &UAV) {
  Metadata *MDVals[kDxilUAVNumFields];

  EmitDxilResourceBase(UAV, &MDVals[0]);

  // UAV-specific fields.
  MDVals[kDxilUAVShape] = Uint32ToConstMD((unsigned)UAV.GetKind());
  MDVals[kDxilUAVGloballyCoherent] = BoolToConstMD(UAV.IsGloballyCoherent());
  MDVals[kDxilUAVCounter] = BoolToConstMD(UAV.HasCounter());
  MDVals[kDxilUAVRasterizerOrderedView] = BoolToConstMD(UAV.IsROV());

  // Name-value list of extended properties.
  MDVals[kDxilUAVNameValueList] = nullptr;
  vector<Metadata *> MDExtraVals;
  m_ExtraPropertyHelper->EmitUAVProperties(UAV, MDExtraVals);
  if (!MDExtraVals.empty()) {
    MDVals[kDxilUAVNameValueList] = MDNode::get(m_Ctx, MDExtraVals);
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilUAV(const MDOperand &MDO, DxilResource &UAV) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilUAVNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  UAV.SetRW(true);

  LoadDxilResourceBase(MDO, UAV);

  // UAV-specific fields.
  UAV.SetKind(
      (DxilResource::Kind)ConstMDToUint32(pTupleMD->getOperand(kDxilUAVShape)));
  UAV.SetGloballyCoherent(
      ConstMDToBool(pTupleMD->getOperand(kDxilUAVGloballyCoherent)));
  UAV.SetHasCounter(ConstMDToBool(pTupleMD->getOperand(kDxilUAVCounter)));
  UAV.SetROV(
      ConstMDToBool(pTupleMD->getOperand(kDxilUAVRasterizerOrderedView)));

  // Name-value list of extended properties.
  m_ExtraPropertyHelper->m_bExtraMetadata = false;
  m_ExtraPropertyHelper->LoadUAVProperties(
      pTupleMD->getOperand(kDxilUAVNameValueList), UAV);
  m_bExtraMetadata |= m_ExtraPropertyHelper->m_bExtraMetadata;
}

MDTuple *DxilMDHelper::EmitDxilCBuffer(const DxilCBuffer &CB) {
  Metadata *MDVals[kDxilCBufferNumFields];

  EmitDxilResourceBase(CB, &MDVals[0]);

  // CBuffer-specific fields.
  // CBuffer size in bytes.
  MDVals[kDxilCBufferSizeInBytes] = Uint32ToConstMD(CB.GetSize());

  // Name-value list of extended properties.
  MDVals[kDxilCBufferNameValueList] = nullptr;
  vector<Metadata *> MDExtraVals;
  m_ExtraPropertyHelper->EmitCBufferProperties(CB, MDExtraVals);
  if (!MDExtraVals.empty()) {
    MDVals[kDxilCBufferNameValueList] = MDNode::get(m_Ctx, MDExtraVals);
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilCBuffer(const MDOperand &MDO, DxilCBuffer &CB) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilCBufferNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  LoadDxilResourceBase(MDO, CB);

  // CBuffer-specific fields.
  CB.SetSize(ConstMDToUint32(pTupleMD->getOperand(kDxilCBufferSizeInBytes)));

  // Name-value list of extended properties.
  m_ExtraPropertyHelper->m_bExtraMetadata = false;
  m_ExtraPropertyHelper->LoadCBufferProperties(
      pTupleMD->getOperand(kDxilCBufferNameValueList), CB);
  m_bExtraMetadata |= m_ExtraPropertyHelper->m_bExtraMetadata;
}

void DxilMDHelper::EmitDxilTypeSystem(DxilTypeSystem &TypeSystem,
                                      vector<GlobalVariable *> &LLVMUsed) {
  auto &TypeMap = TypeSystem.GetStructAnnotationMap();
  vector<Metadata *> MDVals;
  MDVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemStructTag)); // Tag
  unsigned GVIdx = 0;
  for (auto it = TypeMap.begin(); it != TypeMap.end(); ++it, GVIdx++) {
    StructType *pStructType = const_cast<StructType *>(it->first);
    DxilStructAnnotation *pA = it->second.get();
    // Don't emit type annotation for empty struct.
    if (pA->IsEmptyStruct())
      continue;
    // Emit struct type field annotations.
    Metadata *pMD = EmitDxilStructAnnotation(*pA);

    MDVals.push_back(ValueAsMetadata::get(UndefValue::get(pStructType)));
    MDVals.push_back(pMD);
  }

  auto &FuncMap = TypeSystem.GetFunctionAnnotationMap();
  vector<Metadata *> MDFuncVals;
  MDFuncVals.emplace_back(Uint32ToConstMD(kDxilTypeSystemFunctionTag)); // Tag
  for (auto it = FuncMap.begin(); it != FuncMap.end(); ++it) {
    DxilFunctionAnnotation *pA = it->second.get();
    MDFuncVals.push_back(
        ValueAsMetadata::get(const_cast<Function *>(pA->GetFunction())));
    // Emit function annotations.

    Metadata *pMD;
    pMD = EmitDxilFunctionAnnotation(*pA);
    MDFuncVals.push_back(pMD);
  }

  NamedMDNode *pDxilTypeAnnotationsMD =
      m_pModule->getNamedMetadata(kDxilTypeSystemMDName);
  if (pDxilTypeAnnotationsMD != nullptr) {
    m_pModule->eraseNamedMetadata(pDxilTypeAnnotationsMD);
  }

  if (MDVals.size() > 1) {
    pDxilTypeAnnotationsMD =
        m_pModule->getOrInsertNamedMetadata(kDxilTypeSystemMDName);

    pDxilTypeAnnotationsMD->addOperand(MDNode::get(m_Ctx, MDVals));
  }
  if (MDFuncVals.size() > 1) {
    NamedMDNode *pDxilTypeAnnotationsMD =
        m_pModule->getNamedMetadata(kDxilTypeSystemMDName);
    if (pDxilTypeAnnotationsMD == nullptr)
      pDxilTypeAnnotationsMD =
          m_pModule->getOrInsertNamedMetadata(kDxilTypeSystemMDName);

    pDxilTypeAnnotationsMD->addOperand(MDNode::get(m_Ctx, MDFuncVals));
  }
}

void DxilMDHelper::LoadDxilTypeSystemNode(const llvm::MDTuple &MDT,
                                          DxilTypeSystem &TypeSystem) {

  unsigned Tag = ConstMDToUint32(MDT.getOperand(0));
  if (Tag == kDxilTypeSystemStructTag) {
    IFTBOOL((MDT.getNumOperands() & 0x1) == 1, DXC_E_INCORRECT_DXIL_METADATA);

    for (unsigned i = 1; i < MDT.getNumOperands(); i += 2) {
      Constant *pGV = dyn_cast<Constant>(ValueMDToValue(MDT.getOperand(i)));
      IFTBOOL(pGV != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
      StructType *pGVType = dyn_cast<StructType>(pGV->getType());
      IFTBOOL(pGVType != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

      DxilStructAnnotation *pSA = TypeSystem.AddStructAnnotation(pGVType);
      LoadDxilStructAnnotation(MDT.getOperand(i + 1), *pSA);
      TypeSystem.FinishStructAnnotation(*pSA);
    }
  } else {
    IFTBOOL((MDT.getNumOperands() & 0x1) == 1, DXC_E_INCORRECT_DXIL_METADATA);
    for (unsigned i = 1; i < MDT.getNumOperands(); i += 2) {
      Function *F = dyn_cast<Function>(ValueMDToValue(MDT.getOperand(i)));
      DxilFunctionAnnotation *pFA = TypeSystem.AddFunctionAnnotation(F);
      LoadDxilFunctionAnnotation(MDT.getOperand(i + 1), *pFA);
      TypeSystem.FinishFunctionAnnotation(*pFA);
    }
  }
}

void DxilMDHelper::LoadDxilTypeSystem(DxilTypeSystem &TypeSystem) {
  NamedMDNode *pDxilTypeAnnotationsMD =
      m_pModule->getNamedMetadata(kDxilTypeSystemMDName);
  if (pDxilTypeAnnotationsMD == nullptr)
    return;

  IFTBOOL(pDxilTypeAnnotationsMD->getNumOperands() <= 2,
          DXC_E_INCORRECT_DXIL_METADATA);
  for (unsigned i = 0; i < pDxilTypeAnnotationsMD->getNumOperands(); i++) {
    const MDTuple *pTupleMD =
        dyn_cast<MDTuple>(pDxilTypeAnnotationsMD->getOperand(i));
    IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    LoadDxilTypeSystemNode(*pTupleMD, TypeSystem);
  }
}

void DxilMDHelper::EmitDxrPayloadAnnotations(DxilTypeSystem &TypeSystem) {
  auto &TypeMap = TypeSystem.GetPayloadAnnotationMap();
  vector<Metadata *> MDVals;
  MDVals.emplace_back(Uint32ToConstMD(kDxilPayloadAnnotationStructTag)); // Tag
  unsigned GVIdx = 0;
  for (auto it = TypeMap.begin(); it != TypeMap.end(); ++it, GVIdx++) {
    StructType *pStructType = const_cast<StructType *>(it->first);
    DxilPayloadAnnotation *pA = it->second.get();
    // Emit struct type field annotations.
    Metadata *pMD = EmitDxrPayloadStructAnnotation(*pA);

    MDVals.push_back(ValueAsMetadata::get(UndefValue::get(pStructType)));
    MDVals.push_back(pMD);
  }

  NamedMDNode *pDxrPayloadAnnotationsMD =
      m_pModule->getNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
  if (pDxrPayloadAnnotationsMD != nullptr) {
    m_pModule->eraseNamedMetadata(pDxrPayloadAnnotationsMD);
  }

  if (MDVals.size() > 1) {
    pDxrPayloadAnnotationsMD =
        m_pModule->getOrInsertNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
    pDxrPayloadAnnotationsMD->addOperand(MDNode::get(m_Ctx, MDVals));
  }
}

Metadata *
DxilMDHelper::EmitDxrPayloadStructAnnotation(const DxilPayloadAnnotation &SA) {
  vector<Metadata *> MDVals;
  MDVals.reserve(SA.GetNumFields());
  MDVals.resize(SA.GetNumFields());

  const StructType *STy = SA.GetStructType();
  for (unsigned i = 0; i < SA.GetNumFields(); i++) {
    MDVals[i] = EmitDxrPayloadFieldAnnotation(SA.GetFieldAnnotation(i),
                                              STy->getElementType(i));
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxrPayloadAccessQualifiers(
    const MDOperand &MDO, DxilPayloadFieldAnnotation &FA) {
  unsigned fieldBitmask = ConstMDToInt32(MDO);
  if (fieldBitmask & ~DXIL::PayloadAccessQualifierValidMask) {
    DXASSERT(false, "Unknown payload access qualifier bits set");
    m_bExtraMetadata = true;
  }
  fieldBitmask &= DXIL::PayloadAccessQualifierValidMask;
  FA.SetPayloadFieldQualifierMask(fieldBitmask);
}

void DxilMDHelper::LoadDxrPayloadFieldAnnoation(
    const MDOperand &MDO, DxilPayloadFieldAnnotation &FA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get()); // Tag-Value list.
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);
    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

    switch (Tag) {
    case kDxilPayloadFieldAnnotationAccessTag:
      LoadDxrPayloadAccessQualifiers(MDO, FA);
      break;
    default:
      DXASSERT(false, "Unknown payload field annotation tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

void DxilMDHelper::LoadDxrPayloadFieldAnnoations(const MDOperand &MDO,
                                                 DxilPayloadAnnotation &SA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == SA.GetNumFields(),
          DXC_E_INCORRECT_DXIL_METADATA);
  for (unsigned i = 0; i < SA.GetNumFields(); ++i) {
    LoadDxrPayloadFieldAnnoation(pTupleMD->getOperand(i),
                                 SA.GetFieldAnnotation(i));
  }
}

void DxilMDHelper::LoadDxrPayloadAnnotationNode(const llvm::MDTuple &MDT,
                                                DxilTypeSystem &TypeSystem) {
  unsigned Tag = ConstMDToUint32(MDT.getOperand(0));
  IFTBOOL(Tag == kDxilPayloadAnnotationStructTag, DXC_E_INCORRECT_DXIL_METADATA)
  IFTBOOL((MDT.getNumOperands() & 0x1) == 1, DXC_E_INCORRECT_DXIL_METADATA);

  Constant *pGV = dyn_cast<Constant>(ValueMDToValue(MDT.getOperand(1)));
  IFTBOOL(pGV != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  StructType *pGVType = dyn_cast<StructType>(pGV->getType());
  IFTBOOL(pGVType != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  // Check if this struct is already part of the DXIL Type System
  DxilPayloadAnnotation *pPA = TypeSystem.AddPayloadAnnotation(pGVType);

  LoadDxrPayloadFieldAnnoations(MDT.getOperand(2), *pPA);
}

void DxilMDHelper::LoadDxrPayloadAnnotations(DxilTypeSystem &TypeSystem) {
  NamedMDNode *pDxilPayloadAnnotationsMD =
      m_pModule->getNamedMetadata(kDxilDxrPayloadAnnotationsMDName);
  if (pDxilPayloadAnnotationsMD == nullptr)
    return;

  if (DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 6) < 0) {
    DXASSERT(false, "payload access qualifier emitted for dxil version < 1.6");
    m_bExtraMetadata = true;
  }
  DXASSERT(pDxilPayloadAnnotationsMD->getNumOperands() != 0,
           "empty metadata node?");

  for (unsigned i = 0; i < pDxilPayloadAnnotationsMD->getNumOperands(); i++) {
    const MDTuple *pTupleMD =
        dyn_cast<MDTuple>(pDxilPayloadAnnotationsMD->getOperand(i));
    IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    LoadDxrPayloadAnnotationNode(*pTupleMD, TypeSystem);
  }
}

Metadata *DxilMDHelper::EmitDxilTemplateArgAnnotation(
    const DxilTemplateArgAnnotation &annotation) {
  SmallVector<Metadata *, 2> MDVals;
  if (annotation.IsType()) {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilTemplateArgTypeTag));
    MDVals.emplace_back(ValueAsMetadata::get(
        UndefValue::get(const_cast<Type *>(annotation.GetType()))));
  } else if (annotation.IsIntegral()) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilTemplateArgIntegralTag));
    MDVals.emplace_back(Uint64ToConstMD((uint64_t)annotation.GetIntegral()));
  }
  return MDNode::get(m_Ctx, MDVals);
}
void DxilMDHelper::LoadDxilTemplateArgAnnotation(
    const llvm::MDOperand &MDO, DxilTemplateArgAnnotation &annotation) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() >= 1, DXC_E_INCORRECT_DXIL_METADATA);
  unsigned Tag = ConstMDToUint32(pTupleMD->getOperand(0));
  switch (Tag) {
  case kDxilTemplateArgTypeTag: {
    IFTBOOL(pTupleMD->getNumOperands() == 2, DXC_E_INCORRECT_DXIL_METADATA);
    Constant *C = dyn_cast<Constant>(
        ValueMDToValue(pTupleMD->getOperand(kDxilTemplateArgValue)));
    IFTBOOL(C != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    annotation.SetType(C->getType());
  } break;
  case kDxilTemplateArgIntegralTag:
    IFTBOOL(pTupleMD->getNumOperands() == 2, DXC_E_INCORRECT_DXIL_METADATA);
    annotation.SetIntegral(
        (int64_t)ConstMDToUint64(pTupleMD->getOperand(kDxilTemplateArgValue)));
    break;
  default:
    DXASSERT(false, "Unknown template argument type tag.");
    m_bExtraMetadata = true;
    break;
  }
}

Metadata *
DxilMDHelper::EmitDxilStructAnnotation(const DxilStructAnnotation &SA) {
  bool bSupportExtended =
      DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 5) >= 0;

  vector<Metadata *> MDVals;
  MDVals.reserve(SA.GetNumFields() +
                 2); // In case of extended 1.5 property list
  MDVals.resize(SA.GetNumFields() + 1);

  MDVals[0] = Uint32ToConstMD(SA.GetCBufferSize());
  for (unsigned i = 0; i < SA.GetNumFields(); i++) {
    MDVals[i + 1] = EmitDxilFieldAnnotation(SA.GetFieldAnnotation(i));
  }

  // Only add template args if shader target requires validator version that
  // supports them.
  if (bSupportExtended && SA.GetNumTemplateArgs()) {
    vector<Metadata *> MDTemplateArgs(SA.GetNumTemplateArgs());
    for (unsigned i = 0; i < SA.GetNumTemplateArgs(); ++i) {
      MDTemplateArgs[i] =
          EmitDxilTemplateArgAnnotation(SA.GetTemplateArgAnnotation(i));
    }
    SmallVector<Metadata *, 2> MDExtraVals;
    MDExtraVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilTemplateArgumentsTag));
    MDExtraVals.emplace_back(MDNode::get(m_Ctx, MDTemplateArgs));
    MDVals.emplace_back(MDNode::get(m_Ctx, MDExtraVals));
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilStructAnnotation(const MDOperand &MDO,
                                            DxilStructAnnotation &SA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  if (pTupleMD->getNumOperands() == 1) {
    SA.MarkEmptyStruct();
  }
  if (pTupleMD->getNumOperands() == SA.GetNumFields() + 2) {
    DXASSERT(DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 5) >= 0,
             "otherwise, template annotation emitted for dxil version < 1.5");
    // Load template args from extended operand
    const MDOperand &MDOExtra = pTupleMD->getOperand(SA.GetNumFields() + 1);
    const MDTuple *pTupleMDExtra = dyn_cast_or_null<MDTuple>(MDOExtra.get());
    if (pTupleMDExtra) {
      for (unsigned i = 0; i < pTupleMDExtra->getNumOperands(); i += 2) {
        unsigned Tag = ConstMDToUint32(pTupleMDExtra->getOperand(i));
        const MDOperand &MDO = pTupleMDExtra->getOperand(i + 1);
        IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

        switch (Tag) {
        case kDxilTemplateArgumentsTag: {
          const MDTuple *pTupleTemplateArgs =
              dyn_cast_or_null<MDTuple>(pTupleMDExtra->getOperand(1).get());
          IFTBOOL(pTupleTemplateArgs, DXC_E_INCORRECT_DXIL_METADATA);
          SA.SetNumTemplateArgs(pTupleTemplateArgs->getNumOperands());
          for (unsigned i = 0; i < pTupleTemplateArgs->getNumOperands(); ++i) {
            LoadDxilTemplateArgAnnotation(pTupleTemplateArgs->getOperand(i),
                                          SA.GetTemplateArgAnnotation(i));
          }
        } break;
        default:
          DXASSERT(false, "unknown extended tag for struct annotation.");
          m_bExtraMetadata = true;
          break;
        }
      }
    }
  } else {
    IFTBOOL(pTupleMD->getNumOperands() == SA.GetNumFields() + 1,
            DXC_E_INCORRECT_DXIL_METADATA);
  }

  SA.SetCBufferSize(ConstMDToUint32(pTupleMD->getOperand(0)));
  for (unsigned i = 0; i < SA.GetNumFields(); i++) {
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);
    DxilFieldAnnotation &FA = SA.GetFieldAnnotation(i);
    LoadDxilFieldAnnotation(MDO, FA);
  }
}

Metadata *
DxilMDHelper::EmitDxilFunctionAnnotation(const DxilFunctionAnnotation &FA) {
  return EmitDxilParamAnnotations(FA);
}

void DxilMDHelper::LoadDxilFunctionAnnotation(const MDOperand &MDO,
                                              DxilFunctionAnnotation &FA) {
  LoadDxilParamAnnotations(MDO, FA);
}

llvm::Metadata *
DxilMDHelper::EmitDxilParamAnnotations(const DxilFunctionAnnotation &FA) {
  vector<Metadata *> MDParamAnnotations(FA.GetNumParameters() + 1);
  MDParamAnnotations[0] = EmitDxilParamAnnotation(FA.GetRetTypeAnnotation());
  for (unsigned i = 0; i < FA.GetNumParameters(); i++) {
    MDParamAnnotations[i + 1] =
        EmitDxilParamAnnotation(FA.GetParameterAnnotation(i));
  }
  return MDNode::get(m_Ctx, MDParamAnnotations);
}

void DxilMDHelper::LoadDxilParamAnnotations(const llvm::MDOperand &MDO,
                                            DxilFunctionAnnotation &FA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD->getNumOperands() == FA.GetNumParameters() + 1,
          DXC_E_INCORRECT_DXIL_METADATA);
  DxilParameterAnnotation &retTyAnnotation = FA.GetRetTypeAnnotation();
  LoadDxilParamAnnotation(pTupleMD->getOperand(0), retTyAnnotation);
  for (unsigned i = 0; i < FA.GetNumParameters(); i++) {
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);
    DxilParameterAnnotation &PA = FA.GetParameterAnnotation(i);
    LoadDxilParamAnnotation(MDO, PA);
  }
}

Metadata *
DxilMDHelper::EmitDxilParamAnnotation(const DxilParameterAnnotation &PA) {
  vector<Metadata *> MDVals(3);
  MDVals[0] = Uint32ToConstMD(static_cast<unsigned>(PA.GetParamInputQual()));
  MDVals[1] = EmitDxilFieldAnnotation(PA);
  MDVals[2] = Uint32VectorToConstMDTuple(PA.GetSemanticIndexVec());

  return MDNode::get(m_Ctx, MDVals);
}
void DxilMDHelper::LoadDxilParamAnnotation(const MDOperand &MDO,
                                           DxilParameterAnnotation &PA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == 3, DXC_E_INCORRECT_DXIL_METADATA);
  PA.SetParamInputQual(static_cast<DxilParamInputQual>(
      ConstMDToUint32(pTupleMD->getOperand(0))));
  LoadDxilFieldAnnotation(pTupleMD->getOperand(1), PA);
  MDTuple *pSemanticIndexVectorMD = dyn_cast<MDTuple>(pTupleMD->getOperand(2));
  vector<unsigned> SemanticIndexVector;
  ConstMDTupleToUint32Vector(pSemanticIndexVectorMD, SemanticIndexVector);
  PA.SetSemanticIndexVec(SemanticIndexVector);
}

Metadata *DxilMDHelper::EmitDxilFieldAnnotation(const DxilFieldAnnotation &FA) {
  vector<Metadata *> MDVals; // Tag-Value list.

  if (FA.HasFieldName()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationFieldNameTag));
    MDVals.emplace_back(MDString::get(m_Ctx, FA.GetFieldName()));
  }
  if (FA.IsPrecise()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationPreciseTag)); // Tag
    MDVals.emplace_back(BoolToConstMD(true)); // Value
  }
  if (FA.HasMatrixAnnotation()) {
    const DxilMatrixAnnotation &MA = FA.GetMatrixAnnotation();
    Metadata *MatrixMD[3];
    MatrixMD[0] = Uint32ToConstMD(MA.Rows);
    MatrixMD[1] = Uint32ToConstMD(MA.Cols);
    MatrixMD[2] = Uint32ToConstMD((unsigned)MA.Orientation);

    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationMatrixTag));
    MDVals.emplace_back(MDNode::get(m_Ctx, MatrixMD));
  }
  if (FA.HasCBufferOffset()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationCBufferOffsetTag));
    MDVals.emplace_back(Uint32ToConstMD(FA.GetCBufferOffset()));
  }
  if (FA.HasSemanticString()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationSemanticStringTag));
    MDVals.emplace_back(MDString::get(m_Ctx, FA.GetSemanticString()));
  }
  if (FA.HasInterpolationMode()) {
    MDVals.emplace_back(
        Uint32ToConstMD(kDxilFieldAnnotationInterpolationModeTag));
    MDVals.emplace_back(
        Uint32ToConstMD((unsigned)FA.GetInterpolationMode().GetKind()));
  }
  if (FA.HasCompType()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationCompTypeTag));
    MDVals.emplace_back(Uint32ToConstMD((unsigned)FA.GetCompType().GetKind()));
  }
  if (FA.IsCBVarUsed() &&
      DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 5) >= 0) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationCBUsedTag));
    MDVals.emplace_back(BoolToConstMD(true));
  }
  if (FA.HasResourceProperties() &&
      DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 8) >= 0) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationResPropTag));
    MDVals.emplace_back(ValueAsMetadata::get(resource_helper::getAsConstant(
        FA.GetResourceProperties(),
        m_pModule->GetDxilModule().GetOP()->GetResourcePropertiesType(),
        *m_pSM)));
  }
  if (DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 7) >= 0) {
    if (FA.HasBitFields()) {
      const std::vector<DxilFieldAnnotation> &BitFields = FA.GetBitFields();
      vector<Metadata *> MDBitFieldVals;
      for (const DxilFieldAnnotation &BitField : BitFields) {
        MDBitFieldVals.emplace_back(EmitDxilFieldAnnotation(BitField));
      }
      auto *BitFieldsMD = MDNode::get(m_Ctx, MDBitFieldVals);
      MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationBitFieldsTag));
      MDVals.emplace_back(BitFieldsMD);
    }
    if (FA.HasBitFieldWidth()) {
      MDVals.emplace_back(
          Uint32ToConstMD(kDxilFieldAnnotationBitFieldWidthTag));
      MDVals.emplace_back(Uint32ToConstMD(FA.GetBitFieldWidth()));
    }
  }

  if (FA.GetVectorSize() &&
      DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 8) >= 0) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilFieldAnnotationVectorSizeTag));
    MDVals.emplace_back(Uint32ToConstMD(FA.GetVectorSize()));
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilFieldAnnotation(const MDOperand &MDO,
                                           DxilFieldAnnotation &FA) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);
    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

    switch (Tag) {
    case kDxilFieldAnnotationPreciseTag:
      FA.SetPrecise(ConstMDToBool(MDO));
      break;
    case kDxilFieldAnnotationMatrixTag: {
      DxilMatrixAnnotation MA;
      const MDTuple *pMATupleMD = dyn_cast<MDTuple>(MDO.get());
      IFTBOOL(pMATupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
      IFTBOOL(pMATupleMD->getNumOperands() == 3, DXC_E_INCORRECT_DXIL_METADATA);
      MA.Rows = ConstMDToUint32(pMATupleMD->getOperand(0));
      MA.Cols = ConstMDToUint32(pMATupleMD->getOperand(1));
      MA.Orientation =
          (MatrixOrientation)ConstMDToUint32(pMATupleMD->getOperand(2));
      FA.SetMatrixAnnotation(MA);
    } break;
    case kDxilFieldAnnotationCBufferOffsetTag:
      FA.SetCBufferOffset(ConstMDToUint32(MDO));
      break;
    case kDxilFieldAnnotationSemanticStringTag:
      FA.SetSemanticString(StringMDToString(MDO));
      break;
    case kDxilFieldAnnotationInterpolationModeTag:
      FA.SetInterpolationMode(
          InterpolationMode((InterpolationMode::Kind)ConstMDToUint32(MDO)));
      break;
    case kDxilFieldAnnotationFieldNameTag:
      FA.SetFieldName(StringMDToString(MDO));
      break;
    case kDxilFieldAnnotationCompTypeTag:
      FA.SetCompType((CompType::Kind)ConstMDToUint32(MDO));
      break;
    case kDxilFieldAnnotationCBUsedTag:
      FA.SetCBVarUsed(ConstMDToBool(MDO));
      break;
    case kDxilFieldAnnotationResPropTag:
      if (Constant *C = dyn_cast_or_null<Constant>(
              dyn_cast<ValueAsMetadata>(MDO)->getValue()))
        FA.SetResourceProperties(resource_helper::loadPropsFromConstant(*C));
      break;
    case kDxilFieldAnnotationBitFieldsTag: {
      if (DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 7) < 0) {
        DXASSERT(false, "bitfields tag emitted for dxil version < 1.7");
      }
      const MDTuple *pBitFieldsTupleMD = dyn_cast<MDTuple>(MDO.get());
      std::vector<DxilFieldAnnotation> BitFields(
          pBitFieldsTupleMD->getNumOperands());
      for (unsigned i = 0; i < pBitFieldsTupleMD->getNumOperands(); ++i) {
        LoadDxilFieldAnnotation(pBitFieldsTupleMD->getOperand(i), BitFields[i]);
      }
      FA.SetBitFields(BitFields);
    } break;
    case kDxilFieldAnnotationBitFieldWidthTag:
      if (DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 7) < 0) {
        DXASSERT(false, "bitfields width tag emitted for dxil version < 1.7");
      }
      FA.SetBitFieldWidth(ConstMDToUint32(MDO));
      break;
    case kDxilFieldAnnotationVectorSizeTag:
      FA.SetVectorSize(ConstMDToUint32(MDO));
      break;
    default:
      DXASSERT(false, "Unknown extended shader properties tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

Metadata *DxilMDHelper::EmitDxrPayloadFieldAnnotation(
    const DxilPayloadFieldAnnotation &FA, Type *fieldType) {
  vector<Metadata *> MDVals; // Tag-Value list.
  MDVals.emplace_back(Uint32ToConstMD(kDxilPayloadFieldAnnotationAccessTag));

  auto mask = FA.GetPayloadFieldQualifierMask();
  MDVals.emplace_back(Uint32ToConstMD(mask));

  return MDNode::get(m_Ctx, MDVals);
}

const Function *
DxilMDHelper::LoadDxilFunctionProps(const MDTuple *pProps,
                                    hlsl::DxilFunctionProps *props) {
  unsigned idx = 0;
  const Function *F = dyn_cast<Function>(
      dyn_cast<ValueAsMetadata>(pProps->getOperand(idx++))->getValue());
  DXIL::ShaderKind shaderKind =
      static_cast<DXIL::ShaderKind>(ConstMDToUint32(pProps->getOperand(idx++)));

  auto DeserializeNumThreads = [&]() {
    props->numThreads[0] = ConstMDToUint32(pProps->getOperand(idx++));
    props->numThreads[1] = ConstMDToUint32(pProps->getOperand(idx++));
    props->numThreads[2] = ConstMDToUint32(pProps->getOperand(idx++));
  };

  bool bRayAttributes = false;
  props->shaderKind = shaderKind;
  switch (shaderKind) {
  case DXIL::ShaderKind::Compute:
    DeserializeNumThreads();
    if (props->IsNode())
      DeserializeNodeProps(pProps, idx, props);
    break;
  case DXIL::ShaderKind::Geometry:
    props->ShaderProps.GS.inputPrimitive =
        (DXIL::InputPrimitive)ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.GS.maxVertexCount =
        ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.GS.instanceCount =
        ConstMDToUint32(pProps->getOperand(idx++));
    for (size_t i = 0;
         i < _countof(props->ShaderProps.GS.streamPrimitiveTopologies); ++i)
      props->ShaderProps.GS.streamPrimitiveTopologies[i] =
          (DXIL::PrimitiveTopology)ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Hull:
    props->ShaderProps.HS.patchConstantFunc = dyn_cast<Function>(
        dyn_cast<ValueAsMetadata>(pProps->getOperand(idx++))->getValue());
    props->ShaderProps.HS.domain =
        (DXIL::TessellatorDomain)ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.HS.partition =
        (DXIL::TessellatorPartitioning)ConstMDToUint32(
            pProps->getOperand(idx++));
    props->ShaderProps.HS.outputPrimitive =
        (DXIL::TessellatorOutputPrimitive)ConstMDToUint32(
            pProps->getOperand(idx++));
    props->ShaderProps.HS.inputControlPoints =
        ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.HS.outputControlPoints =
        ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.HS.maxTessFactor =
        ConstMDToFloat(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Domain:
    props->ShaderProps.DS.domain =
        (DXIL::TessellatorDomain)ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.DS.inputControlPoints =
        ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Pixel:
    props->ShaderProps.PS.EarlyDepthStencil =
        ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::AnyHit:
  case DXIL::ShaderKind::ClosestHit:
    bRayAttributes = true;
    LLVM_FALLTHROUGH;
  case DXIL::ShaderKind::Miss:
  case DXIL::ShaderKind::Callable:
    // payload/params unioned and first:
    props->ShaderProps.Ray.payloadSizeInBytes =
        ConstMDToUint32(pProps->getOperand(idx++));
    if (bRayAttributes)
      props->ShaderProps.Ray.attributeSizeInBytes =
          ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Mesh:
    DeserializeNumThreads();
    props->ShaderProps.MS.maxVertexCount =
        ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.MS.maxPrimitiveCount =
        ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.MS.outputTopology =
        (DXIL::MeshOutputTopology)ConstMDToUint32(pProps->getOperand(idx++));
    props->ShaderProps.MS.payloadSizeInBytes =
        ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Amplification:
    DeserializeNumThreads();
    props->ShaderProps.AS.payloadSizeInBytes =
        ConstMDToUint32(pProps->getOperand(idx++));
    break;
  case DXIL::ShaderKind::Node:
    DeserializeNumThreads();
    // Node specific attributes
    DeserializeNodeProps(pProps, idx, props);
    break;
  default:
    break;
  }
  return F;
}

MDTuple *DxilMDHelper::EmitDxilEntryProperties(uint64_t rawShaderFlag,
                                               const DxilFunctionProps &props,
                                               unsigned autoBindingSpace) {
  vector<Metadata *> MDVals;

  // DXIL shader flags.
  if (props.IsPS()) {
    if (props.ShaderProps.PS.EarlyDepthStencil) {
      ShaderFlags flags;
      flags.SetShaderFlagsRaw(rawShaderFlag);
      flags.SetForceEarlyDepthStencil(true);
      rawShaderFlag = flags.GetShaderFlagsRaw();
    }
  }
  if (rawShaderFlag != 0) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilShaderFlagsTag));
    MDVals.emplace_back(Uint64ToConstMD(rawShaderFlag));
  }

  // Add shader kind for lib entrys.
  if (m_pSM->IsLib() && props.shaderKind != DXIL::ShaderKind::Library) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilShaderKindTag));
    MDVals.emplace_back(
        Uint32ToConstMD(static_cast<unsigned>(props.shaderKind)));
  }

  if (props.IsNode())
    EmitDxilNodeState(MDVals, props);

  switch (props.shaderKind) {
    // Compute shader.
  case DXIL::ShaderKind::Compute: {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNumThreadsTag));
    vector<Metadata *> NumThreadVals;
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[0]));
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[1]));
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[2]));
    MDVals.emplace_back(MDNode::get(m_Ctx, NumThreadVals));

    if (props.WaveSize.IsDefined()) {
      if (props.WaveSize.IsRange())
        DXASSERT(DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 8) >= 0,
                 "DXIL version must be > 1.8");
      const hlsl::ShaderModel *SM = GetShaderModel();

      MDVals.emplace_back(Uint32ToConstMD(
          SM->IsSM68Plus() ? DxilMDHelper::kDxilRangedWaveSizeTag
                           : DxilMDHelper::kDxilWaveSizeTag));
      SmallVector<Metadata *, 3> WaveSizeVal;
      WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Min));
      if (SM->IsSM68Plus()) {
        WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Max));
        WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Preferred));
      }
      MDVals.emplace_back(MDNode::get(m_Ctx, WaveSizeVal));
    }

    const hlsl::ShaderModel *SM = GetShaderModel();
    if (SM->IsSMAtLeast(6, 10)) {
      MDVals.emplace_back(
          Uint32ToConstMD(DxilMDHelper::kDxilGroupSharedLimitTag));
      MDVals.emplace_back(Uint32ToConstMD(props.groupSharedLimitBytes));
    }
  } break;
    // Geometry shader.
  case DXIL::ShaderKind::Geometry: {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilGSStateTag));
    DXIL::PrimitiveTopology topo = DXIL::PrimitiveTopology::Undefined;
    unsigned activeStreamMask = 0;
    for (size_t i = 0;
         i < _countof(props.ShaderProps.GS.streamPrimitiveTopologies); ++i) {
      if (props.ShaderProps.GS.streamPrimitiveTopologies[i] !=
          DXIL::PrimitiveTopology::Undefined) {
        activeStreamMask |= 1 << i;
        DXASSERT_NOMSG(topo == DXIL::PrimitiveTopology::Undefined ||
                       topo ==
                           props.ShaderProps.GS.streamPrimitiveTopologies[i]);
        topo = props.ShaderProps.GS.streamPrimitiveTopologies[i];
      }
    }
    MDTuple *pMDTuple =
        EmitDxilGSState(props.ShaderProps.GS.inputPrimitive,
                        props.ShaderProps.GS.maxVertexCount, activeStreamMask,
                        topo, props.ShaderProps.GS.instanceCount);
    MDVals.emplace_back(pMDTuple);
  } break;
    // Domain shader.
  case DXIL::ShaderKind::Domain: {
    auto &DS = props.ShaderProps.DS;
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilDSStateTag));
    MDTuple *pMDTuple = EmitDxilDSState(DS.domain, DS.inputControlPoints);
    MDVals.emplace_back(pMDTuple);
  } break;
    // Hull shader.
  case DXIL::ShaderKind::Hull: {
    auto &HS = props.ShaderProps.HS;
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilHSStateTag));
    MDTuple *pMDTuple = EmitDxilHSState(
        HS.patchConstantFunc, HS.inputControlPoints, HS.outputControlPoints,
        HS.domain, HS.partition, HS.outputPrimitive, HS.maxTessFactor);
    MDVals.emplace_back(pMDTuple);
  } break;
    // Raytracing.
  case DXIL::ShaderKind::AnyHit:
  case DXIL::ShaderKind::ClosestHit: {
    MDVals.emplace_back(Uint32ToConstMD(kDxilRayPayloadSizeTag));
    MDVals.emplace_back(
        Uint32ToConstMD(props.ShaderProps.Ray.payloadSizeInBytes));

    MDVals.emplace_back(Uint32ToConstMD(kDxilRayAttribSizeTag));
    MDVals.emplace_back(
        Uint32ToConstMD(props.ShaderProps.Ray.attributeSizeInBytes));
  } break;
  case DXIL::ShaderKind::Miss:
  case DXIL::ShaderKind::Callable: {
    MDVals.emplace_back(Uint32ToConstMD(kDxilRayPayloadSizeTag));

    MDVals.emplace_back(
        Uint32ToConstMD(props.ShaderProps.Ray.payloadSizeInBytes));
  } break;
  case DXIL::ShaderKind::Mesh: {
    auto &MS = props.ShaderProps.MS;
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilMSStateTag));
    MDTuple *pMDTuple = EmitDxilMSState(props.numThreads, MS.maxVertexCount,
                                        MS.maxPrimitiveCount, MS.outputTopology,
                                        MS.payloadSizeInBytes);
    MDVals.emplace_back(pMDTuple);
  } break;
  case DXIL::ShaderKind::Amplification: {
    auto &AS = props.ShaderProps.AS;
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilASStateTag));
    MDTuple *pMDTuple =
        EmitDxilASState(props.numThreads, AS.payloadSizeInBytes);
    MDVals.emplace_back(pMDTuple);
  } break;
  case DXIL::ShaderKind::Node: {
    // The Node specific properties have already been handled by
    // EmitDxilNodeState function above. Here we emit the metadata for those
    // Node shader attributes that are shared with other shader types (only CS
    // for now)
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNumThreadsTag));
    vector<Metadata *> NumThreadVals;
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[0]));
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[1]));
    NumThreadVals.emplace_back(Uint32ToConstMD(props.numThreads[2]));
    MDVals.emplace_back(MDNode::get(m_Ctx, NumThreadVals));
  } break;
  default:
    break;
  }

  if (autoBindingSpace != UINT_MAX && m_pSM->IsSMAtLeast(6, 3)) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilAutoBindingSpaceTag));
    MDVals.emplace_back(
        MDNode::get(m_Ctx, {Uint32ToConstMD(autoBindingSpace)}));
  }

  if (!props.serializedRootSignature.empty() &&
      DXIL::CompareVersions(m_MinValMajor, m_MinValMinor, 1, 6) > 0) {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilEntryRootSigTag));
    MDVals.emplace_back(
        EmitSerializedRootSignature(props.serializedRootSignature, m_Ctx));
  }

  if (!MDVals.empty())
    return MDNode::get(m_Ctx, MDVals);
  else
    return nullptr;
}

void DxilMDHelper::LoadDxilEntryProperties(const MDOperand &MDO,
                                           uint64_t &rawShaderFlag,
                                           DxilFunctionProps &props,
                                           uint32_t &autoBindingSpace) {
  if (MDO.get() == nullptr)
    return;

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);
  bool bEarlyDepth = false;

  if (!m_pSM->IsLib()) {
    props.shaderKind = m_pSM->GetKind();
  } else {
    props.shaderKind = DXIL::ShaderKind::Library;
  }

  bool hasNodeTag = false;
  for (unsigned iNode = 0; iNode < pTupleMD->getNumOperands(); iNode += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(iNode));
    const MDOperand &MDO = pTupleMD->getOperand(iNode + 1);
    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

    switch (Tag) {
    case DxilMDHelper::kDxilShaderFlagsTag: {
      rawShaderFlag = ConstMDToUint64(MDO);
      ShaderFlags flags;
      flags.SetShaderFlagsRaw(rawShaderFlag);
      bEarlyDepth = flags.GetForceEarlyDepthStencil();
    } break;

    case DxilMDHelper::kDxilNumThreadsTag: {
      DXASSERT(props.IsCS() || props.shaderKind == DXIL::ShaderKind::Node,
               "else invalid shader kind");
      MDNode *pNode = cast<MDNode>(MDO.get());
      props.numThreads[0] = ConstMDToUint32(pNode->getOperand(0));
      props.numThreads[1] = ConstMDToUint32(pNode->getOperand(1));
      props.numThreads[2] = ConstMDToUint32(pNode->getOperand(2));
    } break;

    case DxilMDHelper::kDxilGroupSharedLimitTag: {
      DXASSERT(props.IsCS(), "else invalid shader kind");
      props.groupSharedLimitBytes = ConstMDToUint32(MDO);
      if (!m_pSM->IsSMAtLeast(6, 10))
        m_bExtraMetadata = true;
    } break;

    case DxilMDHelper::kDxilGSStateTag: {
      DXASSERT(props.IsGS(), "else invalid shader kind");
      auto &GS = props.ShaderProps.GS;
      DXIL::PrimitiveTopology topo = DXIL::PrimitiveTopology::Undefined;
      unsigned activeStreamMask;
      LoadDxilGSState(MDO, GS.inputPrimitive, GS.maxVertexCount,
                      activeStreamMask, topo, GS.instanceCount);
      if (topo != DXIL::PrimitiveTopology::Undefined) {
        for (size_t i = 0; i < _countof(GS.streamPrimitiveTopologies); ++i) {
          unsigned mask = 1 << i;
          if (activeStreamMask & mask) {
            GS.streamPrimitiveTopologies[i] = topo;
          } else {
            GS.streamPrimitiveTopologies[i] =
                DXIL::PrimitiveTopology::Undefined;
          }
        }
      }
    } break;

    case DxilMDHelper::kDxilDSStateTag: {
      DXASSERT(props.IsDS(), "else invalid shader kind");
      auto &DS = props.ShaderProps.DS;
      LoadDxilDSState(MDO, DS.domain, DS.inputControlPoints);
    } break;

    case DxilMDHelper::kDxilHSStateTag: {
      DXASSERT(props.IsHS(), "else invalid shader kind");
      auto &HS = props.ShaderProps.HS;
      LoadDxilHSState(MDO, HS.patchConstantFunc, HS.inputControlPoints,
                      HS.outputControlPoints, HS.domain, HS.partition,
                      HS.outputPrimitive, HS.maxTessFactor);
    } break;

    case DxilMDHelper::kDxilAutoBindingSpaceTag: {
      MDNode *pNode = cast<MDNode>(MDO.get());
      autoBindingSpace = ConstMDToUint32(pNode->getOperand(0));
      break;
    }
    case DxilMDHelper::kDxilRayPayloadSizeTag: {
      DXASSERT(props.IsAnyHit() || props.IsClosestHit() || props.IsMiss() ||
                   props.IsCallable(),
               "else invalid shader kind");
      props.ShaderProps.Ray.payloadSizeInBytes = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilRayAttribSizeTag: {
      DXASSERT(props.IsAnyHit() || props.IsClosestHit(),
               "else invalid shader kind");
      props.ShaderProps.Ray.attributeSizeInBytes = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilShaderKindTag: {
      DXIL::ShaderKind kind =
          static_cast<DXIL::ShaderKind>(ConstMDToUint32(MDO));
      DXASSERT(props.shaderKind == DXIL::ShaderKind::Library,
               "else invalid shader kind");
      props.shaderKind = kind;
    } break;
    case DxilMDHelper::kDxilMSStateTag: {
      DXASSERT(props.IsMS(), "else invalid shader kind");
      auto &MS = props.ShaderProps.MS;
      LoadDxilMSState(MDO, props.numThreads, MS.maxVertexCount,
                      MS.maxPrimitiveCount, MS.outputTopology,
                      MS.payloadSizeInBytes);
    } break;
    case DxilMDHelper::kDxilASStateTag: {
      DXASSERT(props.IsAS(), "else invalid shader kind");
      auto &AS = props.ShaderProps.AS;
      LoadDxilASState(MDO, props.numThreads, AS.payloadSizeInBytes);
    } break;
    case DxilMDHelper::kDxilWaveSizeTag: {
      MDNode *pNode = cast<MDNode>(MDO.get());
      props.WaveSize.Min = ConstMDToUint32(pNode->getOperand(0));
    } break;
    case DxilMDHelper::kDxilRangedWaveSizeTag: {
      // if we're here, we're using the range variant.
      // Extra metadata is used if SM < 6.8
      if (!m_pSM->IsSMAtLeast(6, 8))
        m_bExtraMetadata = true;
      MDNode *pNode = cast<MDNode>(MDO.get());
      // TODO: Issue #6239 we need to validate that there are 3 integer
      // parameters here, and emit a diagnostic if not.
      DXASSERT(pNode->getNumOperands() == 3,
               "else wavesize range tag has incorrect number of parameters");
      props.WaveSize.Min = ConstMDToUint32(pNode->getOperand(0));
      props.WaveSize.Max = ConstMDToUint32(pNode->getOperand(1));
      props.WaveSize.Preferred = ConstMDToUint32(pNode->getOperand(2));
    } break;
    case DxilMDHelper::kDxilEntryRootSigTag: {
      MDNode *pNode = cast<MDNode>(MDO.get());
      LoadSerializedRootSignature(pNode, props.serializedRootSignature, m_Ctx);
    } break;
    case DxilMDHelper::kDxilNodeLaunchTypeTag: {
      hasNodeTag = true;
      auto &Node = props.Node;
      Node.LaunchType = static_cast<DXIL::NodeLaunchType>(ConstMDToUint32(MDO));
    } break;
    case DxilMDHelper::kDxilNodeIsProgramEntryTag: {
      hasNodeTag = true;
      props.Node.IsProgramEntry = ConstMDToBool(MDO);
    } break;
    case DxilMDHelper::kDxilNodeIdTag: {
      hasNodeTag = true;
      MDNode *pNode = cast<MDNode>(MDO.get());
      props.NodeShaderID.Name = StringMDToString(pNode->getOperand(0));
      props.NodeShaderID.Index = ConstMDToUint32(pNode->getOperand(1));
    } break;
    case DxilMDHelper::kDxilNodeLocalRootArgumentsTableIndexTag: {
      hasNodeTag = true;
      auto &Node = props.Node;
      Node.LocalRootArgumentsTableIndex = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilShareInputOfTag: {
      hasNodeTag = true;
      MDNode *pNode = cast<MDNode>(MDO.get());
      props.NodeShaderSharedInput.Name = StringMDToString(pNode->getOperand(0));
      props.NodeShaderSharedInput.Index = ConstMDToUint32(pNode->getOperand(1));
    } break;
    case DxilMDHelper::kDxilNodeDispatchGridTag: {
      hasNodeTag = true;
      auto &Node = props.Node;
      MDNode *pNode = cast<MDNode>(MDO.get());
      Node.DispatchGrid[0] = ConstMDToUint32(pNode->getOperand(0));
      Node.DispatchGrid[1] = ConstMDToUint32(pNode->getOperand(1));
      Node.DispatchGrid[2] = ConstMDToUint32(pNode->getOperand(2));
    } break;
    case DxilMDHelper::kDxilNodeMaxDispatchGridTag: {
      hasNodeTag = true;
      auto &Node = props.Node;
      MDNode *pNode = cast<MDNode>(MDO.get());
      Node.MaxDispatchGrid[0] = ConstMDToUint32(pNode->getOperand(0));
      Node.MaxDispatchGrid[1] = ConstMDToUint32(pNode->getOperand(1));
      Node.MaxDispatchGrid[2] = ConstMDToUint32(pNode->getOperand(2));
    } break;
    case DxilMDHelper::kDxilNodeMaxRecursionDepthTag: {
      hasNodeTag = true;
      auto &Node = props.Node;
      Node.MaxRecursionDepth = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilNodeInputsTag: {
      hasNodeTag = true;
      const MDTuple *pNodeInputs = dyn_cast<MDTuple>(MDO.get());
      IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
      for (unsigned i = 0; i != pNodeInputs->getNumOperands(); ++i) {
        const MDOperand &NodeInput = pNodeInputs->getOperand(i);
        IFTBOOL(NodeInput.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
        props.InputNodes.push_back(LoadDxilNodeIOState(NodeInput));
      }
    } break;
    case DxilMDHelper::kDxilNodeOutputsTag: {
      hasNodeTag = true;
      const MDTuple *pNodeOutputs = dyn_cast<MDTuple>(MDO.get());
      IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
      for (unsigned i = 0; i != pNodeOutputs->getNumOperands(); ++i) {
        const MDOperand &NodeOutput = pNodeOutputs->getOperand(i);
        IFTBOOL(NodeOutput.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
        props.OutputNodes.push_back(LoadDxilNodeIOState(NodeOutput));
      }
    } break;
    default:
      DXASSERT(false, "Unknown extended shader properties tag");
      m_bExtraMetadata = true;
      break;
    }
  }

  DXASSERT(!hasNodeTag ||
               props.Node.LaunchType != DXIL::NodeLaunchType::Invalid,
           "else invalid shader kind");

  if (bEarlyDepth) {
    DXASSERT(props.IsPS(), "else invalid shader kind");
    props.ShaderProps.PS.EarlyDepthStencil = true;
  }
}

void DxilMDHelper::SerializeNodeProps(SmallVectorImpl<llvm::Metadata *> &MDVals,
                                      unsigned &valIdx,
                                      const hlsl::DxilFunctionProps *props) {
  auto &NodeProps = props->Node;

  MDVals.push_back(
      Uint32ToConstMD(static_cast<unsigned>((NodeProps.LaunchType))));
  MDVals.push_back(BoolToConstMD(NodeProps.IsProgramEntry));
  MDVals.push_back(MDString::get(m_Ctx, props->NodeShaderID.Name));
  MDVals.push_back(Uint32ToConstMD(props->NodeShaderID.Index));
  MDVals.push_back(MDString::get(m_Ctx, props->NodeShaderSharedInput.Name));
  MDVals.push_back(Uint32ToConstMD(props->NodeShaderSharedInput.Index));
  MDVals.push_back(Uint32ToConstMD(NodeProps.LocalRootArgumentsTableIndex));
  MDVals.push_back(Uint32ToConstMD(NodeProps.DispatchGrid[0]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.DispatchGrid[1]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.DispatchGrid[2]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.MaxDispatchGrid[0]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.MaxDispatchGrid[1]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.MaxDispatchGrid[2]));
  MDVals.push_back(Uint32ToConstMD(NodeProps.MaxRecursionDepth));
  for (auto &nodeinput : props->InputNodes) {
    MDVals.push_back(Uint32ToConstMD(nodeinput.Flags));
    MDVals.push_back(Uint32ToConstMD(nodeinput.MaxRecords));
    MDVals.push_back(Uint32ToConstMD(nodeinput.RecordType.size));
    MDVals.push_back(
        Uint32ToConstMD(nodeinput.RecordType.SV_DispatchGrid.ByteOffset));
    MDVals.push_back(Uint32ToConstMD(static_cast<unsigned>(
        nodeinput.RecordType.SV_DispatchGrid.ComponentType)));
    MDVals.push_back(
        Uint32ToConstMD(nodeinput.RecordType.SV_DispatchGrid.NumComponents));
    MDVals.push_back(Uint32ToConstMD(nodeinput.RecordType.alignment));
  }
  for (auto &nodeoutput : props->OutputNodes) {
    MDVals.push_back(Uint32ToConstMD(nodeoutput.Flags));
    MDVals.push_back(Uint32ToConstMD(nodeoutput.RecordType.size));
    MDVals.push_back(
        Uint32ToConstMD(nodeoutput.RecordType.SV_DispatchGrid.ByteOffset));
    MDVals.push_back(Uint32ToConstMD(static_cast<unsigned>(
        nodeoutput.RecordType.SV_DispatchGrid.ComponentType)));
    MDVals.push_back(
        Uint32ToConstMD(nodeoutput.RecordType.SV_DispatchGrid.NumComponents));
    MDVals.push_back(MDString::get(m_Ctx, nodeoutput.OutputID.Name));
    MDVals.push_back(Uint32ToConstMD(nodeoutput.OutputID.Index));
    MDVals.push_back(Uint32ToConstMD(nodeoutput.MaxRecords));
    MDVals.push_back(Int32ToConstMD(nodeoutput.MaxRecordsSharedWith));
    MDVals.push_back(Uint32ToConstMD(nodeoutput.OutputArraySize));
    MDVals.push_back(BoolToConstMD(nodeoutput.AllowSparseNodes));
    MDVals.push_back(Uint32ToConstMD(nodeoutput.RecordType.alignment));
  }
}

void DxilMDHelper::DeserializeNodeProps(const MDTuple *pProps, unsigned &idx,
                                        hlsl::DxilFunctionProps *props) {
  auto &NodeProps = props->Node;

  NodeProps.LaunchType = static_cast<DXIL::NodeLaunchType>(
      ConstMDToUint32(pProps->getOperand(idx++)));
  NodeProps.IsProgramEntry = ConstMDToBool(pProps->getOperand(idx++));
  props->NodeShaderID.Name = StringMDToString(pProps->getOperand(idx++));
  props->NodeShaderID.Index = ConstMDToUint32(pProps->getOperand(idx++));
  props->NodeShaderSharedInput.Name =
      StringMDToString(pProps->getOperand(idx++));
  props->NodeShaderSharedInput.Index =
      ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.LocalRootArgumentsTableIndex =
      ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.DispatchGrid[0] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.DispatchGrid[1] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.DispatchGrid[2] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.MaxDispatchGrid[0] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.MaxDispatchGrid[1] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.MaxDispatchGrid[2] = ConstMDToUint32(pProps->getOperand(idx++));
  NodeProps.MaxRecursionDepth = ConstMDToUint32(pProps->getOperand(idx++));
  for (auto &nodeinput : props->InputNodes) {
    nodeinput.Flags = NodeFlags(ConstMDToUint32(pProps->getOperand(idx++)));
    nodeinput.MaxRecords = ConstMDToUint32(pProps->getOperand(idx++));
    nodeinput.RecordType.size = ConstMDToUint32(pProps->getOperand(idx++));
    nodeinput.RecordType.SV_DispatchGrid.ByteOffset =
        ConstMDToUint32(pProps->getOperand(idx++));
    nodeinput.RecordType.SV_DispatchGrid.ComponentType =
        static_cast<DXIL::ComponentType>(
            ConstMDToUint32(pProps->getOperand(idx++)));
    nodeinput.RecordType.SV_DispatchGrid.NumComponents =
        ConstMDToUint32(pProps->getOperand(idx++));
    if (pProps->getNumOperands() > idx) {
      nodeinput.RecordType.alignment =
          ConstMDToUint32(pProps->getOperand(idx++));
    }
  }

  for (auto &nodeoutput : props->OutputNodes) {
    nodeoutput.Flags = NodeFlags(ConstMDToUint32(pProps->getOperand(idx++)));
    nodeoutput.RecordType.size = ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.RecordType.SV_DispatchGrid.ByteOffset =
        ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.RecordType.SV_DispatchGrid.ComponentType =
        static_cast<DXIL::ComponentType>(
            ConstMDToUint32(pProps->getOperand(idx++)));
    nodeoutput.RecordType.SV_DispatchGrid.NumComponents =
        ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.OutputID.Name = StringMDToString(pProps->getOperand(idx++));
    nodeoutput.OutputID.Index = ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.MaxRecords = ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.MaxRecordsSharedWith = ConstMDToInt32(pProps->getOperand(idx++));
    nodeoutput.OutputArraySize = ConstMDToUint32(pProps->getOperand(idx++));
    nodeoutput.AllowSparseNodes = ConstMDToBool(pProps->getOperand(idx++));
    if (pProps->getNumOperands() > idx) {
      nodeoutput.RecordType.alignment =
          ConstMDToUint32(pProps->getOperand(idx++));
    }
  }
}

MDTuple *
DxilMDHelper::EmitDxilFunctionProps(const hlsl::DxilFunctionProps *props,
                                    const Function *F) {
  bool bRayAttributes = false;
  SmallVector<Metadata *, 35> MDVals;

  auto SerializeNumThreads = [&]() {
    MDVals.push_back(Uint32ToConstMD(props->numThreads[0]));
    MDVals.push_back(Uint32ToConstMD(props->numThreads[1]));
    MDVals.push_back(Uint32ToConstMD(props->numThreads[2]));
  };

  unsigned valIdx = 0;
  MDVals.push_back(ValueAsMetadata::get(const_cast<Function *>(F)));
  MDVals.push_back(Uint32ToConstMD(static_cast<unsigned>(props->shaderKind)));
  switch (props->shaderKind) {
  case DXIL::ShaderKind::Compute:
    SerializeNumThreads();
    if (props->IsNode())
      SerializeNodeProps(MDVals, valIdx, props);
    break;
  case DXIL::ShaderKind::Geometry:
    MDVals.push_back(
        Uint8ToConstMD((uint8_t)props->ShaderProps.GS.inputPrimitive));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.GS.maxVertexCount));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.GS.instanceCount));
    for (size_t i = 0;
         i < _countof(props->ShaderProps.GS.streamPrimitiveTopologies); ++i)
      MDVals.push_back(Uint8ToConstMD(
          (uint8_t)props->ShaderProps.GS.streamPrimitiveTopologies[i]));
    break;
  case DXIL::ShaderKind::Hull:
    MDVals.push_back(
        ValueAsMetadata::get(props->ShaderProps.HS.patchConstantFunc));
    MDVals.push_back(Uint8ToConstMD((uint8_t)props->ShaderProps.HS.domain));
    MDVals.push_back(Uint8ToConstMD((uint8_t)props->ShaderProps.HS.partition));
    MDVals.push_back(
        Uint8ToConstMD((uint8_t)props->ShaderProps.HS.outputPrimitive));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.HS.inputControlPoints));
    MDVals.push_back(
        Uint32ToConstMD(props->ShaderProps.HS.outputControlPoints));
    MDVals.push_back(FloatToConstMD(props->ShaderProps.HS.maxTessFactor));
    break;
  case DXIL::ShaderKind::Domain:
    MDVals.push_back(Uint8ToConstMD((uint8_t)props->ShaderProps.DS.domain));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.DS.inputControlPoints));
    break;
  case DXIL::ShaderKind::Pixel:
    MDVals.push_back(BoolToConstMD(props->ShaderProps.PS.EarlyDepthStencil));
    break;
  case DXIL::ShaderKind::AnyHit:
  case DXIL::ShaderKind::ClosestHit:
    bRayAttributes = true;
    LLVM_FALLTHROUGH;
  case DXIL::ShaderKind::Miss:
  case DXIL::ShaderKind::Callable:
    // payload/params unioned and first:
    MDVals.push_back(
        Uint32ToConstMD(props->ShaderProps.Ray.payloadSizeInBytes));
    if (bRayAttributes)
      MDVals.push_back(
          Uint32ToConstMD(props->ShaderProps.Ray.attributeSizeInBytes));
    break;
  case DXIL::ShaderKind::Mesh:
    SerializeNumThreads();
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.MS.maxVertexCount));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.MS.maxPrimitiveCount));
    MDVals.push_back(
        Uint8ToConstMD((uint8_t)props->ShaderProps.MS.outputTopology));
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.MS.payloadSizeInBytes));
    break;
  case DXIL::ShaderKind::Amplification:
    SerializeNumThreads();
    MDVals.push_back(Uint32ToConstMD(props->ShaderProps.AS.payloadSizeInBytes));
    break;
  case DXIL::ShaderKind::Node:
    SerializeNumThreads();
    // Node specific properties
    SerializeNodeProps(MDVals, valIdx, props);
    break;
  default:
    break;
  }
  return MDTuple::get(m_Ctx,
                      ArrayRef<llvm::Metadata *>(MDVals.data(), MDVals.size()));
}

void DxilMDHelper::EmitDxilViewIdState(std::vector<unsigned> &SerializedState) {
  const vector<unsigned> &Data = SerializedState;
  // If all UINTs are zero, do not emit ViewIdState.
  if (!std::any_of(Data.begin(), Data.end(), [](unsigned e) { return e != 0; }))
    return;

  Constant *V = ConstantDataArray::get(m_Ctx, ArrayRef<uint32_t>(Data));
  NamedMDNode *pViewIdNamedMD =
      m_pModule->getNamedMetadata(kDxilViewIdStateMDName);
  IFTBOOL(pViewIdNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pViewIdNamedMD = m_pModule->getOrInsertNamedMetadata(kDxilViewIdStateMDName);
  pViewIdNamedMD->addOperand(MDNode::get(m_Ctx, {ConstantAsMetadata::get(V)}));
}

void DxilMDHelper::LoadDxilViewIdState(std::vector<unsigned> &SerializedState) {
  NamedMDNode *pViewIdStateNamedMD =
      m_pModule->getNamedMetadata(kDxilViewIdStateMDName);
  if (!pViewIdStateNamedMD)
    return;

  IFTBOOL(pViewIdStateNamedMD->getNumOperands() == 1,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pNode = pViewIdStateNamedMD->getOperand(0);
  IFTBOOL(pNode->getNumOperands() == 1, DXC_E_INCORRECT_DXIL_METADATA);
  const MDOperand &MDO = pNode->getOperand(0);

  const ConstantAsMetadata *pMetaData = dyn_cast<ConstantAsMetadata>(MDO.get());
  IFTBOOL(pMetaData != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  if (isa<ConstantAggregateZero>(pMetaData->getValue()))
    return;
  const ConstantDataArray *pData =
      dyn_cast<ConstantDataArray>(pMetaData->getValue());
  IFTBOOL(pData != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pData->getElementType() == Type::getInt32Ty(m_Ctx),
          DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pData->getRawDataValues().size() < UINT_MAX &&
              (pData->getRawDataValues().size() & 3) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  SerializedState.clear();
  unsigned size = (unsigned)pData->getRawDataValues().size() / 4;
  SerializedState.resize(size);
  const unsigned *Ptr = (const unsigned *)pData->getRawDataValues().begin();
  memcpy(SerializedState.data(), Ptr, size * sizeof(unsigned));
}

MDNode *
DxilMDHelper::EmitControlFlowHints(llvm::LLVMContext &Ctx,
                                   std::vector<DXIL::ControlFlowHint> &hints) {
  SmallVector<Metadata *, 4> Args;
  // Reserve operand 0 for self reference.
  auto TempNode = MDNode::getTemporary(Ctx, None);
  Args.emplace_back(TempNode.get());
  Args.emplace_back(MDString::get(Ctx, kDxilControlFlowHintMDName));
  for (DXIL::ControlFlowHint &hint : hints)
    Args.emplace_back(Uint32ToConstMD(static_cast<unsigned>(hint), Ctx));

  MDNode *hintsNode = MDNode::get(Ctx, Args);
  // Set the first operand to itself.
  hintsNode->replaceOperandWith(0, hintsNode);
  return hintsNode;
}

unsigned DxilMDHelper::GetControlFlowHintMask(const Instruction *I) {
  // Check that there are control hint to use
  // branch.
  MDNode *MD = I->getMetadata(hlsl::DxilMDHelper::kDxilControlFlowHintMDName);
  if (!MD)
    return 0;

  if (MD->getNumOperands() < 3)
    return 0;
  unsigned mask = 0;
  for (unsigned i = 2; i < MD->getNumOperands(); i++) {
    Metadata *Op = MD->getOperand(2).get();
    auto ConstOp = cast<ConstantAsMetadata>(Op);
    unsigned hint = ConstOp->getValue()->getUniqueInteger().getLimitedValue();
    mask |= 1 << hint;
  }
  return mask;
}

bool DxilMDHelper::HasControlFlowHintToPreventFlatten(
    const llvm::Instruction *I) {
  unsigned mask = GetControlFlowHintMask(I);
  const unsigned BranchMask = 1 << (unsigned)(DXIL::ControlFlowHint::Branch) |
                              1 << (unsigned)(DXIL::ControlFlowHint::Call) |
                              1 << (unsigned)(DXIL::ControlFlowHint::ForceCase);
  return mask & BranchMask;
}

void DxilMDHelper::EmitSubobjects(const DxilSubobjects &Subobjects) {
  NamedMDNode *pSubobjectsNamedMD =
      m_pModule->getNamedMetadata(kDxilSubobjectsMDName);
  IFTBOOL(pSubobjectsNamedMD == nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  pSubobjectsNamedMD =
      m_pModule->getOrInsertNamedMetadata(kDxilSubobjectsMDName);

  const auto &objMap = Subobjects.GetSubobjects();
  for (auto &it : objMap)
    pSubobjectsNamedMD->addOperand(cast<MDNode>(EmitSubobject(*it.second)));
}

void DxilMDHelper::LoadSubobjects(DxilSubobjects &Subobjects) {
  NamedMDNode *pSubobjectsNamedMD =
      m_pModule->getNamedMetadata(kDxilSubobjectsMDName);
  if (!pSubobjectsNamedMD)
    return;

  for (unsigned i = 0; i < pSubobjectsNamedMD->getNumOperands(); ++i)
    LoadSubobject(*pSubobjectsNamedMD->getOperand(i), Subobjects);
}

Metadata *DxilMDHelper::EmitSubobject(const DxilSubobject &obj) {
  SmallVector<Metadata *, 6> Args;
  Args.emplace_back(MDString::get(m_Ctx, obj.GetName()));
  Args.emplace_back(Uint32ToConstMD((unsigned)obj.GetKind()));
  bool bLocalRS = false;
  IFTBOOL(DXIL::IsValidSubobjectKind(obj.GetKind()),
          DXC_E_INCORRECT_DXIL_METADATA);
  switch (obj.GetKind()) {
  case DXIL::SubobjectKind::StateObjectConfig: {
    uint32_t Flags;
    IFTBOOL(obj.GetStateObjectConfig(Flags), DXC_E_INCORRECT_DXIL_METADATA);
    Args.emplace_back(Uint32ToConstMD((unsigned)Flags));
    break;
  }
  case DXIL::SubobjectKind::LocalRootSignature:
    bLocalRS = true;
    LLVM_FALLTHROUGH;
  case DXIL::SubobjectKind::GlobalRootSignature: {
    const char *Text;
    const void *Data;
    uint32_t Size;
    IFTBOOL(obj.GetRootSignature(bLocalRS, Data, Size, &Text),
            DXC_E_INCORRECT_DXIL_METADATA);
    Constant *V = ConstantDataArray::get(
        m_Ctx, ArrayRef<uint8_t>((const uint8_t *)Data, Size));
    Args.emplace_back(MDNode::get(m_Ctx, {ConstantAsMetadata::get(V)}));
    Args.emplace_back(MDString::get(m_Ctx, Text));
    break;
  }
  case DXIL::SubobjectKind::SubobjectToExportsAssociation: {
    StringRef Subobj;
    const char *const *Exports;
    uint32_t NumExports;
    IFTBOOL(obj.GetSubobjectToExportsAssociation(Subobj, Exports, NumExports),
            DXC_E_INCORRECT_DXIL_METADATA);
    SmallVector<Metadata *, 4> strArgs;
    for (unsigned i = 0; i < NumExports; ++i) {
      strArgs.emplace_back(MDString::get(m_Ctx, Exports[i]));
    }
    Args.emplace_back(MDString::get(m_Ctx, Subobj));
    Args.emplace_back(MDNode::get(m_Ctx, strArgs));
    break;
  }
  case DXIL::SubobjectKind::RaytracingShaderConfig: {
    uint32_t MaxPayloadSizeInBytes;
    uint32_t MaxAttributeSizeInBytes;
    IFTBOOL(obj.GetRaytracingShaderConfig(MaxPayloadSizeInBytes,
                                          MaxAttributeSizeInBytes),
            DXC_E_INCORRECT_DXIL_METADATA);
    Args.emplace_back(Uint32ToConstMD(MaxPayloadSizeInBytes));
    Args.emplace_back(Uint32ToConstMD(MaxAttributeSizeInBytes));
    break;
  }
  case DXIL::SubobjectKind::RaytracingPipelineConfig: {
    uint32_t MaxTraceRecursionDepth;
    IFTBOOL(obj.GetRaytracingPipelineConfig(MaxTraceRecursionDepth),
            DXC_E_INCORRECT_DXIL_METADATA);
    Args.emplace_back(Uint32ToConstMD(MaxTraceRecursionDepth));
    break;
  }
  case DXIL::SubobjectKind::HitGroup: {
    llvm::StringRef Intersection, AnyHit, ClosestHit;
    DXIL::HitGroupType hgType;
    IFTBOOL(obj.GetHitGroup(hgType, AnyHit, ClosestHit, Intersection),
            DXC_E_INCORRECT_DXIL_METADATA);
    Args.emplace_back(Uint32ToConstMD((uint32_t)hgType));
    Args.emplace_back(MDString::get(m_Ctx, Intersection));
    Args.emplace_back(MDString::get(m_Ctx, AnyHit));
    Args.emplace_back(MDString::get(m_Ctx, ClosestHit));
    break;
  }
  case DXIL::SubobjectKind::RaytracingPipelineConfig1: {
    uint32_t MaxTraceRecursionDepth;
    uint32_t Flags;
    IFTBOOL(obj.GetRaytracingPipelineConfig1(MaxTraceRecursionDepth, Flags),
            DXC_E_INCORRECT_DXIL_METADATA);
    Args.emplace_back(Uint32ToConstMD(MaxTraceRecursionDepth));
    Args.emplace_back(Uint32ToConstMD(Flags));
    break;
  }
  default:
    DXASSERT(false, "otherwise, we didn't handle a valid subobject kind");
    m_bExtraMetadata = true;
    break;
  }
  return MDNode::get(m_Ctx, Args);
}
void DxilMDHelper::LoadSubobject(const llvm::MDNode &MD,
                                 DxilSubobjects &Subobjects) {
  IFTBOOL(MD.getNumOperands() >= 2, DXC_E_INCORRECT_DXIL_METADATA);
  unsigned i = 0;
  StringRef name(StringMDToStringRef(MD.getOperand(i++)));
  DXIL::SubobjectKind kind =
      (DXIL::SubobjectKind)ConstMDToUint32(MD.getOperand(i++));
  IFTBOOL(DXIL::IsValidSubobjectKind(kind), DXC_E_INCORRECT_DXIL_METADATA);
  bool bLocalRS = false;
  switch (kind) {
  case DXIL::SubobjectKind::StateObjectConfig: {
    uint32_t Flags = ConstMDToUint32(MD.getOperand(i++));
    IFTBOOL(0 == ((~(uint32_t)DXIL::StateObjectFlags::ValidMask) & Flags),
            DXC_E_INCORRECT_DXIL_METADATA);
    Subobjects.CreateStateObjectConfig(name, Flags);
    break;
  }
  case DXIL::SubobjectKind::LocalRootSignature:
    bLocalRS = true;
    LLVM_FALLTHROUGH;
  case DXIL::SubobjectKind::GlobalRootSignature: {
    const MDNode *pDataMDWrapper = dyn_cast<MDNode>(MD.getOperand(i++));
    IFTBOOL(pDataMDWrapper != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    IFTBOOL(pDataMDWrapper->getNumOperands() == 1,
            DXC_E_INCORRECT_DXIL_METADATA);
    const ConstantAsMetadata *pDataMD =
        dyn_cast<ConstantAsMetadata>(pDataMDWrapper->getOperand(0));
    const ConstantDataArray *pData =
        dyn_cast<ConstantDataArray>(pDataMD->getValue());
    IFTBOOL(pData != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
    IFTBOOL(pData->getElementType() == Type::getInt8Ty(m_Ctx),
            DXC_E_INCORRECT_DXIL_METADATA);
    IFTBOOL(pData->getRawDataValues().size() < UINT_MAX &&
                (pData->getRawDataValues().size() & 3) == 0,
            DXC_E_INCORRECT_DXIL_METADATA);
    const void *Data = pData->getRawDataValues().begin();
    uint32_t Size = pData->getRawDataValues().size();
    StringRef Text(StringMDToStringRef(MD.getOperand(i++)));
    Subobjects.CreateRootSignature(name, bLocalRS, Data, Size,
                                   Text.size() ? &Text : nullptr);
    break;
  }
  case DXIL::SubobjectKind::SubobjectToExportsAssociation: {
    StringRef Subobj(StringMDToStringRef(MD.getOperand(i++)));
    const MDNode *exportMD = dyn_cast<MDNode>(MD.getOperand(i++));
    SmallVector<StringRef, 4> Exports;
    for (unsigned iExport = 0; iExport < exportMD->getNumOperands();
         iExport++) {
      Exports.push_back(StringMDToStringRef(exportMD->getOperand(iExport)));
    }
    Subobjects.CreateSubobjectToExportsAssociation(name, Subobj, Exports.data(),
                                                   Exports.size());
    break;
  }
  case DXIL::SubobjectKind::RaytracingShaderConfig: {
    uint32_t MaxPayloadSizeInBytes = ConstMDToUint32(MD.getOperand(i++));
    ;
    uint32_t MaxAttributeSizeInBytes = ConstMDToUint32(MD.getOperand(i++));
    ;
    Subobjects.CreateRaytracingShaderConfig(name, MaxPayloadSizeInBytes,
                                            MaxAttributeSizeInBytes);
    break;
  }
  case DXIL::SubobjectKind::RaytracingPipelineConfig: {
    uint32_t MaxTraceRecursionDepth = ConstMDToUint32(MD.getOperand(i++));
    ;
    Subobjects.CreateRaytracingPipelineConfig(name, MaxTraceRecursionDepth);
    break;
  }
  case DXIL::SubobjectKind::HitGroup: {
    uint32_t hgType = ConstMDToUint32(MD.getOperand(i++));
    StringRef Intersection(StringMDToStringRef(MD.getOperand(i++)));
    StringRef AnyHit(StringMDToStringRef(MD.getOperand(i++)));
    StringRef ClosestHit(StringMDToStringRef(MD.getOperand(i++)));
    Subobjects.CreateHitGroup(name, (DXIL::HitGroupType)hgType, AnyHit,
                              ClosestHit, Intersection);
    break;
  }
  case DXIL::SubobjectKind::RaytracingPipelineConfig1: {
    uint32_t MaxTraceRecursionDepth = ConstMDToUint32(MD.getOperand(i++));
    uint32_t Flags = ConstMDToUint32(MD.getOperand(i++));
    IFTBOOL(0 ==
                ((~(uint32_t)DXIL::RaytracingPipelineFlags::ValidMask) & Flags),
            DXC_E_INCORRECT_DXIL_METADATA);
    Subobjects.CreateRaytracingPipelineConfig1(name, MaxTraceRecursionDepth,
                                               Flags);
    break;
  }
  default:
    DXASSERT(false, "otherwise, we didn't handle a valid subobject kind");
    m_bExtraMetadata = true;
    break;
  }
}

MDTuple *DxilMDHelper::EmitDxilSampler(const DxilSampler &S) {
  Metadata *MDVals[kDxilSamplerNumFields];

  EmitDxilResourceBase(S, &MDVals[0]);

  // Sampler-specific fields.
  MDVals[kDxilSamplerType] = Uint32ToConstMD((unsigned)S.GetSamplerKind());

  // Name-value list of extended properties.
  MDVals[kDxilSamplerNameValueList] = nullptr;
  vector<Metadata *> MDExtraVals;
  m_ExtraPropertyHelper->EmitSamplerProperties(S, MDExtraVals);
  if (!MDExtraVals.empty()) {
    MDVals[kDxilSamplerNameValueList] = MDNode::get(m_Ctx, MDExtraVals);
  }

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilSampler(const MDOperand &MDO, DxilSampler &S) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilSamplerNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  LoadDxilResourceBase(MDO, S);

  // Sampler-specific fields.
  S.SetSamplerKind((DxilSampler::SamplerKind)ConstMDToUint32(
      pTupleMD->getOperand(kDxilSamplerType)));

  // Name-value list of extended properties.
  m_ExtraPropertyHelper->m_bExtraMetadata = false;
  m_ExtraPropertyHelper->LoadSamplerProperties(
      pTupleMD->getOperand(kDxilSamplerNameValueList), S);
  m_bExtraMetadata |= m_ExtraPropertyHelper->m_bExtraMetadata;
}

//
// DxilExtraPropertyHelper shader-specific methods.
//
MDTuple *DxilMDHelper::EmitDxilGSState(
    DXIL::InputPrimitive Primitive, unsigned MaxVertexCount,
    unsigned ActiveStreamMask, DXIL::PrimitiveTopology StreamPrimitiveTopology,
    unsigned GSInstanceCount) {
  Metadata *MDVals[kDxilGSStateNumFields];

  MDVals[kDxilGSStateInputPrimitive] = Uint32ToConstMD((unsigned)Primitive);
  MDVals[kDxilGSStateMaxVertexCount] = Uint32ToConstMD(MaxVertexCount);
  MDVals[kDxilGSStateActiveStreamMask] = Uint32ToConstMD(ActiveStreamMask);
  MDVals[kDxilGSStateOutputStreamTopology] =
      Uint32ToConstMD((unsigned)StreamPrimitiveTopology);
  MDVals[kDxilGSStateGSInstanceCount] = Uint32ToConstMD(GSInstanceCount);

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilGSState(
    const MDOperand &MDO, DXIL::InputPrimitive &Primitive,
    unsigned &MaxVertexCount, unsigned &ActiveStreamMask,
    DXIL::PrimitiveTopology &StreamPrimitiveTopology,
    unsigned &GSInstanceCount) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilGSStateNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  Primitive = (DXIL::InputPrimitive)ConstMDToUint32(
      pTupleMD->getOperand(kDxilGSStateInputPrimitive));
  MaxVertexCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilGSStateMaxVertexCount));
  ActiveStreamMask =
      ConstMDToUint32(pTupleMD->getOperand(kDxilGSStateActiveStreamMask));
  StreamPrimitiveTopology = (DXIL::PrimitiveTopology)ConstMDToUint32(
      pTupleMD->getOperand(kDxilGSStateOutputStreamTopology));
  GSInstanceCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilGSStateGSInstanceCount));
}

MDTuple *DxilMDHelper::EmitDxilDSState(DXIL::TessellatorDomain Domain,
                                       unsigned InputControlPointCount) {
  Metadata *MDVals[kDxilDSStateNumFields];

  MDVals[kDxilDSStateTessellatorDomain] = Uint32ToConstMD((unsigned)Domain);
  MDVals[kDxilDSStateInputControlPointCount] =
      Uint32ToConstMD(InputControlPointCount);

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilDSState(const MDOperand &MDO,
                                   DXIL::TessellatorDomain &Domain,
                                   unsigned &InputControlPointCount) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilDSStateNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  Domain = (DXIL::TessellatorDomain)ConstMDToUint32(
      pTupleMD->getOperand(kDxilDSStateTessellatorDomain));
  InputControlPointCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilDSStateInputControlPointCount));
}

MDTuple *DxilMDHelper::EmitDxilHSState(
    Function *pPatchConstantFunction, unsigned InputControlPointCount,
    unsigned OutputControlPointCount, DXIL::TessellatorDomain TessDomain,
    DXIL::TessellatorPartitioning TessPartitioning,
    DXIL::TessellatorOutputPrimitive TessOutputPrimitive, float MaxTessFactor) {
  Metadata *MDVals[kDxilHSStateNumFields];

  MDVals[kDxilHSStatePatchConstantFunction] =
      ValueAsMetadata::get(pPatchConstantFunction);
  MDVals[kDxilHSStateInputControlPointCount] =
      Uint32ToConstMD(InputControlPointCount);
  MDVals[kDxilHSStateOutputControlPointCount] =
      Uint32ToConstMD(OutputControlPointCount);
  MDVals[kDxilHSStateTessellatorDomain] = Uint32ToConstMD((unsigned)TessDomain);
  MDVals[kDxilHSStateTessellatorPartitioning] =
      Uint32ToConstMD((unsigned)TessPartitioning);
  MDVals[kDxilHSStateTessellatorOutputPrimitive] =
      Uint32ToConstMD((unsigned)TessOutputPrimitive);
  MDVals[kDxilHSStateMaxTessellationFactor] = FloatToConstMD(MaxTessFactor);

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilHSState(
    const MDOperand &MDO, Function *&pPatchConstantFunction,
    unsigned &InputControlPointCount, unsigned &OutputControlPointCount,
    DXIL::TessellatorDomain &TessDomain,
    DXIL::TessellatorPartitioning &TessPartitioning,
    DXIL::TessellatorOutputPrimitive &TessOutputPrimitive,
    float &MaxTessFactor) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilHSStateNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  pPatchConstantFunction = dyn_cast<Function>(
      ValueMDToValue(pTupleMD->getOperand(kDxilHSStatePatchConstantFunction)));
  InputControlPointCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilHSStateInputControlPointCount));
  OutputControlPointCount = ConstMDToUint32(
      pTupleMD->getOperand(kDxilHSStateOutputControlPointCount));
  TessDomain = (DXIL::TessellatorDomain)ConstMDToUint32(
      pTupleMD->getOperand(kDxilHSStateTessellatorDomain));
  TessPartitioning = (DXIL::TessellatorPartitioning)ConstMDToUint32(
      pTupleMD->getOperand(kDxilHSStateTessellatorPartitioning));
  TessOutputPrimitive = (DXIL::TessellatorOutputPrimitive)ConstMDToUint32(
      pTupleMD->getOperand(kDxilHSStateTessellatorOutputPrimitive));
  MaxTessFactor =
      ConstMDToFloat(pTupleMD->getOperand(kDxilHSStateMaxTessellationFactor));
}

MDTuple *DxilMDHelper::EmitDxilMSState(const unsigned *NumThreads,
                                       unsigned MaxVertexCount,
                                       unsigned MaxPrimitiveCount,
                                       DXIL::MeshOutputTopology OutputTopology,
                                       unsigned payloadSizeInBytes) {
  Metadata *MDVals[kDxilMSStateNumFields];
  vector<Metadata *> NumThreadVals;

  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[0]));
  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[1]));
  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[2]));
  MDVals[kDxilMSStateNumThreads] = MDNode::get(m_Ctx, NumThreadVals);
  MDVals[kDxilMSStateMaxVertexCount] = Uint32ToConstMD(MaxVertexCount);
  MDVals[kDxilMSStateMaxPrimitiveCount] = Uint32ToConstMD(MaxPrimitiveCount);
  MDVals[kDxilMSStateOutputTopology] =
      Uint32ToConstMD((unsigned)OutputTopology);
  MDVals[kDxilMSStatePayloadSizeInBytes] = Uint32ToConstMD(payloadSizeInBytes);

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilMSState(const MDOperand &MDO, unsigned *NumThreads,
                                   unsigned &MaxVertexCount,
                                   unsigned &MaxPrimitiveCount,
                                   DXIL::MeshOutputTopology &OutputTopology,
                                   unsigned &payloadSizeInBytes) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilMSStateNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pNode = cast<MDNode>(pTupleMD->getOperand(kDxilMSStateNumThreads));
  NumThreads[0] = ConstMDToUint32(pNode->getOperand(0));
  NumThreads[1] = ConstMDToUint32(pNode->getOperand(1));
  NumThreads[2] = ConstMDToUint32(pNode->getOperand(2));
  MaxVertexCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilMSStateMaxVertexCount));
  MaxPrimitiveCount =
      ConstMDToUint32(pTupleMD->getOperand(kDxilMSStateMaxPrimitiveCount));
  OutputTopology = (DXIL::MeshOutputTopology)ConstMDToUint32(
      pTupleMD->getOperand(kDxilMSStateOutputTopology));
  payloadSizeInBytes =
      ConstMDToUint32(pTupleMD->getOperand(kDxilMSStatePayloadSizeInBytes));
}

MDTuple *DxilMDHelper::EmitDxilASState(const unsigned *NumThreads,
                                       unsigned payloadSizeInBytes) {
  Metadata *MDVals[kDxilASStateNumFields];
  vector<Metadata *> NumThreadVals;

  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[0]));
  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[1]));
  NumThreadVals.emplace_back(Uint32ToConstMD(NumThreads[2]));
  MDVals[kDxilASStateNumThreads] = MDNode::get(m_Ctx, NumThreadVals);
  MDVals[kDxilASStatePayloadSizeInBytes] = Uint32ToConstMD(payloadSizeInBytes);

  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::LoadDxilASState(const MDOperand &MDO, unsigned *NumThreads,
                                   unsigned &payloadSizeInBytes) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL(pTupleMD->getNumOperands() == kDxilASStateNumFields,
          DXC_E_INCORRECT_DXIL_METADATA);

  MDNode *pNode = cast<MDNode>(pTupleMD->getOperand(kDxilASStateNumThreads));
  NumThreads[0] = ConstMDToUint32(pNode->getOperand(0));
  NumThreads[1] = ConstMDToUint32(pNode->getOperand(1));
  NumThreads[2] = ConstMDToUint32(pNode->getOperand(2));
  payloadSizeInBytes =
      ConstMDToUint32(pTupleMD->getOperand(kDxilASStatePayloadSizeInBytes));
}

void DxilMDHelper::EmitDxilNodeState(std::vector<llvm::Metadata *> &MDVals,
                                     const DxilFunctionProps &props) {
  auto &Node = props.Node;

  // Required Fields
  MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeLaunchTypeTag));
  MDVals.emplace_back(Uint32ToConstMD(static_cast<unsigned>(Node.LaunchType)));

  // Optional Fields

  if (props.WaveSize.IsDefined()) {

    const hlsl::ShaderModel *SM = GetShaderModel();

    MDVals.emplace_back(
        Uint32ToConstMD(SM->IsSM68Plus() ? DxilMDHelper::kDxilRangedWaveSizeTag
                                         : DxilMDHelper::kDxilWaveSizeTag));
    SmallVector<Metadata *, 3> WaveSizeVal;
    WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Min));
    if (SM->IsSM68Plus()) {
      WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Max));
      WaveSizeVal.emplace_back(Uint32ToConstMD(props.WaveSize.Preferred));
    }
    MDVals.emplace_back(MDNode::get(m_Ctx, WaveSizeVal));
  }

  if (Node.IsProgramEntry) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeIsProgramEntryTag));
    MDVals.emplace_back(BoolToConstMD(true));
  }

  if (!props.NodeShaderID.Name.empty()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilNodeIdTag));
    vector<Metadata *> NodeIDVals;
    NodeIDVals.emplace_back(MDString::get(m_Ctx, props.NodeShaderID.Name));
    NodeIDVals.emplace_back(Uint32ToConstMD(props.NodeShaderID.Index));
    MDVals.emplace_back(MDNode::get(m_Ctx, NodeIDVals));
  }

  MDVals.emplace_back(
      Uint32ToConstMD(DxilMDHelper::kDxilNodeLocalRootArgumentsTableIndexTag));
  MDVals.emplace_back(Uint32ToConstMD(Node.LocalRootArgumentsTableIndex));

  if (!props.NodeShaderSharedInput.Name.empty()) {
    MDVals.emplace_back(Uint32ToConstMD(kDxilShareInputOfTag));
    vector<Metadata *> NodeIDVals;
    NodeIDVals.emplace_back(
        MDString::get(m_Ctx, props.NodeShaderSharedInput.Name));
    NodeIDVals.emplace_back(Uint32ToConstMD(props.NodeShaderSharedInput.Index));
    MDVals.emplace_back(MDNode::get(m_Ctx, NodeIDVals));
  }

  if (Node.DispatchGrid[0] || Node.DispatchGrid[1] || Node.DispatchGrid[2]) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeDispatchGridTag));
    vector<Metadata *> DispatchGridVals;
    DispatchGridVals.emplace_back(Uint32ToConstMD(Node.DispatchGrid[0]));
    DispatchGridVals.emplace_back(Uint32ToConstMD(Node.DispatchGrid[1]));
    DispatchGridVals.emplace_back(Uint32ToConstMD(Node.DispatchGrid[2]));
    MDVals.emplace_back(MDNode::get(m_Ctx, DispatchGridVals));
  }

  if (Node.MaxDispatchGrid[0] || Node.MaxDispatchGrid[1] ||
      Node.MaxDispatchGrid[2]) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeMaxDispatchGridTag));
    vector<Metadata *> MaxDispatchGridVals;
    MaxDispatchGridVals.emplace_back(Uint32ToConstMD(Node.MaxDispatchGrid[0]));
    MaxDispatchGridVals.emplace_back(Uint32ToConstMD(Node.MaxDispatchGrid[1]));
    MaxDispatchGridVals.emplace_back(Uint32ToConstMD(Node.MaxDispatchGrid[2]));
    MDVals.emplace_back(MDNode::get(m_Ctx, MaxDispatchGridVals));
  }

  if (Node.MaxRecursionDepth) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeMaxRecursionDepthTag));
    MDVals.emplace_back(Uint32ToConstMD(Node.MaxRecursionDepth));
  }

  if (props.InputNodes.size()) {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeInputsTag));
    vector<Metadata *> NodeInputVals;
    for (auto &InputNode : props.InputNodes)
      NodeInputVals.emplace_back(EmitDxilNodeIOState(InputNode));
    MDVals.emplace_back(MDNode::get(m_Ctx, NodeInputVals));
  }

  if (props.OutputNodes.size()) {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeOutputsTag));
    vector<Metadata *> NodeOutputVals;
    for (auto &OutputNode : props.OutputNodes)
      NodeOutputVals.emplace_back(EmitDxilNodeIOState(OutputNode));
    MDVals.emplace_back(MDNode::get(m_Ctx, NodeOutputVals));
  }
}

llvm::MDTuple *
DxilMDHelper::EmitDxilNodeRecordType(const NodeRecordType &RecordType) {
  vector<Metadata *> MDVals;
  MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeRecordSizeTag));
  MDVals.emplace_back(Uint32ToConstMD(RecordType.size));

  if (RecordType.SV_DispatchGrid.NumComponents) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeSVDispatchGridTag));
    vector<Metadata *> SVDispatchGridVals;
    SVDispatchGridVals.emplace_back(
        Uint32ToConstMD(RecordType.SV_DispatchGrid.ByteOffset));
    SVDispatchGridVals.emplace_back(Uint32ToConstMD(
        static_cast<unsigned>(RecordType.SV_DispatchGrid.ComponentType)));
    SVDispatchGridVals.emplace_back(
        Uint32ToConstMD(RecordType.SV_DispatchGrid.NumComponents));
    MDVals.emplace_back(MDNode::get(m_Ctx, SVDispatchGridVals));
  }
  if (RecordType.alignment) {
    MDVals.emplace_back(
        Uint32ToConstMD(DxilMDHelper::kDxilNodeRecordAlignmentTag));
    MDVals.emplace_back(Uint32ToConstMD(RecordType.alignment));
  }
  return MDNode::get(m_Ctx, MDVals);
}

llvm::MDTuple *
DxilMDHelper::EmitDxilNodeIOState(const hlsl::NodeIOProperties &Node) {
  vector<Metadata *> MDVals;
  MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeIOFlagsTag));
  MDVals.emplace_back(Uint32ToConstMD(Node.Flags));

  if (Node.RecordType.size) {
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeRecordTypeTag));
    MDVals.emplace_back(EmitDxilNodeRecordType(Node.RecordType));
  }

  if (Node.Flags.IsOutputNode()) {
    // Required Field
    MDVals.emplace_back(Uint32ToConstMD(DxilMDHelper::kDxilNodeMaxRecordsTag));
    MDVals.emplace_back(Uint32ToConstMD(Node.MaxRecords));

    if (Node.OutputArraySize) {
      MDVals.emplace_back(
          Uint32ToConstMD(DxilMDHelper::kDxilNodeOutputArraySizeTag));
      MDVals.emplace_back(Uint32ToConstMD(Node.OutputArraySize));
    }

    if (Node.MaxRecordsSharedWith >= 0) {
      MDVals.emplace_back(
          Uint32ToConstMD(DxilMDHelper::kDxilNodeMaxRecordsSharedWithTag));
      MDVals.emplace_back(Int32ToConstMD(Node.MaxRecordsSharedWith));
    }

    if (Node.AllowSparseNodes) {
      MDVals.emplace_back(
          Uint32ToConstMD(DxilMDHelper::kDxilNodeAllowSparseNodesTag));
      MDVals.emplace_back(BoolToConstMD((uint32_t)Node.AllowSparseNodes));
    }

    if (!Node.OutputID.Name.empty()) {
      MDVals.emplace_back(Uint32ToConstMD(kDxilNodeOutputIDTag));
      vector<Metadata *> NodeOpIDVals;
      NodeOpIDVals.emplace_back(MDString::get(m_Ctx, Node.OutputID.Name));
      NodeOpIDVals.emplace_back(Uint32ToConstMD(Node.OutputID.Index));
      MDVals.emplace_back(MDNode::get(m_Ctx, NodeOpIDVals));
    }
  } else {
    DXASSERT(Node.Flags.IsInputRecord(), "Invalid NodeIO Kind");
    if (Node.MaxRecords) {
      MDVals.emplace_back(
          Uint32ToConstMD(DxilMDHelper::kDxilNodeMaxRecordsTag));
      MDVals.emplace_back(Uint32ToConstMD(Node.MaxRecords));
    }
  }
  return MDNode::get(m_Ctx, MDVals);
}

NodeRecordType
DxilMDHelper::LoadDxilNodeRecordType(const llvm::MDOperand &MDO) {
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  NodeRecordType Record = {};
  for (unsigned iNode = 0; iNode < pTupleMD->getNumOperands(); iNode += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(iNode));
    const MDOperand &MDO = pTupleMD->getOperand(iNode + 1);
    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

    switch (Tag) {
    case DxilMDHelper::kDxilNodeRecordSizeTag: {
      Record.size = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilNodeSVDispatchGridTag: {
      MDTuple *pSVDTupleMD = cast<MDTuple>(MDO.get());
      // < 3 if fatal
      IFTBOOL(pSVDTupleMD->getNumOperands() >= 3,
              DXC_E_INCORRECT_DXIL_METADATA);
      // > 3 is extra metadata, validator will fail.
      if (pSVDTupleMD->getNumOperands() > 3)
        m_bExtraMetadata = true;
      Record.SV_DispatchGrid.ByteOffset =
          ConstMDToUint32(pSVDTupleMD->getOperand(0));
      Record.SV_DispatchGrid.ComponentType = static_cast<DXIL::ComponentType>(
          ConstMDToUint32(pSVDTupleMD->getOperand(1)));
      Record.SV_DispatchGrid.NumComponents =
          ConstMDToUint32(pSVDTupleMD->getOperand(2));
    } break;
    case DxilMDHelper::kDxilNodeRecordAlignmentTag: {
      Record.alignment = ConstMDToUint32(MDO);
    } break;
    default:
      m_bExtraMetadata = true;
      break;
    }
  }
  return Record;
}

NodeIOProperties DxilMDHelper::LoadDxilNodeIOState(const llvm::MDOperand &MDO) {
  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  NodeIOProperties Node = {};
  for (unsigned iNode = 0; iNode < pTupleMD->getNumOperands(); iNode += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(iNode));
    const MDOperand &MDO = pTupleMD->getOperand(iNode + 1);
    IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

    switch (Tag) {
    case DxilMDHelper::kDxilNodeIOFlagsTag: {
      Node.Flags = NodeFlags(ConstMDToUint32(MDO));
    } break;
    case DxilMDHelper::kDxilNodeRecordTypeTag: {
      Node.RecordType = LoadDxilNodeRecordType(MDO);
    } break;
    case DxilMDHelper::kDxilNodeOutputArraySizeTag: {
      Node.OutputArraySize = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilNodeMaxRecordsTag: {
      Node.MaxRecords = ConstMDToUint32(MDO);
    } break;
    case DxilMDHelper::kDxilNodeMaxRecordsSharedWithTag: {
      Node.MaxRecordsSharedWith = ConstMDToInt32(MDO);
    } break;
    case DxilMDHelper::kDxilNodeAllowSparseNodesTag: {
      Node.AllowSparseNodes = ConstMDToBool(MDO);
    } break;
    case DxilMDHelper::kDxilNodeOutputIDTag: {
      MDNode *pNode = cast<MDNode>(MDO.get());
      Node.OutputID.Name = StringMDToString(pNode->getOperand(0));
      Node.OutputID.Index = ConstMDToUint32(pNode->getOperand(1));
    } break;
    default:
      m_bExtraMetadata = true;
      break;
    }
  }
  return Node;
}

void DxilMDHelper::AddCounterIfNonZero(uint32_t value, StringRef name,
                                       vector<Metadata *> &MDVals) {
  if (value) {
    MDVals.emplace_back(MDString::get(m_Ctx, name));
    MDVals.emplace_back(Uint32ToConstMD(value));
  }
}

void DxilMDHelper::EmitDxilCounters(const DxilCounters &counters) {
  NamedMDNode *pDxilCountersMD =
      m_pModule->getNamedMetadata(kDxilCountersMDName);
  if (pDxilCountersMD)
    m_pModule->eraseNamedMetadata(pDxilCountersMD);

  vector<Metadata *> MDVals;
  // clang-format off
  // Python lines need to be not formatted.
  // <py::lines('OPCODE-COUNTERS')>['AddCounterIfNonZero(counters.%s, "%s", MDVals);' % (c,c) for c in hctdb_instrhelp.get_counters()]</py>
  // clang-format on
  // OPCODE-COUNTERS:BEGIN
  AddCounterIfNonZero(counters.array_local_bytes, "array_local_bytes", MDVals);
  AddCounterIfNonZero(counters.array_local_ldst, "array_local_ldst", MDVals);
  AddCounterIfNonZero(counters.array_static_bytes, "array_static_bytes",
                      MDVals);
  AddCounterIfNonZero(counters.array_static_ldst, "array_static_ldst", MDVals);
  AddCounterIfNonZero(counters.array_tgsm_bytes, "array_tgsm_bytes", MDVals);
  AddCounterIfNonZero(counters.array_tgsm_ldst, "array_tgsm_ldst", MDVals);
  AddCounterIfNonZero(counters.atomic, "atomic", MDVals);
  AddCounterIfNonZero(counters.barrier, "barrier", MDVals);
  AddCounterIfNonZero(counters.branches, "branches", MDVals);
  AddCounterIfNonZero(counters.fence, "fence", MDVals);
  AddCounterIfNonZero(counters.floats, "floats", MDVals);
  AddCounterIfNonZero(counters.gs_cut, "gs_cut", MDVals);
  AddCounterIfNonZero(counters.gs_emit, "gs_emit", MDVals);
  AddCounterIfNonZero(counters.insts, "insts", MDVals);
  AddCounterIfNonZero(counters.ints, "ints", MDVals);
  AddCounterIfNonZero(counters.sig_ld, "sig_ld", MDVals);
  AddCounterIfNonZero(counters.sig_st, "sig_st", MDVals);
  AddCounterIfNonZero(counters.tex_bias, "tex_bias", MDVals);
  AddCounterIfNonZero(counters.tex_cmp, "tex_cmp", MDVals);
  AddCounterIfNonZero(counters.tex_grad, "tex_grad", MDVals);
  AddCounterIfNonZero(counters.tex_load, "tex_load", MDVals);
  AddCounterIfNonZero(counters.tex_norm, "tex_norm", MDVals);
  AddCounterIfNonZero(counters.tex_store, "tex_store", MDVals);
  AddCounterIfNonZero(counters.uints, "uints", MDVals);
  // OPCODE-COUNTERS:END

  if (MDVals.size()) {
    pDxilCountersMD = m_pModule->getOrInsertNamedMetadata(kDxilCountersMDName);
    pDxilCountersMD->addOperand(MDNode::get(m_Ctx, MDVals));
  }
}

void DxilMDHelper::LoadCounterMD(const MDOperand &MDName,
                                 const MDOperand &MDValue,
                                 DxilCounters &counters) const {
  StringRef name = StringMDToStringRef(MDName);
  uint32_t value = ConstMDToUint32(MDValue);
  uint32_t *counter = LookupByName(name, counters);
  if (counter)
    *counter = value;
}

void DxilMDHelper::LoadDxilCounters(DxilCounters &counters) const {
  ZeroMemory(&counters, sizeof(counters));
  if (NamedMDNode *pDxilCountersMD =
          m_pModule->getNamedMetadata(kDxilCountersMDName)) {
    MDNode *pMDCounters = pDxilCountersMD->getOperand(0);
    for (unsigned i = 0; i < pMDCounters->getNumOperands(); i += 2) {
      LoadCounterMD(pMDCounters->getOperand(i), pMDCounters->getOperand(i + 1),
                    counters);
    }
  }
}

//
// DxilExtraPropertyHelper methods.
//
DxilMDHelper::ExtraPropertyHelper::ExtraPropertyHelper(Module *pModule)
    : m_Ctx(pModule->getContext()), m_pModule(pModule),
      m_bExtraMetadata(false) {}

DxilExtraPropertyHelper::DxilExtraPropertyHelper(Module *pModule)
    : ExtraPropertyHelper(pModule) {}

void DxilExtraPropertyHelper::EmitSRVProperties(
    const DxilResource &SRV, std::vector<Metadata *> &MDVals) {
  // Element type for typed resource.
  if (!SRV.IsStructuredBuffer() && !SRV.IsRawBuffer()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilTypedBufferElementTypeTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        (unsigned)SRV.GetCompType().GetKind(), m_Ctx));
  }
  // Element stride for structured buffer.
  if (SRV.IsStructuredBuffer()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilStructuredBufferElementStrideTag, m_Ctx));
    MDVals.emplace_back(
        DxilMDHelper::Uint32ToConstMD(SRV.GetElementStride(), m_Ctx));
  }
}

void DxilExtraPropertyHelper::LoadSRVProperties(const MDOperand &MDO,
                                                DxilResource &SRV) {
  SRV.SetElementStride(SRV.IsRawBuffer() ? 1 : 4);
  SRV.SetCompType(CompType());

  if (MDO.get() == nullptr) {
    return;
  }

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);

    switch (Tag) {
    case DxilMDHelper::kDxilTypedBufferElementTypeTag:
      DXASSERT_NOMSG(!SRV.IsStructuredBuffer() && !SRV.IsRawBuffer());
      SRV.SetCompType(CompType(DxilMDHelper::ConstMDToUint32(MDO)));
      break;
    case DxilMDHelper::kDxilStructuredBufferElementStrideTag:
      DXASSERT_NOMSG(SRV.IsStructuredBuffer());
      SRV.SetElementStride(DxilMDHelper::ConstMDToUint32(MDO));
      break;
    default:
      DXASSERT(false, "Unknown resource record tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

void DxilExtraPropertyHelper::EmitUAVProperties(
    const DxilResource &UAV, std::vector<Metadata *> &MDVals) {
  // Element type for typed RW resource.
  if (!UAV.IsStructuredBuffer() && !UAV.IsRawBuffer() &&
      !UAV.GetCompType().IsInvalid()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilTypedBufferElementTypeTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        (unsigned)UAV.GetCompType().GetKind(), m_Ctx));
  }
  // Element stride for structured RW buffer.
  if (UAV.IsStructuredBuffer()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilStructuredBufferElementStrideTag, m_Ctx));
    MDVals.emplace_back(
        DxilMDHelper::Uint32ToConstMD(UAV.GetElementStride(), m_Ctx));
  }
  // Sampler feedback kind
  if (UAV.IsFeedbackTexture()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilSamplerFeedbackKindTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        (unsigned)UAV.GetSamplerFeedbackType(), m_Ctx));
  }
  // Whether resource is used for 64-bit atomic op
  if (DXIL::CompareVersions(m_ValMajor, m_ValMinor, 1, 6) >= 0 &&
      UAV.HasAtomic64Use()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilAtomic64UseTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD((unsigned)true, m_Ctx));
  }
  // Whether resource is reordercoherent.
  if (DXIL::CompareVersions(m_ValMajor, m_ValMinor, 1, 9) >= 0 &&
      UAV.IsReorderCoherent()) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilReorderCoherentTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::BoolToConstMD(true, m_Ctx));
  }
}

void DxilExtraPropertyHelper::LoadUAVProperties(const MDOperand &MDO,
                                                DxilResource &UAV) {
  UAV.SetElementStride(UAV.IsRawBuffer() ? 1 : 4);
  UAV.SetCompType(CompType());

  if (MDO.get() == nullptr) {
    return;
  }

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);

    switch (Tag) {
    case DxilMDHelper::kDxilTypedBufferElementTypeTag:
      DXASSERT_NOMSG(!UAV.IsStructuredBuffer() && !UAV.IsRawBuffer());
      UAV.SetCompType(CompType(DxilMDHelper::ConstMDToUint32(MDO)));
      break;
    case DxilMDHelper::kDxilStructuredBufferElementStrideTag:
      DXASSERT_NOMSG(UAV.IsStructuredBuffer());
      UAV.SetElementStride(DxilMDHelper::ConstMDToUint32(MDO));
      break;
    case DxilMDHelper::kDxilSamplerFeedbackKindTag:
      DXASSERT_NOMSG(UAV.IsFeedbackTexture());
      UAV.SetSamplerFeedbackType(
          (DXIL::SamplerFeedbackType)DxilMDHelper::ConstMDToUint32(MDO));
      break;
    case DxilMDHelper::kDxilAtomic64UseTag:
      UAV.SetHasAtomic64Use(DxilMDHelper::ConstMDToBool(MDO));
      break;
    case DxilMDHelper::kDxilReorderCoherentTag:
      UAV.SetReorderCoherent(DxilMDHelper::ConstMDToBool(MDO));
      break;
    default:
      DXASSERT(false, "Unknown resource record tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

void DxilExtraPropertyHelper::EmitCBufferProperties(
    const DxilCBuffer &CB, vector<Metadata *> &MDVals) {
  // Emit property to preserve tbuffer kind
  if (CB.GetKind() == DXIL::ResourceKind::TBuffer) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kHLCBufferIsTBufferTag, m_Ctx));
    MDVals.emplace_back(DxilMDHelper::BoolToConstMD(true, m_Ctx));
  }
}

void DxilExtraPropertyHelper::LoadCBufferProperties(const MDOperand &MDO,
                                                    DxilCBuffer &CB) {
  if (MDO.get() == nullptr)
    return;

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  // Override kind for tbuffer that has not yet been converted to SRV.
  CB.SetKind(DXIL::ResourceKind::CBuffer);
  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);

    switch (Tag) {
    case DxilMDHelper::kHLCBufferIsTBufferTag:
      if (DxilMDHelper::ConstMDToBool(MDO)) {
        CB.SetKind(DXIL::ResourceKind::TBuffer);
      }
      break;
    default:
      DXASSERT(false, "Unknown cbuffer tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

void DxilExtraPropertyHelper::EmitSamplerProperties(
    const DxilSampler &S, std::vector<Metadata *> &MDVals) {
  // Nothing yet.
}

void DxilExtraPropertyHelper::LoadSamplerProperties(const MDOperand &MDO,
                                                    DxilSampler &S) {
  // Nothing yet.
}

void DxilExtraPropertyHelper::EmitSignatureElementProperties(
    const DxilSignatureElement &SE, vector<Metadata *> &MDVals) {
  // Output stream, if non-zero.
  if (SE.GetOutputStream() != 0) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilSignatureElementOutputStreamTag, m_Ctx));
    MDVals.emplace_back(
        DxilMDHelper::Uint32ToConstMD(SE.GetOutputStream(), m_Ctx));
  }

  // Mask of Dynamically indexed components.
  if (SE.GetDynIdxCompMask() != 0) {
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilSignatureElementDynIdxCompMaskTag, m_Ctx));
    MDVals.emplace_back(
        DxilMDHelper::Uint32ToConstMD(SE.GetDynIdxCompMask(), m_Ctx));
  }

  if (SE.GetUsageMask() != 0 &&
      DXIL::CompareVersions(m_ValMajor, m_ValMinor, 1, 5) >= 0) {
    // Emitting this will not hurt old reatil loader (only asserts),
    // and is required for signatures to match in validation.
    MDVals.emplace_back(DxilMDHelper::Uint32ToConstMD(
        DxilMDHelper::kDxilSignatureElementUsageCompMaskTag, m_Ctx));
    MDVals.emplace_back(
        DxilMDHelper::Uint32ToConstMD(SE.GetUsageMask(), m_Ctx));
  }
}

void DxilExtraPropertyHelper::LoadSignatureElementProperties(
    const MDOperand &MDO, DxilSignatureElement &SE) {
  if (MDO.get() == nullptr)
    return;

  const MDTuple *pTupleMD = dyn_cast<MDTuple>(MDO.get());
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  IFTBOOL((pTupleMD->getNumOperands() & 0x1) == 0,
          DXC_E_INCORRECT_DXIL_METADATA);

  // Stream.
  for (unsigned i = 0; i < pTupleMD->getNumOperands(); i += 2) {
    unsigned Tag = DxilMDHelper::ConstMDToUint32(pTupleMD->getOperand(i));
    const MDOperand &MDO = pTupleMD->getOperand(i + 1);

    switch (Tag) {
    case DxilMDHelper::kDxilSignatureElementOutputStreamTag:
      SE.SetOutputStream(DxilMDHelper::ConstMDToUint32(MDO));
      break;
    case DxilMDHelper::kHLSignatureElementGlobalSymbolTag:
      break;
    case DxilMDHelper::kDxilSignatureElementDynIdxCompMaskTag:
      SE.SetDynIdxCompMask(DxilMDHelper::ConstMDToUint32(MDO));
      break;
    case DxilMDHelper::kDxilSignatureElementUsageCompMaskTag:
      SE.SetUsageMask(DxilMDHelper::ConstMDToUint32(MDO));
      break;
    default:
      DXASSERT(false, "Unknown signature element tag");
      m_bExtraMetadata = true;
      break;
    }
  }
}

//
// Utilities.
//
bool DxilMDHelper::IsKnownNamedMetaData(const llvm::NamedMDNode &Node) {
  StringRef name = Node.getName();
  for (unsigned i = 0; i < DxilMDNames.size(); i++) {
    if (name == DxilMDNames[i]) {
      return true;
    }
  }
  return false;
}

bool DxilMDHelper::IsKnownMetadataID(LLVMContext &Ctx, unsigned ID) {
  SmallVector<unsigned, 2> IDs;
  GetKnownMetadataIDs(Ctx, &IDs);
  return std::find(IDs.begin(), IDs.end(), ID) != IDs.end();
}

void DxilMDHelper::GetKnownMetadataIDs(LLVMContext &Ctx,
                                       SmallVectorImpl<unsigned> *pIDs) {
  SmallVector<StringRef, 4> Names;
  Ctx.getMDKindNames(Names);
  for (auto Name : Names) {
    if (Name == hlsl::DxilMDHelper::kDxilPreciseAttributeMDName ||
        Name == hlsl::DxilMDHelper::kDxilNonUniformAttributeMDName) {
      pIDs->push_back(Ctx.getMDKindID(Name));
    }
  }
}

void DxilMDHelper::combineDxilMetadata(llvm::Instruction *K,
                                       const llvm::Instruction *J) {
  if (IsMarkedNonUniform(J))
    MarkNonUniform(K);
  if (IsMarkedPrecise(J))
    MarkPrecise(K);
}

ConstantAsMetadata *DxilMDHelper::Int32ToConstMD(int32_t v, LLVMContext &Ctx) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(Ctx, 32), APInt(32, v)));
}

ConstantAsMetadata *DxilMDHelper::Int32ToConstMD(int32_t v) {
  return DxilMDHelper::Int32ToConstMD(v, m_Ctx);
}

ConstantAsMetadata *DxilMDHelper::Uint32ToConstMD(unsigned v,
                                                  LLVMContext &Ctx) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(Ctx, 32), APInt(32, v)));
}

ConstantAsMetadata *DxilMDHelper::Uint32ToConstMD(unsigned v) {
  return DxilMDHelper::Uint32ToConstMD(v, m_Ctx);
}

ConstantAsMetadata *DxilMDHelper::Uint64ToConstMD(uint64_t v,
                                                  LLVMContext &Ctx) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(Ctx, 64), APInt(64, v)));
}
ConstantAsMetadata *DxilMDHelper::Uint64ToConstMD(uint64_t v) {
  return DxilMDHelper::Uint64ToConstMD(v, m_Ctx);
}
ConstantAsMetadata *DxilMDHelper::Int8ToConstMD(int8_t v) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(m_Ctx, 8), APInt(8, v)));
}
ConstantAsMetadata *DxilMDHelper::Uint8ToConstMD(uint8_t v) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(m_Ctx, 8), APInt(8, v)));
}

ConstantAsMetadata *DxilMDHelper::BoolToConstMD(bool v, LLVMContext &Ctx) {
  return ConstantAsMetadata::get(
      Constant::getIntegerValue(IntegerType::get(Ctx, 1), APInt(1, v ? 1 : 0)));
}
ConstantAsMetadata *DxilMDHelper::BoolToConstMD(bool v) {
  return DxilMDHelper::BoolToConstMD(v, m_Ctx);
}

ConstantAsMetadata *DxilMDHelper::FloatToConstMD(float v) {
  return ConstantAsMetadata::get(ConstantFP::get(m_Ctx, APFloat(v)));
}

int32_t DxilMDHelper::ConstMDToInt32(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return (int32_t)pConst->getZExtValue();
}

unsigned DxilMDHelper::ConstMDToUint32(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return (unsigned)pConst->getZExtValue();
}

uint64_t DxilMDHelper::ConstMDToUint64(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pConst->getZExtValue();
}

int8_t DxilMDHelper::ConstMDToInt8(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return (int8_t)pConst->getZExtValue();
}

uint8_t DxilMDHelper::ConstMDToUint8(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return (uint8_t)pConst->getZExtValue();
}

bool DxilMDHelper::ConstMDToBool(const MDOperand &MDO) {
  ConstantInt *pConst = mdconst::extract<ConstantInt>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pConst->getZExtValue() != 0;
}

float DxilMDHelper::ConstMDToFloat(const MDOperand &MDO) {
  ConstantFP *pConst = mdconst::extract<ConstantFP>(MDO);
  IFTBOOL(pConst != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pConst->getValueAPF().convertToFloat();
}

string DxilMDHelper::StringMDToString(const MDOperand &MDO) {
  MDString *pMDString = dyn_cast<MDString>(MDO.get());
  IFTBOOL(pMDString != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pMDString->getString().str();
}

StringRef DxilMDHelper::StringMDToStringRef(const MDOperand &MDO) {
  MDString *pMDString = dyn_cast<MDString>(MDO.get());
  IFTBOOL(pMDString != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pMDString->getString();
}

Value *DxilMDHelper::ValueMDToValue(const MDOperand &MDO) {
  IFTBOOL(MDO.get() != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  ValueAsMetadata *pValAsMD = dyn_cast<ValueAsMetadata>(MDO.get());
  IFTBOOL(pValAsMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  Value *pValue = pValAsMD->getValue();
  IFTBOOL(pValue != nullptr, DXC_E_INCORRECT_DXIL_METADATA);
  return pValue;
}

MDTuple *
DxilMDHelper::Uint32VectorToConstMDTuple(const std::vector<unsigned> &Vec) {
  vector<Metadata *> MDVals;

  MDVals.resize(Vec.size());
  for (size_t i = 0; i < Vec.size(); i++) {
    MDVals[i] = Uint32ToConstMD(Vec[i]);
  }
  return MDNode::get(m_Ctx, MDVals);
}

void DxilMDHelper::ConstMDTupleToUint32Vector(MDTuple *pTupleMD,
                                              std::vector<unsigned> &Vec) {
  IFTBOOL(pTupleMD != nullptr, DXC_E_INCORRECT_DXIL_METADATA);

  Vec.resize(pTupleMD->getNumOperands());
  for (size_t i = 0; i < pTupleMD->getNumOperands(); i++) {
    Vec[i] = ConstMDToUint32(pTupleMD->getOperand(i));
  }
}

bool DxilMDHelper::IsMarkedPrecise(const Instruction *inst) {
  int32_t val = 0;
  if (MDNode *precise = inst->getMetadata(kDxilPreciseAttributeMDName)) {
    assert(precise->getNumOperands() == 1);
    val = ConstMDToInt32(precise->getOperand(0));
  }
  return val;
}

void DxilMDHelper::MarkPrecise(Instruction *I) {
  LLVMContext &Ctx = I->getContext();
  MDNode *preciseNode = MDNode::get(
      Ctx,
      {ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Ctx), 1))});

  I->setMetadata(DxilMDHelper::kDxilPreciseAttributeMDName, preciseNode);
}

bool DxilMDHelper::IsMarkedNonUniform(const Instruction *inst) {
  int32_t val = 0;
  if (MDNode *precise = inst->getMetadata(kDxilNonUniformAttributeMDName)) {
    assert(precise->getNumOperands() == 1);
    val = ConstMDToInt32(precise->getOperand(0));
  }
  return val;
}

void DxilMDHelper::MarkNonUniform(Instruction *I) {
  LLVMContext &Ctx = I->getContext();
  MDNode *preciseNode = MDNode::get(
      Ctx,
      {ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(Ctx), 1))});

  I->setMetadata(DxilMDHelper::kDxilNonUniformAttributeMDName, preciseNode);
}

bool DxilMDHelper::GetVariableDebugLayout(
    llvm::DbgDeclareInst *inst, unsigned &StartOffsetInBits,
    std::vector<DxilDIArrayDim> &ArrayDims) {
  llvm::MDTuple *Tuple = dyn_cast_or_null<MDTuple>(
      inst->getMetadata(DxilMDHelper::kDxilVariableDebugLayoutMDName));
  if (Tuple == nullptr)
    return false;

  IFTBOOL(Tuple->getNumOperands() % 2 == 1, DXC_E_INCORRECT_DXIL_METADATA);

  StartOffsetInBits = ConstMDToUint32(Tuple->getOperand(0));

  for (unsigned Idx = 1; Idx < Tuple->getNumOperands(); Idx += 2) {
    DxilDIArrayDim ArrayDim = {};
    ArrayDim.StrideInBits = ConstMDToUint32(Tuple->getOperand(Idx + 0));
    ArrayDim.NumElements = ConstMDToUint32(Tuple->getOperand(Idx + 1));
    ArrayDims.emplace_back(ArrayDim);
  }

  return true;
}

void DxilMDHelper::SetVariableDebugLayout(
    llvm::DbgDeclareInst *inst, unsigned StartOffsetInBits,
    const std::vector<DxilDIArrayDim> &ArrayDims) {
  LLVMContext &Ctx = inst->getContext();

  std::vector<Metadata *> MDVals;
  MDVals.reserve(ArrayDims.size() + 1);
  MDVals.emplace_back(Uint32ToConstMD(StartOffsetInBits, Ctx));
  for (const DxilDIArrayDim &ArrayDim : ArrayDims) {
    MDVals.emplace_back(Uint32ToConstMD(ArrayDim.StrideInBits, Ctx));
    MDVals.emplace_back(Uint32ToConstMD(ArrayDim.NumElements, Ctx));
  }

  inst->setMetadata(DxilMDHelper::kDxilVariableDebugLayoutMDName,
                    MDNode::get(Ctx, MDVals));
}

void DxilMDHelper::CopyMetadata(Instruction &I, Instruction &SrcInst,
                                ArrayRef<unsigned> WL) {
  if (!SrcInst.hasMetadata())
    return;

  DenseSet<unsigned> WLS;
  for (unsigned M : WL)
    WLS.insert(M);

  // Otherwise, enumerate and copy over metadata from the old instruction to the
  // new one.
  SmallVector<std::pair<unsigned, MDNode *>, 4> TheMDs;
  SrcInst.getAllMetadataOtherThanDebugLoc(TheMDs);
  for (const auto &MD : TheMDs) {
    if (WL.empty() || WLS.count(MD.first))
      I.setMetadata(MD.first, MD.second);
  }
  if (WL.empty() || WLS.count(LLVMContext::MD_dbg))
    I.setDebugLoc(SrcInst.getDebugLoc());
}

} // namespace hlsl
