brintos

brintos / llvm-project-archived public Read only

0
0
Text · 58.1 KiB · ca291b5 Raw
1452 lines · cpp
1//===- SPIRVSerializationGen.cpp - SPIR-V serialization utility generator -===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// SPIRVSerializationGen generates common utility functions for SPIR-V10// serialization.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/TableGen/Attribute.h"15#include "mlir/TableGen/CodeGenHelpers.h"16#include "mlir/TableGen/EnumInfo.h"17#include "mlir/TableGen/Format.h"18#include "mlir/TableGen/GenInfo.h"19#include "mlir/TableGen/Operator.h"20#include "llvm/ADT/STLExtras.h"21#include "llvm/ADT/Sequence.h"22#include "llvm/ADT/SmallVector.h"23#include "llvm/ADT/StringExtras.h"24#include "llvm/ADT/StringMap.h"25#include "llvm/ADT/StringRef.h"26#include "llvm/ADT/StringSet.h"27#include "llvm/Support/FormatVariadic.h"28#include "llvm/Support/raw_ostream.h"29#include "llvm/TableGen/Error.h"30#include "llvm/TableGen/Record.h"31#include "llvm/TableGen/TableGenBackend.h"32 33#include <list>34#include <optional>35 36using llvm::ArrayRef;37using llvm::cast;38using llvm::formatv;39using llvm::isa;40using llvm::raw_ostream;41using llvm::raw_string_ostream;42using llvm::Record;43using llvm::RecordKeeper;44using llvm::SmallVector;45using llvm::SMLoc;46using llvm::StringMap;47using llvm::StringRef;48using mlir::tblgen::Attribute;49using mlir::tblgen::EnumCase;50using mlir::tblgen::EnumInfo;51using mlir::tblgen::NamedAttribute;52using mlir::tblgen::NamedTypeConstraint;53using mlir::tblgen::Operator;54 55//===----------------------------------------------------------------------===//56// Availability Wrapper Class57//===----------------------------------------------------------------------===//58 59namespace {60// Wrapper class with helper methods for accessing availability defined in61// TableGen.62class Availability {63public:64  explicit Availability(const Record *def);65 66  // Returns the name of the direct TableGen class for this availability67  // instance.68  StringRef getClass() const;69 70  // Returns the generated C++ interface's class namespace.71  StringRef getInterfaceClassNamespace() const;72 73  // Returns the generated C++ interface's class name.74  StringRef getInterfaceClassName() const;75 76  // Returns the generated C++ interface's description.77  StringRef getInterfaceDescription() const;78 79  // Returns the name of the query function insided the generated C++ interface.80  StringRef getQueryFnName() const;81 82  // Returns the return type of the query function insided the generated C++83  // interface.84  StringRef getQueryFnRetType() const;85 86  // Returns the code for merging availability requirements.87  StringRef getMergeActionCode() const;88 89  // Returns the initializer expression for initializing the final availability90  // requirements.91  StringRef getMergeInitializer() const;92 93  // Returns the C++ type for an availability instance.94  StringRef getMergeInstanceType() const;95 96  // Returns the C++ statements for preparing availability instance.97  StringRef getMergeInstancePreparation() const;98 99  // Returns the concrete availability instance carried in this case.100  StringRef getMergeInstance() const;101 102  // Returns the underlying LLVM TableGen Record.103  const Record *getDef() const { return def; }104 105private:106  // The TableGen definition of this availability.107  const Record *def;108};109} // namespace110 111Availability::Availability(const Record *def) : def(def) {112  assert(def->isSubClassOf("Availability") &&113         "must be subclass of TableGen 'Availability' class");114}115 116StringRef Availability::getClass() const {117  if (def->getDirectSuperClasses().size() != 1) {118    PrintFatalError(def->getLoc(),119                    "expected to only have one direct superclass");120  }121  const Record *parentClass = def->getDirectSuperClasses().front().first;122  return parentClass->getName();123}124 125StringRef Availability::getInterfaceClassNamespace() const {126  return def->getValueAsString("cppNamespace");127}128 129StringRef Availability::getInterfaceClassName() const {130  return def->getValueAsString("interfaceName");131}132 133StringRef Availability::getInterfaceDescription() const {134  return def->getValueAsString("interfaceDescription");135}136 137StringRef Availability::getQueryFnRetType() const {138  return def->getValueAsString("queryFnRetType");139}140 141StringRef Availability::getQueryFnName() const {142  return def->getValueAsString("queryFnName");143}144 145StringRef Availability::getMergeActionCode() const {146  return def->getValueAsString("mergeAction");147}148 149StringRef Availability::getMergeInitializer() const {150  return def->getValueAsString("initializer");151}152 153StringRef Availability::getMergeInstanceType() const {154  return def->getValueAsString("instanceType");155}156 157StringRef Availability::getMergeInstancePreparation() const {158  return def->getValueAsString("instancePreparation");159}160 161StringRef Availability::getMergeInstance() const {162  return def->getValueAsString("instance");163}164 165// Returns the availability spec of the given `def`.166std::vector<Availability> getAvailabilities(const Record &def) {167  std::vector<Availability> availabilities;168 169  if (def.getValue("availability")) {170    std::vector<const Record *> availDefs =171        def.getValueAsListOfDefs("availability");172    availabilities.reserve(availDefs.size());173    for (const Record *avail : availDefs)174      availabilities.emplace_back(avail);175  }176 177  return availabilities;178}179 180//===----------------------------------------------------------------------===//181// Availability Interface Definitions AutoGen182//===----------------------------------------------------------------------===//183 184static void emitInterfaceDef(const Availability &availability,185                             raw_ostream &os) {186 187  os << availability.getQueryFnRetType() << " ";188 189  StringRef cppNamespace = availability.getInterfaceClassNamespace();190  cppNamespace.consume_front("::");191  if (!cppNamespace.empty())192    os << cppNamespace << "::";193 194  StringRef methodName = availability.getQueryFnName();195  os << availability.getInterfaceClassName() << "::" << methodName << "() {\n"196     << "  return getImpl()->" << methodName << "(getImpl(), getOperation());\n"197     << "}\n";198}199 200static bool emitInterfaceDefs(const RecordKeeper &records, raw_ostream &os) {201  llvm::emitSourceFileHeader("Availability Interface Definitions", os, records);202 203  auto defs = records.getAllDerivedDefinitions("Availability");204  SmallVector<const Record *, 1> handledClasses;205  for (const Record *def : defs) {206    if (def->getDirectSuperClasses().size() != 1) {207      PrintFatalError(def->getLoc(),208                      "expected to only have one direct superclass");209    }210    const Record *parent = def->getDirectSuperClasses().front().first;211    if (llvm::is_contained(handledClasses, parent))212      continue;213 214    Availability availability(def);215    emitInterfaceDef(availability, os);216    handledClasses.push_back(parent);217  }218  return false;219}220 221//===----------------------------------------------------------------------===//222// Availability Interface Declarations AutoGen223//===----------------------------------------------------------------------===//224 225static void emitConceptDecl(const Availability &availability, raw_ostream &os) {226  os << "  class Concept {\n"227     << "  public:\n"228     << "    virtual ~Concept() = default;\n"229     << "    virtual " << availability.getQueryFnRetType() << " "230     << availability.getQueryFnName()231     << "(const Concept *impl, Operation *tblgen_opaque_op) const = 0;\n"232     << "  };\n";233}234 235static void emitModelDecl(const Availability &availability, raw_ostream &os) {236  for (const char *modelClass : {"Model", "FallbackModel"}) {237    os << "  template<typename ConcreteOp>\n";238    os << "  class " << modelClass << " : public Concept {\n"239       << "  public:\n"240       << "    using Interface = " << availability.getInterfaceClassName()241       << ";\n"242       << "    " << availability.getQueryFnRetType() << " "243       << availability.getQueryFnName()244       << "(const Concept *impl, Operation *tblgen_opaque_op) const final {\n"245       << "      auto op = llvm::cast<ConcreteOp>(tblgen_opaque_op);\n"246       << "      (void)op;\n"247       // Forward to the method on the concrete operation type.248       << "      return op." << availability.getQueryFnName() << "();\n"249       << "    }\n"250       << "  };\n";251  }252  os << "  template<typename ConcreteModel, typename ConcreteOp>\n";253  os << "  class ExternalModel : public FallbackModel<ConcreteOp> {};\n";254}255 256static void emitInterfaceDecl(const Availability &availability,257                              raw_ostream &os) {258  StringRef interfaceName = availability.getInterfaceClassName();259  std::string interfaceTraitsName =260      std::string(formatv("{0}Traits", interfaceName));261 262  llvm::NamespaceEmitter nsEmitter(os,263                                   availability.getInterfaceClassNamespace());264  os << "class " << interfaceName << ";\n\n";265 266  // Emit the traits struct containing the concept and model declarations.267  os << "namespace detail {\n"268     << "struct " << interfaceTraitsName << " {\n";269  emitConceptDecl(availability, os);270  os << '\n';271  emitModelDecl(availability, os);272  os << "};\n} // namespace detail\n\n";273 274  // Emit the main interface class declaration.275  os << "/*\n" << availability.getInterfaceDescription().trim() << "\n*/\n";276  os << llvm::formatv("class {0} : public OpInterface<{1}, detail::{2}> {\n"277                      "public:\n"278                      "  using OpInterface<{1}, detail::{2}>::OpInterface;\n",279                      interfaceName, interfaceName, interfaceTraitsName);280 281  // Emit query function declaration.282  os << "  " << availability.getQueryFnRetType() << " "283     << availability.getQueryFnName() << "();\n";284  os << "};\n\n";285}286 287static bool emitInterfaceDecls(const RecordKeeper &records, raw_ostream &os) {288  llvm::emitSourceFileHeader("Availability Interface Declarations", os,289                             records);290 291  auto defs = records.getAllDerivedDefinitions("Availability");292  SmallVector<const Record *, 4> handledClasses;293  for (const Record *def : defs) {294    if (def->getDirectSuperClasses().size() != 1) {295      PrintFatalError(def->getLoc(),296                      "expected to only have one direct superclass");297    }298    const Record *parent = def->getDirectSuperClasses().front().first;299    if (llvm::is_contained(handledClasses, parent))300      continue;301 302    Availability avail(def);303    emitInterfaceDecl(avail, os);304    handledClasses.push_back(parent);305  }306  return false;307}308 309//===----------------------------------------------------------------------===//310// Availability Interface Hook Registration311//===----------------------------------------------------------------------===//312 313// Registers the operation interface generator to mlir-tblgen.314static mlir::GenRegistration315    genInterfaceDecls("gen-avail-interface-decls",316                      "Generate availability interface declarations",317                      [](const RecordKeeper &records, raw_ostream &os) {318                        return emitInterfaceDecls(records, os);319                      });320 321// Registers the operation interface generator to mlir-tblgen.322static mlir::GenRegistration323    genInterfaceDefs("gen-avail-interface-defs",324                     "Generate op interface definitions",325                     [](const RecordKeeper &records, raw_ostream &os) {326                       return emitInterfaceDefs(records, os);327                     });328 329//===----------------------------------------------------------------------===//330// Enum Availability Query AutoGen331//===----------------------------------------------------------------------===//332 333static void emitAvailabilityQueryForIntEnum(const Record &enumDef,334                                            raw_ostream &os) {335  EnumInfo enumInfo(enumDef);336  StringRef enumName = enumInfo.getEnumClassName();337  std::vector<EnumCase> enumerants = enumInfo.getAllCases();338 339  // Mapping from availability class name to (enumerant, availability340  // specification) pairs.341  llvm::StringMap<llvm::SmallVector<std::pair<EnumCase, Availability>, 1>>342      classCaseMap;343 344  // Place all availability specifications to their corresponding345  // availability classes.346  for (const EnumCase &enumerant : enumerants)347    for (const Availability &avail : getAvailabilities(enumerant.getDef()))348      classCaseMap[avail.getClass()].push_back({enumerant, avail});349 350  for (const auto &classCasePair : classCaseMap) {351    Availability avail = classCasePair.getValue().front().second;352 353    os << formatv("std::optional<{0}> {1}({2} value) {{\n",354                  avail.getMergeInstanceType(), avail.getQueryFnName(),355                  enumName);356 357    os << "  switch (value) {\n";358    for (const auto &caseSpecPair : classCasePair.getValue()) {359      EnumCase enumerant = caseSpecPair.first;360      Availability avail = caseSpecPair.second;361      os << formatv("  case {0}::{1}: { {2} return {3}({4}); }\n", enumName,362                    enumerant.getSymbol(), avail.getMergeInstancePreparation(),363                    avail.getMergeInstanceType(), avail.getMergeInstance());364    }365    // Only emit default if uncovered cases.366    if (classCasePair.getValue().size() < enumInfo.getAllCases().size())367      os << "  default: break;\n";368    os << "  }\n"369       << "  return std::nullopt;\n"370       << "}\n";371  }372}373 374static void emitAvailabilityQueryForBitEnum(const Record &enumDef,375                                            raw_ostream &os) {376  EnumInfo enumInfo(enumDef);377  StringRef enumName = enumInfo.getEnumClassName();378  std::string underlyingType = std::string(enumInfo.getUnderlyingType());379  std::vector<EnumCase> enumerants = enumInfo.getAllCases();380 381  // Mapping from availability class name to (enumerant, availability382  // specification) pairs.383  llvm::StringMap<llvm::SmallVector<std::pair<EnumCase, Availability>, 1>>384      classCaseMap;385 386  // Place all availability specifications to their corresponding387  // availability classes.388  for (const EnumCase &enumerant : enumerants)389    for (const Availability &avail : getAvailabilities(enumerant.getDef()))390      classCaseMap[avail.getClass()].push_back({enumerant, avail});391 392  for (const auto &classCasePair : classCaseMap) {393    Availability avail = classCasePair.getValue().front().second;394 395    os << formatv("std::optional<{0}> {1}({2} value) {{\n",396                  avail.getMergeInstanceType(), avail.getQueryFnName(),397                  enumName);398 399    os << formatv("  assert(::llvm::popcount(static_cast<{0}>(value)) <= 1"400                  " && \"cannot have more than one bit set\");\n",401                  underlyingType);402 403    os << "  switch (value) {\n";404    for (const auto &caseSpecPair : classCasePair.getValue()) {405      EnumCase enumerant = caseSpecPair.first;406      Availability avail = caseSpecPair.second;407      os << formatv("  case {0}::{1}: { {2} return {3}({4}); }\n", enumName,408                    enumerant.getSymbol(), avail.getMergeInstancePreparation(),409                    avail.getMergeInstanceType(), avail.getMergeInstance());410    }411    os << "  default: break;\n";412    os << "  }\n"413       << "  return std::nullopt;\n"414       << "}\n";415  }416}417 418static void emitEnumDecl(const Record &enumDef, raw_ostream &os) {419  EnumInfo enumInfo(enumDef);420  StringRef enumName = enumInfo.getEnumClassName();421  auto enumerants = enumInfo.getAllCases();422 423  llvm::NamespaceEmitter ns(os, enumInfo.getCppNamespace());424  llvm::StringSet<> handledClasses;425 426  // Place all availability specifications to their corresponding427  // availability classes.428  for (const EnumCase &enumerant : enumerants)429    for (const Availability &avail : getAvailabilities(enumerant.getDef())) {430      StringRef className = avail.getClass();431      if (handledClasses.count(className))432        continue;433      os << formatv("std::optional<{0}> {1}({2} value);\n",434                    avail.getMergeInstanceType(), avail.getQueryFnName(),435                    enumName);436      handledClasses.insert(className);437    }438}439 440static bool emitEnumDecls(const RecordKeeper &records, raw_ostream &os) {441  llvm::emitSourceFileHeader("SPIR-V Enum Availability Declarations", os,442                             records);443 444  auto defs = records.getAllDerivedDefinitions("EnumInfo");445  for (const auto *def : defs)446    emitEnumDecl(*def, os);447 448  return false;449}450 451static void emitEnumDef(const Record &enumDef, raw_ostream &os) {452  EnumInfo enumInfo(enumDef);453  llvm::NamespaceEmitter ns(os, enumInfo.getCppNamespace());454 455  if (enumInfo.isBitEnum())456    emitAvailabilityQueryForBitEnum(enumDef, os);457  else458    emitAvailabilityQueryForIntEnum(enumDef, os);459}460 461static bool emitEnumDefs(const RecordKeeper &records, raw_ostream &os) {462  llvm::emitSourceFileHeader("SPIR-V Enum Availability Definitions", os,463                             records);464 465  for (const Record *def : records.getAllDerivedDefinitions("EnumInfo"))466    emitEnumDef(*def, os);467 468  return false;469}470 471//===----------------------------------------------------------------------===//472// Enum Availability Query Hook Registration473//===----------------------------------------------------------------------===//474 475// Registers the enum utility generator to mlir-tblgen.476static mlir::GenRegistration477    genEnumDecls("gen-spirv-enum-avail-decls",478                 "Generate SPIR-V enum availability declarations",479                 [](const RecordKeeper &records, raw_ostream &os) {480                   return emitEnumDecls(records, os);481                 });482 483// Registers the enum utility generator to mlir-tblgen.484static mlir::GenRegistration485    genEnumDefs("gen-spirv-enum-avail-defs",486                "Generate SPIR-V enum availability definitions",487                [](const RecordKeeper &records, raw_ostream &os) {488                  return emitEnumDefs(records, os);489                });490 491//===----------------------------------------------------------------------===//492// Serialization AutoGen493//===----------------------------------------------------------------------===//494 495// These enums are encoded as <id> to constant values in SPIR-V blob, but we496// directly use the constant value as attribute in SPIR-V dialect. So need497// to handle them separately from normal enum attributes.498constexpr llvm::StringLiteral constantIdEnumAttrs[] = {499    "SPIRV_ScopeAttr", "SPIRV_KHR_CooperativeMatrixUseAttr",500    "SPIRV_KHR_CooperativeMatrixLayoutAttr", "SPIRV_MemorySemanticsAttr",501    "SPIRV_MatrixLayoutAttr"};502 503/// Generates code to serialize attributes of a SPIRV_Op `op` into `os`. The504/// generates code extracts the attribute with name `attrName` from505/// `operandList` of `op`.506static void emitAttributeSerialization(const Attribute &attr,507                                       ArrayRef<SMLoc> loc, StringRef tabs,508                                       StringRef opVar, StringRef operandList,509                                       StringRef attrName, raw_ostream &os) {510  os << tabs511     << formatv("if (auto attr = {0}->getAttr(\"{1}\")) {{\n", opVar, attrName);512  if (llvm::is_contained(constantIdEnumAttrs, attr.getAttrDefName())) {513    EnumInfo baseEnum(attr.getDef().getValueAsDef("enum"));514    os << tabs515       << formatv("  {0}.push_back(prepareConstantInt({1}.getLoc(), "516                  "Builder({1}).getI32IntegerAttr(static_cast<uint32_t>("517                  "::llvm::cast<{2}::{3}Attr>(attr).getValue()))));\n",518                  operandList, opVar, baseEnum.getCppNamespace(),519                  baseEnum.getEnumClassName());520  } else if (attr.isSubClassOf("SPIRV_BitEnumAttr") ||521             attr.isSubClassOf("SPIRV_I32EnumAttr")) {522    EnumInfo baseEnum(attr.getDef().getValueAsDef("enum"));523    os << tabs524       << formatv("  {0}.push_back(static_cast<uint32_t>("525                  "::llvm::cast<{1}::{2}Attr>(attr).getValue()));\n",526                  operandList, baseEnum.getCppNamespace(),527                  baseEnum.getEnumClassName());528  } else if (attr.getAttrDefName() == "I32ArrayAttr") {529    // Serialize all the elements of the array530    os << tabs << "  for (auto attrElem : llvm::cast<ArrayAttr>(attr)) {\n";531    os << tabs532       << formatv("    {0}.push_back(static_cast<uint32_t>("533                  "llvm::cast<IntegerAttr>(attrElem).getValue().getZExtValue())"534                  ");\n",535                  operandList);536    os << tabs << "  }\n";537  } else if (attr.getAttrDefName() == "I32Attr") {538    os << tabs539       << formatv(540              "  {0}.push_back(static_cast<uint32_t>("541              "llvm::cast<IntegerAttr>(attr).getValue().getZExtValue()));\n",542              operandList);543  } else if (attr.isEnumAttr() || attr.isTypeAttr()) {544    // It may be the first time this type appears in the IR, so we need to545    // process it.546    StringRef attrTypeID = "attrTypeID";547    os << tabs << formatv("  uint32_t {0} = 0;\n", attrTypeID);548    os << tabs549       << formatv("  if (failed(processType({0}.getLoc(), "550                  "llvm::cast<TypeAttr>(attr).getValue(), {1}))) {{\n",551                  opVar, attrTypeID);552    os << tabs << "    return failure();\n";553    os << tabs << "  }\n";554    os << tabs << formatv("  {0}.push_back(attrTypeID);\n", operandList);555  } else {556    PrintFatalError(557        loc,558        llvm::Twine(559            "unhandled attribute type in SPIR-V serialization generation : '") +560            attr.getAttrDefName() + llvm::Twine("'"));561  }562  os << tabs << "}\n";563}564 565/// Generates code to serialize the operands of a SPIRV_Op `op` into `os`. The566/// generated queries the SSA-ID if operand is a SSA-Value, or serializes the567/// attributes. The `operands` vector is updated appropriately. `elidedAttrs`568/// updated as well to include the serialized attributes.569static void emitArgumentSerialization(const Operator &op, ArrayRef<SMLoc> loc,570                                      StringRef tabs, StringRef opVar,571                                      StringRef operands, StringRef elidedAttrs,572                                      raw_ostream &os) {573  using mlir::tblgen::Argument;574 575  // SPIR-V ops can mix operands and attributes in the definition. These576  // operands and attributes are serialized in the exact order of the definition577  // to match SPIR-V binary format requirements. It can cause excessive578  // generated code bloat because we are emitting code to handle each579  // operand/attribute separately. So here we probe first to check whether all580  // the operands are ahead of attributes. Then we can serialize all operands581  // together.582 583  // Whether all operands are ahead of all attributes in the op's spec.584  bool areOperandsAheadOfAttrs = true;585  // Find the first attribute.586  const Argument *it = llvm::find_if(op.getArgs(), [](const Argument &arg) {587    return isa<NamedAttribute *>(arg);588  });589  // Check whether all following arguments are attributes.590  for (const Argument *ie = op.arg_end(); it != ie; ++it) {591    if (!isa<NamedAttribute *>(*it)) {592      areOperandsAheadOfAttrs = false;593      break;594    }595  }596 597  // Serialize all operands together.598  if (areOperandsAheadOfAttrs) {599    if (op.getNumOperands() != 0) {600      os << tabs601         << formatv("for (Value operand : {0}->getOperands()) {{\n", opVar);602      os << tabs << "  auto id = getValueID(operand);\n";603      os << tabs << "  assert(id && \"use before def!\");\n";604      os << tabs << formatv("  {0}.push_back(id);\n", operands);605      os << tabs << "}\n";606    }607    for (const NamedAttribute &attr : op.getAttributes()) {608      emitAttributeSerialization(609          (attr.attr.isOptional() ? attr.attr.getBaseAttr() : attr.attr), loc,610          tabs, opVar, operands, attr.name, os);611      os << tabs612         << formatv("{0}.push_back(\"{1}\");\n", elidedAttrs, attr.name);613    }614    return;615  }616 617  // Serialize operands separately.618  auto operandNum = 0;619  for (unsigned i = 0, e = op.getNumArgs(); i < e; ++i) {620    auto argument = op.getArg(i);621    os << tabs << "{\n";622    if (isa<NamedTypeConstraint *>(argument)) {623      os << tabs624         << formatv("  for (auto arg : {0}.getODSOperands({1})) {{\n", opVar,625                    operandNum);626      os << tabs << "    auto argID = getValueID(arg);\n";627      os << tabs << "    if (!argID) {\n";628      os << tabs629         << formatv("      return emitError({0}.getLoc(), "630                    "\"operand #{1} has a use before def\");\n",631                    opVar, operandNum);632      os << tabs << "    }\n";633      os << tabs << formatv("    {0}.push_back(argID);\n", operands);634      os << "    }\n";635      operandNum++;636    } else {637      NamedAttribute *attr = cast<NamedAttribute *>(argument);638      auto newtabs = tabs.str() + "  ";639      emitAttributeSerialization(640          (attr->attr.isOptional() ? attr->attr.getBaseAttr() : attr->attr),641          loc, newtabs, opVar, operands, attr->name, os);642      os << newtabs643         << formatv("{0}.push_back(\"{1}\");\n", elidedAttrs, attr->name);644    }645    os << tabs << "}\n";646  }647}648 649/// Generates code to serializes the result of SPIRV_Op `op` into `os`. The650/// generated gets the ID for the type of the result (if any), the SSA-ID of651/// the result and updates `resultID` with the SSA-ID.652static void emitResultSerialization(const Operator &op, ArrayRef<SMLoc> loc,653                                    StringRef tabs, StringRef opVar,654                                    StringRef operands, StringRef resultID,655                                    raw_ostream &os) {656  if (op.getNumResults() == 1) {657    StringRef resultTypeID("resultTypeID");658    os << tabs << formatv("uint32_t {0} = 0;\n", resultTypeID);659    os << tabs660       << formatv(661              "if (failed(processType({0}.getLoc(), {0}.getType(), {1}))) {{\n",662              opVar, resultTypeID);663    os << tabs << "  return failure();\n";664    os << tabs << "}\n";665    os << tabs << formatv("{0}.push_back({1});\n", operands, resultTypeID);666    // Create an SSA result <id> for the op667    os << tabs << formatv("{0} = getNextID();\n", resultID);668    os << tabs669       << formatv("valueIDMap[{0}.getResult()] = {1};\n", opVar, resultID);670    os << tabs << formatv("{0}.push_back({1});\n", operands, resultID);671  } else if (op.getNumResults() != 0) {672    PrintFatalError(loc, "SPIR-V ops can only have zero or one result");673  }674}675 676/// Generates code to serialize attributes of SPIRV_Op `op` that become677/// decorations on the `resultID` of the serialized operation `opVar` in the678/// SPIR-V binary.679static void emitDecorationSerialization(const Operator &op, StringRef tabs,680                                        StringRef opVar, StringRef elidedAttrs,681                                        StringRef resultID, raw_ostream &os) {682  if (op.getNumResults() == 1) {683    // All non-argument attributes translated into OpDecorate instruction684    os << tabs << formatv("for (auto attr : {0}->getAttrs()) {{\n", opVar);685    os << tabs686       << formatv("  if (llvm::is_contained({0}, attr.getName())) {{",687                  elidedAttrs);688    os << tabs << "    continue;\n";689    os << tabs << "  }\n";690    os << tabs691       << formatv(692              "  if (failed(processDecoration({0}.getLoc(), {1}, attr))) {{\n",693              opVar, resultID);694    os << tabs << "    return failure();\n";695    os << tabs << "  }\n";696    os << tabs << "}\n";697  }698}699 700/// Generates code to serialize an SPIRV_Op `op` into `os`.701static void emitSerializationFunction(const Record *attrClass,702                                      const Record *record, const Operator &op,703                                      raw_ostream &os) {704  // If the record has 'autogenSerialization' set to 0, nothing to do705  if (!record->getValueAsBit("autogenSerialization"))706    return;707 708  StringRef opVar("op"), operands("operands"), elidedAttrs("elidedAttrs"),709      resultID("resultID");710 711  os << formatv(712      "template <> LogicalResult\nSerializer::processOp<{0}>({0} {1}) {{\n",713      op.getQualCppClassName(), opVar);714 715  // Special case for ops without attributes in TableGen definitions716  if (op.getNumAttributes() == 0 && op.getNumVariableLengthOperands() == 0) {717    std::string extInstSet;718    std::string opcode;719    if (record->isSubClassOf("SPIRV_ExtInstOp")) {720      extInstSet =721          formatv("\"{0}\"", record->getValueAsString("extendedInstSetName"));722      opcode = std::to_string(record->getValueAsInt("extendedInstOpcode"));723    } else {724      extInstSet = "\"\"";725      opcode = formatv("static_cast<uint32_t>(spirv::Opcode::{0})",726                       record->getValueAsString("spirvOpName"));727    }728 729    os << formatv("  return processOpWithoutGrammarAttr({0}, {1}, {2});\n}\n\n",730                  opVar, extInstSet, opcode);731    return;732  }733 734  os << formatv("  SmallVector<uint32_t, 4> {0};\n", operands);735  os << formatv("  SmallVector<StringRef, 2> {0};\n", elidedAttrs);736 737  // Serialize result information.738  if (op.getNumResults() == 1) {739    os << formatv("  uint32_t {0} = 0;\n", resultID);740    emitResultSerialization(op, record->getLoc(), "  ", opVar, operands,741                            resultID, os);742  }743 744  // Process arguments.745  emitArgumentSerialization(op, record->getLoc(), "  ", opVar, operands,746                            elidedAttrs, os);747 748  if (record->isSubClassOf("SPIRV_ExtInstOp")) {749    os << formatv(750        "  (void)encodeExtensionInstruction({0}, \"{1}\", {2}, {3});\n", opVar,751        record->getValueAsString("extendedInstSetName"),752        record->getValueAsInt("extendedInstOpcode"), operands);753  } else {754    // Emit debug info.755    os << formatv("  (void)emitDebugLine(functionBody, {0}.getLoc());\n",756                  opVar);757    os << formatv("  (void)encodeInstructionInto("758                  "functionBody, spirv::Opcode::{0}, {1});\n",759                  record->getValueAsString("spirvOpName"), operands);760  }761 762  // Process decorations.763  emitDecorationSerialization(op, "  ", opVar, elidedAttrs, resultID, os);764 765  os << "  return success();\n";766  os << "}\n\n";767}768 769/// Generates the prologue for the function that dispatches the serialization of770/// the operation `opVar` based on its opcode.771static void initDispatchSerializationFn(StringRef opVar, raw_ostream &os) {772  os << formatv(773      "LogicalResult Serializer::dispatchToAutogenSerialization(Operation "774      "*{0}) {{\n",775      opVar);776}777 778/// Generates the body of the dispatch function. This function generates the779/// check that if satisfied, will call the serialization function generated for780/// the `op`.781static void emitSerializationDispatch(const Operator &op, StringRef tabs,782                                      StringRef opVar, raw_ostream &os) {783  os << tabs784     << formatv("if (isa<{0}>({1})) {{\n", op.getQualCppClassName(), opVar);785  os << tabs786     << formatv("  return processOp(cast<{0}>({1}));\n",787                op.getQualCppClassName(), opVar);788  os << tabs << "}\n";789}790 791/// Generates the epilogue for the function that dispatches the serialization of792/// the operation.793static void finalizeDispatchSerializationFn(StringRef opVar, raw_ostream &os) {794  os << formatv(795      "  return {0}->emitError(\"unhandled operation serialization\");\n",796      opVar);797  os << "}\n\n";798}799 800/// Generates code to deserialize the attribute of a SPIRV_Op into `os`. The801/// generated code reads the `words` of the serialized instruction at802/// position `wordIndex` and adds the deserialized attribute into `attrList`.803static void emitAttributeDeserialization(const Attribute &attr,804                                         ArrayRef<SMLoc> loc, StringRef tabs,805                                         StringRef attrList, StringRef attrName,806                                         StringRef words, StringRef wordIndex,807                                         raw_ostream &os) {808  if (llvm::is_contained(constantIdEnumAttrs, attr.getAttrDefName())) {809    EnumInfo baseEnum(attr.getDef().getValueAsDef("enum"));810    os << tabs811       << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "812                  "opBuilder.getAttr<{2}::{3}Attr>(static_cast<{2}::{3}>("813                  "getConstantInt({4}[{5}++]).getValue().getZExtValue()))));\n",814                  attrList, attrName, baseEnum.getCppNamespace(),815                  baseEnum.getEnumClassName(), words, wordIndex);816  } else if (attr.isSubClassOf("SPIRV_BitEnumAttr") ||817             attr.isSubClassOf("SPIRV_I32EnumAttr")) {818    EnumInfo baseEnum(attr.getDef().getValueAsDef("enum"));819    os << tabs820       << formatv("  {0}.push_back(opBuilder.getNamedAttr(\"{1}\", "821                  "opBuilder.getAttr<{2}::{3}Attr>("822                  "static_cast<{2}::{3}>({4}[{5}++]))));\n",823                  attrList, attrName, baseEnum.getCppNamespace(),824                  baseEnum.getEnumClassName(), words, wordIndex);825  } else if (attr.getAttrDefName() == "I32ArrayAttr") {826    os << tabs << "SmallVector<Attribute, 4> attrListElems;\n";827    os << tabs << formatv("while ({0} < {1}.size()) {{\n", wordIndex, words);828    os << tabs829       << formatv(830              "  "831              "attrListElems.push_back(opBuilder.getI32IntegerAttr({0}[{1}++]))"832              ";\n",833              words, wordIndex);834    os << tabs << "}\n";835    os << tabs836       << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "837                  "opBuilder.getArrayAttr(attrListElems)));\n",838                  attrList, attrName);839  } else if (attr.getAttrDefName() == "I32Attr") {840    os << tabs841       << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "842                  "opBuilder.getI32IntegerAttr({2}[{3}++])));\n",843                  attrList, attrName, words, wordIndex);844  } else if (attr.isEnumAttr() || attr.isTypeAttr()) {845    os << tabs846       << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "847                  "TypeAttr::get(getType({2}[{3}++]))));\n",848                  attrList, attrName, words, wordIndex);849  } else {850    PrintFatalError(851        loc, llvm::Twine(852                 "unhandled attribute type in deserialization generation : '") +853                 attrName + llvm::Twine("'"));854  }855}856 857/// Generates the code to deserialize the result of an SPIRV_Op `op` into858/// `os`. The generated code gets the type of the result specified at859/// `words`[`wordIndex`], the SSA ID for the result at position `wordIndex` + 1860/// and updates the `resultType` and `valueID` with the parsed type and SSA ID,861/// respectively.862static void emitResultDeserialization(const Operator &op, ArrayRef<SMLoc> loc,863                                      StringRef tabs, StringRef words,864                                      StringRef wordIndex,865                                      StringRef resultTypes, StringRef valueID,866                                      raw_ostream &os) {867  // Deserialize result information if it exists868  if (op.getNumResults() == 1) {869    os << tabs << "{\n";870    os << tabs << formatv("  if ({0} >= {1}.size()) {{\n", wordIndex, words);871    os << tabs872       << formatv(873              "    return emitError(unknownLoc, \"expected result type <id> "874              "while deserializing {0}\");\n",875              op.getQualCppClassName());876    os << tabs << "  }\n";877    os << tabs << formatv("  auto ty = getType({0}[{1}]);\n", words, wordIndex);878    os << tabs << "  if (!ty) {\n";879    os << tabs880       << formatv(881              "    return emitError(unknownLoc, \"unknown type result <id> : "882              "\") << {0}[{1}];\n",883              words, wordIndex);884    os << tabs << "  }\n";885    os << tabs << formatv("  {0}.push_back(ty);\n", resultTypes);886    os << tabs << formatv("  {0}++;\n", wordIndex);887    os << tabs << formatv("  if ({0} >= {1}.size()) {{\n", wordIndex, words);888    os << tabs889       << formatv(890              "    return emitError(unknownLoc, \"expected result <id> while "891              "deserializing {0}\");\n",892              op.getQualCppClassName());893    os << tabs << "  }\n";894    os << tabs << "}\n";895    os << tabs << formatv("{0} = {1}[{2}++];\n", valueID, words, wordIndex);896  } else if (op.getNumResults() != 0) {897    PrintFatalError(loc, "SPIR-V ops can have only zero or one result");898  }899}900 901/// Generates the code to deserialize the operands of an SPIRV_Op `op` into902/// `os`. The generated code reads the `words` of the binary instruction, from903/// position `wordIndex` to the end, and either gets the Value corresponding to904/// the ID encoded, or deserializes the attributes encoded. The parsed905/// operand(attribute) is added to the `operands` list or `attributes` list.906static void emitOperandDeserialization(const Operator &op, ArrayRef<SMLoc> loc,907                                       StringRef tabs, StringRef words,908                                       StringRef wordIndex, StringRef operands,909                                       StringRef attributes, raw_ostream &os) {910  // Process operands/attributes911  for (unsigned i = 0, e = op.getNumArgs(); i < e; ++i) {912    auto argument = op.getArg(i);913    if (auto *valueArg =914            llvm::dyn_cast_if_present<NamedTypeConstraint *>(argument)) {915      if (valueArg->isVariableLength()) {916        if (i != e - 1) {917          PrintFatalError(918              loc, "SPIR-V ops can have Variadic<..> or "919                   "Optional<...> arguments only if it's the last argument");920        }921        os << tabs922           << formatv("for (; {0} < {1}.size(); ++{0})", wordIndex, words);923      } else {924        os << tabs << formatv("if ({0} < {1}.size())", wordIndex, words);925      }926      os << " {\n";927      os << tabs928         << formatv("  auto arg = getValue({0}[{1}]);\n", words, wordIndex);929      os << tabs << "  if (!arg) {\n";930      os << tabs931         << formatv(932                "    return emitError(unknownLoc, \"unknown result <id> : \") "933                "<< {0}[{1}];\n",934                words, wordIndex);935      os << tabs << "  }\n";936      os << tabs << formatv("  {0}.push_back(arg);\n", operands);937      if (!valueArg->isVariableLength()) {938        os << tabs << formatv("  {0}++;\n", wordIndex);939      }940      os << tabs << "}\n";941    } else {942      os << tabs << formatv("if ({0} < {1}.size()) {{\n", wordIndex, words);943      auto *attr = cast<NamedAttribute *>(argument);944      auto newtabs = tabs.str() + "  ";945      emitAttributeDeserialization(946          (attr->attr.isOptional() ? attr->attr.getBaseAttr() : attr->attr),947          loc, newtabs, attributes, attr->name, words, wordIndex, os);948      os << "  }\n";949    }950  }951 952  os << tabs << formatv("if ({0} != {1}.size()) {{\n", wordIndex, words);953  os << tabs954     << formatv(955            "  return emitError(unknownLoc, \"found more operands than "956            "expected when deserializing {0}, only \") << {1} << \" of \" << "957            "{2}.size() << \" processed\";\n",958            op.getQualCppClassName(), wordIndex, words);959  os << tabs << "}\n\n";960}961 962/// Generates code to update the `attributes` vector with the attributes963/// obtained from parsing the decorations in the SPIR-V binary associated with964/// an <id> `valueID`965static void emitDecorationDeserialization(const Operator &op, StringRef tabs,966                                          StringRef valueID,967                                          StringRef attributes,968                                          raw_ostream &os) {969  // Import decorations parsed970  if (op.getNumResults() == 1) {971    os << tabs << formatv("if (decorations.count({0})) {{\n", valueID);972    os << tabs973       << formatv("  auto attrs = decorations[{0}].getAttrs();\n", valueID);974    os << tabs975       << formatv("  {0}.append(attrs.begin(), attrs.end());\n", attributes);976    os << tabs << "}\n";977  }978}979 980/// Generates code to deserialize an SPIRV_Op `op` into `os`.981static void emitDeserializationFunction(const Record *attrClass,982                                        const Record *record,983                                        const Operator &op, raw_ostream &os) {984  // If the record has 'autogenSerialization' set to 0, nothing to do985  if (!record->getValueAsBit("autogenSerialization"))986    return;987 988  StringRef resultTypes("resultTypes"), valueID("valueID"), words("words"),989      wordIndex("wordIndex"), opVar("op"), operands("operands"),990      attributes("attributes");991 992  // Method declaration993  os << formatv("template <> "994                "LogicalResult\nDeserializer::processOp<{0}>(ArrayRef<"995                "uint32_t> {1}) {{\n",996                op.getQualCppClassName(), words);997 998  // Special case for ops without attributes in TableGen definitions999  if (op.getNumAttributes() == 0 && op.getNumVariableLengthOperands() == 0) {1000    os << formatv("  return processOpWithoutGrammarAttr("1001                  "{0}, \"{1}\", {2}, {3});\n}\n\n",1002                  words, op.getOperationName(),1003                  op.getNumResults() ? "true" : "false", op.getNumOperands());1004    return;1005  }1006 1007  os << formatv("  SmallVector<Type, 1> {0};\n", resultTypes);1008  os << formatv("  size_t {0} = 0; (void){0};\n", wordIndex);1009  os << formatv("  uint32_t {0} = 0; (void){0};\n", valueID);1010 1011  // Deserialize result information1012  emitResultDeserialization(op, record->getLoc(), "  ", words, wordIndex,1013                            resultTypes, valueID, os);1014 1015  os << formatv("  SmallVector<Value, 4> {0};\n", operands);1016  os << formatv("  SmallVector<NamedAttribute, 4> {0};\n", attributes);1017  // Operand deserialization1018  emitOperandDeserialization(op, record->getLoc(), "  ", words, wordIndex,1019                             operands, attributes, os);1020 1021  // Decorations1022  emitDecorationDeserialization(op, "  ", valueID, attributes, os);1023 1024  os << formatv("  Location loc = createFileLineColLoc(opBuilder);\n");1025  os << formatv("  auto {1} = {0}::create(opBuilder, loc, {2}, {3}, {4}); "1026                "(void){1};\n",1027                op.getQualCppClassName(), opVar, resultTypes, operands,1028                attributes);1029  if (op.getNumResults() == 1) {1030    os << formatv("  valueMap[{0}] = {1}.getResult();\n\n", valueID, opVar);1031  }1032 1033  // According to SPIR-V spec:1034  // This location information applies to the instructions physically following1035  // this instruction, up to the first occurrence of any of the following: the1036  // next end of block.1037  os << formatv("  if ({0}.hasTrait<OpTrait::IsTerminator>())\n", opVar);1038  os << formatv("    (void)clearDebugLine();\n");1039  os << "  return success();\n";1040  os << "}\n\n";1041}1042 1043/// Generates the prologue for the function that dispatches the deserialization1044/// based on the `opcode`.1045static void initDispatchDeserializationFn(StringRef opcode, StringRef words,1046                                          raw_ostream &os) {1047  os << formatv("LogicalResult spirv::Deserializer::"1048                "dispatchToAutogenDeserialization(spirv::Opcode {0},"1049                " ArrayRef<uint32_t> {1}) {{\n",1050                opcode, words);1051  os << formatv("  switch ({0}) {{\n", opcode);1052}1053 1054/// Generates the body of the dispatch function, by generating the case label1055/// for an opcode and the call to the method to perform the deserialization.1056static void emitDeserializationDispatch(const Operator &op, const Record *def,1057                                        StringRef tabs, StringRef words,1058                                        raw_ostream &os) {1059  os << tabs1060     << formatv("case spirv::Opcode::{0}:\n",1061                def->getValueAsString("spirvOpName"));1062  os << tabs1063     << formatv("  return processOp<{0}>({1});\n", op.getQualCppClassName(),1064                words);1065}1066 1067/// Generates the epilogue for the function that dispatches the deserialization1068/// of the operation.1069static void finalizeDispatchDeserializationFn(StringRef opcode,1070                                              raw_ostream &os) {1071  os << "  default:\n";1072  os << "    ;\n";1073  os << "  }\n";1074  StringRef opcodeVar("opcodeString");1075  os << formatv("  auto {0} = spirv::stringifyOpcode({1});\n", opcodeVar,1076                opcode);1077  os << formatv("  if (!{0}.empty()) {{\n", opcodeVar);1078  os << formatv("    return emitError(unknownLoc, \"unhandled deserialization "1079                "of \") << {0};\n",1080                opcodeVar);1081  os << "  } else {\n";1082  os << formatv("   return emitError(unknownLoc, \"unhandled opcode \") << "1083                "static_cast<uint32_t>({0});\n",1084                opcode);1085  os << "  }\n";1086  os << "}\n";1087}1088 1089static void initExtendedSetDeserializationDispatch(StringRef extensionSetName,1090                                                   StringRef instructionID,1091                                                   StringRef words,1092                                                   raw_ostream &os) {1093  os << formatv("LogicalResult spirv::Deserializer::"1094                "dispatchToExtensionSetAutogenDeserialization("1095                "StringRef {0}, uint32_t {1}, ArrayRef<uint32_t> {2}) {{\n",1096                extensionSetName, instructionID, words);1097}1098 1099static void emitExtendedSetDeserializationDispatch(const RecordKeeper &records,1100                                                   raw_ostream &os) {1101  StringRef extensionSetName("extensionSetName"),1102      instructionID("instructionID"), words("words");1103 1104  // First iterate over all ops derived from SPIRV_ExtensionSetOps to get all1105  // extensionSets.1106 1107  // For each of the extensions a separate raw_string_ostream is used to1108  // generate code into. These are then concatenated at the end. Since1109  // raw_string_ostream needs a string&, use a vector to store all the string1110  // that are captured by reference within raw_string_ostream.1111  StringMap<raw_string_ostream> extensionSets;1112  std::list<std::string> extensionSetNames;1113 1114  initExtendedSetDeserializationDispatch(extensionSetName, instructionID, words,1115                                         os);1116  auto defs = records.getAllDerivedDefinitions("SPIRV_ExtInstOp");1117  for (const auto *def : defs) {1118    if (!def->getValueAsBit("autogenSerialization")) {1119      continue;1120    }1121    Operator op(def);1122    auto setName = def->getValueAsString("extendedInstSetName");1123    if (!extensionSets.count(setName)) {1124      extensionSetNames.emplace_back("");1125      extensionSets.try_emplace(setName, extensionSetNames.back());1126      auto &setos = extensionSets.find(setName)->second;1127      setos << formatv("  if ({0} == \"{1}\") {{\n", extensionSetName, setName);1128      setos << formatv("    switch ({0}) {{\n", instructionID);1129    }1130    auto &setos = extensionSets.find(setName)->second;1131    setos << formatv("    case {0}:\n",1132                     def->getValueAsInt("extendedInstOpcode"));1133    setos << formatv("      return processOp<{0}>({1});\n",1134                     op.getQualCppClassName(), words);1135  }1136 1137  // Append the dispatch code for all the extended sets.1138  for (auto &extensionSet : extensionSets) {1139    os << extensionSet.second.str();1140    os << "    default:\n";1141    os << formatv(1142        "      return emitError(unknownLoc, \"unhandled deserializations of "1143        "\") << {0} << \" from extension set \" << {1};\n",1144        instructionID, extensionSetName);1145    os << "    }\n";1146    os << "  }\n";1147  }1148 1149  os << formatv("  return emitError(unknownLoc, \"unhandled deserialization of "1150                "extended instruction set {0}\");\n",1151                extensionSetName);1152  os << "}\n";1153}1154 1155/// Emits all the autogenerated serialization/deserializations functions for the1156/// SPIRV_Ops.1157static bool emitSerializationFns(const RecordKeeper &records, raw_ostream &os) {1158  llvm::emitSourceFileHeader("SPIR-V Serialization Utilities/Functions", os,1159                             records);1160 1161  std::string dSerFnString, dDesFnString, serFnString, deserFnString;1162  raw_string_ostream dSerFn(dSerFnString), dDesFn(dDesFnString),1163      serFn(serFnString), deserFn(deserFnString);1164  const Record *attrClass = records.getClass("Attr");1165 1166  // Emit the serialization and deserialization functions simultaneously.1167  StringRef opVar("op");1168  StringRef opcode("opcode"), words("words");1169 1170  // Handle the SPIR-V ops.1171  initDispatchSerializationFn(opVar, dSerFn);1172  initDispatchDeserializationFn(opcode, words, dDesFn);1173  auto defs = records.getAllDerivedDefinitions("SPIRV_Op");1174  for (const auto *def : defs) {1175    Operator op(def);1176    emitSerializationFunction(attrClass, def, op, serFn);1177    emitDeserializationFunction(attrClass, def, op, deserFn);1178    if (def->getValueAsBit("hasOpcode") ||1179        def->isSubClassOf("SPIRV_ExtInstOp")) {1180      emitSerializationDispatch(op, "  ", opVar, dSerFn);1181    }1182    if (def->getValueAsBit("hasOpcode")) {1183      emitDeserializationDispatch(op, def, "  ", words, dDesFn);1184    }1185  }1186  finalizeDispatchSerializationFn(opVar, dSerFn);1187  finalizeDispatchDeserializationFn(opcode, dDesFn);1188 1189  emitExtendedSetDeserializationDispatch(records, dDesFn);1190 1191  os << "#ifdef GET_SERIALIZATION_FNS\n\n";1192  os << serFn.str();1193  os << dSerFn.str();1194  os << "#endif // GET_SERIALIZATION_FNS\n\n";1195 1196  os << "#ifdef GET_DESERIALIZATION_FNS\n\n";1197  os << deserFn.str();1198  os << dDesFn.str();1199  os << "#endif // GET_DESERIALIZATION_FNS\n\n";1200 1201  return false;1202}1203 1204//===----------------------------------------------------------------------===//1205// Serialization Hook Registration1206//===----------------------------------------------------------------------===//1207 1208static mlir::GenRegistration genSerialization(1209    "gen-spirv-serialization",1210    "Generate SPIR-V (de)serialization utilities and functions",1211    [](const RecordKeeper &records, raw_ostream &os) {1212      return emitSerializationFns(records, os);1213    });1214 1215//===----------------------------------------------------------------------===//1216// Op Utils AutoGen1217//===----------------------------------------------------------------------===//1218 1219static void emitEnumGetAttrNameFnDecl(raw_ostream &os) {1220  os << formatv("template <typename EnumClass> inline constexpr StringRef "1221                "attributeName();\n");1222}1223 1224static void emitEnumGetAttrNameFnDefn(const EnumInfo &enumInfo,1225                                      raw_ostream &os) {1226  auto enumName = enumInfo.getEnumClassName();1227  os << formatv("template <> inline StringRef attributeName<{0}>() {{\n",1228                enumName);1229  os << "  "1230     << formatv("static constexpr const char attrName[] = \"{0}\";\n",1231                llvm::convertToSnakeFromCamelCase(enumName));1232  os << "  return attrName;\n";1233  os << "}\n";1234}1235 1236static bool emitAttrUtils(const RecordKeeper &records, raw_ostream &os) {1237  llvm::emitSourceFileHeader("SPIR-V Attribute Utilities", os, records);1238 1239  auto defs = records.getAllDerivedDefinitions("EnumInfo");1240  os << "#ifndef MLIR_DIALECT_SPIRV_IR_ATTR_UTILS_H_\n";1241  os << "#define MLIR_DIALECT_SPIRV_IR_ATTR_UTILS_H_\n";1242  emitEnumGetAttrNameFnDecl(os);1243  for (const auto *def : defs) {1244    EnumInfo enumInfo(*def);1245    emitEnumGetAttrNameFnDefn(enumInfo, os);1246  }1247  os << "#endif // MLIR_DIALECT_SPIRV_IR_ATTR_UTILS_H\n";1248  return false;1249}1250 1251//===----------------------------------------------------------------------===//1252// Op Utils Hook Registration1253//===----------------------------------------------------------------------===//1254 1255static mlir::GenRegistration1256    genOpUtils("gen-spirv-attr-utils",1257               "Generate SPIR-V attribute utility definitions",1258               [](const RecordKeeper &records, raw_ostream &os) {1259                 return emitAttrUtils(records, os);1260               });1261 1262//===----------------------------------------------------------------------===//1263// SPIR-V Availability Impl AutoGen1264//===----------------------------------------------------------------------===//1265 1266static void emitAvailabilityImpl(const Operator &srcOp, raw_ostream &os) {1267  mlir::tblgen::FmtContext fctx;1268  fctx.addSubst("overall", "tblgen_overall");1269 1270  std::vector<Availability> opAvailabilities =1271      getAvailabilities(srcOp.getDef());1272 1273  // First collect all availability classes this op should implement.1274  // All availability instances keep information for the generated interface and1275  // the instance's specific requirement. Here we remember a random instance so1276  // we can get the information regarding the generated interface.1277  llvm::StringMap<Availability> availClasses;1278  for (const Availability &avail : opAvailabilities)1279    availClasses.try_emplace(avail.getClass(), avail);1280  for (const NamedAttribute &namedAttr : srcOp.getAttributes()) {1281    if (!namedAttr.attr.isSubClassOf("SPIRV_BitEnumAttr") &&1282        !namedAttr.attr.isSubClassOf("SPIRV_I32EnumAttr"))1283      continue;1284    EnumInfo enumInfo(namedAttr.attr.getDef().getValueAsDef("enum"));1285 1286    for (const EnumCase &enumerant : enumInfo.getAllCases())1287      for (const Availability &caseAvail :1288           getAvailabilities(enumerant.getDef()))1289        availClasses.try_emplace(caseAvail.getClass(), caseAvail);1290  }1291 1292  // Then generate implementation for each availability class.1293  for (const auto &availClass : availClasses) {1294    StringRef availClassName = availClass.getKey();1295    Availability avail = availClass.getValue();1296 1297    // Generate the implementation method signature.1298    os << formatv("{0} {1}::{2}() {{\n", avail.getQueryFnRetType(),1299                  srcOp.getCppClassName(), avail.getQueryFnName());1300 1301    // Create the variable for the final requirement and initialize it.1302    os << formatv("  {0} tblgen_overall = {1};\n", avail.getQueryFnRetType(),1303                  avail.getMergeInitializer());1304 1305    // Update with the op's specific availability spec.1306    for (const Availability &avail : opAvailabilities)1307      if (avail.getClass() == availClassName &&1308          (!avail.getMergeInstancePreparation().empty() ||1309           !avail.getMergeActionCode().empty())) {1310        os << "  {\n    "1311           // Prepare this instance.1312           << avail.getMergeInstancePreparation()1313           << "\n    "1314           // Merge this instance.1315           << std::string(1316                  tgfmt(avail.getMergeActionCode(),1317                        &fctx.addSubst("instance", avail.getMergeInstance())))1318           << ";\n  }\n";1319      }1320 1321    // Update with enum attributes' specific availability spec.1322    for (const NamedAttribute &namedAttr : srcOp.getAttributes()) {1323      if (!namedAttr.attr.isSubClassOf("SPIRV_BitEnumAttr") &&1324          !namedAttr.attr.isSubClassOf("SPIRV_I32EnumAttr"))1325        continue;1326      EnumInfo enumInfo(namedAttr.attr.getDef().getValueAsDef("enum"));1327 1328      // (enumerant, availability specification) pairs for this availability1329      // class.1330      SmallVector<std::pair<EnumCase, Availability>, 1> caseSpecs;1331 1332      // Collect all cases' availability specs.1333      for (const EnumCase &enumerant : enumInfo.getAllCases())1334        for (const Availability &caseAvail :1335             getAvailabilities(enumerant.getDef()))1336          if (availClassName == caseAvail.getClass())1337            caseSpecs.push_back({enumerant, caseAvail});1338 1339      // If this attribute kind does not have any availability spec from any of1340      // its cases, no more work to do.1341      if (caseSpecs.empty())1342        continue;1343 1344      if (enumInfo.isBitEnum()) {1345        // For BitEnumAttr, we need to iterate over each bit to query its1346        // availability spec.1347        os << formatv("  for (unsigned i = 0; "1348                      "i < std::numeric_limits<{0}>::digits; ++i) {{\n",1349                      enumInfo.getUnderlyingType());1350        os << formatv("    {0}::{1} tblgen_attrVal = this->{2}() & "1351                      "static_cast<{0}::{1}>(1 << i);\n",1352                      enumInfo.getCppNamespace(), enumInfo.getEnumClassName(),1353                      srcOp.getGetterName(namedAttr.name));1354        os << formatv(1355            "    if (static_cast<{0}>(tblgen_attrVal) == 0) continue;\n",1356            enumInfo.getUnderlyingType());1357      } else {1358        // For IntEnumAttr, we just need to query the value as a whole.1359        os << "  {\n";1360        os << formatv("    auto tblgen_attrVal = this->{0}();\n",1361                      srcOp.getGetterName(namedAttr.name));1362      }1363      os << formatv("    auto tblgen_instance = {0}::{1}(tblgen_attrVal);\n",1364                    enumInfo.getCppNamespace(), avail.getQueryFnName());1365      os << "    if (tblgen_instance) "1366         // TODO` here once ODS supports1367         // dialect-specific contents so that we can use not implementing the1368         // availability interface as indication of no requirements.1369         << std::string(tgfmt(caseSpecs.front().second.getMergeActionCode(),1370                              &fctx.addSubst("instance", "*tblgen_instance")))1371         << ";\n";1372      os << "  }\n";1373    }1374 1375    os << "  return tblgen_overall;\n";1376    os << "}\n";1377  }1378}1379 1380static bool emitAvailabilityImpl(const RecordKeeper &records, raw_ostream &os) {1381  llvm::emitSourceFileHeader("SPIR-V Op Availability Implementations", os,1382                             records);1383 1384  auto defs = records.getAllDerivedDefinitions("SPIRV_Op");1385  for (const auto *def : defs) {1386    Operator op(def);1387    if (def->getValueAsBit("autogenAvailability"))1388      emitAvailabilityImpl(op, os);1389  }1390  return false;1391}1392 1393//===----------------------------------------------------------------------===//1394// Op Availability Implementation Hook Registration1395//===----------------------------------------------------------------------===//1396 1397static mlir::GenRegistration1398    genOpAvailabilityImpl("gen-spirv-avail-impls",1399                          "Generate SPIR-V operation utility definitions",1400                          [](const RecordKeeper &records, raw_ostream &os) {1401                            return emitAvailabilityImpl(records, os);1402                          });1403 1404//===----------------------------------------------------------------------===//1405// SPIR-V Capability Implication AutoGen1406//===----------------------------------------------------------------------===//1407 1408static bool emitCapabilityImplication(const RecordKeeper &records,1409                                      raw_ostream &os) {1410  llvm::emitSourceFileHeader("SPIR-V Capability Implication", os, records);1411 1412  EnumInfo enumInfo(1413      records.getDef("SPIRV_CapabilityAttr")->getValueAsDef("enum"));1414 1415  os << "ArrayRef<spirv::Capability> "1416        "spirv::getDirectImpliedCapabilities(spirv::Capability cap) {\n"1417     << "  switch (cap) {\n"1418     << "  default: return {};\n";1419  for (const EnumCase &enumerant : enumInfo.getAllCases()) {1420    const Record &def = enumerant.getDef();1421    if (!def.getValue("implies"))1422      continue;1423 1424    std::vector<const Record *> impliedCapsDefs =1425        def.getValueAsListOfDefs("implies");1426    os << "  case spirv::Capability::" << enumerant.getSymbol()1427       << ": {static const spirv::Capability implies[" << impliedCapsDefs.size()1428       << "] = {";1429    llvm::interleaveComma(impliedCapsDefs, os, [&](const Record *capDef) {1430      os << "spirv::Capability::" << EnumCase(capDef).getSymbol();1431    });1432    os << "}; return ArrayRef<spirv::Capability>(implies, "1433       << impliedCapsDefs.size() << "); }\n";1434  }1435  os << "  }\n";1436  os << "}\n";1437 1438  return false;1439}1440 1441//===----------------------------------------------------------------------===//1442// SPIR-V Capability Implication Hook Registration1443//===----------------------------------------------------------------------===//1444 1445static mlir::GenRegistration1446    genCapabilityImplication("gen-spirv-capability-implication",1447                             "Generate utility function to return implied "1448                             "capabilities for a given capability",1449                             [](const RecordKeeper &records, raw_ostream &os) {1450                               return emitCapabilityImplication(records, os);1451                             });1452