1242 lines · cpp
1//===- OpPythonBindingGen.cpp - Generator of Python API for MLIR Ops ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// OpPythonBindingGen uses ODS specification of MLIR ops to generate Python10// binding classes wrapping a generic operation API.11//12//===----------------------------------------------------------------------===//13 14#include "OpGenHelpers.h"15 16#include "mlir/Support/IndentedOstream.h"17#include "mlir/TableGen/GenInfo.h"18#include "mlir/TableGen/Operator.h"19#include "llvm/ADT/StringSet.h"20#include "llvm/Support/CommandLine.h"21#include "llvm/Support/FormatVariadic.h"22#include "llvm/TableGen/Error.h"23#include "llvm/TableGen/Record.h"24#include <regex>25 26using namespace mlir;27using namespace mlir::tblgen;28using llvm::formatv;29using llvm::Record;30using llvm::RecordKeeper;31 32/// File header and includes.33/// {0} is the dialect namespace.34constexpr const char *fileHeader = R"Py(35# Autogenerated by mlir-tblgen; don't manually edit.36 37from ._ods_common import _cext as _ods_cext38from ._ods_common import (39 equally_sized_accessor as _ods_equally_sized_accessor,40 get_default_loc_context as _ods_get_default_loc_context,41 get_op_results_or_values as _get_op_results_or_values,42 segmented_accessor as _ods_segmented_accessor,43)44_ods_ir = _ods_cext.ir45_ods_cext.globals.register_traceback_file_exclusion(__file__)46 47import builtins48from typing import Sequence as _Sequence, Union as _Union, Optional as _Optional49 50)Py";51 52/// Template for dialect class:53/// {0} is the dialect namespace.54constexpr const char *dialectClassTemplate = R"Py(55@_ods_cext.register_dialect56class _Dialect(_ods_ir.Dialect):57 DIALECT_NAMESPACE = "{0}"58)Py";59 60constexpr const char *dialectExtensionTemplate = R"Py(61from ._{0}_ops_gen import _Dialect62)Py";63 64/// Template for operation class:65/// {0} is the Python class name;66/// {1} is the operation name;67/// {2} is the docstring for this operation.68constexpr const char *opClassTemplate = R"Py(69@_ods_cext.register_operation(_Dialect)70class {0}(_ods_ir.OpView):{2}71 OPERATION_NAME = "{1}"72)Py";73 74/// Template for class level declarations of operand and result75/// segment specs.76/// {0} is either "OPERAND" or "RESULT"77/// {1} is the segment spec78/// Each segment spec is either None (default) or an array of integers79/// where:80/// 1 = single element (expect non sequence operand/result)81/// 0 = optional element (expect a value or std::nullopt)82/// -1 = operand/result is a sequence corresponding to a variadic83constexpr const char *opClassSizedSegmentsTemplate = R"Py(84 _ODS_{0}_SEGMENTS = {1}85)Py";86 87/// Template for class level declarations of the _ODS_REGIONS spec:88/// {0} is the minimum number of regions89/// {1} is the Python bool literal for hasNoVariadicRegions90constexpr const char *opClassRegionSpecTemplate = R"Py(91 _ODS_REGIONS = ({0}, {1})92)Py";93 94/// Template for single-element accessor:95/// {0} is the name of the accessor;96/// {1} is either 'operand' or 'result';97/// {2} is the position in the element list.98/// {3} is the type hint.99constexpr const char *opSingleTemplate = R"Py(100 @builtins.property101 def {0}(self) -> {3}:102 return self.operation.{1}s[{2}]103)Py";104 105/// Template for single-element accessor after a variable-length group:106/// {0} is the name of the accessor;107/// {1} is either 'operand' or 'result';108/// {2} is the total number of element groups;109/// {3} is the position of the current group in the group list.110/// {4} is the type hint.111/// This works for both a single variadic group (non-negative length) and an112/// single optional element (zero length if the element is absent).113constexpr const char *opSingleAfterVariableTemplate = R"Py(114 @builtins.property115 def {0}(self) -> {4}:116 _ods_variadic_group_length = len(self.operation.{1}s) - {2} + 1117 return self.operation.{1}s[{3} + _ods_variadic_group_length - 1]118)Py";119 120/// Template for an optional element accessor:121/// {0} is the name of the accessor;122/// {1} is either 'operand' or 'result';123/// {2} is the total number of element groups;124/// {3} is the position of the current group in the group list.125/// {4} is the type hint.126/// This works if we have only one variable-length group (and it's the optional127/// operand/result): we can deduce it's absent if the `len(operation.{1}s)` is128/// smaller than the total number of groups.129constexpr const char *opOneOptionalTemplate = R"Py(130 @builtins.property131 def {0}(self) -> _Optional[{4}]:132 return None if len(self.operation.{1}s) < {2} else self.operation.{1}s[{3}]133)Py";134 135/// Template for the variadic group accessor in the single variadic group case:136/// {0} is the name of the accessor;137/// {1} is either 'operand' or 'result';138/// {2} is the total number of element groups;139/// {3} is the position of the current group in the group list.140/// {4} is the type hint.141constexpr const char *opOneVariadicTemplate = R"Py(142 @builtins.property143 def {0}(self) -> {4}:144 _ods_variadic_group_length = len(self.operation.{1}s) - {2} + 1145 return self.operation.{1}s[{3}:{3} + _ods_variadic_group_length]146)Py";147 148/// First part of the template for equally-sized variadic group accessor:149/// {0} is the name of the accessor;150/// {1} is either 'operand' or 'result';151/// {2} is the total number of non-variadic groups;152/// {3} is the total number of variadic groups;153/// {4} is the number of non-variadic groups preceding the current group;154/// {5} is the number of variadic groups preceding the current group.155/// {6} is the type hint.156constexpr const char *opVariadicEqualPrefixTemplate = R"Py(157 @builtins.property158 def {0}(self) -> {6}:159 start, elements_per_group = _ods_equally_sized_accessor(self.operation.{1}s, {2}, {3}, {4}, {5}))Py";160 161/// Second part of the template for equally-sized case, accessing a single162/// element:163/// {0} is either 'operand' or 'result'.164constexpr const char *opVariadicEqualSimpleTemplate = R"Py(165 return self.operation.{0}s[start]166)Py";167 168/// Second part of the template for equally-sized case, accessing a variadic169/// group:170/// {0} is either 'operand' or 'result'.171constexpr const char *opVariadicEqualVariadicTemplate = R"Py(172 return self.operation.{0}s[start:start + elements_per_group]173)Py";174 175/// Template for an attribute-sized group accessor:176/// {0} is the name of the accessor;177/// {1} is either 'operand' or 'result';178/// {2} is the position of the group in the group list;179/// {3} is a return suffix (expected [0] for single-element, empty for180/// variadic, and opVariadicSegmentOptionalTrailingTemplate for optional).181/// {4} is the type hint.182constexpr const char *opVariadicSegmentTemplate = R"Py(183 @builtins.property184 def {0}(self) -> {4}:185 {1}_range = _ods_segmented_accessor(186 self.operation.{1}s,187 self.operation.attributes["{1}SegmentSizes"], {2})188 return {1}_range{3}189)Py";190 191/// Template for a suffix when accessing an optional element in the192/// attribute-sized case:193/// {0} is either 'operand' or 'result';194constexpr const char *opVariadicSegmentOptionalTrailingTemplate =195 R"Py([0] if len({0}_range) > 0 else None)Py";196 197/// Template for an operation attribute getter:198/// {0} is the name of the attribute sanitized for Python;199/// {1} is the original name of the attribute.200/// {2} is the type hint.201constexpr const char *attributeGetterTemplate = R"Py(202 @builtins.property203 def {0}(self) -> {2}:204 return self.operation.attributes["{1}"]205)Py";206 207/// Template for an optional operation attribute getter:208/// {0} is the name of the attribute sanitized for Python;209/// {1} is the original name of the attribute.210/// {2} is the type hint.211constexpr const char *optionalAttributeGetterTemplate = R"Py(212 @builtins.property213 def {0}(self) -> _Optional[{2}]:214 if "{1}" not in self.operation.attributes:215 return None216 return self.operation.attributes["{1}"]217)Py";218 219/// Template for a getter of a unit operation attribute, returns True of the220/// unit attribute is present, False otherwise (unit attributes have meaning221/// by mere presence):222/// {0} is the name of the attribute sanitized for Python,223/// {1} is the original name of the attribute.224constexpr const char *unitAttributeGetterTemplate = R"Py(225 @builtins.property226 def {0}(self) -> bool:227 return "{1}" in self.operation.attributes228)Py";229 230/// Template for an operation attribute setter:231/// {0} is the name of the attribute sanitized for Python;232/// {1} is the original name of the attribute.233/// {2} is the type hint.234constexpr const char *attributeSetterTemplate = R"Py(235 @{0}.setter236 def {0}(self, value: {2}):237 if value is None:238 raise ValueError("'None' not allowed as value for mandatory attributes")239 self.operation.attributes["{1}"] = value240)Py";241 242/// Template for a setter of an optional operation attribute, setting to None243/// removes the attribute:244/// {0} is the name of the attribute sanitized for Python;245/// {1} is the original name of the attribute.246/// {2} is the type hint.247constexpr const char *optionalAttributeSetterTemplate = R"Py(248 @{0}.setter249 def {0}(self, value: _Optional[{2}]):250 if value is not None:251 self.operation.attributes["{1}"] = value252 elif "{1}" in self.operation.attributes:253 del self.operation.attributes["{1}"]254)Py";255 256/// Template for a setter of a unit operation attribute, setting to None or257/// False removes the attribute:258/// {0} is the name of the attribute sanitized for Python;259/// {1} is the original name of the attribute.260constexpr const char *unitAttributeSetterTemplate = R"Py(261 @{0}.setter262 def {0}(self, value):263 if bool(value):264 self.operation.attributes["{1}"] = _ods_ir.UnitAttr.get()265 elif "{1}" in self.operation.attributes:266 del self.operation.attributes["{1}"]267)Py";268 269/// Template for a deleter of an optional or a unit operation attribute, removes270/// the attribute from the operation:271/// {0} is the name of the attribute sanitized for Python;272/// {1} is the original name of the attribute.273constexpr const char *attributeDeleterTemplate = R"Py(274 @{0}.deleter275 def {0}(self):276 del self.operation.attributes["{1}"]277)Py";278 279constexpr const char *regionAccessorTemplate = R"Py(280 @builtins.property281 def {0}(self) -> {2}:282 return self.regions[{1}]283)Py";284 285constexpr const char *valueBuilderTemplate = R"Py(286def {0}({2}) -> {4}:287 return {1}({3}){5}288)Py";289 290constexpr const char *valueBuilderVariadicTemplate = R"Py(291def {0}({2}) -> _Union[_ods_ir.OpResult, _ods_ir.OpResultList, {1}]:292 op = {1}({3}); results = op.results293 return results if len(results) > 1 else (results[0] if len(results) == 1 else op)294)Py";295 296static llvm::cl::OptionCategory297 clOpPythonBindingCat("Options for -gen-python-op-bindings");298 299static llvm::cl::opt<std::string>300 clDialectName("bind-dialect",301 llvm::cl::desc("The dialect to run the generator for"),302 llvm::cl::init(""), llvm::cl::cat(clOpPythonBindingCat));303 304static llvm::cl::opt<std::string> clDialectExtensionName(305 "dialect-extension", llvm::cl::desc("The prefix of the dialect extension"),306 llvm::cl::init(""), llvm::cl::cat(clOpPythonBindingCat));307 308using AttributeClasses = DenseMap<StringRef, StringRef>;309 310/// Checks whether `str` would shadow a generated variable or attribute311/// part of the OpView API.312static bool isODSReserved(StringRef str) {313 static llvm::StringSet<> reserved(314 {"attributes", "create", "context", "ip", "operands", "print", "get_asm",315 "loc", "verify", "regions", "results", "self", "operation",316 "DIALECT_NAMESPACE", "OPERATION_NAME"});317 return str.starts_with("_ods_") || str.ends_with("_ods") ||318 reserved.contains(str);319}320 321/// Modifies the `name` in a way that it becomes suitable for Python bindings322/// (does not change the `name` if it already is suitable) and returns the323/// modified version.324static std::string sanitizeName(StringRef name) {325 std::string processedStr = name.str();326 std::replace_if(327 processedStr.begin(), processedStr.end(),328 [](char c) { return !llvm::isAlnum(c); }, '_');329 330 if (llvm::isDigit(*processedStr.begin()))331 return "_" + processedStr;332 333 if (isPythonReserved(processedStr) || isODSReserved(processedStr))334 return processedStr + "_";335 return processedStr;336}337 338static std::string attrSizedTraitForKind(const char *kind) {339 return formatv("::mlir::OpTrait::AttrSized{0}{1}Segments",340 StringRef(kind).take_front().upper(),341 StringRef(kind).drop_front());342}343 344static StringRef getPythonType(StringRef cppType) {345 return llvm::StringSwitch<StringRef>(cppType)346 .Case("::mlir::MemRefType", "_ods_ir.MemRefType")347 .Case("::mlir::UnrankedMemRefType", "_ods_ir.UnrankedMemRefType")348 .Case("::mlir::RankedTensorType", "_ods_ir.RankedTensorType")349 .Case("::mlir::UnrankedTensorType", "_ods_ir.UnrankedTensorType")350 .Case("::mlir::VectorType", "_ods_ir.VectorType")351 .Case("::mlir::IntegerType", "_ods_ir.IntegerType")352 .Case("::mlir::FloatType", "_ods_ir.FloatType")353 .Case("::mlir::IndexType", "_ods_ir.IndexType")354 .Case("::mlir::ComplexType", "_ods_ir.ComplexType")355 .Case("::mlir::TupleType", "_ods_ir.TupleType")356 .Case("::mlir::NoneType", "_ods_ir.NoneType")357 .Default(StringRef());358}359 360/// Emits accessors to "elements" of an Op definition. Currently, the supported361/// elements are operands and results, indicated by `kind`, which must be either362/// `operand` or `result` and is used verbatim in the emitted code.363static void emitElementAccessors(364 const Operator &op, raw_ostream &os, const char *kind,365 unsigned numVariadicGroups, unsigned numElements,366 llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)>367 getElement) {368 assert(llvm::is_contained(SmallVector<StringRef, 2>{"operand", "result"},369 kind) &&370 "unsupported kind");371 372 // Traits indicating how to process variadic elements.373 std::string sameSizeTrait = formatv("::mlir::OpTrait::SameVariadic{0}{1}Size",374 StringRef(kind).take_front().upper(),375 StringRef(kind).drop_front());376 std::string attrSizedTrait = attrSizedTraitForKind(kind);377 378 // If there is only one variable-length element group, its size can be379 // inferred from the total number of elements. If there are none, the380 // generation is straightforward.381 if (numVariadicGroups <= 1) {382 bool seenVariableLength = false;383 for (unsigned i = 0; i < numElements; ++i) {384 const NamedTypeConstraint &element = getElement(op, i);385 if (element.isVariableLength())386 seenVariableLength = true;387 if (element.name.empty())388 continue;389 std::string type = std::strcmp(kind, "operand") == 0 ? "_ods_ir.Value"390 : "_ods_ir.OpResult";391 if (StringRef pythonType = getPythonType(element.constraint.getCppType());392 !pythonType.empty())393 type = llvm::formatv("{0}[{1}]", type, pythonType);394 if (element.isVariableLength()) {395 if (element.isOptional()) {396 os << formatv(opOneOptionalTemplate, sanitizeName(element.name), kind,397 numElements, i, type);398 } else {399 type = std::strcmp(kind, "operand") == 0 ? "_ods_ir.OpOperandList"400 : "_ods_ir.OpResultList";401 os << formatv(opOneVariadicTemplate, sanitizeName(element.name), kind,402 numElements, i, type);403 }404 } else if (seenVariableLength) {405 os << formatv(opSingleAfterVariableTemplate, sanitizeName(element.name),406 kind, numElements, i, type);407 } else {408 os << formatv(opSingleTemplate, sanitizeName(element.name), kind, i,409 type);410 }411 }412 return;413 }414 415 // Handle the operations where variadic groups have the same size.416 if (op.getTrait(sameSizeTrait)) {417 // Count the number of simple elements418 unsigned numSimpleLength = 0;419 for (unsigned i = 0; i < numElements; ++i) {420 const NamedTypeConstraint &element = getElement(op, i);421 if (!element.isVariableLength()) {422 ++numSimpleLength;423 }424 }425 426 // Generate the accessors427 int numPrecedingSimple = 0;428 int numPrecedingVariadic = 0;429 for (unsigned i = 0; i < numElements; ++i) {430 const NamedTypeConstraint &element = getElement(op, i);431 if (!element.name.empty()) {432 std::string type;433 if (element.isVariableLength()) {434 type = std::strcmp(kind, "operand") == 0 ? "_ods_ir.OpOperandList"435 : "_ods_ir.OpResultList";436 } else {437 type = std::strcmp(kind, "operand") == 0 ? "_ods_ir.Value"438 : "_ods_ir.OpResult";439 }440 if (std::strcmp(type.c_str(), "_ods_ir.Value") == 0 ||441 std::strcmp(type.c_str(), "_ods_ir.OpResult") == 0) {442 StringRef pythonType = getPythonType(element.constraint.getCppType());443 if (!pythonType.empty())444 type += "[" + pythonType.str() + "]";445 }446 os << formatv(opVariadicEqualPrefixTemplate, sanitizeName(element.name),447 kind, numSimpleLength, numVariadicGroups,448 numPrecedingSimple, numPrecedingVariadic, type);449 os << formatv(element.isVariableLength()450 ? opVariadicEqualVariadicTemplate451 : opVariadicEqualSimpleTemplate,452 kind);453 }454 if (element.isVariableLength())455 ++numPrecedingVariadic;456 else457 ++numPrecedingSimple;458 }459 return;460 }461 462 // Handle the operations where the size of groups (variadic or not) is463 // provided as an attribute. For non-variadic elements, make sure to return464 // an element rather than a singleton container.465 if (op.getTrait(attrSizedTrait)) {466 for (unsigned i = 0; i < numElements; ++i) {467 const NamedTypeConstraint &element = getElement(op, i);468 if (element.name.empty())469 continue;470 std::string trailing;471 std::string type = std::strcmp(kind, "operand") == 0472 ? "_ods_ir.OpOperandList"473 : "_ods_ir.OpResultList";474 if (!element.isVariableLength() || element.isOptional()) {475 type = std::strcmp(kind, "operand") == 0 ? "_ods_ir.Value"476 : "_ods_ir.OpResult";477 if (std::strcmp(type.c_str(), "_ods_ir.Value") == 0 ||478 std::strcmp(type.c_str(), "_ods_ir.OpResult") == 0) {479 StringRef pythonType = getPythonType(element.constraint.getCppType());480 if (!pythonType.empty())481 type += "[" + pythonType.str() + "]";482 }483 if (!element.isVariableLength()) {484 trailing = "[0]";485 } else if (element.isOptional()) {486 type = "_Optional[" + type + "]";487 trailing = std::string(488 formatv(opVariadicSegmentOptionalTrailingTemplate, kind));489 }490 }491 492 os << formatv(opVariadicSegmentTemplate, sanitizeName(element.name), kind,493 i, trailing, type);494 }495 return;496 }497 498 llvm::PrintFatalError("unsupported " + llvm::Twine(kind) + " structure");499}500 501/// Free function helpers accessing Operator components.502static int getNumOperands(const Operator &op) { return op.getNumOperands(); }503static const NamedTypeConstraint &getOperand(const Operator &op, int i) {504 return op.getOperand(i);505}506static int getNumResults(const Operator &op) { return op.getNumResults(); }507static const NamedTypeConstraint &getResult(const Operator &op, int i) {508 return op.getResult(i);509}510 511/// Emits accessors to Op operands.512static void emitOperandAccessors(const Operator &op, raw_ostream &os) {513 emitElementAccessors(op, os, "operand", op.getNumVariableLengthOperands(),514 getNumOperands(op), getOperand);515}516 517/// Emits accessors Op results.518static void emitResultAccessors(const Operator &op, raw_ostream &os) {519 emitElementAccessors(op, os, "result", op.getNumVariableLengthResults(),520 getNumResults(op), getResult);521}522 523static std::string getPythonAttrName(mlir::tblgen::Attribute attr) {524 auto storageTypeStr = attr.getStorageType();525 if (storageTypeStr == "::mlir::AffineMapAttr")526 return "AffineMapAttr";527 if (storageTypeStr == "::mlir::ArrayAttr")528 return "ArrayAttr";529 if (storageTypeStr == "::mlir::BoolAttr")530 return "BoolAttr";531 if (storageTypeStr == "::mlir::DenseBoolArrayAttr")532 return "DenseBoolArrayAttr";533 if (storageTypeStr == "::mlir::DenseElementsAttr") {534 llvm::StringSet<> superClasses;535 for (const Record *sc : attr.getDef().getSuperClasses())536 superClasses.insert(sc->getNameInitAsString());537 if (superClasses.contains("FloatElementsAttr") ||538 superClasses.contains("RankedFloatElementsAttr")) {539 return "DenseFPElementsAttr";540 }541 return "DenseElementsAttr";542 }543 if (storageTypeStr == "::mlir::DenseF32ArrayAttr")544 return "DenseF32ArrayAttr";545 if (storageTypeStr == "::mlir::DenseF64ArrayAttr")546 return "DenseF64ArrayAttr";547 if (storageTypeStr == "::mlir::DenseFPElementsAttr")548 return "DenseFPElementsAttr";549 if (storageTypeStr == "::mlir::DenseI16ArrayAttr")550 return "DenseI16ArrayAttr";551 if (storageTypeStr == "::mlir::DenseI32ArrayAttr")552 return "DenseI32ArrayAttr";553 if (storageTypeStr == "::mlir::DenseI64ArrayAttr")554 return "DenseI64ArrayAttr";555 if (storageTypeStr == "::mlir::DenseI8ArrayAttr")556 return "DenseI8ArrayAttr";557 if (storageTypeStr == "::mlir::DenseIntElementsAttr")558 return "DenseIntElementsAttr";559 if (storageTypeStr == "::mlir::DenseResourceElementsAttr")560 return "DenseResourceElementsAttr";561 if (storageTypeStr == "::mlir::DictionaryAttr")562 return "DictAttr";563 if (storageTypeStr == "::mlir::FlatSymbolRefAttr")564 return "FlatSymbolRefAttr";565 if (storageTypeStr == "::mlir::FloatAttr")566 return "FloatAttr";567 if (storageTypeStr == "::mlir::IntegerAttr") {568 if (attr.getAttrDefName().str() == "I1Attr")569 return "BoolAttr";570 return "IntegerAttr";571 }572 if (storageTypeStr == "::mlir::IntegerSetAttr")573 return "IntegerSetAttr";574 if (storageTypeStr == "::mlir::OpaqueAttr")575 return "OpaqueAttr";576 if (storageTypeStr == "::mlir::StridedLayoutAttr")577 return "StridedLayoutAttr";578 if (storageTypeStr == "::mlir::StringAttr")579 return "StringAttr";580 if (storageTypeStr == "::mlir::SymbolRefAttr")581 return "SymbolRefAttr";582 if (storageTypeStr == "::mlir::TypeAttr")583 return "TypeAttr";584 if (storageTypeStr == "::mlir::UnitAttr")585 return "UnitAttr";586 return "Attribute";587}588 589/// Emits accessors to Op attributes.590static void emitAttributeAccessors(const Operator &op, raw_ostream &os) {591 for (const auto &namedAttr : op.getAttributes()) {592 // Skip "derived" attributes because they are just C++ functions that we593 // don't currently expose.594 if (namedAttr.attr.isDerivedAttr())595 continue;596 597 if (namedAttr.name.empty())598 continue;599 600 std::string sanitizedName = sanitizeName(namedAttr.name);601 602 // Unit attributes are handled specially.603 if (namedAttr.attr.getStorageType().trim() == "::mlir::UnitAttr") {604 os << formatv(unitAttributeGetterTemplate, sanitizedName, namedAttr.name);605 os << formatv(unitAttributeSetterTemplate, sanitizedName, namedAttr.name);606 os << formatv(attributeDeleterTemplate, sanitizedName, namedAttr.name);607 continue;608 }609 610 std::string type = "_ods_ir." + getPythonAttrName(namedAttr.attr);611 if (namedAttr.attr.isOptional()) {612 os << formatv(optionalAttributeGetterTemplate, sanitizedName,613 namedAttr.name, type);614 os << formatv(optionalAttributeSetterTemplate, sanitizedName,615 namedAttr.name, type);616 os << formatv(attributeDeleterTemplate, sanitizedName, namedAttr.name);617 } else {618 os << formatv(attributeGetterTemplate, sanitizedName, namedAttr.name,619 type);620 os << formatv(attributeSetterTemplate, sanitizedName, namedAttr.name,621 type);622 // Non-optional attributes cannot be deleted.623 }624 }625}626 627/// Template for the default auto-generated builder.628/// {0} is a comma-separated list of builder arguments, including the trailing629/// `loc` and `ip`;630/// {1} is the code populating `operands`, `results` and `attributes`,631/// `successors` fields.632constexpr const char *initTemplate = R"Py(633 def __init__(self, {0}):634 operands = []635 attributes = {{}636 regions = None637 {1}638 super().__init__({2})639)Py";640 641/// Template for appending a single element to the operand/result list.642/// {0} is the field name.643constexpr const char *singleOperandAppendTemplate = "operands.append({0})";644constexpr const char *singleResultAppendTemplate = "results.append({0})";645 646/// Template for appending an optional element to the operand/result list.647/// {0} is the field name.648constexpr const char *optionalAppendOperandTemplate =649 "if {0} is not None: operands.append({0})";650constexpr const char *optionalAppendAttrSizedOperandsTemplate =651 "operands.append({0})";652constexpr const char *optionalAppendResultTemplate =653 "if {0} is not None: results.append({0})";654 655/// Template for appending a list of elements to the operand/result list.656/// {0} is the field name.657constexpr const char *multiOperandAppendTemplate =658 "operands.extend(_get_op_results_or_values({0}))";659constexpr const char *multiOperandAppendPackTemplate =660 "operands.append(_get_op_results_or_values({0}))";661constexpr const char *multiResultAppendTemplate = "results.extend({0})";662 663/// Template for attribute builder from raw input in the operation builder.664/// {0} is the builder argument name;665/// {1} is the attribute builder from raw;666/// {2} is the attribute builder from raw.667/// Use the value the user passed in if either it is already an Attribute or668/// there is no method registered to make it an Attribute.669constexpr const char *initAttributeWithBuilderTemplate =670 R"Py(attributes["{1}"] = ({0} if (671 isinstance({0}, _ods_ir.Attribute) or672 not _ods_ir.AttrBuilder.contains('{2}')) else673 _ods_ir.AttrBuilder.get('{2}')({0}, context=_ods_context)))Py";674 675/// Template for attribute builder from raw input for optional attribute in the676/// operation builder.677/// {0} is the builder argument name;678/// {1} is the attribute builder from raw;679/// {2} is the attribute builder from raw.680/// Use the value the user passed in if either it is already an Attribute or681/// there is no method registered to make it an Attribute.682constexpr const char *initOptionalAttributeWithBuilderTemplate =683 R"Py(if {0} is not None: attributes["{1}"] = ({0} if (684 isinstance({0}, _ods_ir.Attribute) or685 not _ods_ir.AttrBuilder.contains('{2}')) else686 _ods_ir.AttrBuilder.get('{2}')({0}, context=_ods_context)))Py";687 688constexpr const char *initUnitAttributeTemplate =689 R"Py(if bool({1}): attributes["{0}"] = _ods_ir.UnitAttr.get(690 _ods_get_default_loc_context(loc)))Py";691 692/// Template to initialize the successors list in the builder if there are any693/// successors.694/// {0} is the value to initialize the successors list to.695constexpr const char *initSuccessorsTemplate = R"Py(_ods_successors = {0})Py";696 697/// Template to append or extend the list of successors in the builder.698/// {0} is the list method ('append' or 'extend');699/// {1} is the value to add.700constexpr const char *addSuccessorTemplate = R"Py(_ods_successors.{0}({1}))Py";701 702/// Returns true if the SameArgumentAndResultTypes trait can be used to infer703/// result types of the given operation.704static bool hasSameArgumentAndResultTypes(const Operator &op) {705 return op.getTrait("::mlir::OpTrait::SameOperandsAndResultType") &&706 op.getNumVariableLengthResults() == 0;707}708 709/// Returns true if the FirstAttrDerivedResultType trait can be used to infer710/// result types of the given operation.711static bool hasFirstAttrDerivedResultTypes(const Operator &op) {712 return op.getTrait("::mlir::OpTrait::FirstAttrDerivedResultType") &&713 op.getNumVariableLengthResults() == 0;714}715 716/// Returns true if the InferTypeOpInterface can be used to infer result types717/// of the given operation.718static bool hasInferTypeInterface(const Operator &op) {719 return op.getTrait("::mlir::InferTypeOpInterface::Trait") &&720 op.getNumRegions() == 0;721}722 723/// Returns true if there is a trait or interface that can be used to infer724/// result types of the given operation.725static bool canInferType(const Operator &op) {726 return hasSameArgumentAndResultTypes(op) ||727 hasFirstAttrDerivedResultTypes(op) || hasInferTypeInterface(op);728}729 730/// Populates `builderArgs` with result names if the builder is expected to731/// accept them as arguments.732static void733populateBuilderArgsResults(const Operator &op,734 SmallVectorImpl<std::string> &builderArgs) {735 if (canInferType(op))736 return;737 738 for (int i = 0, e = op.getNumResults(); i < e; ++i) {739 std::string name = op.getResultName(i).str();740 if (name.empty()) {741 if (op.getNumResults() == 1) {742 // Special case for one result, make the default name be 'result'743 // to properly match the built-in result accessor.744 name = "result";745 } else {746 name = formatv("_gen_res_{0}", i);747 }748 }749 name = sanitizeName(name);750 builderArgs.push_back(name);751 }752}753 754/// Populates `builderArgs` with the Python-compatible names of builder function755/// arguments using intermixed attributes and operands in the same order as they756/// appear in the `arguments` field of the op definition. Additionally,757/// `operandNames` is populated with names of operands in their order of758/// appearance.759static void populateBuilderArgs(const Operator &op,760 SmallVectorImpl<std::string> &builderArgs,761 SmallVectorImpl<std::string> &operandNames) {762 for (int i = 0, e = op.getNumArgs(); i < e; ++i) {763 std::string name = op.getArgName(i).str();764 if (name.empty())765 name = formatv("_gen_arg_{0}", i);766 name = sanitizeName(name);767 builderArgs.push_back(name);768 if (!isa<NamedAttribute *>(op.getArg(i)))769 operandNames.push_back(name);770 }771}772 773/// Populates `builderArgs` with the Python-compatible names of builder function774/// successor arguments. Additionally, `successorArgNames` is also populated.775static void776populateBuilderArgsSuccessors(const Operator &op,777 SmallVectorImpl<std::string> &builderArgs,778 SmallVectorImpl<std::string> &successorArgNames) {779 780 for (int i = 0, e = op.getNumSuccessors(); i < e; ++i) {781 NamedSuccessor successor = op.getSuccessor(i);782 std::string name = std::string(successor.name);783 if (name.empty())784 name = formatv("_gen_successor_{0}", i);785 name = sanitizeName(name);786 builderArgs.push_back(name);787 successorArgNames.push_back(name);788 }789}790 791/// Populates `builderLines` with additional lines that are required in the792/// builder to set up operation attributes. `argNames` is expected to contain793/// the names of builder arguments that correspond to op arguments, i.e. to the794/// operands and attributes in the same order as they appear in the `arguments`795/// field.796static void797populateBuilderLinesAttr(const Operator &op, ArrayRef<std::string> argNames,798 SmallVectorImpl<std::string> &builderLines) {799 builderLines.push_back("_ods_context = _ods_get_default_loc_context(loc)");800 for (int i = 0, e = op.getNumArgs(); i < e; ++i) {801 Argument arg = op.getArg(i);802 auto *attribute = llvm::dyn_cast_if_present<NamedAttribute *>(arg);803 if (!attribute)804 continue;805 806 // Unit attributes are handled specially.807 if (attribute->attr.getStorageType().trim() == "::mlir::UnitAttr") {808 builderLines.push_back(809 formatv(initUnitAttributeTemplate, attribute->name, argNames[i]));810 continue;811 }812 813 builderLines.push_back(formatv(814 attribute->attr.isOptional() || attribute->attr.hasDefaultValue()815 ? initOptionalAttributeWithBuilderTemplate816 : initAttributeWithBuilderTemplate,817 argNames[i], attribute->name, attribute->attr.getAttrDefName()));818 }819}820 821/// Populates `builderLines` with additional lines that are required in the822/// builder to set up successors. successorArgNames is expected to correspond823/// to the Python argument name for each successor on the op.824static void825populateBuilderLinesSuccessors(const Operator &op,826 ArrayRef<std::string> successorArgNames,827 SmallVectorImpl<std::string> &builderLines) {828 if (successorArgNames.empty()) {829 builderLines.push_back(formatv(initSuccessorsTemplate, "None"));830 return;831 }832 833 builderLines.push_back(formatv(initSuccessorsTemplate, "[]"));834 for (int i = 0, e = successorArgNames.size(); i < e; ++i) {835 auto &argName = successorArgNames[i];836 const NamedSuccessor &successor = op.getSuccessor(i);837 builderLines.push_back(formatv(addSuccessorTemplate,838 successor.isVariadic() ? "extend" : "append",839 argName));840 }841}842 843/// Populates `builderLines` with additional lines that are required in the844/// builder to set up op operands.845static void846populateBuilderLinesOperand(const Operator &op, ArrayRef<std::string> names,847 SmallVectorImpl<std::string> &builderLines) {848 bool sizedSegments = op.getTrait(attrSizedTraitForKind("operand")) != nullptr;849 850 // For each element, find or generate a name.851 for (int i = 0, e = op.getNumOperands(); i < e; ++i) {852 const NamedTypeConstraint &element = op.getOperand(i);853 std::string name = names[i];854 855 // Choose the formatting string based on the element kind.856 StringRef formatString;857 if (!element.isVariableLength()) {858 formatString = singleOperandAppendTemplate;859 } else if (element.isOptional()) {860 if (sizedSegments) {861 formatString = optionalAppendAttrSizedOperandsTemplate;862 } else {863 formatString = optionalAppendOperandTemplate;864 }865 } else {866 assert(element.isVariadic() && "unhandled element group type");867 // If emitting with sizedSegments, then we add the actual list-typed868 // element. Otherwise, we extend the actual operands.869 if (sizedSegments) {870 formatString = multiOperandAppendPackTemplate;871 } else {872 formatString = multiOperandAppendTemplate;873 }874 }875 876 builderLines.push_back(formatv(formatString.data(), name));877 }878}879 880/// Python code template of generating result types for881/// FirstAttrDerivedResultType trait882/// - {0} is the name of the attribute from which to derive the types.883/// - {1} is the number of results.884constexpr const char *firstAttrDerivedResultTypeTemplate =885 R"Py(if results is None:886 _ods_result_type_source_attr = attributes["{0}"]887 _ods_derived_result_type = (888 _ods_ir.TypeAttr(_ods_result_type_source_attr).value889 if _ods_ir.TypeAttr.isinstance(_ods_result_type_source_attr) else890 _ods_result_type_source_attr.type)891 results = [_ods_derived_result_type] * {1})Py";892 893/// Python code template of generating result types for894/// SameOperandsAndResultType trait895/// - {0} is the number of results.896constexpr const char *sameOperandsAndResultTypeTemplate =897 R"Py(if results is None: results = [operands[0].type] * {0})Py";898 899/// Appends the given multiline string as individual strings into900/// `builderLines`.901static void appendLineByLine(StringRef string,902 SmallVectorImpl<std::string> &builderLines) {903 904 std::pair<StringRef, StringRef> split = std::make_pair(string, string);905 do {906 split = split.second.split('\n');907 builderLines.push_back(split.first.str());908 } while (!split.second.empty());909}910 911/// Populates `builderLines` with additional lines that are required in the912/// builder to set up op results.913static void914populateBuilderLinesResult(const Operator &op, ArrayRef<std::string> names,915 SmallVectorImpl<std::string> &builderLines) {916 if (hasSameArgumentAndResultTypes(op)) {917 appendLineByLine(918 formatv(sameOperandsAndResultTypeTemplate, op.getNumResults()).str(),919 builderLines);920 return;921 }922 923 if (hasFirstAttrDerivedResultTypes(op)) {924 const NamedAttribute &firstAttr = op.getAttribute(0);925 assert(!firstAttr.name.empty() && "unexpected empty name for the attribute "926 "from which the type is derived");927 appendLineByLine(formatv(firstAttrDerivedResultTypeTemplate, firstAttr.name,928 op.getNumResults())929 .str(),930 builderLines);931 return;932 }933 934 if (hasInferTypeInterface(op))935 return;936 937 bool sizedSegments = op.getTrait(attrSizedTraitForKind("result")) != nullptr;938 builderLines.push_back("results = []");939 940 // For each element, find or generate a name.941 for (int i = 0, e = op.getNumResults(); i < e; ++i) {942 const NamedTypeConstraint &element = op.getResult(i);943 std::string name = names[i];944 945 // Choose the formatting string based on the element kind.946 StringRef formatString;947 if (!element.isVariableLength()) {948 formatString = singleResultAppendTemplate;949 } else if (element.isOptional()) {950 formatString = optionalAppendResultTemplate;951 } else {952 assert(element.isVariadic() && "unhandled element group type");953 // If emitting with sizedSegments, then we add the actual list-typed954 // element. Otherwise, we extend the actual operands.955 if (sizedSegments) {956 formatString = singleResultAppendTemplate;957 } else {958 formatString = multiResultAppendTemplate;959 }960 }961 962 builderLines.push_back(formatv(formatString.data(), name));963 }964}965 966/// If the operation has variadic regions, adds a builder argument to specify967/// the number of those regions and builder lines to forward it to the generic968/// constructor.969static void populateBuilderRegions(const Operator &op,970 SmallVectorImpl<std::string> &builderArgs,971 SmallVectorImpl<std::string> &builderLines) {972 if (op.hasNoVariadicRegions())973 return;974 975 // This is currently enforced when Operator is constructed.976 assert(op.getNumVariadicRegions() == 1 &&977 op.getRegion(op.getNumRegions() - 1).isVariadic() &&978 "expected the last region to be varidic");979 980 const NamedRegion ®ion = op.getRegion(op.getNumRegions() - 1);981 std::string name =982 ("num_" + region.name.take_front().lower() + region.name.drop_front())983 .str();984 builderArgs.push_back(name);985 builderLines.push_back(986 formatv("regions = {0} + {1}", op.getNumRegions() - 1, name));987}988 989/// Emits a default builder constructing an operation from the list of its990/// result types, followed by a list of its operands. Returns vector991/// of fully built functionArgs for downstream users (to save having to992/// rebuild anew).993static SmallVector<std::string> emitDefaultOpBuilder(const Operator &op,994 raw_ostream &os) {995 SmallVector<std::string> builderArgs;996 SmallVector<std::string> builderLines;997 SmallVector<std::string> operandArgNames;998 SmallVector<std::string> successorArgNames;999 builderArgs.reserve(op.getNumOperands() + op.getNumResults() +1000 op.getNumNativeAttributes() + op.getNumSuccessors());1001 populateBuilderArgsResults(op, builderArgs);1002 size_t numResultArgs = builderArgs.size();1003 populateBuilderArgs(op, builderArgs, operandArgNames);1004 size_t numOperandAttrArgs = builderArgs.size() - numResultArgs;1005 populateBuilderArgsSuccessors(op, builderArgs, successorArgNames);1006 1007 populateBuilderLinesOperand(op, operandArgNames, builderLines);1008 populateBuilderLinesAttr(op, ArrayRef(builderArgs).drop_front(numResultArgs),1009 builderLines);1010 populateBuilderLinesResult(1011 op, ArrayRef(builderArgs).take_front(numResultArgs), builderLines);1012 populateBuilderLinesSuccessors(op, successorArgNames, builderLines);1013 populateBuilderRegions(op, builderArgs, builderLines);1014 1015 // Layout of builderArgs vector elements:1016 // [ result_args operand_attr_args successor_args regions ]1017 1018 // Determine whether the argument corresponding to a given index into the1019 // builderArgs vector is a python keyword argument or not.1020 auto isKeywordArgFn = [&](size_t builderArgIndex) -> bool {1021 // All result, successor, and region arguments are positional arguments.1022 if ((builderArgIndex < numResultArgs) ||1023 (builderArgIndex >= (numResultArgs + numOperandAttrArgs)))1024 return false;1025 // Keyword arguments:1026 // - optional named attributes (including unit attributes)1027 // - default-valued named attributes1028 // - optional operands1029 Argument a = op.getArg(builderArgIndex - numResultArgs);1030 if (auto *nattr = llvm::dyn_cast_if_present<NamedAttribute *>(a))1031 return (nattr->attr.isOptional() || nattr->attr.hasDefaultValue());1032 if (auto *ntype = llvm::dyn_cast_if_present<NamedTypeConstraint *>(a))1033 return ntype->isOptional();1034 return false;1035 };1036 1037 // StringRefs in functionArgs refer to strings allocated by builderArgs.1038 SmallVector<StringRef> functionArgs;1039 1040 // Add positional arguments.1041 for (size_t i = 0, cnt = builderArgs.size(); i < cnt; ++i) {1042 if (!isKeywordArgFn(i))1043 functionArgs.push_back(builderArgs[i]);1044 }1045 1046 // Add a bare '*' to indicate that all following arguments must be keyword1047 // arguments.1048 functionArgs.push_back("*");1049 1050 // Add a default 'None' value to each keyword arg string, and then add to the1051 // function args list.1052 for (size_t i = 0, cnt = builderArgs.size(); i < cnt; ++i) {1053 if (isKeywordArgFn(i)) {1054 builderArgs[i].append("=None");1055 functionArgs.push_back(builderArgs[i]);1056 }1057 }1058 if (canInferType(op)) {1059 functionArgs.push_back("results=None");1060 }1061 functionArgs.push_back("loc=None");1062 functionArgs.push_back("ip=None");1063 1064 SmallVector<std::string> initArgs;1065 initArgs.push_back("self.OPERATION_NAME");1066 initArgs.push_back("self._ODS_REGIONS");1067 initArgs.push_back("self._ODS_OPERAND_SEGMENTS");1068 initArgs.push_back("self._ODS_RESULT_SEGMENTS");1069 initArgs.push_back("attributes=attributes");1070 initArgs.push_back("results=results");1071 initArgs.push_back("operands=operands");1072 initArgs.push_back("successors=_ods_successors");1073 initArgs.push_back("regions=regions");1074 initArgs.push_back("loc=loc");1075 initArgs.push_back("ip=ip");1076 1077 os << formatv(initTemplate, llvm::join(functionArgs, ", "),1078 llvm::join(builderLines, "\n "), llvm::join(initArgs, ", "));1079 return llvm::to_vector<8>(1080 llvm::map_range(functionArgs, [](StringRef s) { return s.str(); }));1081}1082 1083static void emitSegmentSpec(1084 const Operator &op, const char *kind,1085 llvm::function_ref<int(const Operator &)> getNumElements,1086 llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)>1087 getElement,1088 raw_ostream &os) {1089 std::string segmentSpec("[");1090 for (int i = 0, e = getNumElements(op); i < e; ++i) {1091 const NamedTypeConstraint &element = getElement(op, i);1092 if (element.isOptional()) {1093 segmentSpec.append("0,");1094 } else if (element.isVariadic()) {1095 segmentSpec.append("-1,");1096 } else {1097 segmentSpec.append("1,");1098 }1099 }1100 segmentSpec.append("]");1101 1102 os << formatv(opClassSizedSegmentsTemplate, kind, segmentSpec);1103}1104 1105static void emitRegionAttributes(const Operator &op, raw_ostream &os) {1106 // Emit _ODS_REGIONS = (min_region_count, has_no_variadic_regions).1107 // Note that the base OpView class defines this as (0, True).1108 unsigned minRegionCount = op.getNumRegions() - op.getNumVariadicRegions();1109 os << formatv(opClassRegionSpecTemplate, minRegionCount,1110 op.hasNoVariadicRegions() ? "True" : "False");1111}1112 1113/// Emits named accessors to regions.1114static void emitRegionAccessors(const Operator &op, raw_ostream &os) {1115 for (const auto &en : llvm::enumerate(op.getRegions())) {1116 const NamedRegion ®ion = en.value();1117 if (region.name.empty())1118 continue;1119 1120 assert((!region.isVariadic() || en.index() == op.getNumRegions() - 1) &&1121 "expected only the last region to be variadic");1122 os << formatv(regionAccessorTemplate, sanitizeName(region.name),1123 std::to_string(en.index()) + (region.isVariadic() ? ":" : ""),1124 region.isVariadic() ? "_ods_ir.RegionSequence"1125 : "_ods_ir.Region");1126 }1127}1128 1129/// Emits builder that extracts results from op1130static void emitValueBuilder(const Operator &op,1131 SmallVector<std::string> functionArgs,1132 raw_ostream &os) {1133 // Params with (possibly) default args.1134 auto valueBuilderParams =1135 llvm::map_range(functionArgs, [](const std::string &argAndMaybeDefault) {1136 SmallVector<StringRef> argMaybeDefault =1137 llvm::to_vector<2>(llvm::split(argAndMaybeDefault, "="));1138 auto arg = llvm::convertToSnakeFromCamelCase(argMaybeDefault[0]);1139 if (argMaybeDefault.size() == 2)1140 return arg + "=" + argMaybeDefault[1].str();1141 return arg;1142 });1143 // Actual args passed to op builder (e.g., opParam=op_param).1144 auto opBuilderArgs = llvm::map_range(1145 llvm::make_filter_range(functionArgs,1146 [](const std::string &s) { return s != "*"; }),1147 [](const std::string &arg) {1148 auto lhs = *llvm::split(arg, "=").begin();1149 return (lhs + "=" + llvm::convertToSnakeFromCamelCase(lhs)).str();1150 });1151 std::string nameWithoutDialect = sanitizeName(1152 op.getOperationName().substr(op.getOperationName().find('.') + 1));1153 if (nameWithoutDialect == op.getCppClassName())1154 nameWithoutDialect += "_";1155 std::string params = llvm::join(valueBuilderParams, ", ");1156 std::string args = llvm::join(opBuilderArgs, ", ");1157 if (op.getNumVariableLengthResults()) {1158 os << formatv(valueBuilderVariadicTemplate, nameWithoutDialect,1159 op.getCppClassName(), params, args);1160 } else {1161 std::string type = op.getCppClassName().str();1162 const char *results = "";1163 if (op.getNumResults() > 1) {1164 type = "_ods_ir.OpResultList";1165 results = ".results";1166 } else if (op.getNumResults() == 1) {1167 type = "_ods_ir.OpResult";1168 results = ".result";1169 }1170 os << formatv(valueBuilderTemplate, nameWithoutDialect,1171 op.getCppClassName(), params, args, type, results);1172 }1173}1174 1175/// Retrieve the description of the given op and generate a docstring for it.1176static std::string makeDocStringForOp(const Operator &op) {1177 if (!op.hasDescription())1178 return "";1179 1180 auto desc = op.getDescription().rtrim(" \t").str();1181 // Replace all """ with \"\"\" to avoid early termination of the literal.1182 desc = std::regex_replace(desc, std::regex(R"(""")"), R"(\"\"\")");1183 1184 std::string docString = "\n";1185 llvm::raw_string_ostream os(docString);1186 raw_indented_ostream identedOs(os);1187 os << R"( r""")" << "\n";1188 identedOs.printReindented(desc, " ");1189 if (!StringRef(desc).ends_with("\n"))1190 os << "\n";1191 os << R"( """)" << "\n";1192 1193 return docString;1194}1195 1196/// Emits bindings for a specific Op to the given output stream.1197static void emitOpBindings(const Operator &op, raw_ostream &os) {1198 os << formatv(opClassTemplate, op.getCppClassName(), op.getOperationName(),1199 makeDocStringForOp(op));1200 1201 // Sized segments.1202 if (op.getTrait(attrSizedTraitForKind("operand")) != nullptr) {1203 emitSegmentSpec(op, "OPERAND", getNumOperands, getOperand, os);1204 }1205 if (op.getTrait(attrSizedTraitForKind("result")) != nullptr) {1206 emitSegmentSpec(op, "RESULT", getNumResults, getResult, os);1207 }1208 1209 emitRegionAttributes(op, os);1210 SmallVector<std::string> functionArgs = emitDefaultOpBuilder(op, os);1211 emitOperandAccessors(op, os);1212 emitAttributeAccessors(op, os);1213 emitResultAccessors(op, os);1214 emitRegionAccessors(op, os);1215 emitValueBuilder(op, functionArgs, os);1216}1217 1218/// Emits bindings for the dialect specified in the command line, including file1219/// headers and utilities. Returns `false` on success to comply with Tablegen1220/// registration requirements.1221static bool emitAllOps(const RecordKeeper &records, raw_ostream &os) {1222 if (clDialectName.empty())1223 llvm::PrintFatalError("dialect name not provided");1224 1225 os << fileHeader;1226 if (!clDialectExtensionName.empty())1227 os << formatv(dialectExtensionTemplate, clDialectName.getValue());1228 else1229 os << formatv(dialectClassTemplate, clDialectName.getValue());1230 1231 for (const Record *rec : records.getAllDerivedDefinitions("Op")) {1232 Operator op(rec);1233 if (op.getDialectName() == clDialectName.getValue())1234 emitOpBindings(op, os);1235 }1236 return false;1237}1238 1239static GenRegistration1240 genPythonBindings("gen-python-op-bindings",1241 "Generate Python bindings for MLIR Ops", &emitAllOps);1242