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