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