brintos

brintos / llvm-project-archived public Read only

0
0
Text · 48.4 KiB · 2c33f4e Raw
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 &region = 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 &region = 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