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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // OpPythonBindingGen uses ODS specification of MLIR ops to generate Python 10 // binding classes wrapping a generic operation API. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/TableGen/GenInfo.h" 15 #include "mlir/TableGen/Operator.h" 16 #include "llvm/ADT/StringSet.h" 17 #include "llvm/Support/CommandLine.h" 18 #include "llvm/Support/FormatVariadic.h" 19 #include "llvm/TableGen/Error.h" 20 #include "llvm/TableGen/Record.h" 21 22 using namespace mlir; 23 using namespace mlir::tblgen; 24 25 /// File header and includes. 26 constexpr const char *fileHeader = R"Py( 27 # Autogenerated by mlir-tblgen; don't manually edit. 28 29 import array 30 from . import _cext 31 from . import _segmented_accessor, _equally_sized_accessor, _get_default_loc_context 32 _ir = _cext.ir 33 )Py"; 34 35 /// Template for dialect class: 36 /// {0} is the dialect namespace. 37 constexpr const char *dialectClassTemplate = R"Py( 38 @_cext.register_dialect 39 class _Dialect(_ir.Dialect): 40 DIALECT_NAMESPACE = "{0}" 41 pass 42 43 )Py"; 44 45 /// Template for operation class: 46 /// {0} is the Python class name; 47 /// {1} is the operation name. 48 constexpr const char *opClassTemplate = R"Py( 49 @_cext.register_operation(_Dialect) 50 class {0}(_ir.OpView): 51 OPERATION_NAME = "{1}" 52 )Py"; 53 54 /// Template for single-element accessor: 55 /// {0} is the name of the accessor; 56 /// {1} is either 'operand' or 'result'; 57 /// {2} is the position in the element list. 58 constexpr const char *opSingleTemplate = R"Py( 59 @property 60 def {0}(self): 61 return self.operation.{1}s[{2}] 62 )Py"; 63 64 /// Template for single-element accessor after a variable-length group: 65 /// {0} is the name of the accessor; 66 /// {1} is either 'operand' or 'result'; 67 /// {2} is the total number of element groups; 68 /// {3} is the position of the current group in the group list. 69 /// This works for both a single variadic group (non-negative length) and an 70 /// single optional element (zero length if the element is absent). 71 constexpr const char *opSingleAfterVariableTemplate = R"Py( 72 @property 73 def {0}(self): 74 variadic_group_length = len(self.operation.{1}s) - {2} + 1 75 return self.operation.{1}s[{3} + variadic_group_length - 1] 76 )Py"; 77 78 /// Template for an optional element accessor: 79 /// {0} is the name of the accessor; 80 /// {1} is either 'operand' or 'result'; 81 /// {2} is the total number of element groups; 82 /// {3} is the position of the current group in the group list. 83 constexpr const char *opOneOptionalTemplate = R"Py( 84 @property 85 def {0}(self); 86 return self.operation.{1}s[{3}] if len(self.operation.{1}s) > {2} 87 else None 88 )Py"; 89 90 /// Template for the variadic group accessor in the single variadic group case: 91 /// {0} is the name of the accessor; 92 /// {1} is either 'operand' or 'result'; 93 /// {2} is the total number of element groups; 94 /// {3} is the position of the current group in the group list. 95 constexpr const char *opOneVariadicTemplate = R"Py( 96 @property 97 def {0}(self): 98 variadic_group_length = len(self.operation.{1}s) - {2} + 1 99 return self.operation.{1}s[{3}:{3} + variadic_group_length] 100 )Py"; 101 102 /// First part of the template for equally-sized variadic group accessor: 103 /// {0} is the name of the accessor; 104 /// {1} is either 'operand' or 'result'; 105 /// {2} is the total number of variadic groups; 106 /// {3} is the number of non-variadic groups preceding the current group; 107 /// {3} is the number of variadic groups preceding the current group. 108 constexpr const char *opVariadicEqualPrefixTemplate = R"Py( 109 @property 110 def {0}(self): 111 start, pg = _equally_sized_accessor(operation.{1}s, {2}, {3}, {4}))Py"; 112 113 /// Second part of the template for equally-sized case, accessing a single 114 /// element: 115 /// {0} is either 'operand' or 'result'. 116 constexpr const char *opVariadicEqualSimpleTemplate = R"Py( 117 return self.operation.{0}s[start] 118 )Py"; 119 120 /// Second part of the template for equally-sized case, accessing a variadic 121 /// group: 122 /// {0} is either 'operand' or 'result'. 123 constexpr const char *opVariadicEqualVariadicTemplate = R"Py( 124 return self.operation.{0}s[start:start + pg] 125 )Py"; 126 127 /// Template for an attribute-sized group accessor: 128 /// {0} is the name of the accessor; 129 /// {1} is either 'operand' or 'result'; 130 /// {2} is the position of the group in the group list; 131 /// {3} is a return suffix (expected [0] for single-element, empty for 132 /// variadic, and opVariadicSegmentOptionalTrailingTemplate for optional). 133 constexpr const char *opVariadicSegmentTemplate = R"Py( 134 @property 135 def {0}(self): 136 {1}_range = _segmented_accessor( 137 self.operation.{1}s, 138 self.operation.attributes["{1}_segment_sizes"], {2}) 139 return {1}_range{3} 140 )Py"; 141 142 /// Template for a suffix when accessing an optional element in the 143 /// attribute-sized case: 144 /// {0} is either 'operand' or 'result'; 145 constexpr const char *opVariadicSegmentOptionalTrailingTemplate = 146 R"Py([0] if len({0}_range) > 0 else None)Py"; 147 148 /// Template for an operation attribute getter: 149 /// {0} is the name of the attribute sanitized for Python; 150 /// {1} is the Python type of the attribute; 151 /// {2} os the original name of the attribute. 152 constexpr const char *attributeGetterTemplate = R"Py( 153 @property 154 def {0}(self): 155 return {1}(self.operation.attributes["{2}"]) 156 )Py"; 157 158 /// Template for an optional operation attribute getter: 159 /// {0} is the name of the attribute sanitized for Python; 160 /// {1} is the Python type of the attribute; 161 /// {2} is the original name of the attribute. 162 constexpr const char *optionalAttributeGetterTemplate = R"Py( 163 @property 164 def {0}(self): 165 if "{2}" not in self.operation.attributes: 166 return None 167 return {1}(self.operation.attributes["{2}"]) 168 )Py"; 169 170 /// Template for a accessing a unit operation attribute, returns True of the 171 /// unit attribute is present, False otherwise (unit attributes have meaning 172 /// by mere presence): 173 /// {0} is the name of the attribute sanitized for Python, 174 /// {1} is the original name of the attribute. 175 constexpr const char *unitAttributeGetterTemplate = R"Py( 176 @property 177 def {0}(self): 178 return "{1}" in self.operation.attributes 179 )Py"; 180 181 static llvm::cl::OptionCategory 182 clOpPythonBindingCat("Options for -gen-python-op-bindings"); 183 184 static llvm::cl::opt<std::string> 185 clDialectName("bind-dialect", 186 llvm::cl::desc("The dialect to run the generator for"), 187 llvm::cl::init(""), llvm::cl::cat(clOpPythonBindingCat)); 188 189 using AttributeClasses = DenseMap<StringRef, StringRef>; 190 191 /// Checks whether `str` is a Python keyword. 192 static bool isPythonKeyword(StringRef str) { 193 static llvm::StringSet<> keywords( 194 {"and", "as", "assert", "break", "class", "continue", 195 "def", "del", "elif", "else", "except", "finally", 196 "for", "from", "global", "if", "import", "in", 197 "is", "lambda", "nonlocal", "not", "or", "pass", 198 "raise", "return", "try", "while", "with", "yield"}); 199 return keywords.contains(str); 200 }; 201 202 /// Modifies the `name` in a way that it becomes suitable for Python bindings 203 /// (does not change the `name` if it already is suitable) and returns the 204 /// modified version. 205 static std::string sanitizeName(StringRef name) { 206 if (isPythonKeyword(name)) 207 return (name + "_").str(); 208 return name.str(); 209 } 210 211 static std::string attrSizedTraitForKind(const char *kind) { 212 return llvm::formatv("::mlir::OpTrait::AttrSized{0}{1}Segments", 213 llvm::StringRef(kind).take_front().upper(), 214 llvm::StringRef(kind).drop_front()); 215 } 216 217 /// Emits accessors to "elements" of an Op definition. Currently, the supported 218 /// elements are operands and results, indicated by `kind`, which must be either 219 /// `operand` or `result` and is used verbatim in the emitted code. 220 static void emitElementAccessors( 221 const Operator &op, raw_ostream &os, const char *kind, 222 llvm::function_ref<unsigned(const Operator &)> getNumVariadic, 223 llvm::function_ref<int(const Operator &)> getNumElements, 224 llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)> 225 getElement) { 226 assert(llvm::is_contained( 227 llvm::SmallVector<StringRef, 2>{"operand", "result"}, kind) && 228 "unsupported kind"); 229 230 // Traits indicating how to process variadic elements. 231 std::string sameSizeTrait = 232 llvm::formatv("::mlir::OpTrait::SameVariadic{0}{1}Size", 233 llvm::StringRef(kind).take_front().upper(), 234 llvm::StringRef(kind).drop_front()); 235 std::string attrSizedTrait = attrSizedTraitForKind(kind); 236 237 unsigned numVariadic = getNumVariadic(op); 238 239 // If there is only one variadic element group, its size can be inferred from 240 // the total number of elements. If there are none, the generation is 241 // straightforward. 242 if (numVariadic <= 1) { 243 bool seenVariableLength = false; 244 for (int i = 0, e = getNumElements(op); i < e; ++i) { 245 const NamedTypeConstraint &element = getElement(op, i); 246 if (element.isVariableLength()) 247 seenVariableLength = true; 248 if (element.name.empty()) 249 continue; 250 if (element.isVariableLength()) { 251 os << llvm::formatv(element.isOptional() ? opOneOptionalTemplate 252 : opOneVariadicTemplate, 253 sanitizeName(element.name), kind, 254 getNumElements(op), i); 255 } else if (seenVariableLength) { 256 os << llvm::formatv(opSingleAfterVariableTemplate, 257 sanitizeName(element.name), kind, 258 getNumElements(op), i); 259 } else { 260 os << llvm::formatv(opSingleTemplate, sanitizeName(element.name), kind, 261 i); 262 } 263 } 264 return; 265 } 266 267 // Handle the operations where variadic groups have the same size. 268 if (op.getTrait(sameSizeTrait)) { 269 int numPrecedingSimple = 0; 270 int numPrecedingVariadic = 0; 271 for (int i = 0, e = getNumElements(op); i < e; ++i) { 272 const NamedTypeConstraint &element = getElement(op, i); 273 if (!element.name.empty()) { 274 os << llvm::formatv(opVariadicEqualPrefixTemplate, 275 sanitizeName(element.name), kind, numVariadic, 276 numPrecedingSimple, numPrecedingVariadic); 277 os << llvm::formatv(element.isVariableLength() 278 ? opVariadicEqualVariadicTemplate 279 : opVariadicEqualSimpleTemplate, 280 kind); 281 } 282 if (element.isVariableLength()) 283 ++numPrecedingVariadic; 284 else 285 ++numPrecedingSimple; 286 } 287 return; 288 } 289 290 // Handle the operations where the size of groups (variadic or not) is 291 // provided as an attribute. For non-variadic elements, make sure to return 292 // an element rather than a singleton container. 293 if (op.getTrait(attrSizedTrait)) { 294 for (int i = 0, e = getNumElements(op); i < e; ++i) { 295 const NamedTypeConstraint &element = getElement(op, i); 296 if (element.name.empty()) 297 continue; 298 std::string trailing; 299 if (!element.isVariableLength()) 300 trailing = "[0]"; 301 else if (element.isOptional()) 302 trailing = std::string( 303 llvm::formatv(opVariadicSegmentOptionalTrailingTemplate, kind)); 304 os << llvm::formatv(opVariadicSegmentTemplate, sanitizeName(element.name), 305 kind, i, trailing); 306 } 307 return; 308 } 309 310 llvm::PrintFatalError("unsupported " + llvm::Twine(kind) + " structure"); 311 } 312 313 /// Free function helpers accessing Operator components. 314 static int getNumOperands(const Operator &op) { return op.getNumOperands(); } 315 static const NamedTypeConstraint &getOperand(const Operator &op, int i) { 316 return op.getOperand(i); 317 } 318 static int getNumResults(const Operator &op) { return op.getNumResults(); } 319 static const NamedTypeConstraint &getResult(const Operator &op, int i) { 320 return op.getResult(i); 321 } 322 323 /// Emits accessors to Op operands. 324 static void emitOperandAccessors(const Operator &op, raw_ostream &os) { 325 auto getNumVariadic = [](const Operator &oper) { 326 return oper.getNumVariableLengthOperands(); 327 }; 328 emitElementAccessors(op, os, "operand", getNumVariadic, getNumOperands, 329 getOperand); 330 } 331 332 /// Emits accessors Op results. 333 static void emitResultAccessors(const Operator &op, raw_ostream &os) { 334 auto getNumVariadic = [](const Operator &oper) { 335 return oper.getNumVariableLengthResults(); 336 }; 337 emitElementAccessors(op, os, "result", getNumVariadic, getNumResults, 338 getResult); 339 } 340 341 /// Emits accessors to Op attributes. 342 static void emitAttributeAccessors(const Operator &op, 343 const AttributeClasses &attributeClasses, 344 raw_ostream &os) { 345 for (const auto &namedAttr : op.getAttributes()) { 346 // Skip "derived" attributes because they are just C++ functions that we 347 // don't currently expose. 348 if (namedAttr.attr.isDerivedAttr()) 349 continue; 350 351 if (namedAttr.name.empty()) 352 continue; 353 354 // Unit attributes are handled specially. 355 if (namedAttr.attr.getStorageType().trim().equals("::mlir::UnitAttr")) { 356 os << llvm::formatv(unitAttributeGetterTemplate, 357 sanitizeName(namedAttr.name), namedAttr.name); 358 continue; 359 } 360 361 // Other kinds of attributes need a mapping to a Python type. 362 if (!attributeClasses.count(namedAttr.attr.getStorageType().trim())) 363 continue; 364 365 os << llvm::formatv( 366 namedAttr.attr.isOptional() ? optionalAttributeGetterTemplate 367 : attributeGetterTemplate, 368 sanitizeName(namedAttr.name), 369 attributeClasses.lookup(namedAttr.attr.getStorageType()), 370 namedAttr.name); 371 } 372 } 373 374 /// Template for the default auto-generated builder. 375 /// {0} is the operation name; 376 /// {1} is a comma-separated list of builder arguments, including the trailing 377 /// `loc` and `ip`; 378 /// {2} is the code populating `operands`, `results` and `attributes` fields. 379 constexpr const char *initTemplate = R"Py( 380 def __init__(self, {1}): 381 operands = [] 382 results = [] 383 attributes = {{} 384 {2} 385 super().__init__(_ir.Operation.create( 386 "{0}", attributes=attributes, operands=operands, results=results, 387 loc=loc, ip=ip)) 388 )Py"; 389 390 /// Template for appending a single element to the operand/result list. 391 /// {0} is either 'operand' or 'result'; 392 /// {1} is the field name. 393 constexpr const char *singleElementAppendTemplate = "{0}s.append({1})"; 394 395 /// Template for appending an optional element to the operand/result list. 396 /// {0} is either 'operand' or 'result'; 397 /// {1} is the field name. 398 constexpr const char *optionalAppendTemplate = 399 "if {1} is not None: {0}s.append({1})"; 400 401 /// Template for appending a variadic element to the operand/result list. 402 /// {0} is either 'operand' or 'result'; 403 /// {1} is the field name. 404 constexpr const char *variadicAppendTemplate = "{0}s += [*{1}]"; 405 406 /// Template for setting up the segment sizes buffer. 407 constexpr const char *segmentDeclarationTemplate = 408 "{0}_segment_sizes = array.array('L')"; 409 410 /// Template for attaching segment sizes to the attribute list. 411 constexpr const char *segmentAttributeTemplate = 412 R"Py(attributes["{0}_segment_sizes"] = _ir.DenseElementsAttr.get({0}_segment_sizes, 413 context=_get_default_loc_context(loc)))Py"; 414 415 /// Template for appending the unit size to the segment sizes. 416 /// {0} is either 'operand' or 'result'; 417 /// {1} is the field name. 418 constexpr const char *singleElementSegmentTemplate = 419 "{0}_segment_sizes.append(1) # {1}"; 420 421 /// Template for appending 0/1 for an optional element to the segment sizes. 422 /// {0} is either 'operand' or 'result'; 423 /// {1} is the field name. 424 constexpr const char *optionalSegmentTemplate = 425 "{0}_segment_sizes.append(0 if {1} is None else 1)"; 426 427 /// Template for appending the length of a variadic group to the segment sizes. 428 /// {0} is either 'operand' or 'result'; 429 /// {1} is the field name. 430 constexpr const char *variadicSegmentTemplate = 431 "{0}_segment_sizes.append(len({1}))"; 432 433 /// Template for setting an attribute in the operation builder. 434 /// {0} is the attribute name; 435 /// {1} is the builder argument name. 436 constexpr const char *initAttributeTemplate = R"Py(attributes["{0}"] = {1})Py"; 437 438 /// Template for setting an optional attribute in the operation builder. 439 /// {0} is the attribute name; 440 /// {1} is the builder argument name. 441 constexpr const char *initOptionalAttributeTemplate = 442 R"Py(if {1} is not None: attributes["{0}"] = {1})Py"; 443 444 constexpr const char *initUnitAttributeTemplate = 445 R"Py(if bool({1}): attributes["{0}"] = _ir.UnitAttr.get( 446 _get_default_loc_context(loc)))Py"; 447 448 /// Populates `builderArgs` with the Python-compatible names of builder function 449 /// arguments, first the results, then the intermixed attributes and operands in 450 /// the same order as they appear in the `arguments` field of the op definition. 451 /// Additionally, `operandNames` is populated with names of operands in their 452 /// order of appearance. 453 static void 454 populateBuilderArgs(const Operator &op, 455 llvm::SmallVectorImpl<std::string> &builderArgs, 456 llvm::SmallVectorImpl<std::string> &operandNames) { 457 for (int i = 0, e = op.getNumResults(); i < e; ++i) { 458 std::string name = op.getResultName(i).str(); 459 if (name.empty()) 460 name = llvm::formatv("_gen_res_{0}", i); 461 name = sanitizeName(name); 462 builderArgs.push_back(name); 463 } 464 for (int i = 0, e = op.getNumArgs(); i < e; ++i) { 465 std::string name = op.getArgName(i).str(); 466 if (name.empty()) 467 name = llvm::formatv("_gen_arg_{0}", i); 468 name = sanitizeName(name); 469 builderArgs.push_back(name); 470 if (!op.getArg(i).is<NamedAttribute *>()) 471 operandNames.push_back(name); 472 } 473 } 474 475 /// Populates `builderLines` with additional lines that are required in the 476 /// builder to set up operation attributes. `argNames` is expected to contain 477 /// the names of builder arguments that correspond to op arguments, i.e. to the 478 /// operands and attributes in the same order as they appear in the `arguments` 479 /// field. 480 static void 481 populateBuilderLinesAttr(const Operator &op, 482 llvm::ArrayRef<std::string> argNames, 483 llvm::SmallVectorImpl<std::string> &builderLines) { 484 for (int i = 0, e = op.getNumArgs(); i < e; ++i) { 485 Argument arg = op.getArg(i); 486 auto *attribute = arg.dyn_cast<NamedAttribute *>(); 487 if (!attribute) 488 continue; 489 490 // Unit attributes are handled specially. 491 if (attribute->attr.getStorageType().trim().equals("::mlir::UnitAttr")) { 492 builderLines.push_back(llvm::formatv(initUnitAttributeTemplate, 493 attribute->name, argNames[i])); 494 continue; 495 } 496 497 builderLines.push_back(llvm::formatv(attribute->attr.isOptional() 498 ? initOptionalAttributeTemplate 499 : initAttributeTemplate, 500 attribute->name, argNames[i])); 501 } 502 } 503 504 /// Populates `builderLines` with additional lines that are required in the 505 /// builder. `kind` must be either "operand" or "result". `names` contains the 506 /// names of init arguments that correspond to the elements. 507 static void populateBuilderLines( 508 const Operator &op, const char *kind, llvm::ArrayRef<std::string> names, 509 llvm::SmallVectorImpl<std::string> &builderLines, 510 llvm::function_ref<int(const Operator &)> getNumElements, 511 llvm::function_ref<const NamedTypeConstraint &(const Operator &, int)> 512 getElement) { 513 // The segment sizes buffer only has to be populated if there attr-sized 514 // segments trait is present. 515 bool includeSegments = op.getTrait(attrSizedTraitForKind(kind)) != nullptr; 516 if (includeSegments) 517 builderLines.push_back(llvm::formatv(segmentDeclarationTemplate, kind)); 518 519 // For each element, find or generate a name. 520 for (int i = 0, e = getNumElements(op); i < e; ++i) { 521 const NamedTypeConstraint &element = getElement(op, i); 522 std::string name = names[i]; 523 524 // Choose the formatting string based on the element kind. 525 llvm::StringRef formatString, segmentFormatString; 526 if (!element.isVariableLength()) { 527 formatString = singleElementAppendTemplate; 528 segmentFormatString = singleElementSegmentTemplate; 529 } else if (element.isOptional()) { 530 formatString = optionalAppendTemplate; 531 segmentFormatString = optionalSegmentTemplate; 532 } else { 533 assert(element.isVariadic() && "unhandled element group type"); 534 formatString = variadicAppendTemplate; 535 segmentFormatString = variadicSegmentTemplate; 536 } 537 538 // Add the lines. 539 builderLines.push_back(llvm::formatv(formatString.data(), kind, name)); 540 if (includeSegments) 541 builderLines.push_back( 542 llvm::formatv(segmentFormatString.data(), kind, name)); 543 } 544 545 if (includeSegments) 546 builderLines.push_back(llvm::formatv(segmentAttributeTemplate, kind)); 547 } 548 549 /// Emits a default builder constructing an operation from the list of its 550 /// result types, followed by a list of its operands. 551 static void emitDefaultOpBuilder(const Operator &op, raw_ostream &os) { 552 // If we are asked to skip default builders, comply. 553 if (op.skipDefaultBuilders()) 554 return; 555 556 llvm::SmallVector<std::string, 8> builderArgs; 557 llvm::SmallVector<std::string, 8> builderLines; 558 llvm::SmallVector<std::string, 4> operandArgNames; 559 builderArgs.reserve(op.getNumOperands() + op.getNumResults() + 560 op.getNumNativeAttributes()); 561 populateBuilderArgs(op, builderArgs, operandArgNames); 562 populateBuilderLines( 563 op, "result", 564 llvm::makeArrayRef(builderArgs).take_front(op.getNumResults()), 565 builderLines, getNumResults, getResult); 566 populateBuilderLines(op, "operand", operandArgNames, builderLines, 567 getNumOperands, getOperand); 568 populateBuilderLinesAttr( 569 op, llvm::makeArrayRef(builderArgs).drop_front(op.getNumResults()), 570 builderLines); 571 572 builderArgs.push_back("loc=None"); 573 builderArgs.push_back("ip=None"); 574 os << llvm::formatv(initTemplate, op.getOperationName(), 575 llvm::join(builderArgs, ", "), 576 llvm::join(builderLines, "\n ")); 577 } 578 579 static void constructAttributeMapping(const llvm::RecordKeeper &records, 580 AttributeClasses &attributeClasses) { 581 for (const llvm::Record *rec : 582 records.getAllDerivedDefinitions("PythonAttr")) { 583 attributeClasses.try_emplace(rec->getValueAsString("cppStorageType").trim(), 584 rec->getValueAsString("pythonType").trim()); 585 } 586 } 587 588 /// Emits bindings for a specific Op to the given output stream. 589 static void emitOpBindings(const Operator &op, 590 const AttributeClasses &attributeClasses, 591 raw_ostream &os) { 592 os << llvm::formatv(opClassTemplate, op.getCppClassName(), 593 op.getOperationName()); 594 emitDefaultOpBuilder(op, os); 595 emitOperandAccessors(op, os); 596 emitAttributeAccessors(op, attributeClasses, os); 597 emitResultAccessors(op, os); 598 } 599 600 /// Emits bindings for the dialect specified in the command line, including file 601 /// headers and utilities. Returns `false` on success to comply with Tablegen 602 /// registration requirements. 603 static bool emitAllOps(const llvm::RecordKeeper &records, raw_ostream &os) { 604 if (clDialectName.empty()) 605 llvm::PrintFatalError("dialect name not provided"); 606 607 AttributeClasses attributeClasses; 608 constructAttributeMapping(records, attributeClasses); 609 610 os << fileHeader; 611 os << llvm::formatv(dialectClassTemplate, clDialectName.getValue()); 612 for (const llvm::Record *rec : records.getAllDerivedDefinitions("Op")) { 613 Operator op(rec); 614 if (op.getDialectName() == clDialectName.getValue()) 615 emitOpBindings(op, attributeClasses, os); 616 } 617 return false; 618 } 619 620 static GenRegistration 621 genPythonBindings("gen-python-op-bindings", 622 "Generate Python bindings for MLIR Ops", &emitAllOps); 623