1# Operation Definition Specification (ODS) 2 3In addition to specializing the `mlir::Op` C++ template, MLIR also supports 4defining operations in a table-driven manner. This is achieved via 5[TableGen][TableGen], which is both a generic language and its tooling to 6maintain records of domain-specific information. Facts regarding an operation 7are specified concisely into a TableGen record, which will be expanded into an 8equivalent `mlir::Op` C++ template specialization at compiler build time. 9 10This manual explains in detail all the available mechanisms for defining 11operations in such a table-driven manner. It aims to be a specification instead 12of a tutorial. Please refer to [Quickstart tutorial to adding MLIR graph 13rewrite](Tutorials/QuickstartRewrites.md) for the latter. 14 15In addition to detailing each mechanism, this manual also tries to capture 16best practices. They are rendered as quoted bullet points. 17 18## Motivation 19 20MLIR allows pluggable dialects, and dialects contain, among others, a list of 21operations. This open and extensible ecosystem leads to the "stringly" type IR 22problem, e.g., repetitive string comparisons during optimization and analysis 23passes, unintuitive accessor methods (e.g., generic/error prone `getOperand(3)` 24vs self-documenting `getStride()`) with more generic return types, verbose and 25generic constructors without default arguments, verbose textual IR dump, and 26so on. Furthermore, operation verification is: 27 281. best case: a central string-to-verification-function map, 291. middle case: duplication of verification across the code base, or 301. worst case: no verification functions. 31 32The fix is to support defining ops in a table-driven manner. Then for each 33dialect, we can have a central place that contains everything you need to know 34about each op, including its constraints, custom assembly form, etc. This 35description is also used to generate helper functions and classes to allow 36building, verification, parsing, printing, analysis, and many more. 37 38## Benefits 39 40Compared to the C++ template, this table-driven approach has several benefits 41including but not limited to: 42 43* **Single source of truth**: We strive to encode all facts regarding an 44 operation into the record, so that readers don't need to jump among code 45 snippets to fully understand an operation. 46* **Removing boilerplate**: We can automatically generate 47 operand/attribute/result getter methods, operation build methods, operation 48 verify methods, and many more utilities from the record. This greatly reduces 49 the boilerplate needed for defining a new op. 50* **Facilitating auto-generation**: The usage of these operation information 51 records are by no means limited to op definition itself. We can use them to 52 drive the auto-generation of many other components, like computation graph 53 serialization. 54 55## TableGen Syntax 56 57We use TableGen as the language for specifying operation information. TableGen 58itself just provides syntax for writing records; the syntax and constructs 59allowed in a TableGen file (typically with filename suffix `.td`) can be found 60[here][TableGenIntro]. The formal language specification can be found 61[here][TableGenRef]. _Roughly_ speaking, 62 63* TableGen `class` is similar to C++ class; it can be templated and 64 subclassed. 65* TableGen `def` is similar to C++ object; it can be declared by specializing 66 a TableGen `class` (e.g., `def MyDef : MyClass<...>;`) or completely 67 independently (e.g., `def MyDef;`). It cannot be further templated or 68 subclassed. 69* TableGen `dag` is a dedicated type for directed acyclic graph of elements. A 70 `dag` has one operator and zero or more arguments. Its syntax is `(operator 71 arg0, arg1, argN)`. The operator can be any TableGen `def`; an argument can 72 be anything, including `dag` itself. We can have names attached to both the 73 operator and the arguments like `(MyOp:$op_name MyArg:$arg_name)`. 74 75Please see the [language introduction][TableGenIntro] to learn about all the 76types and expressions supported by TableGen. 77 78## Operation Definition 79 80MLIR defines several common constructs to help operation definition and provide 81their semantics via a special [TableGen backend][TableGenBackend]: 82[`OpDefinitionsGen`][OpDefinitionsGen]. These constructs are defined in 83[`OpBase.td`][OpBase]. The main ones are 84 85* The `Op` class: It is the main construct for defining operations. All facts 86 regarding the operation are specified when specializing this class, with the 87 help of the following constructs. 88* The `Dialect` class: Operations belonging to one logical group are placed in 89 the same dialect. The `Dialect` class contains dialect-level information. 90* The `OpTrait` class hierarchy: They are used to specify special properties 91 and constraints of the operation, including whether the operation has side 92 effect or whether its output has the same shape as the input. 93* The `ins`/`outs` marker: These are two special makers builtin to the 94 `OpDefinitionsGen` backend. They lead the definitions of operands/attributes 95 and results respectively. 96* The `TypeConstraint` class hierarchy: They are used to specify the 97 constraints over operands or results. A notable subclass hierarchy is 98 `Type`, which stands for constraints for common C++ types. 99* The `AttrConstraint` class hierarchy: They are used to specify the 100 constraints over attributes. A notable subclass hierarchy is `Attr`, which 101 stands for constraints for attributes whose values are of common types. 102 103An operation is defined by specializing the `Op` class with concrete contents 104for all the fields it requires. For example, `tf.AvgPool` is defined as 105 106```tablegen 107def TF_AvgPoolOp : TF_Op<"AvgPool", [NoSideEffect]> { 108 let summary = "Performs average pooling on the input."; 109 110 let description = [{ 111Each entry in `output` is the mean of the corresponding size `ksize` 112window in `value`. 113 }]; 114 115 let arguments = (ins 116 TF_FpTensor:$value, 117 118 Confined<I64ArrayAttr, [ArrayMinCount<4>]>:$ksize, 119 Confined<I64ArrayAttr, [ArrayMinCount<4>]>:$strides, 120 TF_AnyStrAttrOf<["SAME", "VALID"]>:$padding, 121 DefaultValuedAttr<TF_ConvertDataFormatAttr, "NHWC">:$data_format 122 ); 123 124 let results = (outs 125 TF_FpTensor:$output 126 ); 127 128 TF_DerivedOperandTypeAttr T = TF_DerivedOperandTypeAttr<0>; 129} 130``` 131 132In the following we describe all the fields needed. Please see the definition 133of the `Op` class for the complete list of fields supported. 134 135### Operation name 136 137The operation name is a unique identifier of the operation within MLIR, e.g., 138`tf.Add` for addition operation in the TensorFlow dialect. This is the 139equivalent of the mnemonic in assembly language. It is used for parsing and 140printing in the textual format. It is also used for pattern matching in graph 141rewrites. 142 143The full operation name is composed of the dialect name and the op name, with 144the former provided via the dialect and the latter provided as the second 145template parameter to the `Op` class. 146 147### Operation documentation 148 149This includes both a one-line `summary` and a longer human-readable 150`description`. They will be used to drive automatic generation of dialect 151documentation. They need to be provided in the operation's definition body: 152 153```tablegen 154let summary = "..."; 155 156let description = [{ 157... 158}]; 159``` 160 161`description` should be written in Markdown syntax. 162 163Placing the documentation at the beginning is recommended since 164it helps in understanding the operation. 165 166> * Place documentation at the beginning of the operation definition 167> * The summary should be short and concise. It should be a one-liner without 168> trailing punctuation. Put expanded explanation in description. 169 170### Operation arguments 171 172There are two kinds of arguments: operands and attributes. Operands are runtime 173values produced by other ops; while attributes are compile-time known constant 174values, including two categories: 175 1761. Natural attributes: these attributes affect the behavior of the operations 177 (e.g., padding for convolution); 1781. Derived attributes: these attributes are not needed to define the operation 179 but are instead derived from information of the operation. E.g., the output 180 shape of type. This is mostly used for convenience interface generation or 181 interaction with other frameworks/translation. 182 183 All derived attributes should be materializable as an Attribute. That is, 184 even though they are not materialized, it should be possible to store as 185 an attribute. 186 187Both operands and attributes are specified inside the `dag`-typed `arguments`, 188led by `ins`: 189 190```tablegen 191let arguments = (ins 192 <type-constraint>:$<operand-name>, 193 ... 194 <attr-constraint>:$<attr-name>, 195 ... 196); 197``` 198 199Here `<type-constraint>` is a TableGen `def` from the `TypeConstraint` class 200hierarchy. Similarly, `<attr-constraint>` is a TableGen `def` from the 201`AttrConstraint` class hierarchy. See [Constraints](#constraints) for more 202information. 203 204There is no requirements on the relative order of operands and attributes; they 205can mix freely. The relative order of operands themselves matters. From each 206named argument a named getter will be generated that returns the argument with 207the return type (in the case of attributes the return type will be 208constructed from the storage type, while for operands it will be `Value`). Each 209attribute's raw value (e.g., as stored) can also be accessed via generated 210`<name>Attr` getters for use in transformation passes where the more user 211friendly return type is less suitable. 212 213All the arguments should be named to 1) provide documentation, 2) drive 214auto-generation of getter methods, 3) provide a handle to reference for other 215places like constraints. 216 217#### Variadic operands 218 219To declare a variadic operand, wrap the `TypeConstraint` for the operand with 220`Variadic<...>`. 221 222Normally operations have no variadic operands or just one variadic operand. For 223the latter case, it is easy to deduce which dynamic operands are for the static 224variadic operand definition. Though, if an operation has more than one variable 225length operands (either optional or variadic), it would be impossible to 226attribute dynamic operands to the corresponding static variadic operand 227definitions without further information from the operation. Therefore, either 228the `SameVariadicOperandSize` or `AttrSizedOperandSegments` trait is needed to 229indicate that all variable length operands have the same number of dynamic 230values. 231 232#### Optional operands 233 234To declare an optional operand, wrap the `TypeConstraint` for the operand with 235`Optional<...>`. 236 237Normally operations have no optional operands or just one optional operand. For 238the latter case, it is easy to deduce which dynamic operands are for the static 239operand definition. Though, if an operation has more than one variable length 240operands (either optional or variadic), it would be impossible to attribute 241dynamic operands to the corresponding static variadic operand definitions 242without further information from the operation. Therefore, either the 243`SameVariadicOperandSize` or `AttrSizedOperandSegments` trait is needed to 244indicate that all variable length operands have the same number of dynamic 245values. 246 247#### Optional attributes 248 249To declare an optional attribute, wrap the `AttrConstraint` for the attribute 250with `OptionalAttr<...>`. 251 252#### Attributes with default values 253 254To declare an attribute with a default value, wrap the `AttrConstraint` for the 255attribute with `DefaultValuedAttr<..., "...">`. 256 257The second parameter to `DefaultValuedAttr` should be a string containing the 258C++ default value. For example, a float default value should be specified as 259like `"0.5f"`, and an integer array default value should be specified as like 260`"{1, 2, 3}"`. 261 262#### Confining attributes 263 264`Confined` is provided as a general mechanism to help modelling further 265constraints on attributes beyond the ones brought by value types. You can use 266`Confined` to compose complex constraints out of more primitive ones. For 267example, a 32-bit integer attribute whose minimum value must be 10 can be 268expressed as `Confined<I32Attr, [IntMinValue<10>]>`. 269 270Right now, the following primitive constraints are supported: 271 272* `IntMinValue<N>`: Specifying an integer attribute to be greater than or 273 equal to `N` 274* `IntMaxValue<N>`: Specifying an integer attribute to be less than or equal 275 to `N` 276* `ArrayMinCount<N>`: Specifying an array attribute to have at least `N` 277 elements 278* `IntArrayNthElemEq<I, N>`: Specifying an integer array attribute's `I`-th 279 element to be equal to `N` 280* `IntArrayNthElemMinValue<I, N>`: Specifying an integer array attribute's 281 `I`-th element to be greater than or equal to `N` 282 283TODO: Design and implement more primitive constraints 284 285### Operation regions 286 287The regions of an operation are specified inside of the `dag`-typed `regions`, 288led by `region`: 289 290```tablegen 291let regions = (region 292 <region-constraint>:$<region-name>, 293 ... 294); 295``` 296 297#### Variadic regions 298 299Similar to the `Variadic` class used for variadic operands and results, 300`VariadicRegion<...>` can be used for regions. Variadic regions can currently 301only be specified as the last region in the regions list. 302 303### Operation results 304 305Similar to operands, results are specified inside the `dag`-typed `results`, led 306by `outs`: 307 308```tablegen 309let results = (outs 310 <type-constraint>:$<result-name>, 311 ... 312); 313``` 314 315#### Variadic results 316 317Similar to variadic operands, `Variadic<...>` can also be used for results. 318And similarly, `SameVariadicResultSize` for multiple variadic results in the 319same operation. 320 321### Operation successors 322 323For terminator operations, the successors are specified inside of the 324`dag`-typed `successors`, led by `successor`: 325 326```tablegen 327let successors = (successor 328 <successor-constraint>:$<successor-name>, 329 ... 330); 331``` 332 333#### Variadic successors 334 335Similar to the `Variadic` class used for variadic operands and results, 336`VariadicSuccessor<...>` can be used for successors. Variadic successors can 337currently only be specified as the last successor in the successor list. 338 339### Operation traits and constraints 340 341Traits are operation properties that affect syntax or semantics. MLIR C++ 342models various traits in the `mlir::OpTrait` namespace. 343 344Both operation traits, [interfaces](#operation-interfaces), and constraints 345involving multiple operands/attributes/results are provided as the second 346template parameter to the `Op` class. They should be deriving from the `OpTrait` 347class. See [Constraints](#constraints) for more information. 348 349### Operation interfaces 350 351[Operation interfaces](Interfaces.md#operation-interfaces) allow 352operations to expose method calls without the 353caller needing to know the exact operation type. Operation interfaces 354defined in C++ can be accessed in the ODS framework via the 355`OpInterfaceTrait` class. Aside from using pre-existing interfaces in 356the C++ API, the ODS framework also provides a simplified mechanism 357for defining such interfaces which removes much of the boilerplate 358necessary. 359 360Providing a definition of the `OpInterface` class will auto-generate the C++ 361classes for the interface. An `OpInterface` includes a name, for the C++ class, 362a description, and a list of interface methods. 363 364```tablegen 365def MyInterface : OpInterface<"MyInterface"> { 366 let description = ...; 367 let methods = [...]; 368} 369``` 370 371There are two types of methods that can be used with an interface, 372`InterfaceMethod` and `StaticInterfaceMethod`. They are both comprised of the 373same core components, with the distinction that `StaticInterfaceMethod` models a 374static method on the derived operation. 375 376An `InterfaceMethod` is comprised of the following components: 377 378* Description 379 - A string description of what this method does and its invariants. 380* ReturnType 381 - A string corresponding to the C++ return type of the method. 382* MethodName 383 - A string corresponding to the desired name of the method. 384* Arguments (Optional) 385 - A dag of strings that correspond to a C++ type and variable name 386 respectively. 387* MethodBody (Optional) 388 - An optional explicit implementation of the interface method. 389 - `ConcreteOp` is an implicitly defined typename that can be used to refer 390 to the type of the derived operation currently being operated on. 391 - In non-static methods, a variable 'ConcreteOp op' is defined and may be 392 used to refer to an instance of the derived operation. 393* DefaultImplementation (Optional) 394 - An optional explicit default implementation of the interface method. 395 - This method is placed within the `Trait` class that is attached to the 396 operation. As such, this method has the same characteristics as any 397 other [`Trait`](Traits.md) method. 398 - `ConcreteOp` is an implicitly defined typename that can be used to refer 399 to the type of the derived operation currently being operated on. 400 401ODS also allows generating the declarations for the `InterfaceMethod` of the op 402if one specifies the interface with `DeclareOpInterfaceMethods` (see example 403below). 404 405Examples: 406 407```tablegen 408def MyInterface : OpInterface<"MyInterface"> { 409 let description = [{ 410 My interface is very interesting. ... 411 }]; 412 413 let methods = [ 414 // A simple non-static method with no inputs. 415 InterfaceMethod<"'foo' is a non-static method with no inputs.", 416 "unsigned", "foo" 417 >, 418 419 // A new non-static method accepting an input argument. 420 InterfaceMethod<"/*insert doc here*/", 421 "Value ", "bar", (ins "unsigned":$i) 422 >, 423 424 // Query a static property of the derived operation. 425 StaticInterfaceMethod<"'fooStatic' is a static method with no inputs.", 426 "unsigned", "fooStatic" 427 >, 428 429 // Provide the definition of a static interface method. 430 // Note: `ConcreteOp` corresponds to the derived operation typename. 431 StaticInterfaceMethod<"/*insert doc here*/", 432 "Operation *", "create", (ins "OpBuilder &":$builder, "Location":$loc), [{ 433 return builder.create<ConcreteOp>(loc); 434 }]>, 435 436 // Provide a definition of the non-static method. 437 // Note: `op` corresponds to the derived operation variable. 438 InterfaceMethod<"/*insert doc here*/", 439 "unsigned", "getNumInputsAndOutputs", (ins), [{ 440 return op.getNumInputs() + op.getNumOutputs(); 441 }]>, 442 443 // Provide only a default definition of the method. 444 // Note: `ConcreteOp` corresponds to the derived operation typename. 445 InterfaceMethod<"/*insert doc here*/", 446 "unsigned", "getNumWithDefault", (ins), /*methodBody=*/[{}], [{ 447 ConcreteOp op = cast<ConcreteOp>(getOperation()); 448 return op.getNumInputs() + op.getNumOutputs(); 449 }]>, 450 ]; 451} 452 453// Interfaces can optionally be wrapped inside DeclareOpInterfaceMethods. This 454// would result in autogenerating declarations for members `foo`, `bar` and 455// `fooStatic`. Methods with bodies are not declared inside the op 456// declaration but instead handled by the op interface trait directly. 457def OpWithInferTypeInterfaceOp : Op<... 458 [DeclareOpInterfaceMethods<MyInterface>]> { ... } 459 460// Methods that have a default implementation do not have declarations 461// generated. If an operation wishes to override the default behavior, it can 462// explicitly specify the method that it wishes to override. This will force 463// the generation of a declaration for those methods. 464def OpWithOverrideInferTypeInterfaceOp : Op<... 465 [DeclareOpInterfaceMethods<MyInterface, ["getNumWithDefault"]>]> { ... } 466``` 467 468A verification method can also be specified on the `OpInterface` by setting 469`verify`. Setting `verify` results in the generated trait having a `verifyTrait` 470method that is applied to all operations implementing the trait. 471 472### Builder methods 473 474For each operation, there are a few builders automatically generated based on 475the arguments and returns types. For example, given the following op definition: 476 477```tablegen 478def MyOp : ... { 479 let arguments = (ins 480 I32:$i32_operand, 481 F32:$f32_operand, 482 ..., 483 484 I32Attr:$i32_attr, 485 F32Attr:$f32_attr, 486 ... 487 ); 488 489 let results = (outs 490 I32:$i32_result, 491 F32:$f32_result, 492 ... 493 ); 494} 495``` 496 497The following builders are generated: 498 499```c++ 500// All result-types/operands/attributes have one aggregate parameter. 501static void build(OpBuilder &odsBuilder, OperationState &odsState, 502 ArrayRef<Type> resultTypes, 503 ValueRange operands, 504 ArrayRef<NamedAttribute> attributes); 505 506// Each result-type/operand/attribute has a separate parameter. The parameters 507// for attributes are of mlir::Attribute types. 508static void build(OpBuilder &odsBuilder, OperationState &odsState, 509 Type i32_result, Type f32_result, ..., 510 Value i32_operand, Value f32_operand, ..., 511 IntegerAttr i32_attr, FloatAttr f32_attr, ...); 512 513// Each result-type/operand/attribute has a separate parameter. The parameters 514// for attributes are raw values unwrapped with mlir::Attribute instances. 515// (Note that this builder will not always be generated. See the following 516// explanation for more details.) 517static void build(OpBuilder &odsBuilder, OperationState &odsState, 518 Type i32_result, Type f32_result, ..., 519 Value i32_operand, Value f32_operand, ..., 520 APInt i32_attr, StringRef f32_attr, ...); 521 522// Each operand/attribute has a separate parameter but result type is aggregate. 523static void build(OpBuilder &odsBuilder, OperationState &odsState, 524 ArrayRef<Type> resultTypes, 525 Value i32_operand, Value f32_operand, ..., 526 IntegerAttr i32_attr, FloatAttr f32_attr, ...); 527 528// All operands/attributes have aggregate parameters. 529// Generated if return type can be inferred. 530static void build(OpBuilder &odsBuilder, OperationState &odsState, 531 ValueRange operands, ArrayRef<NamedAttribute> attributes); 532 533// (And manually specified builders depending on the specific op.) 534``` 535 536The first form provides basic uniformity so that we can create ops using the 537same form regardless of the exact op. This is particularly useful for 538implementing declarative pattern rewrites. 539 540The second and third forms are good for use in manually written code given that 541they provide better guarantee via signatures. 542 543The third form will be generated if any of the op's attribute has different 544`Attr.returnType` from `Attr.storageType` and we know how to build an attribute 545from an unwrapped value (i.e., `Attr.constBuilderCall` is defined.) 546Additionally, for the third form, if an attribute appearing later in the 547`arguments` list has a default value, the default value will be supplied in the 548declaration. This works for `BoolAttr`, `StrAttr`, `EnumAttr` for now and the 549list can grow in the future. So if possible, default valued attribute should be 550placed at the end of the `arguments` list to leverage this feature. (This 551behavior is essentially due to C++ function parameter default value placement 552restrictions.) Otherwise, the builder of the third form will still be generated 553but default values for the attributes not at the end of the `arguments` list 554will not be supplied in the builder's signature. 555 556ODS will generate a builder that doesn't require return type specified if 557 558* Op implements InferTypeOpInterface interface; 559* All return types are either buildable types or are the same as a given 560 operand (e.g., `AllTypesMatch` constraint between operand and result); 561 562And there may potentially exist other builders depending on the specific op; 563please refer to the 564[generated C++ file](#run-mlir-tblgen-to-see-the-generated-content) for the 565complete list. 566 567#### Custom builder methods 568 569However, if the above cases cannot satisfy all needs, you can define additional 570convenience build methods with `OpBuilder`. 571 572`OpBuilder` is a class that takes the parameter list and the optional `build()` 573method body. They are separated because we need to generate op declaration and 574definition into separate files. The parameter list should _include_ `Builder 575*builder, OperationState &state`. If the `body` is not provided, _only_ the 576builder declaration will be generated; this provides a way to define complicated 577builders entirely in C++ files. 578 579For example, for the following op: 580 581```tablegen 582def MyOp : Op<"my_op", []> { 583 let arguments = (ins F32Attr:$attr); 584 585 let results = (outs); 586} 587``` 588 589If we want to define a builder with a default value for the only attribute, we 590can add into `MyOp`: 591 592```tablegen 593def MyOp : ... { 594 ... 595 596 let builders = [ 597 OpBuilder<"OpBuilder &builder, OperationState &state, float val = 0.5f", [{ 598 state.addAttribute("attr", builder.getF32FloatAttr(val)); 599 }]> 600 ]; 601} 602``` 603 604The generated builder will look like: 605 606```c++ 607static void build(OpBuilder &builder, OperationState &state, float val = 0.5f) { 608 state.addAttribute("attr", builder.getF32FloatAttr(val)); 609} 610``` 611 612### Custom parser and printer methods 613 614Functions to parse and print the operation's custom assembly form. 615 616### Custom verifier code 617 618Verification code will be automatically generated for 619[constraints](#constraints) specified on various entities of the op. To 620perform _additional_ verification, you can use 621 622```tablegen 623let verifier = [{ 624 ... 625}]; 626``` 627 628Code placed in `verifier` will be called after the auto-generated verification 629code. The order of trait verification excluding those of `verifier` should not 630be relied upon. 631 632### Declarative Assembly Format 633 634The custom assembly form of the operation may be specified in a declarative 635string that matches the operations operands, attributes, etc. With the ability 636to express additional information that needs to be parsed to build the 637operation: 638 639```tablegen 640def CallOp : Std_Op<"call", ...> { 641 let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$args); 642 let results = (outs Variadic<AnyType>); 643 644 let assemblyFormat = [{ 645 $callee `(` $args `)` attr-dict `:` functional-type($args, results) 646 }]; 647} 648``` 649 650The format is comprised of three components: 651 652#### Directives 653 654A directive is a type of builtin function, with an optional set of arguments. 655The available directives are as follows: 656 657* `attr-dict` 658 659 - Represents the attribute dictionary of the operation. 660 661* `attr-dict-with-keyword` 662 663 - Represents the attribute dictionary of the operation, but prefixes the 664 dictionary with an `attributes` keyword. 665 666* `functional-type` ( inputs , results ) 667 668 - Formats the `inputs` and `results` arguments as a 669 [function type](LangRef.md#function-type). 670 - The constraints on `inputs` and `results` are the same as the `input` of 671 the `type` directive. 672 673* `operands` 674 675 - Represents all of the operands of an operation. 676 677* `results` 678 679 - Represents all of the results of an operation. 680 681* `successors` 682 683 - Represents all of the successors of an operation. 684 685* `type` ( input ) 686 687 - Represents the type of the given input. 688 - `input` must be either an operand or result [variable](#variables), the 689 `operands` directive, or the `results` directive. 690 691#### Literals 692 693A literal is either a keyword or punctuation surrounded by \`\`. 694 695The following are the set of valid punctuation: 696 `:`, `,`, `=`, `<`, `>`, `(`, `)`, `[`, `]`, `->` 697 698#### Variables 699 700A variable is an entity that has been registered on the operation itself, i.e. 701an argument(attribute or operand), result, successor, etc. In the `CallOp` 702example above, the variables would be `$callee` and `$args`. 703 704Attribute variables are printed with their respective value type, unless that 705value type is buildable. In those cases, the type of the attribute is elided. 706 707#### Optional Groups 708 709In certain situations operations may have "optional" information, e.g. 710attributes or an empty set of variadic operands. In these situations a section 711of the assembly format can be marked as `optional` based on the presence of this 712information. An optional group is defined by wrapping a set of elements within 713`()` followed by a `?` and has the following requirements: 714 715* The first element of the group must either be a literal or an operand. 716 - This is because the first element must be optionally parsable. 717* Exactly one argument variable within the group must be marked as the anchor 718 of the group. 719 - The anchor is the element whose presence controls whether the group 720 should be printed/parsed. 721 - An element is marked as the anchor by adding a trailing `^`. 722 - The first element is *not* required to be the anchor of the group. 723* Literals, variables, and type directives are the only valid elements within 724 the group. 725 - Any attribute variable may be used, but only optional attributes can be 726 marked as the anchor. 727 - Only variadic or optional operand arguments can be used. 728 - The operands to a type directive must be defined within the optional 729 group. 730 731An example of an operation with an optional group is `std.return`, which has a 732variadic number of operands. 733 734``` 735def ReturnOp : ... { 736 let arguments = (ins Variadic<AnyType>:$operands); 737 738 // We only print the operands and types if there are a non-zero number 739 // of operands. 740 let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; 741} 742``` 743 744#### Requirements 745 746The format specification has a certain set of requirements that must be adhered 747to: 748 7491. The output and operation name are never shown as they are fixed and cannot be 750 altered. 7511. All operands within the operation must appear within the format, either 752 individually or with the `operands` directive. 7531. All operand and result types must appear within the format using the various 754 `type` directives, either individually or with the `operands` or `results` 755 directives. 7561. The `attr-dict` directive must always be present. 7571. Must not contain overlapping information; e.g. multiple instances of 758 'attr-dict', types, operands, etc. 759 - Note that `attr-dict` does not overlap with individual attributes. These 760 attributes will simply be elided when printing the attribute dictionary. 761 762##### Type Inference 763 764One requirement of the format is that the types of operands and results must 765always be present. In certain instances, the type of a variable may be deduced 766via type constraints or other information available. In these cases, the type of 767that variable may be elided from the format. 768 769* Buildable Types 770 771Some type constraints may only have one representation, allowing for them to 772be directly buildable; for example the `I32` or `Index` types. Types in `ODS` 773may mark themselves as buildable by setting the `builderCall` field or 774inheriting from the `BuildableType` class. 775 776* Trait Equality Constraints 777 778There are many operations that have known type equality constraints registered 779as traits on the operation; for example the true, false, and result values of a 780`select` operation often have the same type. The assembly format may inspect 781these equal constraints to discern the types of missing variables. The currently 782supported traits are: `AllTypesMatch`, `SameTypeOperands`, and 783`SameOperandsAndResultType`. 784 785### `hasCanonicalizer` 786 787This boolean field indicate whether canonicalization patterns have been defined 788for this operation. If it is `1`, then `::getCanonicalizationPatterns()` should 789be defined. 790 791### `hasFolder` 792 793This boolean field indicate whether general folding rules have been defined 794for this operation. If it is `1`, then `::fold()` should be defined. 795 796### Extra declarations 797 798One of the goals of table-driven op definition is to auto-generate as much logic 799and methods needed for each op as possible. With that said, there will always be 800long-tail cases that won't be covered. For such cases, you can use 801`extraClassDeclaration`. Code in `extraClassDeclaration` will be copied 802literally to the generated C++ op class. 803 804Note that `extraClassDeclaration` is a mechanism intended for long-tail cases 805by power users; for not-yet-implemented widely-applicable cases, improving the 806infrastructure is preferable. 807 808### Generated C++ code 809 810[OpDefinitionsGen][OpDefinitionsGen] processes the op definition spec file and 811generates two files containing the corresponding C++ code: one for declarations, 812the other for definitions. The former is generated via the `-gen-op-decls` 813command-line option, while the latter is via the `-gen-op-defs` option. 814 815The definition file contains all the op method definitions, which can be 816included and enabled by defining `GET_OP_CLASSES`. For each operation, 817OpDefinitionsGen generates an operation class and an 818[operand adaptor](#operand-adaptors) class. Besides, it also contains a 819comma-separated list of all defined ops, which can be included and enabled by 820defining `GET_OP_LIST`. 821 822#### Class name and namespaces 823 824For each operation, its generated C++ class name is the symbol `def`ed with 825TableGen with dialect prefix removed. The first `_` serves as the delimiter. 826For example, for `def TF_AddOp`, the C++ class name would be `AddOp`. 827We remove the `TF` prefix because it is for scoping ops; other dialects 828may as well define their own `AddOp`s. 829 830The namespaces of the generated C++ class will come from the dialect's 831`cppNamespace` field. For example, if a dialect's `cppNamespace` is `A::B`, 832then an op of that dialect will be placed in 833`namespace A { namespace B { ... } }`. If a dialect does not specify a 834`cppNamespace`, we then use the dialect's name as the namespace. 835 836This means the qualified name of the generated C++ class does not necessarily 837match exactly with the operation name as explained in 838[Operation name](#operation-name). This is to allow flexible naming to satisfy 839coding style requirements. 840 841#### Operand adaptors 842 843For each operation, we automatically generate an _operand adaptor_. This class 844solves the problem of accessing operands provided as a list of `Value`s without 845using "magic" constants. The operand adaptor takes a reference to an array of 846`Value` and provides methods with the same names as those in the operation class 847to access them. For example, for a binary arithmetic operation, it may provide 848`.lhs()` to access the first operand and `.rhs()` to access the second operand. 849 850The operand adaptor class lives in the same namespace as the operation class, 851and has the name of the operation followed by `OperandAdaptor`. A template 852declaration `OperandAdaptor<>` is provided to look up the operand adaptor for 853the given operation. 854 855Operand adaptors can be used in function templates that also process operations: 856 857```c++ 858template <typename BinaryOpTy> 859std::pair<Value, Value> zip(BinaryOpTy &&op) { 860 return std::make_pair(op.lhs(), op.rhs());; 861} 862 863void process(AddOp op, ArrayRef<Value> newOperands) { 864 zip(op); 865 zip(OperandAdaptor<AddOp>(newOperands)); 866 /*...*/ 867} 868``` 869 870## Constraints 871 872Constraint is a core concept in table-driven operation definition: operation 873verification and graph operation matching are all based on satisfying 874constraints. So both the operation definition and rewrite rules specification 875significantly involve writing constraints. We have the `Constraint` class in 876[`OpBase.td`][OpBase] has the common base class for all constraints. 877 878An operation's constraint can cover different range; it may 879 880* Only concern a single attribute (e.g. being a 32-bit integer greater than 5), 881* Multiple operands and results (e.g., the 1st result's shape must be the same 882 as the 1st operand), or 883* Intrinsic to the operation itself (e.g., having no side effect). 884 885We call them as single-entity constraint, multi-entity constraint, and traits, 886respectively. 887 888### Single-entity constraint 889 890Constraints scoped to a single operand, attribute, or result are specified at 891the entity's declaration place as described in 892[Operation arguments](#operation-arguments) and 893[Operation results](#operation-results). 894 895To help modelling constraints of common types, a set of `TypeConstraint`s are 896created; they are the `Type` subclass hierarchy. It includes `F32` for the 897constraints of being a float, `TensorOf<[F32]>` for the constraints of being 898a float tensor, and so on. 899 900Similarly, a set of `AttrConstraint`s are created for helping modelling 901constraints of common attribute kinds. They are the `Attr` subclass hierarchy. 902It includes `F32Attr` for the constraints of being a float attribute, 903`F32ArrayAttr` for the constraints of being a float array attribute, and so on. 904 905### Multi-entity constraint 906 907Constraints involving more than one operand/attribute/result are quite common 908on operations, like the element type and shape relation between operands and 909results. These constraints should be specified as the `Op` class template 910parameter as described in 911[Operation traits and constraints](#operation-traits-and-constraints). 912 913Multi-entity constraints are modeled as `PredOpTrait` (a subclass of `OpTrait`) 914in [`OpBase.td`][OpBase].A bunch of constraint primitives are provided to help 915specification. See [`OpBase.td`][OpBase] for the complete list. 916 917### Trait 918 919Traits are intrinsic properties of the operation like having side effect or not, 920commutative or not, whether is a terminator, etc. These constraints should be 921specified as the `Op` class template parameter as described in 922[Operation traits and constraints](#operation-traits-and-constraints). 923 924Traits are modeled as `NativeOpTrait` (a subclass of `OpTrait`) in 925[`OpBase.td`][OpBase]. They are backed and will be translated into the 926corresponding C++ `mlir::OpTrait` classes. 927 928### How to specify new constraint 929 930To write a constraint, you need to provide its predicates and give it a 931descriptive name. Predicates, modeled with the `Pred` class, are the workhorse 932for composing constraints. The predicate for a constraint is typically built up 933in a nested manner, using the two categories of predicates: 934 9351. `CPred`: the primitive leaf predicate. 9362. Compound predicate: a predicate composed from child predicates using 937 predicate combiners (conjunction: `And`, disjunction: `Or`, negation: `Neg`, 938 substitution: `SubstLeaves`, concatenation: `Concat`). 939 940`CPred` is the basis for composing more complex predicates. It is the "atom" 941predicate from the perspective of TableGen and the "interface" between 942TableGen and C++. What is inside is already C++ code, which will be treated 943as opaque strings with special placeholders to be substituted. 944 945You can put any C++ code that returns a boolean value inside a `CPred`, 946including evaluating expressions, calling functions, calling class methods, 947and so on. 948 949To help interaction with the C++ environment, there are a few special 950placeholders provided to refer to entities in the context where this predicate 951is used. They serve as "hooks" to the enclosing environment. This includes 952`$_builder`, `$_op`, and `$_self`: 953 954* `$_builder` will be replaced by a `mlir::Builder` instance so that you can 955 access common build methods. 956* `$_op` will be replaced by the current operation so that you can access 957 information of the current operation. 958* `$_self` will be replaced with the entity this predicate is attached to. 959 E.g., `BoolAttr` is an attribute constraint that wraps a 960 `CPred<"$_self.isa<BoolAttr>()">`. Then for `F32:$attr`,`$_self` will be 961 replaced by `$attr`. For type constraints, it's a little bit special since 962 we want the constraints on each type definition reads naturally and we want 963 to attach type constraints directly to an operand/result, `$_self` will be 964 replaced by the operand/result's type. E.g., for `F32` in `F32:$operand`, its 965 `$_self` will be expanded as `getOperand(...).getType()`. 966 967TODO(b/130663252): Reconsider the leading symbol for special placeholders. 968Eventually we want to allow referencing operand/result $-names; such $-names 969can start with underscore. 970 971For example, to write an attribute `attr` is an `IntegerAttr`, in C++ you can 972just call `attr.isa<IntegerAttr>()`. The code can be wrapped in a `CPred` as 973`$_self.isa<IntegerAttr>()`, with `$_self` as the special placeholder to be 974replaced by the current attribute `attr` at expansion time. 975 976For more complicated predicates, you can wrap it in a single `CPred`, or you 977can use predicate combiners to combine them. For example, to write the 978constraint that an attribute `attr` is a 32-bit or 64-bit integer, you can 979write it as 980 981```tablegen 982And<[ 983 CPred<"$_self.isa<IntegerAttr>()">, 984 Or<[ 985 CPred<"$_self.cast<IntegerAttr>().getType().isInteger(32)">, 986 CPred<"$_self.cast<IntegerAttr>().getType().isInteger(64)"> 987 ]> 988]> 989``` 990 991(Note that the above is just to show with a familiar example how you can use 992`CPred` and predicate combiners to write complicated predicates. For integer 993attributes specifically, [`OpBase.td`][OpBase] already defines `I32Attr` and 994`I64Attr`. So you can actually reuse them to write it as `Or<[I32Attr.predicate, 995I64Attr.predicate]>`.) 996 997TODO: Build up a library of reusable primitive constraints 998 999If the predicate is very complex to write with `CPred` together with predicate 1000combiners, you can also write it as a normal C++ function and use the `CPred` 1001as a way to "invoke" the function. For example, to verify an attribute `attr` 1002has some property, you can write a C++ function like 1003 1004```cpp 1005bool HasSomeProperty(Attribute attr) { ... } 1006``` 1007 1008and then define the op as: 1009 1010```tablegen 1011def HasSomeProperty : AttrConstraint<CPred<"HasSomeProperty($_self)">, 1012 "has some property">; 1013 1014def MyOp : Op<...> { 1015 let arguments = (ins 1016 ... 1017 HasSomeProperty:$attr 1018 ); 1019} 1020``` 1021 1022As to whether we should define the predicate using a single `CPred` wrapping 1023the whole expression, multiple `CPred`s with predicate combiners, or a single 1024`CPred` "invoking" a function, there are no clear-cut criteria. Defining using 1025`CPred` and predicate combiners is preferable since it exposes more information 1026(instead hiding all the logic behind a C++ function) into the op definition spec 1027so that it can potentially drive more auto-generation cases. But it will 1028require a nice library of common predicates as the building blocks to avoid the 1029duplication, which is being worked on right now. 1030 1031## Attribute Definition 1032 1033An attribute is a compile-time known constant of an operation. 1034 1035ODS provides attribute wrappers over C++ attribute classes. There are a few 1036common C++ [attribute classes][AttrClasses] defined in MLIR's core IR library 1037and one is free to define dialect-specific attribute classes. ODS allows one 1038to use these attributes in TableGen to define operations, potentially with 1039more fine-grained constraints. For example, `StrAttr` directly maps to 1040`StringAttr`; `F32Attr`/`F64Attr` requires the `FloatAttr` to additionally 1041be of a certain bitwidth. 1042 1043ODS attributes are defined as having a storage type (corresponding to a backing 1044`mlir::Attribute` that _stores_ the attribute), a return type (corresponding to 1045the C++ _return_ type of the generated of the helper getters) as well as method 1046to convert between the internal storage and the helper method. 1047 1048### Attribute decorators 1049 1050There are a few important attribute adapters/decorators/modifers that can be 1051applied to ODS attributes to specify common additional properties like 1052optionality, default values, etc.: 1053 1054* `DefaultValuedAttr`: specifies the 1055 [default value](#attributes-with-default-values) for an attribute. 1056* `OptionalAttr`: specifies an attribute as [optional](#optional-attributes). 1057* `Confined`: adapts an attribute with 1058 [further constraints](#confining-attributes). 1059 1060### Enum attributes 1061 1062Some attributes can only take values from a predefined enum, e.g., the 1063comparison kind of a comparison op. To define such attributes, ODS provides 1064several mechanisms: `StrEnumAttr`, `IntEnumAttr`, and `BitEnumAttr`. 1065 1066* `StrEnumAttr`: each enum case is a string, the attribute is stored as a 1067 [`StringAttr`][StringAttr] in the op. 1068* `IntEnumAttr`: each enum case is an integer, the attribute is stored as a 1069 [`IntegerAttr`][IntegerAttr] in the op. 1070* `BitEnumAttr`: each enum case is a bit, the attribute is stored as a 1071 [`IntegerAttr`][IntegerAttr] in the op. 1072 1073All these `*EnumAttr` attributes require fully specifying all of the allowed 1074cases via their corresponding `*EnumAttrCase`. With this, ODS is able to 1075generate additional verification to only accept allowed cases. To facilitate the 1076interaction between `*EnumAttr`s and their C++ consumers, the 1077[`EnumsGen`][EnumsGen] TableGen backend can generate a few common utilities: a 1078C++ enum class, `llvm::DenseMapInfo` for the enum class, conversion functions 1079from/to strings. This is controlled via the `-gen-enum-decls` and 1080`-gen-enum-defs` command-line options of `mlir-tblgen`. 1081 1082For example, given the following `EnumAttr`: 1083 1084```tablegen 1085def Case15: I32EnumAttrCase<"Case15", 15>; 1086def Case20: I32EnumAttrCase<"Case20", 20>; 1087 1088def MyIntEnum: I32EnumAttr<"MyIntEnum", "An example int enum", 1089 [Case15, Case20]> { 1090 let cppNamespace = "Outer::Inner"; 1091 let stringToSymbolFnName = "ConvertToEnum"; 1092 let symbolToStringFnName = "ConvertToString"; 1093} 1094``` 1095 1096The following will be generated via `mlir-tblgen -gen-enum-decls`: 1097 1098```c++ 1099namespace Outer { 1100namespace Inner { 1101// An example int enum 1102enum class MyIntEnum : uint32_t { 1103 Case15 = 15, 1104 Case20 = 20, 1105}; 1106 1107llvm::Optional<MyIntEnum> symbolizeMyIntEnum(uint32_t); 1108llvm::StringRef ConvertToString(MyIntEnum); 1109llvm::Optional<MyIntEnum> ConvertToEnum(llvm::StringRef); 1110inline constexpr unsigned getMaxEnumValForMyIntEnum() { 1111 return 20; 1112} 1113 1114} // namespace Inner 1115} // namespace Outer 1116 1117namespace llvm { 1118template<> struct DenseMapInfo<Outer::Inner::MyIntEnum> { 1119 using StorageInfo = llvm::DenseMapInfo<uint32_t>; 1120 1121 static inline Outer::Inner::MyIntEnum getEmptyKey() { 1122 return static_cast<Outer::Inner::MyIntEnum>(StorageInfo::getEmptyKey()); 1123 } 1124 1125 static inline Outer::Inner::MyIntEnum getTombstoneKey() { 1126 return static_cast<Outer::Inner::MyIntEnum>(StorageInfo::getTombstoneKey()); 1127 } 1128 1129 static unsigned getHashValue(const Outer::Inner::MyIntEnum &val) { 1130 return StorageInfo::getHashValue(static_cast<uint32_t>(val)); 1131 } 1132 1133 static bool isEqual(const Outer::Inner::MyIntEnum &lhs, const Outer::Inner::MyIntEnum &rhs) { 1134 return lhs == rhs; 1135 } 1136}; 1137} 1138``` 1139 1140The following will be generated via `mlir-tblgen -gen-enum-defs`: 1141 1142```c++ 1143namespace Outer { 1144namespace Inner { 1145llvm::StringRef ConvertToString(MyIntEnum val) { 1146 switch (val) { 1147 case MyIntEnum::Case15: return "Case15"; 1148 case MyIntEnum::Case20: return "Case20"; 1149 } 1150 return ""; 1151} 1152 1153llvm::Optional<MyIntEnum> ConvertToEnum(llvm::StringRef str) { 1154 return llvm::StringSwitch<llvm::Optional<MyIntEnum>>(str) 1155 .Case("Case15", MyIntEnum::Case15) 1156 .Case("Case20", MyIntEnum::Case20) 1157 .Default(llvm::None); 1158} 1159llvm::Optional<MyIntEnum> symbolizeMyIntEnum(uint32_t value) { 1160 switch (value) { 1161 case 15: return MyIntEnum::Case15; 1162 case 20: return MyIntEnum::Case20; 1163 default: return llvm::None; 1164 } 1165} 1166 1167} // namespace Inner 1168} // namespace Outer 1169``` 1170 1171Similarly for the following `BitEnumAttr` definition: 1172 1173```tablegen 1174def None: BitEnumAttrCase<"None", 0x0000>; 1175def Bit1: BitEnumAttrCase<"Bit1", 0x0001>; 1176def Bit2: BitEnumAttrCase<"Bit2", 0x0002>; 1177def Bit3: BitEnumAttrCase<"Bit3", 0x0004>; 1178 1179def MyBitEnum: BitEnumAttr<"MyBitEnum", "An example bit enum", 1180 [None, Bit1, Bit2, Bit3]>; 1181``` 1182 1183We can have: 1184 1185```c++ 1186// An example bit enum 1187enum class MyBitEnum : uint32_t { 1188 None = 0, 1189 Bit1 = 1, 1190 Bit2 = 2, 1191 Bit3 = 4, 1192}; 1193 1194llvm::Optional<MyBitEnum> symbolizeMyBitEnum(uint32_t); 1195std::string stringifyMyBitEnum(MyBitEnum); 1196llvm::Optional<MyBitEnum> symbolizeMyBitEnum(llvm::StringRef); 1197inline MyBitEnum operator|(MyBitEnum lhs, MyBitEnum rhs) { 1198 return static_cast<MyBitEnum>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs)); 1199} 1200inline MyBitEnum operator&(MyBitEnum lhs, MyBitEnum rhs) { 1201 return static_cast<MyBitEnum>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)); 1202} 1203inline bool bitEnumContains(MyBitEnum bits, MyBitEnum bit) { 1204 return (static_cast<uint32_t>(bits) & static_cast<uint32_t>(bit)) != 0; 1205} 1206 1207namespace llvm { 1208template<> struct DenseMapInfo<::MyBitEnum> { 1209 using StorageInfo = llvm::DenseMapInfo<uint32_t>; 1210 1211 static inline ::MyBitEnum getEmptyKey() { 1212 return static_cast<::MyBitEnum>(StorageInfo::getEmptyKey()); 1213 } 1214 1215 static inline ::MyBitEnum getTombstoneKey() { 1216 return static_cast<::MyBitEnum>(StorageInfo::getTombstoneKey()); 1217 } 1218 1219 static unsigned getHashValue(const ::MyBitEnum &val) { 1220 return StorageInfo::getHashValue(static_cast<uint32_t>(val)); 1221 } 1222 1223 static bool isEqual(const ::MyBitEnum &lhs, const ::MyBitEnum &rhs) { 1224 return lhs == rhs; 1225 } 1226}; 1227``` 1228 1229```c++ 1230std::string stringifyMyBitEnum(MyBitEnum symbol) { 1231 auto val = static_cast<uint32_t>(symbol); 1232 // Special case for all bits unset. 1233 if (val == 0) return "None"; 1234 1235 llvm::SmallVector<llvm::StringRef, 2> strs; 1236 if (1u & val) { strs.push_back("Bit1"); val &= ~1u; } 1237 if (2u & val) { strs.push_back("Bit2"); val &= ~2u; } 1238 if (4u & val) { strs.push_back("Bit3"); val &= ~4u; } 1239 1240 if (val) return ""; 1241 return llvm::join(strs, "|"); 1242} 1243 1244llvm::Optional<MyBitEnum> symbolizeMyBitEnum(llvm::StringRef str) { 1245 // Special case for all bits unset. 1246 if (str == "None") return MyBitEnum::None; 1247 1248 llvm::SmallVector<llvm::StringRef, 2> symbols; 1249 str.split(symbols, "|"); 1250 1251 uint32_t val = 0; 1252 for (auto symbol : symbols) { 1253 auto bit = llvm::StringSwitch<llvm::Optional<uint32_t>>(symbol) 1254 .Case("Bit1", 1) 1255 .Case("Bit2", 2) 1256 .Case("Bit3", 4) 1257 .Default(llvm::None); 1258 if (bit) { val |= *bit; } else { return llvm::None; } 1259 } 1260 return static_cast<MyBitEnum>(val); 1261} 1262 1263llvm::Optional<MyBitEnum> symbolizeMyBitEnum(uint32_t value) { 1264 // Special case for all bits unset. 1265 if (value == 0) return MyBitEnum::None; 1266 1267 if (value & ~(1u | 2u | 4u)) return llvm::None; 1268 return static_cast<MyBitEnum>(value); 1269} 1270``` 1271 1272## Debugging Tips 1273 1274### Run `mlir-tblgen` to see the generated content 1275 1276TableGen syntax sometimes can be obscure; reading the generated content can be 1277a very helpful way to understand and debug issues. To build `mlir-tblgen`, run 1278`cmake --build . --target mlir-tblgen` in your build directory and find the 1279`mlir-tblgen` binary in the `bin/` subdirectory. All the supported generators 1280can be found via `mlir-tblgen --help`. For example, `--gen-op-decls` and 1281`--gen-op-defs` as explained in [Generated C++ code](#generated-c++-code). 1282 1283To see the generated code, invoke `mlir-tblgen` with a specific generator by 1284providing include paths via `-I`. For example, 1285 1286```sh 1287# To see op C++ class declaration 1288mlir-tblgen --gen-op-decls -I /path/to/mlir/include /path/to/input/td/file 1289# To see op C++ class definition 1290mlir-tblgen --gen-op-defs -I /path/to/mlir/include /path/to/input/td/file 1291# To see op documentation 1292mlir-tblgen --gen-dialect-doc -I /path/to/mlir/include /path/to/input/td/file 1293 1294# To see op interface C++ class declaration 1295mlir-tblgen --gen-op-interface-decls -I /path/to/mlir/include /path/to/input/td/file 1296# To see op interface C++ class definition 1297mlir-tblgen --gen-op-interface-defs -I /path/to/mlir/include /path/to/input/td/file 1298# To see op interface documentation 1299mlir-tblgen --gen-op-interface-doc -I /path/to/mlir/include /path/to/input/td/file 1300``` 1301 1302## Appendix 1303 1304### Requirements and existing mechanisms analysis 1305 1306The op description should as declarative as possible to allow a wide range of 1307tools to work with them and query methods generated from them. In particular 1308this means specifying traits, constraints and shape inference information in 1309a way that is easily analyzable (e.g., avoid opaque calls to C++ functions where 1310possible). 1311 1312We considered the approaches of several contemporary systems and focused on 1313requirements that were desirable: 1314 1315* Ops registered using a registry separate from C++ code. 1316 * Unknown ops are allowed in MLIR, so ops need not be registered. The 1317 ability of the compiler to optimize those ops or graphs containing those 1318 ops is constrained but correct. 1319 * The current proposal does not include a runtime op description, but it 1320 does not preclude such description, it can be added later. 1321 * The op registry is essential for generating C++ classes that make 1322 manipulating ops, verifying correct construction etc. in C++ easier by 1323 providing a typed representation and accessors. 1324* The op registry will be defined in 1325 [TableGen](https://llvm.org/docs/TableGen/index.html) and be used to 1326 generate C++ classes and utility functions 1327 (builder/verifier/parser/printer). 1328 * TableGen is a modelling specification language used by LLVM's backends 1329 and fits in well with trait-based modelling. This is an implementation 1330 decision and there are alternative ways of doing this. But the 1331 specification language is good for the requirements of modelling the 1332 traits (as seen from usage in LLVM processor backend modelling) and easy 1333 to extend, so a practical choice. If another good option comes up, we 1334 will consider it. 1335* MLIR allows both defined and undefined ops. 1336 * Defined ops should have fixed semantics and could have a corresponding 1337 reference implementation defined using, for example, EDSC. 1338 * Dialects are under full control of the dialect owner and normally live 1339 with the framework of the dialect. 1340* The op's traits (e.g., commutative) are modelled along with the op in the 1341 registry. 1342* The op's operand/return type constraints are modelled along with the op in 1343 the registry (see [Shape inference](ShapeInference.md) discussion below), 1344 this allows (e.g.) optimized concise syntax in textual dumps. 1345* Behavior of the op is documented along with the op with a summary and a 1346 description. The description is written in markdown and extracted for 1347 inclusion in the generated LangRef section of the dialect. 1348* The generic assembly form of printing and parsing is available as normal, 1349 but a custom parser and printer can either be specified or automatically 1350 generated from an optional string representation showing the mapping of the 1351 "assembly" string to operands/type. 1352 * Parser-level remappings (e.g., `eq` to enum) will be supported as part 1353 of the parser generation. 1354* Matching patterns are specified separately from the op description. 1355 * Contrasted with LLVM there is no "base" set of ops that every backend 1356 needs to be aware of. Instead there are many different dialects and the 1357 transformations/legalizations between these dialects form a graph of 1358 transformations. 1359* Reference implementation may be provided along with the op definition. 1360 1361 * The reference implementation may be in terms of either standard ops or 1362 other reference implementations. 1363 1364 TODO: document expectation if the dependent op's definition changes. 1365 1366[TableGen]: https://llvm.org/docs/TableGen/index.html 1367[TableGenIntro]: https://llvm.org/docs/TableGen/LangIntro.html 1368[TableGenRef]: https://llvm.org/docs/TableGen/LangRef.html 1369[TableGenBackend]: https://llvm.org/docs/TableGen/BackEnds.html#introduction 1370[OpBase]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/IR/OpBase.td 1371[OpDefinitionsGen]: https://github.com/llvm/llvm-project/blob/master/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp 1372[EnumsGen]: https://github.com/llvm/llvm-project/blob/master/mlir/tools/mlir-tblgen/EnumsGen.cpp 1373[StringAttr]: LangRef.md#string-attribute 1374[IntegerAttr]: LangRef.md#integer-attribute 1375[AttrClasses]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/IR/Attributes.h 1376