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