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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // SPIRVSerializationGen generates common utility functions for SPIR-V
10 // serialization.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "mlir/TableGen/Attribute.h"
15 #include "mlir/TableGen/Format.h"
16 #include "mlir/TableGen/GenInfo.h"
17 #include "mlir/TableGen/Operator.h"
18 #include "llvm/ADT/Sequence.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringExtras.h"
21 #include "llvm/ADT/StringMap.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/StringSet.h"
24 #include "llvm/Support/FormatVariadic.h"
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/TableGen/Error.h"
27 #include "llvm/TableGen/Record.h"
28 #include "llvm/TableGen/TableGenBackend.h"
29 
30 using llvm::ArrayRef;
31 using llvm::formatv;
32 using llvm::raw_ostream;
33 using llvm::raw_string_ostream;
34 using llvm::Record;
35 using llvm::RecordKeeper;
36 using llvm::SmallVector;
37 using llvm::SMLoc;
38 using llvm::StringMap;
39 using llvm::StringRef;
40 using llvm::Twine;
41 using mlir::tblgen::Attribute;
42 using mlir::tblgen::EnumAttr;
43 using mlir::tblgen::EnumAttrCase;
44 using mlir::tblgen::NamedAttribute;
45 using mlir::tblgen::NamedTypeConstraint;
46 using mlir::tblgen::Operator;
47 
48 //===----------------------------------------------------------------------===//
49 // Availability Wrapper Class
50 //===----------------------------------------------------------------------===//
51 
52 namespace {
53 // Wrapper class with helper methods for accessing availability defined in
54 // TableGen.
55 class Availability {
56 public:
57   explicit Availability(const Record *def);
58 
59   // Returns the name of the direct TableGen class for this availability
60   // instance.
61   StringRef getClass() const;
62 
63   // Returns the generated C++ interface's class name.
64   StringRef getInterfaceClassName() const;
65 
66   // Returns the generated C++ interface's description.
67   StringRef getInterfaceDescription() const;
68 
69   // Returns the name of the query function insided the generated C++ interface.
70   StringRef getQueryFnName() const;
71 
72   // Returns the return type of the query function insided the generated C++
73   // interface.
74   StringRef getQueryFnRetType() const;
75 
76   // Returns the code for merging availability requirements.
77   StringRef getMergeActionCode() const;
78 
79   // Returns the initializer expression for initializing the final availability
80   // requirements.
81   StringRef getMergeInitializer() const;
82 
83   // Returns the C++ type for an availability instance.
84   StringRef getMergeInstanceType() const;
85 
86   // Returns the C++ statements for preparing availability instance.
87   StringRef getMergeInstancePreparation() const;
88 
89   // Returns the concrete availability instance carried in this case.
90   StringRef getMergeInstance() const;
91 
92 private:
93   // The TableGen definition of this availability.
94   const llvm::Record *def;
95 };
96 } // namespace
97 
98 Availability::Availability(const llvm::Record *def) : def(def) {
99   assert(def->isSubClassOf("Availability") &&
100          "must be subclass of TableGen 'Availability' class");
101 }
102 
103 StringRef Availability::getClass() const {
104   SmallVector<Record *, 1> parentClass;
105   def->getDirectSuperClasses(parentClass);
106   if (parentClass.size() != 1) {
107     PrintFatalError(def->getLoc(),
108                     "expected to only have one direct superclass");
109   }
110   return parentClass.front()->getName();
111 }
112 
113 StringRef Availability::getInterfaceClassName() const {
114   return def->getValueAsString("interfaceName");
115 }
116 
117 StringRef Availability::getInterfaceDescription() const {
118   return def->getValueAsString("interfaceDescription");
119 }
120 
121 StringRef Availability::getQueryFnRetType() const {
122   return def->getValueAsString("queryFnRetType");
123 }
124 
125 StringRef Availability::getQueryFnName() const {
126   return def->getValueAsString("queryFnName");
127 }
128 
129 StringRef Availability::getMergeActionCode() const {
130   return def->getValueAsString("mergeAction");
131 }
132 
133 StringRef Availability::getMergeInitializer() const {
134   return def->getValueAsString("initializer");
135 }
136 
137 StringRef Availability::getMergeInstanceType() const {
138   return def->getValueAsString("instanceType");
139 }
140 
141 StringRef Availability::getMergeInstancePreparation() const {
142   return def->getValueAsString("instancePreparation");
143 }
144 
145 StringRef Availability::getMergeInstance() const {
146   return def->getValueAsString("instance");
147 }
148 
149 // Returns the availability spec of the given `def`.
150 std::vector<Availability> getAvailabilities(const Record &def) {
151   std::vector<Availability> availabilities;
152 
153   if (def.getValue("availability")) {
154     std::vector<Record *> availDefs = def.getValueAsListOfDefs("availability");
155     availabilities.reserve(availDefs.size());
156     for (const Record *avail : availDefs)
157       availabilities.emplace_back(avail);
158   }
159 
160   return availabilities;
161 }
162 
163 //===----------------------------------------------------------------------===//
164 // Availability Interface Definitions AutoGen
165 //===----------------------------------------------------------------------===//
166 
167 static void emitInterfaceDef(const Availability &availability,
168                              raw_ostream &os) {
169   StringRef methodName = availability.getQueryFnName();
170   os << availability.getQueryFnRetType() << " "
171      << availability.getInterfaceClassName() << "::" << methodName << "() {\n"
172      << "  return getImpl()->" << methodName << "(getOperation());\n"
173      << "}\n";
174 }
175 
176 static bool emitInterfaceDefs(const RecordKeeper &recordKeeper,
177                               raw_ostream &os) {
178   llvm::emitSourceFileHeader("Availability Interface Definitions", os);
179 
180   auto defs = recordKeeper.getAllDerivedDefinitions("Availability");
181   SmallVector<const Record *, 1> handledClasses;
182   for (const Record *def : defs) {
183     SmallVector<Record *, 1> parent;
184     def->getDirectSuperClasses(parent);
185     if (parent.size() != 1) {
186       PrintFatalError(def->getLoc(),
187                       "expected to only have one direct superclass");
188     }
189     if (llvm::is_contained(handledClasses, parent.front()))
190       continue;
191 
192     Availability availability(def);
193     emitInterfaceDef(availability, os);
194     handledClasses.push_back(parent.front());
195   }
196   return false;
197 }
198 
199 //===----------------------------------------------------------------------===//
200 // Availability Interface Declarations AutoGen
201 //===----------------------------------------------------------------------===//
202 
203 static void emitConceptDecl(const Availability &availability, raw_ostream &os) {
204   os << "  class Concept {\n"
205      << "  public:\n"
206      << "    virtual ~Concept() = default;\n"
207      << "    virtual " << availability.getQueryFnRetType() << " "
208      << availability.getQueryFnName() << "(Operation *tblgen_opaque_op) = 0;\n"
209      << "  };\n";
210 }
211 
212 static void emitModelDecl(const Availability &availability, raw_ostream &os) {
213   os << "  template<typename ConcreteOp>\n";
214   os << "  class Model : public Concept {\n"
215      << "  public:\n"
216      << "    " << availability.getQueryFnRetType() << " "
217      << availability.getQueryFnName()
218      << "(Operation *tblgen_opaque_op) final {\n"
219      << "      auto op = llvm::cast<ConcreteOp>(tblgen_opaque_op);\n"
220      << "      (void)op;\n"
221      // Forward to the method on the concrete operation type.
222      << "      return op." << availability.getQueryFnName() << "();\n"
223      << "    }\n"
224      << "  };\n";
225 }
226 
227 static void emitInterfaceDecl(const Availability &availability,
228                               raw_ostream &os) {
229   StringRef interfaceName = availability.getInterfaceClassName();
230   std::string interfaceTraitsName =
231       std::string(formatv("{0}Traits", interfaceName));
232 
233   // Emit the traits struct containing the concept and model declarations.
234   os << "namespace detail {\n"
235      << "struct " << interfaceTraitsName << " {\n";
236   emitConceptDecl(availability, os);
237   os << '\n';
238   emitModelDecl(availability, os);
239   os << "};\n} // end namespace detail\n\n";
240 
241   // Emit the main interface class declaration.
242   os << "/*\n" << availability.getInterfaceDescription().trim() << "\n*/\n";
243   os << llvm::formatv("class {0} : public OpInterface<{1}, detail::{2}> {\n"
244                       "public:\n"
245                       "  using OpInterface<{1}, detail::{2}>::OpInterface;\n",
246                       interfaceName, interfaceName, interfaceTraitsName);
247 
248   // Emit query function declaration.
249   os << "  " << availability.getQueryFnRetType() << " "
250      << availability.getQueryFnName() << "();\n";
251   os << "};\n\n";
252 }
253 
254 static bool emitInterfaceDecls(const RecordKeeper &recordKeeper,
255                                raw_ostream &os) {
256   llvm::emitSourceFileHeader("Availability Interface Declarations", os);
257 
258   auto defs = recordKeeper.getAllDerivedDefinitions("Availability");
259   SmallVector<const Record *, 4> handledClasses;
260   for (const Record *def : defs) {
261     SmallVector<Record *, 1> parent;
262     def->getDirectSuperClasses(parent);
263     if (parent.size() != 1) {
264       PrintFatalError(def->getLoc(),
265                       "expected to only have one direct superclass");
266     }
267     if (llvm::is_contained(handledClasses, parent.front()))
268       continue;
269 
270     Availability avail(def);
271     emitInterfaceDecl(avail, os);
272     handledClasses.push_back(parent.front());
273   }
274   return false;
275 }
276 
277 //===----------------------------------------------------------------------===//
278 // Availability Interface Hook Registration
279 //===----------------------------------------------------------------------===//
280 
281 // Registers the operation interface generator to mlir-tblgen.
282 static mlir::GenRegistration
283     genInterfaceDecls("gen-avail-interface-decls",
284                       "Generate availability interface declarations",
285                       [](const RecordKeeper &records, raw_ostream &os) {
286                         return emitInterfaceDecls(records, os);
287                       });
288 
289 // Registers the operation interface generator to mlir-tblgen.
290 static mlir::GenRegistration
291     genInterfaceDefs("gen-avail-interface-defs",
292                      "Generate op interface definitions",
293                      [](const RecordKeeper &records, raw_ostream &os) {
294                        return emitInterfaceDefs(records, os);
295                      });
296 
297 //===----------------------------------------------------------------------===//
298 // Enum Availability Query AutoGen
299 //===----------------------------------------------------------------------===//
300 
301 static void emitAvailabilityQueryForIntEnum(const Record &enumDef,
302                                             raw_ostream &os) {
303   EnumAttr enumAttr(enumDef);
304   StringRef enumName = enumAttr.getEnumClassName();
305   std::vector<EnumAttrCase> enumerants = enumAttr.getAllCases();
306 
307   // Mapping from availability class name to (enumerant, availability
308   // specification) pairs.
309   llvm::StringMap<llvm::SmallVector<std::pair<EnumAttrCase, Availability>, 1>>
310       classCaseMap;
311 
312   // Place all availability specifications to their corresponding
313   // availability classes.
314   for (const EnumAttrCase &enumerant : enumerants)
315     for (const Availability &avail : getAvailabilities(enumerant.getDef()))
316       classCaseMap[avail.getClass()].push_back({enumerant, avail});
317 
318   for (const auto &classCasePair : classCaseMap) {
319     Availability avail = classCasePair.getValue().front().second;
320 
321     os << formatv("llvm::Optional<{0}> {1}({2} value) {{\n",
322                   avail.getMergeInstanceType(), avail.getQueryFnName(),
323                   enumName);
324 
325     os << "  switch (value) {\n";
326     for (const auto &caseSpecPair : classCasePair.getValue()) {
327       EnumAttrCase enumerant = caseSpecPair.first;
328       Availability avail = caseSpecPair.second;
329       os << formatv("  case {0}::{1}: { {2} return {3}({4}); }\n", enumName,
330                     enumerant.getSymbol(), avail.getMergeInstancePreparation(),
331                     avail.getMergeInstanceType(), avail.getMergeInstance());
332     }
333     // Only emit default if uncovered cases.
334     if (classCasePair.getValue().size() < enumAttr.getAllCases().size())
335       os << "  default: break;\n";
336     os << "  }\n"
337        << "  return llvm::None;\n"
338        << "}\n";
339   }
340 }
341 
342 static void emitAvailabilityQueryForBitEnum(const Record &enumDef,
343                                             raw_ostream &os) {
344   EnumAttr enumAttr(enumDef);
345   StringRef enumName = enumAttr.getEnumClassName();
346   std::string underlyingType = std::string(enumAttr.getUnderlyingType());
347   std::vector<EnumAttrCase> enumerants = enumAttr.getAllCases();
348 
349   // Mapping from availability class name to (enumerant, availability
350   // specification) pairs.
351   llvm::StringMap<llvm::SmallVector<std::pair<EnumAttrCase, Availability>, 1>>
352       classCaseMap;
353 
354   // Place all availability specifications to their corresponding
355   // availability classes.
356   for (const EnumAttrCase &enumerant : enumerants)
357     for (const Availability &avail : getAvailabilities(enumerant.getDef()))
358       classCaseMap[avail.getClass()].push_back({enumerant, avail});
359 
360   for (const auto &classCasePair : classCaseMap) {
361     Availability avail = classCasePair.getValue().front().second;
362 
363     os << formatv("llvm::Optional<{0}> {1}({2} value) {{\n",
364                   avail.getMergeInstanceType(), avail.getQueryFnName(),
365                   enumName);
366 
367     os << formatv(
368         "  assert(::llvm::countPopulation(static_cast<{0}>(value)) <= 1"
369         " && \"cannot have more than one bit set\");\n",
370         underlyingType);
371 
372     os << "  switch (value) {\n";
373     for (const auto &caseSpecPair : classCasePair.getValue()) {
374       EnumAttrCase enumerant = caseSpecPair.first;
375       Availability avail = caseSpecPair.second;
376       os << formatv("  case {0}::{1}: { {2} return {3}({4}); }\n", enumName,
377                     enumerant.getSymbol(), avail.getMergeInstancePreparation(),
378                     avail.getMergeInstanceType(), avail.getMergeInstance());
379     }
380     os << "  default: break;\n";
381     os << "  }\n"
382        << "  return llvm::None;\n"
383        << "}\n";
384   }
385 }
386 
387 static void emitEnumDecl(const Record &enumDef, raw_ostream &os) {
388   EnumAttr enumAttr(enumDef);
389   StringRef enumName = enumAttr.getEnumClassName();
390   StringRef cppNamespace = enumAttr.getCppNamespace();
391   auto enumerants = enumAttr.getAllCases();
392 
393   llvm::SmallVector<StringRef, 2> namespaces;
394   llvm::SplitString(cppNamespace, namespaces, "::");
395 
396   for (auto ns : namespaces)
397     os << "namespace " << ns << " {\n";
398 
399   llvm::StringSet<> handledClasses;
400 
401   // Place all availability specifications to their corresponding
402   // availability classes.
403   for (const EnumAttrCase &enumerant : enumerants)
404     for (const Availability &avail : getAvailabilities(enumerant.getDef())) {
405       StringRef className = avail.getClass();
406       if (handledClasses.count(className))
407         continue;
408       os << formatv("llvm::Optional<{0}> {1}({2} value);\n",
409                     avail.getMergeInstanceType(), avail.getQueryFnName(),
410                     enumName);
411       handledClasses.insert(className);
412     }
413 
414   for (auto ns : llvm::reverse(namespaces))
415     os << "} // namespace " << ns << "\n";
416 }
417 
418 static bool emitEnumDecls(const RecordKeeper &recordKeeper, raw_ostream &os) {
419   llvm::emitSourceFileHeader("SPIR-V Enum Availability Declarations", os);
420 
421   auto defs = recordKeeper.getAllDerivedDefinitions("EnumAttrInfo");
422   for (const auto *def : defs)
423     emitEnumDecl(*def, os);
424 
425   return false;
426 }
427 
428 static void emitEnumDef(const Record &enumDef, raw_ostream &os) {
429   EnumAttr enumAttr(enumDef);
430   StringRef cppNamespace = enumAttr.getCppNamespace();
431 
432   llvm::SmallVector<StringRef, 2> namespaces;
433   llvm::SplitString(cppNamespace, namespaces, "::");
434 
435   for (auto ns : namespaces)
436     os << "namespace " << ns << " {\n";
437 
438   if (enumAttr.isBitEnum()) {
439     emitAvailabilityQueryForBitEnum(enumDef, os);
440   } else {
441     emitAvailabilityQueryForIntEnum(enumDef, os);
442   }
443 
444   for (auto ns : llvm::reverse(namespaces))
445     os << "} // namespace " << ns << "\n";
446   os << "\n";
447 }
448 
449 static bool emitEnumDefs(const RecordKeeper &recordKeeper, raw_ostream &os) {
450   llvm::emitSourceFileHeader("SPIR-V Enum Availability Definitions", os);
451 
452   auto defs = recordKeeper.getAllDerivedDefinitions("EnumAttrInfo");
453   for (const auto *def : defs)
454     emitEnumDef(*def, os);
455 
456   return false;
457 }
458 
459 //===----------------------------------------------------------------------===//
460 // Enum Availability Query Hook Registration
461 //===----------------------------------------------------------------------===//
462 
463 // Registers the enum utility generator to mlir-tblgen.
464 static mlir::GenRegistration
465     genEnumDecls("gen-spirv-enum-avail-decls",
466                  "Generate SPIR-V enum availability declarations",
467                  [](const RecordKeeper &records, raw_ostream &os) {
468                    return emitEnumDecls(records, os);
469                  });
470 
471 // Registers the enum utility generator to mlir-tblgen.
472 static mlir::GenRegistration
473     genEnumDefs("gen-spirv-enum-avail-defs",
474                 "Generate SPIR-V enum availability definitions",
475                 [](const RecordKeeper &records, raw_ostream &os) {
476                   return emitEnumDefs(records, os);
477                 });
478 
479 //===----------------------------------------------------------------------===//
480 // Serialization AutoGen
481 //===----------------------------------------------------------------------===//
482 
483 // Writes the following function to `os`:
484 //   inline uint32_t getOpcode(<op-class-name>) { return <opcode>; }
485 static void emitGetOpcodeFunction(const Record *record, Operator const &op,
486                                   raw_ostream &os) {
487   os << formatv("template <> constexpr inline ::mlir::spirv::Opcode "
488                 "getOpcode<{0}>() {{\n",
489                 op.getQualCppClassName());
490   os << formatv("  return ::mlir::spirv::Opcode::{0};\n",
491                 record->getValueAsString("spirvOpName"));
492   os << "}\n";
493 }
494 
495 /// Forward declaration of function to return the SPIR-V opcode corresponding to
496 /// an operation. This function will be generated for all SPV_Op instances that
497 /// have hasOpcode = 1.
498 static void declareOpcodeFn(raw_ostream &os) {
499   os << "template <typename OpClass> inline constexpr ::mlir::spirv::Opcode "
500         "getOpcode();\n";
501 }
502 
503 /// Generates code to serialize attributes of a SPV_Op `op` into `os`. The
504 /// generates code extracts the attribute with name `attrName` from
505 /// `operandList` of `op`.
506 static void emitAttributeSerialization(const Attribute &attr,
507                                        ArrayRef<SMLoc> loc, StringRef tabs,
508                                        StringRef opVar, StringRef operandList,
509                                        StringRef attrName, raw_ostream &os) {
510   os << tabs << formatv("auto attr = {0}.getAttr(\"{1}\");\n", opVar, attrName);
511   os << tabs << "if (attr) {\n";
512   if (attr.getAttrDefName() == "SPV_ScopeAttr" ||
513       attr.getAttrDefName() == "SPV_MemorySemanticsAttr") {
514     os << tabs
515        << formatv("  {0}.push_back(prepareConstantInt({1}.getLoc(), "
516                   "attr.cast<IntegerAttr>()));\n",
517                   operandList, opVar);
518   } else if (attr.getAttrDefName() == "I32ArrayAttr") {
519     // Serialize all the elements of the array
520     os << tabs << "  for (auto attrElem : attr.cast<ArrayAttr>()) {\n";
521     os << tabs
522        << formatv("    {0}.push_back(static_cast<uint32_t>("
523                   "attrElem.cast<IntegerAttr>().getValue().getZExtValue()));\n",
524                   operandList);
525     os << tabs << "  }\n";
526   } else if (attr.isEnumAttr() || attr.getAttrDefName() == "I32Attr") {
527     os << tabs
528        << formatv("  {0}.push_back(static_cast<uint32_t>("
529                   "attr.cast<IntegerAttr>().getValue().getZExtValue()));\n",
530                   operandList);
531   } else {
532     PrintFatalError(
533         loc,
534         llvm::Twine(
535             "unhandled attribute type in SPIR-V serialization generation : '") +
536             attr.getAttrDefName() + llvm::Twine("'"));
537   }
538   os << tabs << "}\n";
539 }
540 
541 /// Generates code to serialize the operands of a SPV_Op `op` into `os`. The
542 /// generated queries the SSA-ID if operand is a SSA-Value, or serializes the
543 /// attributes. The `operands` vector is updated appropriately. `elidedAttrs`
544 /// updated as well to include the serialized attributes.
545 static void emitOperandSerialization(const Operator &op, ArrayRef<SMLoc> loc,
546                                      StringRef tabs, StringRef opVar,
547                                      StringRef operands, StringRef elidedAttrs,
548                                      raw_ostream &os) {
549   auto operandNum = 0;
550   for (unsigned i = 0, e = op.getNumArgs(); i < e; ++i) {
551     auto argument = op.getArg(i);
552     os << tabs << "{\n";
553     if (argument.is<NamedTypeConstraint *>()) {
554       os << tabs
555          << formatv("  for (auto arg : {0}.getODSOperands({1})) {{\n", opVar,
556                     operandNum);
557       os << tabs << "    auto argID = getValueID(arg);\n";
558       os << tabs << "    if (!argID) {\n";
559       os << tabs
560          << formatv("      return emitError({0}.getLoc(), "
561                     "\"operand #{1} has a use before def\");\n",
562                     opVar, operandNum);
563       os << tabs << "    }\n";
564       os << tabs << formatv("    {0}.push_back(argID);\n", operands);
565       os << "    }\n";
566       operandNum++;
567     } else {
568       auto attr = argument.get<NamedAttribute *>();
569       auto newtabs = tabs.str() + "  ";
570       emitAttributeSerialization(
571           (attr->attr.isOptional() ? attr->attr.getBaseAttr() : attr->attr),
572           loc, newtabs, opVar, operands, attr->name, os);
573       os << newtabs
574          << formatv("{0}.push_back(\"{1}\");\n", elidedAttrs, attr->name);
575     }
576     os << tabs << "}\n";
577   }
578 }
579 
580 /// Generates code to serializes the result of SPV_Op `op` into `os`. The
581 /// generated gets the ID for the type of the result (if any), the SSA-ID of
582 /// the result and updates `resultID` with the SSA-ID.
583 static void emitResultSerialization(const Operator &op, ArrayRef<SMLoc> loc,
584                                     StringRef tabs, StringRef opVar,
585                                     StringRef operands, StringRef resultID,
586                                     raw_ostream &os) {
587   if (op.getNumResults() == 1) {
588     StringRef resultTypeID("resultTypeID");
589     os << tabs << formatv("uint32_t {0} = 0;\n", resultTypeID);
590     os << tabs
591        << formatv(
592               "if (failed(processType({0}.getLoc(), {0}.getType(), {1}))) {{\n",
593               opVar, resultTypeID);
594     os << tabs << "  return failure();\n";
595     os << tabs << "}\n";
596     os << tabs << formatv("{0}.push_back({1});\n", operands, resultTypeID);
597     // Create an SSA result <id> for the op
598     os << tabs << formatv("{0} = getNextID();\n", resultID);
599     os << tabs
600        << formatv("valueIDMap[{0}.getResult()] = {1};\n", opVar, resultID);
601     os << tabs << formatv("{0}.push_back({1});\n", operands, resultID);
602   } else if (op.getNumResults() != 0) {
603     PrintFatalError(loc, "SPIR-V ops can only have zero or one result");
604   }
605 }
606 
607 /// Generates code to serialize attributes of SPV_Op `op` that become
608 /// decorations on the `resultID` of the serialized operation `opVar` in the
609 /// SPIR-V binary.
610 static void emitDecorationSerialization(const Operator &op, StringRef tabs,
611                                         StringRef opVar, StringRef elidedAttrs,
612                                         StringRef resultID, raw_ostream &os) {
613   if (op.getNumResults() == 1) {
614     // All non-argument attributes translated into OpDecorate instruction
615     os << tabs << formatv("for (auto attr : {0}.getAttrs()) {{\n", opVar);
616     os << tabs
617        << formatv("  if (llvm::any_of({0}, [&](StringRef elided)", elidedAttrs);
618     os << " {return attr.first == elided;})) {\n";
619     os << tabs << "    continue;\n";
620     os << tabs << "  }\n";
621     os << tabs
622        << formatv(
623               "  if (failed(processDecoration({0}.getLoc(), {1}, attr))) {{\n",
624               opVar, resultID);
625     os << tabs << "    return failure();\n";
626     os << tabs << "  }\n";
627     os << tabs << "}\n";
628   }
629 }
630 
631 /// Generates code to serialize an SPV_Op `op` into `os`.
632 static void emitSerializationFunction(const Record *attrClass,
633                                       const Record *record, const Operator &op,
634                                       raw_ostream &os) {
635   // If the record has 'autogenSerialization' set to 0, nothing to do
636   if (!record->getValueAsBit("autogenSerialization")) {
637     return;
638   }
639   StringRef opVar("op"), operands("operands"), elidedAttrs("elidedAttrs"),
640       resultID("resultID");
641   os << formatv(
642       "template <> LogicalResult\nSerializer::processOp<{0}>({0} {1}) {{\n",
643       op.getQualCppClassName(), opVar);
644   os << formatv("  SmallVector<uint32_t, 4> {0};\n", operands);
645   os << formatv("  SmallVector<StringRef, 2> {0};\n", elidedAttrs);
646 
647   // Serialize result information.
648   if (op.getNumResults() == 1) {
649     os << formatv("  uint32_t {0} = 0;\n", resultID);
650     emitResultSerialization(op, record->getLoc(), "  ", opVar, operands,
651                             resultID, os);
652   }
653 
654   // Process arguments.
655   emitOperandSerialization(op, record->getLoc(), "  ", opVar, operands,
656                            elidedAttrs, os);
657 
658   if (record->isSubClassOf("SPV_ExtInstOp")) {
659     os << formatv("  encodeExtensionInstruction({0}, \"{1}\", {2}, {3});\n",
660                   opVar, record->getValueAsString("extendedInstSetName"),
661                   record->getValueAsInt("extendedInstOpcode"), operands);
662   } else {
663     os << formatv("  encodeInstructionInto("
664                   "functionBody, spirv::getOpcode<{0}>(), {1});\n",
665                   op.getQualCppClassName(), operands);
666   }
667 
668   // Process decorations.
669   emitDecorationSerialization(op, "  ", opVar, elidedAttrs, resultID, os);
670 
671   os << "  return success();\n";
672   os << "}\n\n";
673 }
674 
675 /// Generates the prologue for the function that dispatches the serialization of
676 /// the operation `opVar` based on its opcode.
677 static void initDispatchSerializationFn(StringRef opVar, raw_ostream &os) {
678   os << formatv(
679       "LogicalResult Serializer::dispatchToAutogenSerialization(Operation "
680       "*{0}) {{\n ",
681       opVar);
682 }
683 
684 /// Generates the body of the dispatch function. This function generates the
685 /// check that if satisfied, will call the serialization function generated for
686 /// the `op`.
687 static void emitSerializationDispatch(const Operator &op, StringRef tabs,
688                                       StringRef opVar, raw_ostream &os) {
689   os << tabs
690      << formatv("if (isa<{0}>({1})) {{\n", op.getQualCppClassName(), opVar);
691   os << tabs
692      << formatv("  return processOp(cast<{0}>({1}));\n",
693                 op.getQualCppClassName(), opVar);
694   os << tabs << "} else";
695 }
696 
697 /// Generates the epilogue for the function that dispatches the serialization of
698 /// the operation.
699 static void finalizeDispatchSerializationFn(StringRef opVar, raw_ostream &os) {
700   os << " {\n";
701   os << formatv(
702       "    return {0}->emitError(\"unhandled operation serialization\");\n",
703       opVar);
704   os << "  }\n";
705   os << "  return success();\n";
706   os << "}\n\n";
707 }
708 
709 /// Generates code to deserialize the attribute of a SPV_Op into `os`. The
710 /// generated code reads the `words` of the serialized instruction at
711 /// position `wordIndex` and adds the deserialized attribute into `attrList`.
712 static void emitAttributeDeserialization(const Attribute &attr,
713                                          ArrayRef<SMLoc> loc, StringRef tabs,
714                                          StringRef attrList, StringRef attrName,
715                                          StringRef words, StringRef wordIndex,
716                                          raw_ostream &os) {
717   if (attr.getAttrDefName() == "SPV_ScopeAttr" ||
718       attr.getAttrDefName() == "SPV_MemorySemanticsAttr") {
719     os << tabs
720        << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "
721                   "getConstantInt({2}[{3}++])));\n",
722                   attrList, attrName, words, wordIndex);
723   } else if (attr.getAttrDefName() == "I32ArrayAttr") {
724     os << tabs << "SmallVector<Attribute, 4> attrListElems;\n";
725     os << tabs << formatv("while ({0} < {1}.size()) {{\n", wordIndex, words);
726     os << tabs
727        << formatv(
728               "  "
729               "attrListElems.push_back(opBuilder.getI32IntegerAttr({0}[{1}++]))"
730               ";\n",
731               words, wordIndex);
732     os << tabs << "}\n";
733     os << tabs
734        << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "
735                   "opBuilder.getArrayAttr(attrListElems)));\n",
736                   attrList, attrName);
737   } else if (attr.isEnumAttr() || attr.getAttrDefName() == "I32Attr") {
738     os << tabs
739        << formatv("{0}.push_back(opBuilder.getNamedAttr(\"{1}\", "
740                   "opBuilder.getI32IntegerAttr({2}[{3}++])));\n",
741                   attrList, attrName, words, wordIndex);
742   } else {
743     PrintFatalError(
744         loc, llvm::Twine(
745                  "unhandled attribute type in deserialization generation : '") +
746                  attr.getAttrDefName() + llvm::Twine("'"));
747   }
748 }
749 
750 /// Generates the code to deserialize the result of an SPV_Op `op` into
751 /// `os`. The generated code gets the type of the result specified at
752 /// `words`[`wordIndex`], the SSA ID for the result at position `wordIndex` + 1
753 /// and updates the `resultType` and `valueID` with the parsed type and SSA ID,
754 /// respectively.
755 static void emitResultDeserialization(const Operator &op, ArrayRef<SMLoc> loc,
756                                       StringRef tabs, StringRef words,
757                                       StringRef wordIndex,
758                                       StringRef resultTypes, StringRef valueID,
759                                       raw_ostream &os) {
760   // Deserialize result information if it exists
761   if (op.getNumResults() == 1) {
762     os << tabs << "{\n";
763     os << tabs << formatv("  if ({0} >= {1}.size()) {{\n", wordIndex, words);
764     os << tabs
765        << formatv(
766               "    return emitError(unknownLoc, \"expected result type <id> "
767               "while deserializing {0}\");\n",
768               op.getQualCppClassName());
769     os << tabs << "  }\n";
770     os << tabs << formatv("  auto ty = getType({0}[{1}]);\n", words, wordIndex);
771     os << tabs << "  if (!ty) {\n";
772     os << tabs
773        << formatv(
774               "    return emitError(unknownLoc, \"unknown type result <id> : "
775               "\") << {0}[{1}];\n",
776               words, wordIndex);
777     os << tabs << "  }\n";
778     os << tabs << formatv("  {0}.push_back(ty);\n", resultTypes);
779     os << tabs << formatv("  {0}++;\n", wordIndex);
780     os << tabs << formatv("  if ({0} >= {1}.size()) {{\n", wordIndex, words);
781     os << tabs
782        << formatv(
783               "    return emitError(unknownLoc, \"expected result <id> while "
784               "deserializing {0}\");\n",
785               op.getQualCppClassName());
786     os << tabs << "  }\n";
787     os << tabs << "}\n";
788     os << tabs << formatv("{0} = {1}[{2}++];\n", valueID, words, wordIndex);
789   } else if (op.getNumResults() != 0) {
790     PrintFatalError(loc, "SPIR-V ops can have only zero or one result");
791   }
792 }
793 
794 /// Generates the code to deserialize the operands of an SPV_Op `op` into
795 /// `os`. The generated code reads the `words` of the binary instruction, from
796 /// position `wordIndex` to the end, and either gets the Value corresponding to
797 /// the ID encoded, or deserializes the attributes encoded. The parsed
798 /// operand(attribute) is added to the `operands` list or `attributes` list.
799 static void emitOperandDeserialization(const Operator &op, ArrayRef<SMLoc> loc,
800                                        StringRef tabs, StringRef words,
801                                        StringRef wordIndex, StringRef operands,
802                                        StringRef attributes, raw_ostream &os) {
803   // Process operands/attributes
804   unsigned operandNum = 0;
805   for (unsigned i = 0, e = op.getNumArgs(); i < e; ++i) {
806     auto argument = op.getArg(i);
807     if (auto valueArg = argument.dyn_cast<NamedTypeConstraint *>()) {
808       if (valueArg->isVariableLength()) {
809         if (i != e - 1) {
810           PrintFatalError(loc, "SPIR-V ops can have Variadic<..> or "
811                                "Optional<...> arguments only if "
812                                "it's the last argument");
813         }
814         os << tabs
815            << formatv("for (; {0} < {1}.size(); ++{0})", wordIndex, words);
816       } else {
817         os << tabs << formatv("if ({0} < {1}.size())", wordIndex, words);
818       }
819       os << " {\n";
820       os << tabs
821          << formatv("  auto arg = getValue({0}[{1}]);\n", words, wordIndex);
822       os << tabs << "  if (!arg) {\n";
823       os << tabs
824          << formatv(
825                 "    return emitError(unknownLoc, \"unknown result <id> : \") "
826                 "<< {0}[{1}];\n",
827                 words, wordIndex);
828       os << tabs << "  }\n";
829       os << tabs << formatv("  {0}.push_back(arg);\n", operands);
830       if (!valueArg->isVariableLength()) {
831         os << tabs << formatv("  {0}++;\n", wordIndex);
832       }
833       operandNum++;
834       os << tabs << "}\n";
835     } else {
836       os << tabs << formatv("if ({0} < {1}.size()) {{\n", wordIndex, words);
837       auto attr = argument.get<NamedAttribute *>();
838       auto newtabs = tabs.str() + "  ";
839       emitAttributeDeserialization(
840           (attr->attr.isOptional() ? attr->attr.getBaseAttr() : attr->attr),
841           loc, newtabs, attributes, attr->name, words, wordIndex, os);
842       os << "  }\n";
843     }
844   }
845 
846   os << tabs << formatv("if ({0} != {1}.size()) {{\n", wordIndex, words);
847   os << tabs
848      << formatv(
849             "  return emitError(unknownLoc, \"found more operands than "
850             "expected when deserializing {0}, only \") << {1} << \" of \" << "
851             "{2}.size() << \" processed\";\n",
852             op.getQualCppClassName(), wordIndex, words);
853   os << tabs << "}\n\n";
854 }
855 
856 /// Generates code to update the `attributes` vector with the attributes
857 /// obtained from parsing the decorations in the SPIR-V binary associated with
858 /// an <id> `valueID`
859 static void emitDecorationDeserialization(const Operator &op, StringRef tabs,
860                                           StringRef valueID,
861                                           StringRef attributes,
862                                           raw_ostream &os) {
863   // Import decorations parsed
864   if (op.getNumResults() == 1) {
865     os << tabs << formatv("if (decorations.count({0})) {{\n", valueID);
866     os << tabs
867        << formatv("  auto attrs = decorations[{0}].getAttrs();\n", valueID);
868     os << tabs
869        << formatv("  {0}.append(attrs.begin(), attrs.end());\n", attributes);
870     os << tabs << "}\n";
871   }
872 }
873 
874 /// Generates code to deserialize an SPV_Op `op` into `os`.
875 static void emitDeserializationFunction(const Record *attrClass,
876                                         const Record *record,
877                                         const Operator &op, raw_ostream &os) {
878   // If the record has 'autogenSerialization' set to 0, nothing to do
879   if (!record->getValueAsBit("autogenSerialization")) {
880     return;
881   }
882   StringRef resultTypes("resultTypes"), valueID("valueID"), words("words"),
883       wordIndex("wordIndex"), opVar("op"), operands("operands"),
884       attributes("attributes");
885   os << formatv("template <> "
886                 "LogicalResult\nDeserializer::processOp<{0}>(ArrayRef<"
887                 "uint32_t> {1}) {{\n",
888                 op.getQualCppClassName(), words);
889   os << formatv("  SmallVector<Type, 1> {0};\n", resultTypes);
890   os << formatv("  size_t {0} = 0; (void){0};\n", wordIndex);
891   os << formatv("  uint32_t {0} = 0; (void){0};\n", valueID);
892 
893   // Deserialize result information
894   emitResultDeserialization(op, record->getLoc(), "  ", words, wordIndex,
895                             resultTypes, valueID, os);
896 
897   os << formatv("  SmallVector<Value, 4> {0};\n", operands);
898   os << formatv("  SmallVector<NamedAttribute, 4> {0};\n", attributes);
899   // Operand deserialization
900   emitOperandDeserialization(op, record->getLoc(), "  ", words, wordIndex,
901                              operands, attributes, os);
902 
903   os << formatv(
904       "  auto {1} = opBuilder.create<{0}>(unknownLoc, {2}, {3}, {4}); "
905       "(void){1};\n",
906       op.getQualCppClassName(), opVar, resultTypes, operands, attributes);
907   if (op.getNumResults() == 1) {
908     os << formatv("  valueMap[{0}] = {1}.getResult();\n\n", valueID, opVar);
909   }
910 
911   // Decorations
912   emitDecorationDeserialization(op, "  ", valueID, attributes, os);
913   os << "  return success();\n";
914   os << "}\n\n";
915 }
916 
917 /// Generates the prologue for the function that dispatches the deserialization
918 /// based on the `opcode`.
919 static void initDispatchDeserializationFn(StringRef opcode, StringRef words,
920                                           raw_ostream &os) {
921   os << formatv(
922       "LogicalResult "
923       "Deserializer::dispatchToAutogenDeserialization(spirv::Opcode {0}, "
924       "ArrayRef<uint32_t> {1}) {{\n",
925       opcode, words);
926   os << formatv("  switch ({0}) {{\n", opcode);
927 }
928 
929 /// Generates the body of the dispatch function, by generating the case label
930 /// for an opcode and the call to the method to perform the deserialization.
931 static void emitDeserializationDispatch(const Operator &op, const Record *def,
932                                         StringRef tabs, StringRef words,
933                                         raw_ostream &os) {
934   os << tabs
935      << formatv("case spirv::Opcode::{0}:\n",
936                 def->getValueAsString("spirvOpName"));
937   os << tabs
938      << formatv("  return processOp<{0}>({1});\n", op.getQualCppClassName(),
939                 words);
940 }
941 
942 /// Generates the epilogue for the function that dispatches the deserialization
943 /// of the operation.
944 static void finalizeDispatchDeserializationFn(StringRef opcode,
945                                               raw_ostream &os) {
946   os << "  default:\n";
947   os << "    ;\n";
948   os << "  }\n";
949   StringRef opcodeVar("opcodeString");
950   os << formatv("  auto {0} = spirv::stringifyOpcode({1});\n", opcodeVar,
951                 opcode);
952   os << formatv("  if (!{0}.empty()) {{\n", opcodeVar);
953   os << formatv("    return emitError(unknownLoc, \"unhandled deserialization "
954                 "of \") << {0};\n",
955                 opcodeVar);
956   os << "  } else {\n";
957   os << formatv("   return emitError(unknownLoc, \"unhandled opcode \") << "
958                 "static_cast<uint32_t>({0});\n",
959                 opcode);
960   os << "  }\n";
961   os << "}\n";
962 }
963 
964 static void initExtendedSetDeserializationDispatch(StringRef extensionSetName,
965                                                    StringRef instructionID,
966                                                    StringRef words,
967                                                    raw_ostream &os) {
968   os << formatv("LogicalResult "
969                 "Deserializer::dispatchToExtensionSetAutogenDeserialization("
970                 "StringRef {0}, uint32_t {1}, ArrayRef<uint32_t> {2}) {{\n",
971                 extensionSetName, instructionID, words);
972 }
973 
974 static void
975 emitExtendedSetDeserializationDispatch(const RecordKeeper &recordKeeper,
976                                        raw_ostream &os) {
977   StringRef extensionSetName("extensionSetName"),
978       instructionID("instructionID"), words("words");
979 
980   // First iterate over all ops derived from SPV_ExtensionSetOps to get all
981   // extensionSets.
982 
983   // For each of the extensions a separate raw_string_ostream is used to
984   // generate code into. These are then concatenated at the end. Since
985   // raw_string_ostream needs a string&, use a vector to store all the string
986   // that are captured by reference within raw_string_ostream.
987   StringMap<raw_string_ostream> extensionSets;
988   SmallVector<std::string, 1> extensionSetNames;
989 
990   initExtendedSetDeserializationDispatch(extensionSetName, instructionID, words,
991                                          os);
992   auto defs = recordKeeper.getAllDerivedDefinitions("SPV_ExtInstOp");
993   for (const auto *def : defs) {
994     if (!def->getValueAsBit("autogenSerialization")) {
995       continue;
996     }
997     Operator op(def);
998     auto setName = def->getValueAsString("extendedInstSetName");
999     if (!extensionSets.count(setName)) {
1000       extensionSetNames.push_back("");
1001       extensionSets.try_emplace(setName, extensionSetNames.back());
1002       auto &setos = extensionSets.find(setName)->second;
1003       setos << formatv("  if ({0} == \"{1}\") {{\n", extensionSetName, setName);
1004       setos << formatv("    switch ({0}) {{\n", instructionID);
1005     }
1006     auto &setos = extensionSets.find(setName)->second;
1007     setos << formatv("    case {0}:\n",
1008                      def->getValueAsInt("extendedInstOpcode"));
1009     setos << formatv("      return processOp<{0}>({1});\n",
1010                      op.getQualCppClassName(), words);
1011   }
1012 
1013   // Append the dispatch code for all the extended sets.
1014   for (auto &extensionSet : extensionSets) {
1015     os << extensionSet.second.str();
1016     os << "    default:\n";
1017     os << formatv(
1018         "      return emitError(unknownLoc, \"unhandled deserializations of "
1019         "\") << {0} << \" from extension set \" << {1};\n",
1020         instructionID, extensionSetName);
1021     os << "    }\n";
1022     os << "  }\n";
1023   }
1024 
1025   os << formatv("  return emitError(unknownLoc, \"unhandled deserialization of "
1026                 "extended instruction set {0}\");\n",
1027                 extensionSetName);
1028   os << "}\n";
1029 }
1030 
1031 /// Emits all the autogenerated serialization/deserializations functions for the
1032 /// SPV_Ops.
1033 static bool emitSerializationFns(const RecordKeeper &recordKeeper,
1034                                  raw_ostream &os) {
1035   llvm::emitSourceFileHeader("SPIR-V Serialization Utilities/Functions", os);
1036 
1037   std::string dSerFnString, dDesFnString, serFnString, deserFnString,
1038       utilsString;
1039   raw_string_ostream dSerFn(dSerFnString), dDesFn(dDesFnString),
1040       serFn(serFnString), deserFn(deserFnString), utils(utilsString);
1041   auto attrClass = recordKeeper.getClass("Attr");
1042 
1043   // Emit the serialization and deserialization functions simultaneously.
1044   declareOpcodeFn(utils);
1045   StringRef opVar("op");
1046   StringRef opcode("opcode"), words("words");
1047 
1048   // Handle the SPIR-V ops.
1049   initDispatchSerializationFn(opVar, dSerFn);
1050   initDispatchDeserializationFn(opcode, words, dDesFn);
1051   auto defs = recordKeeper.getAllDerivedDefinitions("SPV_Op");
1052   for (const auto *def : defs) {
1053     Operator op(def);
1054     emitSerializationFunction(attrClass, def, op, serFn);
1055     emitDeserializationFunction(attrClass, def, op, deserFn);
1056     if (def->getValueAsBit("hasOpcode") || def->isSubClassOf("SPV_ExtInstOp")) {
1057       emitSerializationDispatch(op, "  ", opVar, dSerFn);
1058     }
1059     if (def->getValueAsBit("hasOpcode")) {
1060       emitGetOpcodeFunction(def, op, utils);
1061       emitDeserializationDispatch(op, def, "  ", words, dDesFn);
1062     }
1063   }
1064   finalizeDispatchSerializationFn(opVar, dSerFn);
1065   finalizeDispatchDeserializationFn(opcode, dDesFn);
1066 
1067   emitExtendedSetDeserializationDispatch(recordKeeper, dDesFn);
1068 
1069   os << "#ifdef GET_SPIRV_SERIALIZATION_UTILS\n";
1070   os << utils.str();
1071   os << "#endif // GET_SPIRV_SERIALIZATION_UTILS\n\n";
1072 
1073   os << "#ifdef GET_SERIALIZATION_FNS\n\n";
1074   os << serFn.str();
1075   os << dSerFn.str();
1076   os << "#endif // GET_SERIALIZATION_FNS\n\n";
1077 
1078   os << "#ifdef GET_DESERIALIZATION_FNS\n\n";
1079   os << deserFn.str();
1080   os << dDesFn.str();
1081   os << "#endif // GET_DESERIALIZATION_FNS\n\n";
1082 
1083   return false;
1084 }
1085 
1086 //===----------------------------------------------------------------------===//
1087 // Serialization Hook Registration
1088 //===----------------------------------------------------------------------===//
1089 
1090 static mlir::GenRegistration genSerialization(
1091     "gen-spirv-serialization",
1092     "Generate SPIR-V (de)serialization utilities and functions",
1093     [](const RecordKeeper &records, raw_ostream &os) {
1094       return emitSerializationFns(records, os);
1095     });
1096 
1097 //===----------------------------------------------------------------------===//
1098 // Op Utils AutoGen
1099 //===----------------------------------------------------------------------===//
1100 
1101 static void emitEnumGetAttrNameFnDecl(raw_ostream &os) {
1102   os << formatv("template <typename EnumClass> inline constexpr StringRef "
1103                 "attributeName();\n");
1104 }
1105 
1106 static void emitEnumGetAttrNameFnDefn(const EnumAttr &enumAttr,
1107                                       raw_ostream &os) {
1108   auto enumName = enumAttr.getEnumClassName();
1109   os << formatv("template <> inline StringRef attributeName<{0}>() {{\n",
1110                 enumName);
1111   os << "  "
1112      << formatv("static constexpr const char attrName[] = \"{0}\";\n",
1113                 llvm::convertToSnakeFromCamelCase(enumName));
1114   os << "  return attrName;\n";
1115   os << "}\n";
1116 }
1117 
1118 static bool emitOpUtils(const RecordKeeper &recordKeeper, raw_ostream &os) {
1119   llvm::emitSourceFileHeader("SPIR-V Op Utilities", os);
1120 
1121   auto defs = recordKeeper.getAllDerivedDefinitions("EnumAttrInfo");
1122   os << "#ifndef SPIRV_OP_UTILS_H_\n";
1123   os << "#define SPIRV_OP_UTILS_H_\n";
1124   emitEnumGetAttrNameFnDecl(os);
1125   for (const auto *def : defs) {
1126     EnumAttr enumAttr(*def);
1127     emitEnumGetAttrNameFnDefn(enumAttr, os);
1128   }
1129   os << "#endif // SPIRV_OP_UTILS_H\n";
1130   return false;
1131 }
1132 
1133 //===----------------------------------------------------------------------===//
1134 // Op Utils Hook Registration
1135 //===----------------------------------------------------------------------===//
1136 
1137 static mlir::GenRegistration
1138     genOpUtils("gen-spirv-op-utils",
1139                "Generate SPIR-V operation utility definitions",
1140                [](const RecordKeeper &records, raw_ostream &os) {
1141                  return emitOpUtils(records, os);
1142                });
1143 
1144 //===----------------------------------------------------------------------===//
1145 // SPIR-V Availability Impl AutoGen
1146 //===----------------------------------------------------------------------===//
1147 
1148 static void emitAvailabilityImpl(const Operator &srcOp, raw_ostream &os) {
1149   mlir::tblgen::FmtContext fctx;
1150   fctx.addSubst("overall", "tblgen_overall");
1151 
1152   std::vector<Availability> opAvailabilities =
1153       getAvailabilities(srcOp.getDef());
1154 
1155   // First collect all availability classes this op should implement.
1156   // All availability instances keep information for the generated interface and
1157   // the instance's specific requirement. Here we remember a random instance so
1158   // we can get the information regarding the generated interface.
1159   llvm::StringMap<Availability> availClasses;
1160   for (const Availability &avail : opAvailabilities)
1161     availClasses.try_emplace(avail.getClass(), avail);
1162   for (const NamedAttribute &namedAttr : srcOp.getAttributes()) {
1163     const auto *enumAttr = llvm::dyn_cast<EnumAttr>(&namedAttr.attr);
1164     if (!enumAttr)
1165       continue;
1166 
1167     for (const EnumAttrCase &enumerant : enumAttr->getAllCases())
1168       for (const Availability &caseAvail :
1169            getAvailabilities(enumerant.getDef()))
1170         availClasses.try_emplace(caseAvail.getClass(), caseAvail);
1171   }
1172 
1173   // Then generate implementation for each availability class.
1174   for (const auto &availClass : availClasses) {
1175     StringRef availClassName = availClass.getKey();
1176     Availability avail = availClass.getValue();
1177 
1178     // Generate the implementation method signature.
1179     os << formatv("{0} {1}::{2}() {{\n", avail.getQueryFnRetType(),
1180                   srcOp.getCppClassName(), avail.getQueryFnName());
1181 
1182     // Create the variable for the final requirement and initialize it.
1183     os << formatv("  {0} tblgen_overall = {1};\n", avail.getQueryFnRetType(),
1184                   avail.getMergeInitializer());
1185 
1186     // Update with the op's specific availability spec.
1187     for (const Availability &avail : opAvailabilities)
1188       if (avail.getClass() == availClassName &&
1189           (!avail.getMergeInstancePreparation().empty() ||
1190            !avail.getMergeActionCode().empty())) {
1191         os << "  {\n    "
1192            // Prepare this instance.
1193            << avail.getMergeInstancePreparation()
1194            << "\n    "
1195            // Merge this instance.
1196            << std::string(
1197                   tgfmt(avail.getMergeActionCode(),
1198                         &fctx.addSubst("instance", avail.getMergeInstance())))
1199            << ";\n  }\n";
1200       }
1201 
1202     // Update with enum attributes' specific availability spec.
1203     for (const NamedAttribute &namedAttr : srcOp.getAttributes()) {
1204       const auto *enumAttr = llvm::dyn_cast<EnumAttr>(&namedAttr.attr);
1205       if (!enumAttr)
1206         continue;
1207 
1208       // (enumerant, availability specification) pairs for this availability
1209       // class.
1210       SmallVector<std::pair<EnumAttrCase, Availability>, 1> caseSpecs;
1211 
1212       // Collect all cases' availability specs.
1213       for (const EnumAttrCase &enumerant : enumAttr->getAllCases())
1214         for (const Availability &caseAvail :
1215              getAvailabilities(enumerant.getDef()))
1216           if (availClassName == caseAvail.getClass())
1217             caseSpecs.push_back({enumerant, caseAvail});
1218 
1219       // If this attribute kind does not have any availability spec from any of
1220       // its cases, no more work to do.
1221       if (caseSpecs.empty())
1222         continue;
1223 
1224       if (enumAttr->isBitEnum()) {
1225         // For BitEnumAttr, we need to iterate over each bit to query its
1226         // availability spec.
1227         os << formatv("  for (unsigned i = 0; "
1228                       "i < std::numeric_limits<{0}>::digits; ++i) {{\n",
1229                       enumAttr->getUnderlyingType());
1230         os << formatv("    {0}::{1} tblgen_attrVal = this->{2}() & "
1231                       "static_cast<{0}::{1}>(1 << i);\n",
1232                       enumAttr->getCppNamespace(), enumAttr->getEnumClassName(),
1233                       namedAttr.name);
1234         os << formatv(
1235             "    if (static_cast<{0}>(tblgen_attrVal) == 0) continue;\n",
1236             enumAttr->getUnderlyingType());
1237       } else {
1238         // For IntEnumAttr, we just need to query the value as a whole.
1239         os << "  {\n";
1240         os << formatv("    auto tblgen_attrVal = this->{0}();\n",
1241                       namedAttr.name);
1242       }
1243       os << formatv("    auto tblgen_instance = {0}::{1}(tblgen_attrVal);\n",
1244                     enumAttr->getCppNamespace(), avail.getQueryFnName());
1245       os << "    if (tblgen_instance) "
1246          // TODO(antiagainst): use `avail.getMergeCode()` here once ODS supports
1247          // dialect-specific contents so that we can use not implementing the
1248          // availability interface as indication of no requirements.
1249          << std::string(tgfmt(caseSpecs.front().second.getMergeActionCode(),
1250                               &fctx.addSubst("instance", "*tblgen_instance")))
1251          << ";\n";
1252       os << "  }\n";
1253     }
1254 
1255     os << "  return tblgen_overall;\n";
1256     os << "}\n";
1257   }
1258 }
1259 
1260 static bool emitAvailabilityImpl(const RecordKeeper &recordKeeper,
1261                                  raw_ostream &os) {
1262   llvm::emitSourceFileHeader("SPIR-V Op Availability Implementations", os);
1263 
1264   auto defs = recordKeeper.getAllDerivedDefinitions("SPV_Op");
1265   for (const auto *def : defs) {
1266     Operator op(def);
1267     emitAvailabilityImpl(op, os);
1268   }
1269   return false;
1270 }
1271 
1272 //===----------------------------------------------------------------------===//
1273 // Op Availability Implementation Hook Registration
1274 //===----------------------------------------------------------------------===//
1275 
1276 static mlir::GenRegistration
1277     genOpAvailabilityImpl("gen-spirv-avail-impls",
1278                           "Generate SPIR-V operation utility definitions",
1279                           [](const RecordKeeper &records, raw_ostream &os) {
1280                             return emitAvailabilityImpl(records, os);
1281                           });
1282 
1283 //===----------------------------------------------------------------------===//
1284 // SPIR-V Capability Implication AutoGen
1285 //===----------------------------------------------------------------------===//
1286 
1287 static bool emitCapabilityImplication(const RecordKeeper &recordKeeper,
1288                                       raw_ostream &os) {
1289   llvm::emitSourceFileHeader("SPIR-V Capability Implication", os);
1290 
1291   EnumAttr enumAttr(recordKeeper.getDef("SPV_CapabilityAttr"));
1292 
1293   os << "ArrayRef<Capability> "
1294         "spirv::getDirectImpliedCapabilities(Capability cap) {\n"
1295      << "  switch (cap) {\n"
1296      << "  default: return {};\n";
1297   for (const EnumAttrCase &enumerant : enumAttr.getAllCases()) {
1298     const Record &def = enumerant.getDef();
1299     if (!def.getValue("implies"))
1300       continue;
1301 
1302     std::vector<Record *> impliedCapsDefs = def.getValueAsListOfDefs("implies");
1303     os << "  case Capability::" << enumerant.getSymbol()
1304        << ": {static const Capability implies[" << impliedCapsDefs.size()
1305        << "] = {";
1306     llvm::interleaveComma(impliedCapsDefs, os, [&](const Record *capDef) {
1307       os << "Capability::" << EnumAttrCase(capDef).getSymbol();
1308     });
1309     os << "}; return ArrayRef<Capability>(implies, " << impliedCapsDefs.size()
1310        << "); }\n";
1311   }
1312   os << "  }\n";
1313   os << "}\n";
1314 
1315   return false;
1316 }
1317 
1318 //===----------------------------------------------------------------------===//
1319 // SPIR-V Capability Implication Hook Registration
1320 //===----------------------------------------------------------------------===//
1321 
1322 static mlir::GenRegistration
1323     genCapabilityImplication("gen-spirv-capability-implication",
1324                              "Generate utility function to return implied "
1325                              "capabilities for a given capability",
1326                              [](const RecordKeeper &records, raw_ostream &os) {
1327                                return emitCapabilityImplication(records, os);
1328                              });
1329