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