//===- DXILEmitter.cpp - DXIL operation Emitter ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// DXILEmitter uses the descriptions of DXIL operation to construct enum and
// helper functions for DXIL operation.
//
//===----------------------------------------------------------------------===//

#include "Basic/SequenceToOffsetTable.h"
#include "Common/CodeGenTarget.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/DXILABI.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"

#include <string>
#include <vector>

using namespace llvm;
using namespace llvm::dxil;

namespace {

struct DXILIntrinsicSelect {
  StringRef Intrinsic;
  SmallVector<const Record *> ArgSelectRecords;
};

static StringRef StripIntrinArgSelectTypePrefix(StringRef Type) {
  StringRef Prefix = "IntrinArgSelect_";
  if (!Type.starts_with(Prefix)) {
    PrintFatalError("IntrinArgSelectType definintion must be prefixed with "
                    "'IntrinArgSelect_'");
  }
  return Type.substr(Prefix.size());
}

struct DXILOperationDesc {
  std::string OpName; // name of DXIL operation
  int OpCode;         // ID of DXIL operation
  StringRef OpClass;  // name of the opcode class
  StringRef Doc;      // the documentation description of this instruction
  // Vector of operand type records - return type is at index 0
  SmallVector<const Record *> OpTypes;
  SmallVector<const Record *> OverloadRecs;
  SmallVector<const Record *> StageRecs;
  SmallVector<const Record *> AttrRecs;
  SmallVector<DXILIntrinsicSelect> IntrinsicSelects;
  SmallVector<StringRef, 4>
      ShaderStages; // shader stages to which this applies, empty for all.
  int OverloadParamIndex;             // Index of parameter with overload type.
                                      //   -1 : no overload types
  SmallVector<StringRef, 4> Counters; // counters for this inst.
  DXILOperationDesc(const Record *);
};
} // end anonymous namespace

/// In-place sort TableGen records of class with a field
///    Version dxil_version
/// in the ascending version order.
static void ascendingSortByVersion(std::vector<const Record *> &Recs) {
  sort(Recs, [](const Record *RecA, const Record *RecB) {
    unsigned RecAMaj =
        RecA->getValueAsDef("dxil_version")->getValueAsInt("Major");
    unsigned RecAMin =
        RecA->getValueAsDef("dxil_version")->getValueAsInt("Minor");
    unsigned RecBMaj =
        RecB->getValueAsDef("dxil_version")->getValueAsInt("Major");
    unsigned RecBMin =
        RecB->getValueAsDef("dxil_version")->getValueAsInt("Minor");

    return (VersionTuple(RecAMaj, RecAMin) < VersionTuple(RecBMaj, RecBMin));
  });
}

/// Take a `int_{intrinsic_name}` and return just the intrinsic_name part if
/// available. Otherwise return the empty string.
static StringRef GetIntrinsicName(const RecordVal *RV) {
  if (RV && RV->getValue()) {
    if (const DefInit *DI = dyn_cast<DefInit>(RV->getValue())) {
      auto *IntrinsicDef = DI->getDef();
      auto DefName = IntrinsicDef->getName();
      assert(DefName.starts_with("int_") && "invalid intrinsic name");
      // Remove the int_ from intrinsic name.
      return DefName.substr(4);
    }
  }
  return "";
}

/// Construct an object using the DXIL Operation records specified
/// in DXIL.td. This serves as the single source of reference of
/// the information extracted from the specified Record R, for
/// C++ code generated by this TableGen backend.
//  \param R Object representing TableGen record of a DXIL Operation
DXILOperationDesc::DXILOperationDesc(const Record *R) {
  OpName = R->getNameInitAsString();
  OpCode = R->getValueAsInt("OpCode");

  Doc = R->getValueAsString("Doc");
  SmallVector<const Record *> ParamTypeRecs;

  ParamTypeRecs.push_back(R->getValueAsDef("result"));

  llvm::append_range(ParamTypeRecs, R->getValueAsListOfDefs("arguments"));
  size_t ParamTypeRecsSize = ParamTypeRecs.size();
  // Populate OpTypes with return type and parameter types

  // Parameter indices of overloaded parameters.
  // This vector contains overload parameters in the order used to
  // resolve an LLVMMatchType in accordance with  convention outlined in
  // the comment before the definition of class LLVMMatchType in
  // llvm/IR/Intrinsics.td
  OverloadParamIndex = -1; // A sigil meaning none.
  for (unsigned I = 0; I < ParamTypeRecsSize; I++) {
    const Record *TR = ParamTypeRecs[I];
    // Track operation parameter indices of any overload types
    if (TR->getValueAsInt("isOverload")) {
      if (OverloadParamIndex != -1) {
        assert(TR == ParamTypeRecs[OverloadParamIndex] &&
               "Specification of multiple differing overload parameter types "
               "is not supported");
      }
      // Keep the earliest parameter index we see, but if it was the return type
      // overwrite it with the first overloaded argument.
      if (OverloadParamIndex <= 0)
        OverloadParamIndex = I;
    }
    OpTypes.emplace_back(TR);
  }

  // Get overload records
  std::vector<const Record *> Recs = R->getValueAsListOfDefs("overloads");

  // Sort records in ascending order of DXIL version
  ascendingSortByVersion(Recs);

  llvm::append_range(OverloadRecs, Recs);

  // Get stage records
  Recs = R->getValueAsListOfDefs("stages");

  if (Recs.empty()) {
    PrintFatalError(R, Twine("Atleast one specification of valid stage for ") +
                           OpName + " is required");
  }

  // Sort records in ascending order of DXIL version
  ascendingSortByVersion(Recs);

  llvm::append_range(StageRecs, Recs);

  // Get attribute records
  Recs = R->getValueAsListOfDefs("attributes");

  // Sort records in ascending order of DXIL version
  ascendingSortByVersion(Recs);

  llvm::append_range(AttrRecs, Recs);

  // Get the operation class
  OpClass = R->getValueAsDef("OpClass")->getName();

  if (OpClass.str() == "UnknownOpClass")
    PrintFatalError(R, Twine("Unspecified DXIL OpClass for DXIL operation - ") +
                           OpName);

  auto IntrinsicSelectRecords = R->getValueAsListOfDefs("intrinsics");
  if (IntrinsicSelectRecords.size()) {
    for (const Record *R : IntrinsicSelectRecords) {
      DXILIntrinsicSelect IntrSelect;
      IntrSelect.Intrinsic = GetIntrinsicName(R->getValue("intrinsic"));
      auto Args = R->getValueAsListOfDefs("arg_selects");
      for (const Record *ArgSelect : Args) {
        IntrSelect.ArgSelectRecords.emplace_back(ArgSelect);
      }
      IntrinsicSelects.emplace_back(std::move(IntrSelect));
    }
  }
}

/// Return a string representation of OverloadKind enum that maps to
/// input LLVMType record
/// \param R TableGen def record of class LLVMType
/// \return std::string string representation of OverloadKind

static StringRef getOverloadKindStr(const Record *R) {
  // TODO: This is a hack. We need to rework how we're handling the set of
  // overloads to avoid this business with the separate OverloadKind enum.
  return StringSwitch<StringRef>(R->getName())
      .Case("HalfTy", "OverloadKind::HALF")
      .Case("FloatTy", "OverloadKind::FLOAT")
      .Case("DoubleTy", "OverloadKind::DOUBLE")
      .Case("Int1Ty", "OverloadKind::I1")
      .Case("Int8Ty", "OverloadKind::I8")
      .Case("Int16Ty", "OverloadKind::I16")
      .Case("Int32Ty", "OverloadKind::I32")
      .Case("Int64Ty", "OverloadKind::I64")
      .Case("ResRetHalfTy", "OverloadKind::HALF")
      .Case("ResRetFloatTy", "OverloadKind::FLOAT")
      .Case("ResRetDoubleTy", "OverloadKind::DOUBLE")
      .Case("ResRetInt16Ty", "OverloadKind::I16")
      .Case("ResRetInt32Ty", "OverloadKind::I32")
      .Case("ResRetInt64Ty", "OverloadKind::I64")
      .Case("CBufRetHalfTy", "OverloadKind::HALF")
      .Case("CBufRetFloatTy", "OverloadKind::FLOAT")
      .Case("CBufRetDoubleTy", "OverloadKind::DOUBLE")
      .Case("CBufRetInt16Ty", "OverloadKind::I16")
      .Case("CBufRetInt32Ty", "OverloadKind::I32")
      .Case("CBufRetInt64Ty", "OverloadKind::I64");
}

/// Return a string representation of valid overload information denoted
// by input records
//
/// \param Recs A vector of records of TableGen Overload records
/// \return std::string string representation of overload mask string
///         predicated by DXIL Version. E.g.,
//          {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...}
static std::string getOverloadMaskString(ArrayRef<const Record *> Recs) {
  std::string MaskString = "";
  std::string Prefix = "";
  MaskString.append("{");
  // If no overload information records were specified, assume the operation
  // a) to be supported in DXIL Version 1.0 and later
  // b) has no overload types
  if (Recs.empty()) {
    MaskString.append("{{1, 0}, OverloadKind::UNDEFINED}}");
  } else {
    for (const auto *Rec : Recs) {
      unsigned Major =
          Rec->getValueAsDef("dxil_version")->getValueAsInt("Major");
      unsigned Minor =
          Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor");
      MaskString.append(Prefix)
          .append("{{")
          .append(std::to_string(Major))
          .append(", ")
          .append(std::to_string(Minor).append("}, "));

      std::string PipePrefix = "";
      auto Tys = Rec->getValueAsListOfDefs("overload_types");
      if (Tys.empty()) {
        MaskString.append("OverloadKind::UNDEFINED");
      }
      for (const auto *Ty : Tys) {
        MaskString.append(PipePrefix).append(getOverloadKindStr(Ty));
        PipePrefix = " | ";
      }

      MaskString.append("}");
      Prefix = ", ";
    }
    MaskString.append("}");
  }
  return MaskString;
}

/// Return a string representation of valid shader stag information denoted
// by input records
//
/// \param Recs A vector of records of TableGen Stages records
/// \return std::string string representation of stages mask string
///         predicated by DXIL Version. E.g.,
//          {{{1, 0}, Mask1}, {{1, 2}, Mask2}, ...}
static std::string getStageMaskString(ArrayRef<const Record *> Recs) {
  std::string MaskString = "";
  std::string Prefix = "";
  MaskString.append("{");
  // Atleast one stage information record is expected to be specified.
  if (Recs.empty()) {
    PrintFatalError("Atleast one specification of valid stages for "
                    "operation must be specified");
  }

  for (const auto *Rec : Recs) {
    unsigned Major = Rec->getValueAsDef("dxil_version")->getValueAsInt("Major");
    unsigned Minor = Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor");
    MaskString.append(Prefix)
        .append("{{")
        .append(std::to_string(Major))
        .append(", ")
        .append(std::to_string(Minor).append("}, "));

    std::string PipePrefix = "";
    auto Stages = Rec->getValueAsListOfDefs("shader_stages");
    if (Stages.empty()) {
      PrintFatalError("No valid stages for operation specified");
    }
    for (const auto *S : Stages) {
      MaskString.append(PipePrefix).append("ShaderKind::").append(S->getName());
      PipePrefix = " | ";
    }

    MaskString.append("}");
    Prefix = ", ";
  }
  MaskString.append("}");
  return MaskString;
}

/// Emit a list valid DXIL Version records
static void emitDXILVersions(const RecordKeeper &Records, raw_ostream &OS) {
  OS << "#ifdef DXIL_VERSION\n";
  for (const Record *Version : Records.getAllDerivedDefinitions("Version")) {
    unsigned Major = Version->getValueAsInt("Major");
    unsigned Minor = Version->getValueAsInt("Minor");
    OS << "DXIL_VERSION(";
    OS << std::to_string(Major) << ", " << std::to_string(Minor);
    OS << ")\n";
  }
  OS << "#undef DXIL_VERSION\n";
  OS << "#endif\n\n";
}

/// Emit a mapping of DXIL opcode to opname
static void emitDXILOpCodes(ArrayRef<DXILOperationDesc> Ops, raw_ostream &OS) {
  OS << "#ifdef DXIL_OPCODE\n";
  for (const DXILOperationDesc &Op : Ops)
    OS << "DXIL_OPCODE(" << Op.OpCode << ", " << Op.OpName << ")\n";
  OS << "#undef DXIL_OPCODE\n";
  OS << "\n";
  OS << "#endif\n\n";
}

/// Emit a list of DXIL op classes
static void emitDXILOpClasses(const RecordKeeper &Records, raw_ostream &OS) {
  OS << "#ifdef DXIL_OPCLASS\n";
  for (const Record *OpClass : Records.getAllDerivedDefinitions("DXILOpClass"))
    OS << "DXIL_OPCLASS(" << OpClass->getName() << ")\n";
  OS << "#undef DXIL_OPCLASS\n";
  OS << "#endif\n\n";
}

/// Emit a list of DXIL op parameter types
static void emitDXILOpParamTypes(const RecordKeeper &Records, raw_ostream &OS) {
  OS << "#ifdef DXIL_OP_PARAM_TYPE\n";
  for (const Record *OpParamType :
       Records.getAllDerivedDefinitions("DXILOpParamType"))
    OS << "DXIL_OP_PARAM_TYPE(" << OpParamType->getName() << ")\n";
  OS << "#undef DXIL_OP_PARAM_TYPE\n";
  OS << "#endif\n\n";
}

/// Emit a list of DXIL op function attributes
static void emitDXILAttributes(const RecordKeeper &Records, raw_ostream &OS) {
  OS << "#ifdef DXIL_ATTRIBUTE\n";
  for (const Record *Attr : Records.getAllDerivedDefinitions("DXILAttribute"))
    OS << "DXIL_ATTRIBUTE(" << Attr->getName() << ")\n";
  OS << "#undef DXIL_ATTRIBUTE\n";
  OS << "#endif\n\n";
}

// Helper function to determine if the given Attr is defined in the vector
// Attrs, by comparing the names
static bool attrIsDefined(std::vector<const Record *> Attrs,
                          const Record *Attr) {
  for (auto CurAttr : Attrs)
    if (CurAttr->getName() == Attr->getName())
      return true;
  return false;
}

/// Emit a table of bools denoting a DXIL op's function attributes
static void emitDXILOpAttributes(const RecordKeeper &Records,
                                 ArrayRef<DXILOperationDesc> Ops,
                                 raw_ostream &OS) {
  // A DXIL op can have multiple function attributes that are specific to a
  // specific DXIL version and higher. AttrRecs models this by grouping the
  // attributes by the versions. So we will output a macro for each version
  // number with a table of bools in the following format:
  //
  //     OpName, VersionMajor, VersionMinor, FnAttr1, FnAttr2, ...
  // Eg)    Abs,            1,            0,    true,   false, ...
  OS << "#ifdef DXIL_OP_ATTRIBUTES\n";
  for (const auto &Op : Ops) {
    for (const auto *Rec : Op.AttrRecs) {
      unsigned Major =
          Rec->getValueAsDef("dxil_version")->getValueAsInt("Major");
      unsigned Minor =
          Rec->getValueAsDef("dxil_version")->getValueAsInt("Minor");
      OS << "DXIL_OP_ATTRIBUTES(dxil::OpCode::" << Op.OpName << ", ";
      OS << std::to_string(Major) << ", " << std::to_string(Minor);
      // These Attrs are the ones set for above DXIL version
      auto Attrs = Rec->getValueAsListOfDefs("fn_attrs");
      // We will then iteratre through all possible attributes and mark the
      // present ones as 'true' and all the others as 'false' to create the
      // boolean table, eg) true, false, false, false
      for (const Record *Attr :
           Records.getAllDerivedDefinitions("DXILAttribute")) {
        std::string HasAttr = ", false";
        if (attrIsDefined(Attrs, Attr))
          HasAttr = ", true";
        OS << HasAttr;
      }
      OS << ")\n";
    }
  }
  OS << "#undef DXIL_OP_ATTRIBUTES\n";
  OS << "#endif\n\n";
}

/// Emit a list of DXIL op function types
static void emitDXILOpFunctionTypes(ArrayRef<DXILOperationDesc> Ops,
                                    raw_ostream &OS) {
  OS << "#ifndef DXIL_OP_FUNCTION_TYPE\n";
  OS << "#define DXIL_OP_FUNCTION_TYPE(OpCode, RetType, ...)\n";
  OS << "#endif\n";
  for (const DXILOperationDesc &Op : Ops) {
    OS << "DXIL_OP_FUNCTION_TYPE(dxil::OpCode::" << Op.OpName;
    for (const Record *Rec : Op.OpTypes)
      OS << ", dxil::OpParamType::" << Rec->getName();
    // If there are no arguments, we need an empty comma for the varargs
    if (Op.OpTypes.size() == 1)
      OS << ", ";
    OS << ")\n";
  }
  OS << "#undef DXIL_OP_FUNCTION_TYPE\n";
}

/// Emit map of DXIL operation to LLVM or DirectX intrinsic
/// \param A vector of DXIL Ops
/// \param Output stream
static void emitDXILIntrinsicMap(ArrayRef<DXILOperationDesc> Ops,
                                 raw_ostream &OS) {

  OS << "#ifdef DXIL_OP_INTRINSIC\n";
  OS << "\n";
  for (const auto &Op : Ops) {
    if (Op.IntrinsicSelects.empty()) {
      continue;
    }
    for (const DXILIntrinsicSelect &MappedIntr : Op.IntrinsicSelects) {
      OS << "DXIL_OP_INTRINSIC(dxil::OpCode::" << Op.OpName
         << ", Intrinsic::" << MappedIntr.Intrinsic << ", ";
      for (const Record *ArgSelect : MappedIntr.ArgSelectRecords) {
        std::string Type =
            ArgSelect->getValueAsDef("type")->getNameInitAsString();
        int Value = ArgSelect->getValueAsInt("value");
        OS << "(IntrinArgSelect{"
           << "IntrinArgSelect::Type::" << StripIntrinArgSelectTypePrefix(Type)
           << "," << Value << "}), ";
      }
      OS << ")\n";
    }
  }
  OS << "\n";
  OS << "#undef DXIL_OP_INTRINSIC\n";
  OS << "#endif\n\n";
}

/// Emit the IntrinArgSelect type for DirectX intrinsic to DXIL Op lowering
static void emitDXILIntrinsicArgSelectTypes(const RecordKeeper &Records,
                                            raw_ostream &OS) {
  OS << "#ifdef DXIL_OP_INTRINSIC_ARG_SELECT_TYPE\n";
  for (const Record *Records :
       Records.getAllDerivedDefinitions("IntrinArgSelectType")) {
    StringRef StrippedName = StripIntrinArgSelectTypePrefix(Records->getName());
    OS << "DXIL_OP_INTRINSIC_ARG_SELECT_TYPE(" << StrippedName << ")\n";
  }
  OS << "#undef DXIL_OP_INTRINSIC_ARG_SELECT_TYPE\n";
  OS << "#endif\n\n";
}

/// Emit DXIL operation table
/// \param A vector of DXIL Ops
/// \param Output stream
static void emitDXILOperationTable(ArrayRef<DXILOperationDesc> Ops,
                                   raw_ostream &OS) {
  // Collect Names.
  SequenceToOffsetTable<std::string> OpClassStrings;
  SequenceToOffsetTable<std::string> OpStrings;

  StringSet<> ClassSet;
  for (const auto &Op : Ops) {
    OpStrings.add(Op.OpName);

    if (ClassSet.insert(Op.OpClass).second)
      OpClassStrings.add(Op.OpClass.data());
  }

  // Layout names.
  OpStrings.layout();
  OpClassStrings.layout();

  // Emit access function getOpcodeProperty() that embeds DXIL Operation table
  // with entries of type struct OpcodeProperty.
  OS << "static const OpCodeProperty *getOpCodeProperty(dxil::OpCode Op) "
        "{\n";

  OS << "  static const OpCodeProperty OpCodeProps[] = {\n";
  std::string Prefix = "";
  for (const auto &Op : Ops) {
    OS << Prefix << "  { dxil::OpCode::" << Op.OpName << ", "
       << OpStrings.get(Op.OpName) << ", OpCodeClass::" << Op.OpClass << ", "
       << OpClassStrings.get(Op.OpClass.data()) << ", "
       << getOverloadMaskString(Op.OverloadRecs) << ", "
       << getStageMaskString(Op.StageRecs) << ", " << Op.OverloadParamIndex
       << " }";
    Prefix = ",\n";
  }
  OS << "  };\n";

  OS << "  // FIXME: change search to indexing with\n";
  OS << "  // Op once all DXIL operations are added.\n";
  OS << "  OpCodeProperty TmpProp;\n";
  OS << "  TmpProp.OpCode = Op;\n";
  OS << "  const OpCodeProperty *Prop =\n";
  OS << "      llvm::lower_bound(OpCodeProps, TmpProp,\n";
  OS << "                        [](const OpCodeProperty &A, const "
        "OpCodeProperty &B) {\n";
  OS << "                          return A.OpCode < B.OpCode;\n";
  OS << "                        });\n";
  OS << "  assert(Prop && \"failed to find OpCodeProperty\");\n";
  OS << "  return Prop;\n";
  OS << "}\n\n";

  // Emit the string tables.
  OS << "static const char *getOpCodeName(dxil::OpCode Op) {\n\n";

  OpStrings.emitStringLiteralDef(OS,
                                 "  static const char DXILOpCodeNameTable[]");

  OS << "  auto *Prop = getOpCodeProperty(Op);\n";
  OS << "  unsigned Index = Prop->OpCodeNameOffset;\n";
  OS << "  return DXILOpCodeNameTable + Index;\n";
  OS << "}\n\n";

  OS << "static const char *getOpCodeClassName(const OpCodeProperty &Prop) "
        "{\n\n";

  OpClassStrings.emitStringLiteralDef(
      OS, "  static const char DXILOpCodeClassNameTable[]");

  OS << "  unsigned Index = Prop.OpCodeClassNameOffset;\n";
  OS << "  return DXILOpCodeClassNameTable + Index;\n";
  OS << "}\n\n";
}

static void emitDXILOperationTableDataStructs(const RecordKeeper &Records,
                                              raw_ostream &OS) {
  // Get Shader stage records
  std::vector<const Record *> ShaderKindRecs =
      Records.getAllDerivedDefinitions("DXILShaderStage");
  // Sort records by name
  llvm::sort(ShaderKindRecs, [](const Record *A, const Record *B) {
    return A->getName() < B->getName();
  });

  OS << "// Valid shader kinds\n\n";
  // Choose the type of enum ShaderKind based on the number of stages declared.
  // This gives the flexibility to just add add new stage records in DXIL.td, if
  // needed, with no need to change this backend code.
  size_t ShaderKindCount = ShaderKindRecs.size();
  uint64_t ShaderKindTySz = PowerOf2Ceil(ShaderKindRecs.size() + 1);
  OS << "enum ShaderKind : uint" << ShaderKindTySz << "_t {\n";
  const std::string AllStages("all_stages");
  const std::string Removed("removed");
  int ShiftVal = 1;
  for (const auto *R : ShaderKindRecs) {
    auto Name = R->getName();
    if (Name.compare(Removed) == 0) {
      OS << "  " << Name
         << " =  0,  // Pseudo-stage indicating op not supported in any "
            "stage\n";
    } else if (Name.compare(AllStages) == 0) {
      OS << "  " << Name << " =  0x"
         << utohexstr(((1 << ShaderKindCount) - 1), false, 0)
         << ", // Pseudo-stage indicating op is supported in all stages\n";
    } else if (Name.compare(AllStages)) {
      OS << "  " << Name << " = 1 << " << std::to_string(ShiftVal++) << ",\n";
    }
  }
  OS << "}; // enum ShaderKind\n\n";
}

/// Entry function call that invokes the functionality of this TableGen backend
/// \param Records TableGen records of DXIL Operations defined in DXIL.td
/// \param OS output stream
static void emitDxilOperation(const RecordKeeper &Records, raw_ostream &OS) {
  OS << "// Generated code, do not edit.\n";
  OS << "\n";
  // Get all DXIL Ops property records
  std::vector<DXILOperationDesc> DXILOps;
  for (const Record *R : Records.getAllDerivedDefinitions("DXILOp")) {
    DXILOps.emplace_back(DXILOperationDesc(R));
  }
  // Sort by opcode.
  llvm::sort(DXILOps,
             [](const DXILOperationDesc &A, const DXILOperationDesc &B) {
               return A.OpCode < B.OpCode;
             });
  int PrevOp = -1;
  for (const DXILOperationDesc &Desc : DXILOps) {
    if (Desc.OpCode == PrevOp)
      PrintFatalError(Twine("Duplicate opcode: ") + Twine(Desc.OpCode));
    PrevOp = Desc.OpCode;
  }

  emitDXILVersions(Records, OS);
  emitDXILOpCodes(DXILOps, OS);
  emitDXILOpClasses(Records, OS);
  emitDXILOpParamTypes(Records, OS);
  emitDXILAttributes(Records, OS);
  emitDXILOpAttributes(Records, DXILOps, OS);
  emitDXILOpFunctionTypes(DXILOps, OS);
  emitDXILIntrinsicArgSelectTypes(Records, OS);
  emitDXILIntrinsicMap(DXILOps, OS);
  OS << "#ifdef DXIL_OP_OPERATION_TABLE\n\n";
  emitDXILOperationTableDataStructs(Records, OS);
  emitDXILOperationTable(DXILOps, OS);
  OS << "#undef DXIL_OP_OPERATION_TABLE\n";
  OS << "#endif\n\n";
}

static TableGen::Emitter::Opt X("gen-dxil-operation", emitDxilOperation,
                                "Generate DXIL operation information");
