1# Operation Definition Specification (ODS) 2 3In addition to specializing the `mlir::Op` C++ template, MLIR also supports 4defining operations and data types in a table-driven manner. This is achieved 5via [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 13[Quickstart tutorial to adding MLIR graph rewrite](Tutorials/QuickstartRewrites.md) 14for the latter. 15 16In addition to detailing each mechanism, this manual also tries to capture best 17practices. They are rendered as quoted bullet points. 18 19## Motivation 20 21MLIR allows pluggable dialects, and dialects contain, among others, a list of 22operations. This open and extensible ecosystem leads to the "stringly" type IR 23problem, e.g., repetitive string comparisons during optimization and analysis 24passes, unintuitive accessor methods (e.g., generic/error prone `getOperand(3)` 25vs self-documenting `getStride()`) with more generic return types, verbose and 26generic constructors without default arguments, verbose textual IR dump, and so 27on. Furthermore, operation verification is: 28 291. best case: a central string-to-verification-function map, 301. middle case: duplication of verification across the code base, or 311. worst case: no verification functions. 32 33The fix is to support defining ops in a table-driven manner. Then for each 34dialect, we can have a central place that contains everything you need to know 35about each op, including its constraints, custom assembly form, etc. This 36description is also used to generate helper functions and classes to allow 37building, verification, parsing, printing, analysis, and many more. 38 39## Benefits 40 41Compared to the C++ template, this table-driven approach has several benefits 42including but not limited to: 43 44* **Single source of truth**: We strive to encode all facts regarding an 45 operation into the record, so that readers don't need to jump among code 46 snippets to fully understand an operation. 47* **Removing boilerplate**: We can automatically generate 48 operand/attribute/result getter methods, operation build methods, operation 49 verify methods, and many more utilities from the record. This greatly 50 reduces the boilerplate needed for defining a new op. 51* **Facilitating auto-generation**: The usage of these operation information 52 records are by no means limited to op definition itself. We can use them to 53 drive the auto-generation of many other components, like computation graph 54 serialization. 55 56## TableGen Syntax 57 58We use TableGen as the language for specifying operation information. TableGen 59itself just provides syntax for writing records; the syntax and constructs 60allowed in a TableGen file (typically with filename suffix `.td`) can be found 61[here][TableGenProgRef]. 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 reference][TableGenProgRef] 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 of 133the `Op` class for the complete list of fields supported. 134 135### Operation name 136 137The operation name is a unique identifier of the operation within MLIR, e.g., 138`tf.Add` for addition operation in the TensorFlow dialect. This is the 139equivalent of the mnemonic in assembly language. It is used for parsing and 140printing in the textual format. It is also used for pattern matching in graph 141rewrites. 142 143The full operation name is composed of the dialect name and the op name, with 144the former provided via the dialect and the latter provided as the second 145template parameter to the `Op` class. 146 147### Operation documentation 148 149This includes both a one-line `summary` and a longer human-readable 150`description`. They will be used to drive automatic generation of dialect 151documentation. They need to be provided in the operation's definition body: 152 153```tablegen 154let summary = "..."; 155 156let description = [{ 157... 158}]; 159``` 160 161`description` should be written in Markdown syntax. 162 163Placing the documentation at the beginning is recommended since it helps in 164understanding 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 an 185 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 constructed 208from the storage type, while for operands it will be `Value`). Each attribute's 209raw value (e.g., as stored) can also be accessed via generated `<name>Attr` 210getters for use in transformation passes where the more user friendly return 211type 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. And 318similarly, `SameVariadicResultSize` for multiple variadic results in the same 319operation. 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++ models 342various traits in the `mlir::OpTrait` namespace. 343 344Both operation traits, [interfaces](Interfaces.md#utilizing-the-ods-framework), 345and constraints involving multiple operands/attributes/results are provided as 346the second template parameter to the `Op` class. They should be deriving from 347the `OpTrait` class. See [Constraints](#constraints) for more information. 348 349### Builder methods 350 351For each operation, there are a few builders automatically generated based on 352the arguments and returns types. For example, given the following op definition: 353 354```tablegen 355def MyOp : ... { 356 let arguments = (ins 357 I32:$i32_operand, 358 F32:$f32_operand, 359 ..., 360 361 I32Attr:$i32_attr, 362 F32Attr:$f32_attr, 363 ... 364 ); 365 366 let results = (outs 367 I32:$i32_result, 368 F32:$f32_result, 369 ... 370 ); 371} 372``` 373 374The following builders are generated: 375 376```c++ 377// All result-types/operands/attributes have one aggregate parameter. 378static void build(OpBuilder &odsBuilder, OperationState &odsState, 379 ArrayRef<Type> resultTypes, 380 ValueRange operands, 381 ArrayRef<NamedAttribute> attributes); 382 383// Each result-type/operand/attribute has a separate parameter. The parameters 384// for attributes are of mlir::Attribute types. 385static void build(OpBuilder &odsBuilder, OperationState &odsState, 386 Type i32_result, Type f32_result, ..., 387 Value i32_operand, Value f32_operand, ..., 388 IntegerAttr i32_attr, FloatAttr f32_attr, ...); 389 390// Each result-type/operand/attribute has a separate parameter. The parameters 391// for attributes are raw values unwrapped with mlir::Attribute instances. 392// (Note that this builder will not always be generated. See the following 393// explanation for more details.) 394static void build(OpBuilder &odsBuilder, OperationState &odsState, 395 Type i32_result, Type f32_result, ..., 396 Value i32_operand, Value f32_operand, ..., 397 APInt i32_attr, StringRef f32_attr, ...); 398 399// Each operand/attribute has a separate parameter but result type is aggregate. 400static void build(OpBuilder &odsBuilder, OperationState &odsState, 401 ArrayRef<Type> resultTypes, 402 Value i32_operand, Value f32_operand, ..., 403 IntegerAttr i32_attr, FloatAttr f32_attr, ...); 404 405// All operands/attributes have aggregate parameters. 406// Generated if return type can be inferred. 407static void build(OpBuilder &odsBuilder, OperationState &odsState, 408 ValueRange operands, ArrayRef<NamedAttribute> attributes); 409 410// (And manually specified builders depending on the specific op.) 411``` 412 413The first form provides basic uniformity so that we can create ops using the 414same form regardless of the exact op. This is particularly useful for 415implementing declarative pattern rewrites. 416 417The second and third forms are good for use in manually written code given that 418they provide better guarantee via signatures. 419 420The third form will be generated if any of the op's attribute has different 421`Attr.returnType` from `Attr.storageType` and we know how to build an attribute 422from an unwrapped value (i.e., `Attr.constBuilderCall` is defined.) 423Additionally, for the third form, if an attribute appearing later in the 424`arguments` list has a default value, the default value will be supplied in the 425declaration. This works for `BoolAttr`, `StrAttr`, `EnumAttr` for now and the 426list can grow in the future. So if possible, default valued attribute should be 427placed at the end of the `arguments` list to leverage this feature. (This 428behavior is essentially due to C++ function parameter default value placement 429restrictions.) Otherwise, the builder of the third form will still be generated 430but default values for the attributes not at the end of the `arguments` list 431will not be supplied in the builder's signature. 432 433ODS will generate a builder that doesn't require return type specified if 434 435* Op implements InferTypeOpInterface interface; 436* All return types are either buildable types or are the same as a given 437 operand (e.g., `AllTypesMatch` constraint between operand and result); 438 439And there may potentially exist other builders depending on the specific op; 440please refer to the 441[generated C++ file](#run-mlir-tblgen-to-see-the-generated-content) for the 442complete list. 443 444#### Custom builder methods 445 446However, if the above cases cannot satisfy all needs, you can define additional 447convenience build methods in the `builders` field as follows. 448 449```tablegen 450def MyOp : Op<"my_op", []> { 451 let arguments = (ins F32Attr:$attr); 452 453 let builders = [ 454 OpBuilder<(ins "float":$val)> 455 ]; 456} 457``` 458 459The `builders` field is a list of custom builders that are added to the Op 460class. In this example, we provide a convenience builder that takes a floating 461point value instead of an attribute. The `ins` prefix is common to many function 462declarations in ODS, which use a TableGen [`dag`](#tablegen-syntax). What 463follows is a comma-separated list of types (quoted string) and names prefixed 464with the `$` sign. This will generate the declaration of a builder method that 465looks like: 466 467```c++ 468class MyOp : /*...*/ { 469 /*...*/ 470 static void build(::mlir::OpBuilder &builder, ::mlir::OperationState &state, 471 float val); 472}; 473``` 474 475Note that the method has two additional leading arguments. These arguments are 476useful to construct the operation. In particular, the method must populate 477`state` with attributes, operands, regions and result types of the operation to 478be constructed. `builder` can be used to construct any IR objects that belong to 479the Op, such as types or nested operations. Since the type and name are 480generated as is in the C++ code, they should be valid C++ constructs for a type 481(in the namespace of the Op) and an identifier (e.g., `class` is not a valid 482identifier). 483 484Implementations of the builder can be provided directly in ODS, using TableGen 485code block as follows. 486 487```tablegen 488def MyOp : Op<"my_op", []> { 489 let arguments = (ins F32Attr:$attr); 490 491 let builders = [ 492 OpBuilder<(ins "float":$val), [{ 493 $_state.addAttribute("attr", $_builder.getF32FloatAttr(val)); 494 }]> 495 ]; 496} 497``` 498 499The equivalents of `builder` and `state` arguments are available as `$_builder` 500and `$_state` special variables. The named arguments listed in the `ins` part 501are available directly, e.g. `val`. The body of the builder will be generated by 502substituting special variables and should otherwise be valid C++. While there is 503no limitation on the code size, we encourage one to define only short builders 504inline in ODS and put definitions of longer builders in C++ files. 505 506Finally, if some arguments need a default value, they can be defined using 507`CArg` to wrap the type and this value as follows. 508 509```tablegen 510def MyOp : Op<"my_op", []> { 511 let arguments = (ins F32Attr:$attr); 512 513 let builders = [ 514 OpBuilder<(ins CArg<"float", "0.5f">:$val), [{ 515 $_state.addAttribute("attr", $_builder.getF32FloatAttr(val)); 516 }]> 517 ]; 518} 519``` 520 521The generated code will use default value in the declaration, but not in the 522definition, as required by C++. 523 524```c++ 525/// Header file. 526class MyOp : /*...*/ { 527 /*...*/ 528 static void build(::mlir::OpBuilder &builder, ::mlir::OperationState &state, 529 float val = 0.5f); 530}; 531 532/// Source file. 533MyOp::build(::mlir::OpBuilder &builder, ::mlir::OperationState &state, 534 float val) { 535 state.addAttribute("attr", builder.getF32FloatAttr(val)); 536} 537``` 538 539**Deprecated:** `OpBuilder` class allows one to specify the custom builder 540signature as a raw string, without separating parameters into different `dag` 541arguments. It also supports leading parameters of `OpBuilder &` and 542`OperationState &` types, which will be used instead of the autogenerated ones 543if present. 544 545### Custom parser and printer methods 546 547Functions to parse and print the operation's custom assembly form. 548 549### Custom verifier code 550 551Verification code will be automatically generated for 552[constraints](#constraints) specified on various entities of the op. To perform 553_additional_ verification, you can use 554 555```tablegen 556let verifier = [{ 557 ... 558}]; 559``` 560 561Code placed in `verifier` will be called after the auto-generated verification 562code. The order of trait verification excluding those of `verifier` should not 563be relied upon. 564 565### Declarative Assembly Format 566 567The custom assembly form of the operation may be specified in a declarative 568string that matches the operations operands, attributes, etc. With the ability 569to express additional information that needs to be parsed to build the 570operation: 571 572```tablegen 573def CallOp : Std_Op<"call", ...> { 574 let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$args); 575 let results = (outs Variadic<AnyType>); 576 577 let assemblyFormat = [{ 578 $callee `(` $args `)` attr-dict `:` functional-type($args, results) 579 }]; 580} 581``` 582 583The format is comprised of three components: 584 585#### Directives 586 587A directive is a type of builtin function, with an optional set of arguments. 588The available directives are as follows: 589 590* `attr-dict` 591 592 - Represents the attribute dictionary of the operation. 593 594* `attr-dict-with-keyword` 595 596 - Represents the attribute dictionary of the operation, but prefixes the 597 dictionary with an `attributes` keyword. 598 599* `custom` < UserDirective > ( Params ) 600 601 - Represents a custom directive implemented by the user in C++. 602 - See the [Custom Directives](#custom-directives) section below for more 603 details. 604 605* `functional-type` ( inputs , results ) 606 607 - Formats the `inputs` and `results` arguments as a 608 [function type](LangRef.md#function-type). 609 - The constraints on `inputs` and `results` are the same as the `input` of 610 the `type` directive. 611 612* `operands` 613 614 - Represents all of the operands of an operation. 615 616* `ref` ( input ) 617 618 - Represents a reference to the a variable or directive, that must have 619 already been resolved, that may be used as a parameter to a `custom` 620 directive. 621 - Used to pass previously parsed entities to custom directives. 622 - The input may be any directive or variable, aside from `functional-type` 623 and `custom`. 624 625* `regions` 626 627 - Represents all of the regions of an operation. 628 629* `results` 630 631 - Represents all of the results of an operation. 632 633* `successors` 634 635 - Represents all of the successors of an operation. 636 637* `type` ( input ) 638 639 - Represents the type of the given input. 640 - `input` must be either an operand or result [variable](#variables), the 641 `operands` directive, or the `results` directive. 642 643#### Literals 644 645A literal is either a keyword or punctuation surrounded by \`\`. 646 647The following are the set of valid punctuation: 648 649`:`, `,`, `=`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, `->`, `?`, `+`, `*` 650 651The following are valid whitespace punctuation: 652 653`\n`, ` ` 654 655The `\n` literal emits a newline an indents to the start of the operation. An 656example is shown below: 657 658```tablegen 659let assemblyFormat = [{ 660 `{` `\n` ` ` ` ` `this_is_on_a_newline` `\n` `}` attr-dict 661}]; 662``` 663 664```mlir 665%results = my.operation { 666 this_is_on_a_newline 667} 668``` 669 670An empty literal \`\` may be used to remove a space that is inserted implicitly 671after certain literal elements, such as `)`/`]`/etc. For example, "`]`" may 672result in an output of `]` it is not the last element in the format. "`]` \`\`" 673would trim the trailing space in this situation. 674 675#### Variables 676 677A variable is an entity that has been registered on the operation itself, i.e. 678an argument(attribute or operand), region, result, successor, etc. In the 679`CallOp` example above, the variables would be `$callee` and `$args`. 680 681Attribute variables are printed with their respective value type, unless that 682value type is buildable. In those cases, the type of the attribute is elided. 683 684#### Custom Directives 685 686The declarative assembly format specification allows for handling a large 687majority of the common cases when formatting an operation. For the operations 688that require or desire specifying parts of the operation in a form not supported 689by the declarative syntax, custom directives may be specified. A custom 690directive essentially allows for users to use C++ for printing and parsing 691subsections of an otherwise declaratively specified format. Looking at the 692specification of a custom directive above: 693 694``` 695custom-directive ::= `custom` `<` UserDirective `>` `(` Params `)` 696``` 697 698A custom directive has two main parts: The `UserDirective` and the `Params`. A 699custom directive is transformed into a call to a `print*` and a `parse*` method 700when generating the C++ code for the format. The `UserDirective` is an 701identifier used as a suffix to these two calls, i.e., `custom<MyDirective>(...)` 702would result in calls to `parseMyDirective` and `printMyDirective` within the 703parser and printer respectively. `Params` may be any combination of variables 704(i.e. Attribute, Operand, Successor, etc.), type directives, and `attr-dict`. 705The type directives must refer to a variable, but that variable need not also be 706a parameter to the custom directive. 707 708The arguments to the `parse<UserDirective>` method are firstly a reference to 709the `OpAsmParser`(`OpAsmParser &`), and secondly a set of output parameters 710corresponding to the parameters specified in the format. The mapping of 711declarative parameter to `parse` method argument is detailed below: 712 713* Attribute Variables 714 - Single: `<Attribute-Storage-Type>(e.g. Attribute) &` 715 - Optional: `<Attribute-Storage-Type>(e.g. Attribute) &` 716* Operand Variables 717 - Single: `OpAsmParser::OperandType &` 718 - Optional: `Optional<OpAsmParser::OperandType> &` 719 - Variadic: `SmallVectorImpl<OpAsmParser::OperandType> &` 720* Ref Directives 721 - A reference directive is passed to the parser using the same mapping as 722 the input operand. For example, a single region would be passed as a 723 `Region &`. 724* Region Variables 725 - Single: `Region &` 726 - Variadic: `SmallVectorImpl<std::unique_ptr<Region>> &` 727* Successor Variables 728 - Single: `Block *&` 729 - Variadic: `SmallVectorImpl<Block *> &` 730* Type Directives 731 - Single: `Type &` 732 - Optional: `Type &` 733 - Variadic: `SmallVectorImpl<Type> &` 734* `attr-dict` Directive: `NamedAttrList &` 735 736When a variable is optional, the value should only be specified if the variable 737is present. Otherwise, the value should remain `None` or null. 738 739The arguments to the `print<UserDirective>` method is firstly a reference to the 740`OpAsmPrinter`(`OpAsmPrinter &`), second the op (e.g. `FooOp op` which can be 741`Operation *op` alternatively), and finally a set of output parameters 742corresponding to the parameters specified in the format. The mapping of 743declarative parameter to `print` method argument is detailed below: 744 745* Attribute Variables 746 - Single: `<Attribute-Storage-Type>(e.g. Attribute)` 747 - Optional: `<Attribute-Storage-Type>(e.g. Attribute)` 748* Operand Variables 749 - Single: `Value` 750 - Optional: `Value` 751 - Variadic: `OperandRange` 752* Ref Directives 753 - A reference directive is passed to the printer using the same mapping as 754 the input operand. For example, a single region would be passed as a 755 `Region &`. 756* Region Variables 757 - Single: `Region &` 758 - Variadic: `MutableArrayRef<Region>` 759* Successor Variables 760 - Single: `Block *` 761 - Variadic: `SuccessorRange` 762* Type Directives 763 - Single: `Type` 764 - Optional: `Type` 765 - Variadic: `TypeRange` 766* `attr-dict` Directive: `DictionaryAttr` 767 768When a variable is optional, the provided value may be null. 769 770#### Optional Groups 771 772In certain situations operations may have "optional" information, e.g. 773attributes or an empty set of variadic operands. In these situations a section 774of the assembly format can be marked as `optional` based on the presence of this 775information. An optional group is defined as follows: 776 777``` 778optional-group: `(` elements `)` (`:` `(` else-elements `)`)? `?` 779``` 780 781The `elements` of an optional group have the following requirements: 782 783* The first element of the group must either be a attribute, literal, operand, 784 or region. 785 - This is because the first element must be optionally parsable. 786* Exactly one argument variable or type directive within the group must be 787 marked as the anchor of the group. 788 - The anchor is the element whose presence controls whether the group 789 should be printed/parsed. 790 - An element is marked as the anchor by adding a trailing `^`. 791 - The first element is *not* required to be the anchor of the group. 792 - When a non-variadic region anchors a group, the detector for printing 793 the group is if the region is empty. 794* Literals, variables, custom directives, and type directives are the only 795 valid elements within the group. 796 - Any attribute variable may be used, but only optional attributes can be 797 marked as the anchor. 798 - Only variadic or optional results and operand arguments and can be used. 799 - All region variables can be used. When a non-variable length region is 800 used, if the group is not present the region is empty. 801 802An example of an operation with an optional group is `std.return`, which has a 803variadic number of operands. 804 805```tablegen 806def ReturnOp : ... { 807 let arguments = (ins Variadic<AnyType>:$operands); 808 809 // We only print the operands and types if there are a non-zero number 810 // of operands. 811 let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; 812} 813``` 814 815##### Unit Attributes 816 817In MLIR, the [`unit` Attribute](LangRef.md#unit-attribute) is special in that it 818only has one possible value, i.e. it derives meaning from its existence. When a 819unit attribute is used to anchor an optional group and is not the first element 820of the group, the presence of the unit attribute can be directly correlated with 821the presence of the optional group itself. As such, in these situations the unit 822attribute will not be printed or present in the output and will be automatically 823inferred when parsing by the presence of the optional group itself. 824 825For example, the following operation: 826 827```tablegen 828def FooOp : ... { 829 let arguments = (ins UnitAttr:$is_read_only); 830 831 let assemblyFormat = "attr-dict (`is_read_only` $is_read_only^)?"; 832} 833``` 834 835would be formatted as such: 836 837```mlir 838// When the unit attribute is present: 839foo.op is_read_only 840 841// When the unit attribute is not present: 842foo.op 843``` 844 845##### Optional "else" Group 846 847Optional groups also have support for an "else" group of elements. These are 848elements that are parsed/printed if the `anchor` element of the optional group 849is *not* present. Unlike the main element group, the "else" group has no 850restriction on the first element and none of the elements may act as the 851`anchor` for the optional. An example is shown below: 852 853```tablegen 854def FooOp : ... { 855 let arguments = (ins UnitAttr:$foo); 856 857 let assemblyFormat = "attr-dict (`foo_is_present` $foo^):(`foo_is_absent`)?"; 858} 859``` 860 861would be formatted as such: 862 863```mlir 864// When the `foo` attribute is present: 865foo.op foo_is_present 866 867// When the `foo` attribute is not present: 868foo.op foo_is_absent 869``` 870 871#### Requirements 872 873The format specification has a certain set of requirements that must be adhered 874to: 875 8761. The output and operation name are never shown as they are fixed and cannot 877 be altered. 8781. All operands within the operation must appear within the format, either 879 individually or with the `operands` directive. 8801. All regions within the operation must appear within the format, either 881 individually or with the `regions` directive. 8821. All successors within the operation must appear within the format, either 883 individually or with the `successors` directive. 8841. All operand and result types must appear within the format using the various 885 `type` directives, either individually or with the `operands` or `results` 886 directives. 8871. The `attr-dict` directive must always be present. 8881. Must not contain overlapping information; e.g. multiple instances of 889 'attr-dict', types, operands, etc. 890 - Note that `attr-dict` does not overlap with individual attributes. These 891 attributes will simply be elided when printing the attribute dictionary. 892 893##### Type Inference 894 895One requirement of the format is that the types of operands and results must 896always be present. In certain instances, the type of a variable may be deduced 897via type constraints or other information available. In these cases, the type of 898that variable may be elided from the format. 899 900* Buildable Types 901 902Some type constraints may only have one representation, allowing for them to be 903directly buildable; for example the `I32` or `Index` types. Types in `ODS` may 904mark themselves as buildable by setting the `builderCall` field or inheriting 905from the `BuildableType` class. 906 907* Trait Equality Constraints 908 909There are many operations that have known type equality constraints registered 910as traits on the operation; for example the true, false, and result values of a 911`select` operation often have the same type. The assembly format may inspect 912these equal constraints to discern the types of missing variables. The currently 913supported traits are: `AllTypesMatch`, `TypesMatchWith`, `SameTypeOperands`, and 914`SameOperandsAndResultType`. 915 916### `hasCanonicalizer` 917 918This boolean field indicate whether canonicalization patterns have been defined 919for this operation. If it is `1`, then `::getCanonicalizationPatterns()` should 920be defined. 921 922### `hasCanonicalizeMethod` 923 924When this boolean field is set to `true`, it indicates that the op implements a 925`canonicalize` method for simple "matchAndRewrite" style canonicalization 926patterns. If `hasCanonicalizer` is 0, then an implementation of 927`::getCanonicalizationPatterns()` is implemented to call this function. 928 929### `hasFolder` 930 931This boolean field indicate whether general folding rules have been defined for 932this operation. If it is `1`, then `::fold()` should be defined. 933 934### Extra declarations 935 936One of the goals of table-driven op definition is to auto-generate as much logic 937and methods needed for each op as possible. With that said, there will always be 938long-tail cases that won't be covered. For such cases, you can use 939`extraClassDeclaration`. Code in `extraClassDeclaration` will be copied 940literally to the generated C++ op class. 941 942Note that `extraClassDeclaration` is a mechanism intended for long-tail cases by 943power users; for not-yet-implemented widely-applicable cases, improving the 944infrastructure is preferable. 945 946### Generated C++ code 947 948[OpDefinitionsGen][OpDefinitionsGen] processes the op definition spec file and 949generates two files containing the corresponding C++ code: one for declarations, 950the other for definitions. The former is generated via the `-gen-op-decls` 951command-line option, while the latter is via the `-gen-op-defs` option. 952 953The definition file contains all the op method definitions, which can be 954included and enabled by defining `GET_OP_CLASSES`. For each operation, 955OpDefinitionsGen generates an operation class and an 956[operand adaptor](#operand-adaptors) class. Besides, it also contains a 957comma-separated list of all defined ops, which can be included and enabled by 958defining `GET_OP_LIST`. 959 960#### Class name and namespaces 961 962For each operation, its generated C++ class name is the symbol `def`ed with 963TableGen with dialect prefix removed. The first `_` serves as the delimiter. For 964example, for `def TF_AddOp`, the C++ class name would be `AddOp`. We remove the 965`TF` prefix because it is for scoping ops; other dialects may as well define 966their own `AddOp`s. 967 968The namespaces of the generated C++ class will come from the dialect's 969`cppNamespace` field. For example, if a dialect's `cppNamespace` is `A::B`, then 970an op of that dialect will be placed in `namespace A { namespace B { ... } }`. 971If a dialect does not specify a `cppNamespace`, we then use the dialect's name 972as the namespace. 973 974This means the qualified name of the generated C++ class does not necessarily 975match exactly with the operation name as explained in 976[Operation name](#operation-name). This is to allow flexible naming to satisfy 977coding style requirements. 978 979#### Operand adaptors 980 981For each operation, we automatically generate an _operand adaptor_. This class 982solves the problem of accessing operands provided as a list of `Value`s without 983using "magic" constants. The operand adaptor takes a reference to an array of 984`Value` and provides methods with the same names as those in the operation class 985to access them. For example, for a binary arithmetic operation, it may provide 986`.lhs()` to access the first operand and `.rhs()` to access the second operand. 987 988The operand adaptor class lives in the same namespace as the operation class, 989and has the name of the operation followed by `Adaptor` as well as an alias 990`Adaptor` inside the op class. 991 992Operand adaptors can be used in function templates that also process operations: 993 994```c++ 995template <typename BinaryOpTy> 996std::pair<Value, Value> zip(BinaryOpTy &&op) { 997 return std::make_pair(op.lhs(), op.rhs());; 998} 999 1000void process(AddOp op, ArrayRef<Value> newOperands) { 1001 zip(op); 1002 zip(Adaptor<AddOp>(newOperands)); 1003 /*...*/ 1004} 1005``` 1006 1007## Constraints 1008 1009Constraint is a core concept in table-driven operation definition: operation 1010verification and graph operation matching are all based on satisfying 1011constraints. So both the operation definition and rewrite rules specification 1012significantly involve writing constraints. We have the `Constraint` class in 1013[`OpBase.td`][OpBase] has the common base class for all constraints. 1014 1015An operation's constraint can cover different range; it may 1016 1017* Only concern a single attribute (e.g. being a 32-bit integer greater than 1018 5), 1019* Multiple operands and results (e.g., the 1st result's shape must be the same 1020 as the 1st operand), or 1021* Intrinsic to the operation itself (e.g., having no side effect). 1022 1023We call them as single-entity constraint, multi-entity constraint, and traits, 1024respectively. 1025 1026### Single-entity constraint 1027 1028Constraints scoped to a single operand, attribute, or result are specified at 1029the entity's declaration place as described in 1030[Operation arguments](#operation-arguments) and 1031[Operation results](#operation-results). 1032 1033To help modelling constraints of common types, a set of `TypeConstraint`s are 1034created; they are the `Type` subclass hierarchy. It includes `F32` for the 1035constraints of being a float, `TensorOf<[F32]>` for the constraints of being a 1036float tensor, and so on. 1037 1038Similarly, a set of `AttrConstraint`s are created for helping modelling 1039constraints of common attribute kinds. They are the `Attr` subclass hierarchy. 1040It includes `F32Attr` for the constraints of being a float attribute, 1041`F32ArrayAttr` for the constraints of being a float array attribute, and so on. 1042 1043### Multi-entity constraint 1044 1045Constraints involving more than one operand/attribute/result are quite common on 1046operations, like the element type and shape relation between operands and 1047results. These constraints should be specified as the `Op` class template 1048parameter as described in 1049[Operation traits and constraints](#operation-traits-and-constraints). 1050 1051Multi-entity constraints are modeled as `PredOpTrait` (a subclass of `OpTrait`) 1052in [`OpBase.td`][OpBase].A bunch of constraint primitives are provided to help 1053specification. See [`OpBase.td`][OpBase] for the complete list. 1054 1055### Trait 1056 1057Traits are intrinsic properties of the operation like having side effect or not, 1058commutative or not, whether is a terminator, etc. These constraints should be 1059specified as the `Op` class template parameter as described in 1060[Operation traits and constraints](#operation-traits-and-constraints). 1061 1062Traits are modeled as `NativeOpTrait` (a subclass of `OpTrait`) in 1063[`OpBase.td`][OpBase]. They are backed and will be translated into the 1064corresponding C++ `mlir::OpTrait` classes. 1065 1066### How to specify new constraint 1067 1068To write a constraint, you need to provide its predicates and give it a 1069descriptive name. Predicates, modeled with the `Pred` class, are the workhorse 1070for composing constraints. The predicate for a constraint is typically built up 1071in a nested manner, using the two categories of predicates: 1072 10731. `CPred`: the primitive leaf predicate. 10742. Compound predicate: a predicate composed from child predicates using 1075 predicate combiners (conjunction: `And`, disjunction: `Or`, negation: `Neg`, 1076 substitution: `SubstLeaves`, concatenation: `Concat`). 1077 1078`CPred` is the basis for composing more complex predicates. It is the "atom" 1079predicate from the perspective of TableGen and the "interface" between TableGen 1080and C++. What is inside is already C++ code, which will be treated as opaque 1081strings with special placeholders to be substituted. 1082 1083You can put any C++ code that returns a boolean value inside a `CPred`, 1084including evaluating expressions, calling functions, calling class methods, and 1085so on. 1086 1087To help interaction with the C++ environment, there are a few special 1088placeholders provided to refer to entities in the context where this predicate 1089is used. They serve as "hooks" to the enclosing environment. This includes 1090`$_builder`, `$_op`, and `$_self`: 1091 1092* `$_builder` will be replaced by a `mlir::Builder` instance so that you can 1093 access common build methods. 1094* `$_op` will be replaced by the current operation so that you can access 1095 information of the current operation. 1096* `$_self` will be replaced with the entity this predicate is attached to. 1097 E.g., `BoolAttr` is an attribute constraint that wraps a 1098 `CPred<"$_self.isa<BoolAttr>()">`. Then for `F32:$attr`,`$_self` will be 1099 replaced by `$attr`. For type constraints, it's a little bit special since 1100 we want the constraints on each type definition reads naturally and we want 1101 to attach type constraints directly to an operand/result, `$_self` will be 1102 replaced by the operand/result's type. E.g., for `F32` in `F32:$operand`, 1103 its `$_self` will be expanded as `getOperand(...).getType()`. 1104 1105TODO: Reconsider the leading symbol for special placeholders. Eventually we want 1106to allow referencing operand/result $-names; such $-names can start with 1107underscore. 1108 1109For example, to write an attribute `attr` is an `IntegerAttr`, in C++ you can 1110just call `attr.isa<IntegerAttr>()`. The code can be wrapped in a `CPred` as 1111`$_self.isa<IntegerAttr>()`, with `$_self` as the special placeholder to be 1112replaced by the current attribute `attr` at expansion time. 1113 1114For more complicated predicates, you can wrap it in a single `CPred`, or you can 1115use predicate combiners to combine them. For example, to write the constraint 1116that an attribute `attr` is a 32-bit or 64-bit integer, you can write it as 1117 1118```tablegen 1119And<[ 1120 CPred<"$_self.isa<IntegerAttr>()">, 1121 Or<[ 1122 CPred<"$_self.cast<IntegerAttr>().getType().isInteger(32)">, 1123 CPred<"$_self.cast<IntegerAttr>().getType().isInteger(64)"> 1124 ]> 1125]> 1126``` 1127 1128(Note that the above is just to show with a familiar example how you can use 1129`CPred` and predicate combiners to write complicated predicates. For integer 1130attributes specifically, [`OpBase.td`][OpBase] already defines `I32Attr` and 1131`I64Attr`. So you can actually reuse them to write it as `Or<[I32Attr.predicate, 1132I64Attr.predicate]>`.) 1133 1134TODO: Build up a library of reusable primitive constraints 1135 1136If the predicate is very complex to write with `CPred` together with predicate 1137combiners, you can also write it as a normal C++ function and use the `CPred` as 1138a way to "invoke" the function. For example, to verify an attribute `attr` has 1139some property, you can write a C++ function like 1140 1141```cpp 1142bool HasSomeProperty(Attribute attr) { ... } 1143``` 1144 1145and then define the op as: 1146 1147```tablegen 1148def HasSomeProperty : AttrConstraint<CPred<"HasSomeProperty($_self)">, 1149 "has some property">; 1150 1151def MyOp : Op<...> { 1152 let arguments = (ins 1153 ... 1154 HasSomeProperty:$attr 1155 ); 1156} 1157``` 1158 1159As to whether we should define the predicate using a single `CPred` wrapping the 1160whole expression, multiple `CPred`s with predicate combiners, or a single 1161`CPred` "invoking" a function, there are no clear-cut criteria. Defining using 1162`CPred` and predicate combiners is preferable since it exposes more information 1163(instead hiding all the logic behind a C++ function) into the op definition spec 1164so that it can potentially drive more auto-generation cases. But it will require 1165a nice library of common predicates as the building blocks to avoid the 1166duplication, which is being worked on right now. 1167 1168## Attribute Definition 1169 1170An attribute is a compile-time known constant of an operation. 1171 1172ODS provides attribute wrappers over C++ attribute classes. There are a few 1173common C++ [attribute classes][AttrClasses] defined in MLIR's core IR library 1174and one is free to define dialect-specific attribute classes. ODS allows one to 1175use these attributes in TableGen to define operations, potentially with more 1176fine-grained constraints. For example, `StrAttr` directly maps to `StringAttr`; 1177`F32Attr`/`F64Attr` requires the `FloatAttr` to additionally be of a certain 1178bitwidth. 1179 1180ODS attributes are defined as having a storage type (corresponding to a backing 1181`mlir::Attribute` that _stores_ the attribute), a return type (corresponding to 1182the C++ _return_ type of the generated of the helper getters) as well as method 1183to convert between the internal storage and the helper method. 1184 1185### Attribute decorators 1186 1187There are a few important attribute adapters/decorators/modifiers that can be 1188applied to ODS attributes to specify common additional properties like 1189optionality, default values, etc.: 1190 1191* `DefaultValuedAttr`: specifies the 1192 [default value](#attributes-with-default-values) for an attribute. 1193* `OptionalAttr`: specifies an attribute as [optional](#optional-attributes). 1194* `Confined`: adapts an attribute with 1195 [further constraints](#confining-attributes). 1196 1197### Enum attributes 1198 1199Some attributes can only take values from a predefined enum, e.g., the 1200comparison kind of a comparison op. To define such attributes, ODS provides 1201several mechanisms: `StrEnumAttr`, `IntEnumAttr`, and `BitEnumAttr`. 1202 1203* `StrEnumAttr`: each enum case is a string, the attribute is stored as a 1204 [`StringAttr`][StringAttr] in the op. 1205* `IntEnumAttr`: each enum case is an integer, the attribute is stored as a 1206 [`IntegerAttr`][IntegerAttr] in the op. 1207* `BitEnumAttr`: each enum case is a bit, the attribute is stored as a 1208 [`IntegerAttr`][IntegerAttr] in the op. 1209 1210All these `*EnumAttr` attributes require fully specifying all of the allowed 1211cases via their corresponding `*EnumAttrCase`. With this, ODS is able to 1212generate additional verification to only accept allowed cases. To facilitate the 1213interaction between `*EnumAttr`s and their C++ consumers, the 1214[`EnumsGen`][EnumsGen] TableGen backend can generate a few common utilities: a 1215C++ enum class, `llvm::DenseMapInfo` for the enum class, conversion functions 1216from/to strings. This is controlled via the `-gen-enum-decls` and 1217`-gen-enum-defs` command-line options of `mlir-tblgen`. 1218 1219For example, given the following `EnumAttr`: 1220 1221```tablegen 1222def Case15: I32EnumAttrCase<"Case15", 15>; 1223def Case20: I32EnumAttrCase<"Case20", 20>; 1224 1225def MyIntEnum: I32EnumAttr<"MyIntEnum", "An example int enum", 1226 [Case15, Case20]> { 1227 let cppNamespace = "Outer::Inner"; 1228 let stringToSymbolFnName = "ConvertToEnum"; 1229 let symbolToStringFnName = "ConvertToString"; 1230} 1231``` 1232 1233The following will be generated via `mlir-tblgen -gen-enum-decls`: 1234 1235```c++ 1236namespace Outer { 1237namespace Inner { 1238// An example int enum 1239enum class MyIntEnum : uint32_t { 1240 Case15 = 15, 1241 Case20 = 20, 1242}; 1243 1244llvm::Optional<MyIntEnum> symbolizeMyIntEnum(uint32_t); 1245llvm::StringRef ConvertToString(MyIntEnum); 1246llvm::Optional<MyIntEnum> ConvertToEnum(llvm::StringRef); 1247inline constexpr unsigned getMaxEnumValForMyIntEnum() { 1248 return 20; 1249} 1250 1251} // namespace Inner 1252} // namespace Outer 1253 1254namespace llvm { 1255template<> struct DenseMapInfo<Outer::Inner::MyIntEnum> { 1256 using StorageInfo = llvm::DenseMapInfo<uint32_t>; 1257 1258 static inline Outer::Inner::MyIntEnum getEmptyKey() { 1259 return static_cast<Outer::Inner::MyIntEnum>(StorageInfo::getEmptyKey()); 1260 } 1261 1262 static inline Outer::Inner::MyIntEnum getTombstoneKey() { 1263 return static_cast<Outer::Inner::MyIntEnum>(StorageInfo::getTombstoneKey()); 1264 } 1265 1266 static unsigned getHashValue(const Outer::Inner::MyIntEnum &val) { 1267 return StorageInfo::getHashValue(static_cast<uint32_t>(val)); 1268 } 1269 1270 static bool isEqual(const Outer::Inner::MyIntEnum &lhs, const Outer::Inner::MyIntEnum &rhs) { 1271 return lhs == rhs; 1272 } 1273}; 1274} 1275``` 1276 1277The following will be generated via `mlir-tblgen -gen-enum-defs`: 1278 1279```c++ 1280namespace Outer { 1281namespace Inner { 1282llvm::StringRef ConvertToString(MyIntEnum val) { 1283 switch (val) { 1284 case MyIntEnum::Case15: return "Case15"; 1285 case MyIntEnum::Case20: return "Case20"; 1286 } 1287 return ""; 1288} 1289 1290llvm::Optional<MyIntEnum> ConvertToEnum(llvm::StringRef str) { 1291 return llvm::StringSwitch<llvm::Optional<MyIntEnum>>(str) 1292 .Case("Case15", MyIntEnum::Case15) 1293 .Case("Case20", MyIntEnum::Case20) 1294 .Default(llvm::None); 1295} 1296llvm::Optional<MyIntEnum> symbolizeMyIntEnum(uint32_t value) { 1297 switch (value) { 1298 case 15: return MyIntEnum::Case15; 1299 case 20: return MyIntEnum::Case20; 1300 default: return llvm::None; 1301 } 1302} 1303 1304} // namespace Inner 1305} // namespace Outer 1306``` 1307 1308Similarly for the following `BitEnumAttr` definition: 1309 1310```tablegen 1311def None: BitEnumAttrCase<"None", 0x0000>; 1312def Bit1: BitEnumAttrCase<"Bit1", 0x0001>; 1313def Bit2: BitEnumAttrCase<"Bit2", 0x0002>; 1314def Bit3: BitEnumAttrCase<"Bit3", 0x0004>; 1315 1316def MyBitEnum: BitEnumAttr<"MyBitEnum", "An example bit enum", 1317 [None, Bit1, Bit2, Bit3]>; 1318``` 1319 1320We can have: 1321 1322```c++ 1323// An example bit enum 1324enum class MyBitEnum : uint32_t { 1325 None = 0, 1326 Bit1 = 1, 1327 Bit2 = 2, 1328 Bit3 = 4, 1329}; 1330 1331llvm::Optional<MyBitEnum> symbolizeMyBitEnum(uint32_t); 1332std::string stringifyMyBitEnum(MyBitEnum); 1333llvm::Optional<MyBitEnum> symbolizeMyBitEnum(llvm::StringRef); 1334inline MyBitEnum operator|(MyBitEnum lhs, MyBitEnum rhs) { 1335 return static_cast<MyBitEnum>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs)); 1336} 1337inline MyBitEnum operator&(MyBitEnum lhs, MyBitEnum rhs) { 1338 return static_cast<MyBitEnum>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)); 1339} 1340inline bool bitEnumContains(MyBitEnum bits, MyBitEnum bit) { 1341 return (static_cast<uint32_t>(bits) & static_cast<uint32_t>(bit)) != 0; 1342} 1343 1344namespace llvm { 1345template<> struct DenseMapInfo<::MyBitEnum> { 1346 using StorageInfo = llvm::DenseMapInfo<uint32_t>; 1347 1348 static inline ::MyBitEnum getEmptyKey() { 1349 return static_cast<::MyBitEnum>(StorageInfo::getEmptyKey()); 1350 } 1351 1352 static inline ::MyBitEnum getTombstoneKey() { 1353 return static_cast<::MyBitEnum>(StorageInfo::getTombstoneKey()); 1354 } 1355 1356 static unsigned getHashValue(const ::MyBitEnum &val) { 1357 return StorageInfo::getHashValue(static_cast<uint32_t>(val)); 1358 } 1359 1360 static bool isEqual(const ::MyBitEnum &lhs, const ::MyBitEnum &rhs) { 1361 return lhs == rhs; 1362 } 1363}; 1364``` 1365 1366```c++ 1367std::string stringifyMyBitEnum(MyBitEnum symbol) { 1368 auto val = static_cast<uint32_t>(symbol); 1369 // Special case for all bits unset. 1370 if (val == 0) return "None"; 1371 1372 llvm::SmallVector<llvm::StringRef, 2> strs; 1373 if (1u & val) { strs.push_back("Bit1"); val &= ~1u; } 1374 if (2u & val) { strs.push_back("Bit2"); val &= ~2u; } 1375 if (4u & val) { strs.push_back("Bit3"); val &= ~4u; } 1376 1377 if (val) return ""; 1378 return llvm::join(strs, "|"); 1379} 1380 1381llvm::Optional<MyBitEnum> symbolizeMyBitEnum(llvm::StringRef str) { 1382 // Special case for all bits unset. 1383 if (str == "None") return MyBitEnum::None; 1384 1385 llvm::SmallVector<llvm::StringRef, 2> symbols; 1386 str.split(symbols, "|"); 1387 1388 uint32_t val = 0; 1389 for (auto symbol : symbols) { 1390 auto bit = llvm::StringSwitch<llvm::Optional<uint32_t>>(symbol) 1391 .Case("Bit1", 1) 1392 .Case("Bit2", 2) 1393 .Case("Bit3", 4) 1394 .Default(llvm::None); 1395 if (bit) { val |= *bit; } else { return llvm::None; } 1396 } 1397 return static_cast<MyBitEnum>(val); 1398} 1399 1400llvm::Optional<MyBitEnum> symbolizeMyBitEnum(uint32_t value) { 1401 // Special case for all bits unset. 1402 if (value == 0) return MyBitEnum::None; 1403 1404 if (value & ~(1u | 2u | 4u)) return llvm::None; 1405 return static_cast<MyBitEnum>(value); 1406} 1407``` 1408 1409## Type Definitions 1410 1411MLIR defines the TypeDef class hierarchy to enable generation of data types from 1412their specifications. A type is defined by specializing the TypeDef class with 1413concrete contents for all the fields it requires. For example, an integer type 1414could be defined as: 1415 1416```tablegen 1417// All of the types will extend this class. 1418class Test_Type<string name> : TypeDef<Test_Dialect, name> { } 1419 1420// An alternate int type. 1421def IntegerType : Test_Type<"TestInteger"> { 1422 let mnemonic = "int"; 1423 1424 let summary = "An integer type with special semantics"; 1425 1426 let description = [{ 1427 An alternate integer type. This type differentiates itself from the 1428 standard integer type by not having a SignednessSemantics parameter, just 1429 a width. 1430 }]; 1431 1432 let parameters = (ins "unsigned":$width); 1433 1434 // We define the printer inline. 1435 let printer = [{ 1436 $_printer << "int<" << getImpl()->width << ">"; 1437 }]; 1438 1439 // The parser is defined here also. 1440 let parser = [{ 1441 if ($_parser.parseLess()) 1442 return Type(); 1443 int width; 1444 if ($_parser.parseInteger(width)) 1445 return Type(); 1446 if ($_parser.parseGreater()) 1447 return Type(); 1448 return get($_ctxt, width); 1449 }]; 1450} 1451``` 1452 1453### Type name 1454 1455The name of the C++ class which gets generated defaults to 1456`<classParamName>Type` (e.g. `TestIntegerType` in the above example). This can 1457be overridden via the `cppClassName` field. The field `mnemonic` is to specify 1458the asm name for parsing. It is optional and not specifying it will imply that 1459no parser or printer methods are attached to this class. 1460 1461### Type documentation 1462 1463The `summary` and `description` fields exist and are to be used the same way as 1464in Operations. Namely, the summary should be a one-liner and `description` 1465should be a longer explanation. 1466 1467### Type parameters 1468 1469The `parameters` field is a list of the types parameters. If no parameters are 1470specified (the default), this type is considered a singleton type. Parameters 1471are in the `"c++Type":$paramName` format. To use C++ types as parameters which 1472need allocation in the storage constructor, there are two options: 1473 1474- Set `hasCustomStorageConstructor` to generate the TypeStorage class with a 1475 constructor which is just declared -- no definition -- so you can write it 1476 yourself. 1477- Use the `TypeParameter` tablegen class instead of the "c++Type" string. 1478 1479### TypeParameter tablegen class 1480 1481This is used to further specify attributes about each of the types parameters. 1482It includes documentation (`summary` and `syntax`), the C++ type to use, a 1483custom allocator to use in the storage constructor method, and a custom 1484comparator to decide if two instances of the parameter type are equal. 1485 1486```tablegen 1487// DO NOT DO THIS! 1488let parameters = (ins "ArrayRef<int>":$dims); 1489``` 1490 1491The default storage constructor blindly copies fields by value. It does not know 1492anything about the types. In this case, the ArrayRef<int> requires allocation 1493with `dims = allocator.copyInto(dims)`. 1494 1495You can specify the necessary constructor by specializing the `TypeParameter` 1496tblgen class: 1497 1498```tablegen 1499class ArrayRefIntParam : 1500 TypeParameter<"::llvm::ArrayRef<int>", "Array of ints"> { 1501 let allocator = "$_dst = $_allocator.copyInto($_self);"; 1502} 1503 1504... 1505 1506let parameters = (ins ArrayRefIntParam:$dims); 1507``` 1508 1509The `allocator` code block has the following substitutions: 1510 1511- `$_allocator` is the TypeStorageAllocator in which to allocate objects. 1512- `$_dst` is the variable in which to place the allocated data. 1513 1514The `comparator` code block has the following substitutions: 1515 1516- `$_lhs` is an instance of the parameter type. 1517- `$_rhs` is an instance of the parameter type. 1518 1519MLIR includes several specialized classes for common situations: 1520 1521- `StringRefParameter<descriptionOfParam>` for StringRefs. 1522- `ArrayRefParameter<arrayOf, descriptionOfParam>` for ArrayRefs of value 1523 types 1524- `SelfAllocationParameter<descriptionOfParam>` for C++ classes which contain 1525 a method called `allocateInto(StorageAllocator &allocator)` to allocate 1526 itself into `allocator`. 1527- `ArrayRefOfSelfAllocationParameter<arrayOf, descriptionOfParam>` for arrays 1528 of objects which self-allocate as per the last specialization. 1529 1530If we were to use one of these included specializations: 1531 1532```tablegen 1533let parameters = (ins 1534 ArrayRefParameter<"int", "The dimensions">:$dims 1535); 1536``` 1537 1538### Parsing and printing 1539 1540If a mnemonic is specified, the `printer` and `parser` code fields are active. 1541The rules for both are: 1542 1543- If null, generate just the declaration. 1544- If non-null and non-empty, use the code in the definition. The `$_printer` 1545 or `$_parser` substitutions are valid and should be used. 1546- It is an error to have an empty code block. 1547 1548For each dialect, two "dispatch" functions will be created: one for parsing and 1549one for printing. You should add calls to these in your `Dialect::printType` and 1550`Dialect::parseType` methods. They are static functions placed alongside the 1551type class definitions and have the following function signatures: 1552 1553```c++ 1554static Type generatedTypeParser(MLIRContext* ctxt, DialectAsmParser& parser, StringRef mnemonic); 1555LogicalResult generatedTypePrinter(Type type, DialectAsmPrinter& printer); 1556``` 1557 1558The mnemonic, parser, and printer fields are optional. If they're not defined, 1559the generated code will not include any parsing or printing code and omit the 1560type from the dispatch functions above. In this case, the dialect author is 1561responsible for parsing/printing the types in `Dialect::printType` and 1562`Dialect::parseType`. 1563 1564### Other fields 1565 1566- If the `genStorageClass` field is set to 1 (the default) a storage class is 1567 generated with member variables corresponding to each of the specified 1568 `parameters`. 1569- If the `genAccessors` field is 1 (the default) accessor methods will be 1570 generated on the Type class (e.g. `int getWidth() const` in the example 1571 above). 1572- If the `genVerifyDecl` field is set, a declaration for a method `static 1573 LogicalResult verify(emitErrorFn, parameters...)` is added to the class as 1574 well as a `getChecked(emitErrorFn, parameters...)` method which checks the 1575 result of `verify` before calling `get`. 1576- The `storageClass` field can be used to set the name of the storage class. 1577- The `storageNamespace` field is used to set the namespace where the storage 1578 class should sit. Defaults to "detail". 1579- The `extraClassDeclaration` field is used to include extra code in the class 1580 declaration. 1581 1582### Type builder methods 1583 1584For each type, there are a few builders(`get`/`getChecked`) automatically 1585generated based on the parameters of the type. For example, given the following 1586type definition: 1587 1588```tablegen 1589def MyType : ... { 1590 let parameters = (ins "int":$intParam); 1591} 1592``` 1593 1594The following builders are generated: 1595 1596```c++ 1597// Type builders are named `get`, and return a new instance of a type for a 1598// given set of parameters. 1599static MyType get(MLIRContext *context, int intParam); 1600 1601// If `genVerifyDecl` is set to 1, the following method is also generated. 1602static MyType getChecked(function_ref<InFlightDiagnostic()> emitError, 1603 MLIRContext *context, int intParam); 1604``` 1605 1606If these autogenerated methods are not desired, such as when they conflict with 1607a custom builder method, a type can set `skipDefaultBuilders` to 1 to signal 1608that they should not be generated. 1609 1610#### Custom type builder methods 1611 1612The default build methods may cover a majority of the simple cases related to 1613type construction, but when they cannot satisfy a type's needs, you can define 1614additional convenience get methods in the `builders` field as follows: 1615 1616```tablegen 1617def MyType : ... { 1618 let parameters = (ins "int":$intParam); 1619 1620 let builders = [ 1621 TypeBuilder<(ins "int":$intParam)>, 1622 TypeBuilder<(ins CArg<"int", "0">:$intParam)>, 1623 TypeBuilder<(ins CArg<"int", "0">:$intParam), [{ 1624 // Write the body of the `get` builder inline here. 1625 return Base::get($_ctxt, intParam); 1626 }]>, 1627 TypeBuilderWithInferredContext<(ins "Type":$typeParam), [{ 1628 // This builder states that it can infer an MLIRContext instance from 1629 // its arguments. 1630 return Base::get(typeParam.getContext(), ...); 1631 }]>, 1632 ]; 1633} 1634``` 1635 1636The `builders` field is a list of custom builders that are added to the type 1637class. In this example, we provide a several different convenience builders that 1638are useful in different scenarios. The `ins` prefix is common to many function 1639declarations in ODS, which use a TableGen [`dag`](#tablegen-syntax). What 1640follows is a comma-separated list of types (quoted string or CArg) and names 1641prefixed with the `$` sign. The use of `CArg` allows for providing a default 1642value to that argument. Let's take a look at each of these builders individually 1643 1644The first builder will generate the declaration of a builder method that looks 1645like: 1646 1647```tablegen 1648 let builders = [ 1649 TypeBuilder<(ins "int":$intParam)>, 1650 ]; 1651``` 1652 1653```c++ 1654class MyType : /*...*/ { 1655 /*...*/ 1656 static MyType get(::mlir::MLIRContext *context, int intParam); 1657}; 1658``` 1659 1660This builder is identical to the one that will be automatically generated for 1661`MyType`. The `context` parameter is implicitly added by the generator, and is 1662used when building the file Type instance (with `Base::get`). The distinction 1663here is that we can provide the implementation of this `get` method. With this 1664style of builder definition only the declaration is generated, the implementor 1665of MyType will need to provide a definition of `MyType::get`. 1666 1667The second builder will generate the declaration of a builder method that looks 1668like: 1669 1670```tablegen 1671 let builders = [ 1672 TypeBuilder<(ins CArg<"int", "0">:$intParam)>, 1673 ]; 1674``` 1675 1676```c++ 1677class MyType : /*...*/ { 1678 /*...*/ 1679 static MyType get(::mlir::MLIRContext *context, int intParam = 0); 1680}; 1681``` 1682 1683The constraints here are identical to the first builder example except for the 1684fact that `intParam` now has a default value attached. 1685 1686The third builder will generate the declaration of a builder method that looks 1687like: 1688 1689```tablegen 1690 let builders = [ 1691 TypeBuilder<(ins CArg<"int", "0">:$intParam), [{ 1692 // Write the body of the `get` builder inline here. 1693 return Base::get($_ctxt, intParam); 1694 }]>, 1695 ]; 1696``` 1697 1698```c++ 1699class MyType : /*...*/ { 1700 /*...*/ 1701 static MyType get(::mlir::MLIRContext *context, int intParam = 0); 1702}; 1703 1704MyType MyType::get(::mlir::MLIRContext *context, int intParam) { 1705 // Write the body of the `get` builder inline here. 1706 return Base::get(context, intParam); 1707} 1708``` 1709 1710This is identical to the second builder example. The difference is that now, a 1711definition for the builder method will be generated automatically using the 1712provided code block as the body. When specifying the body inline, `$_ctxt` may 1713be used to access the `MLIRContext *` parameter. 1714 1715The fourth builder will generate the declaration of a builder method that looks 1716like: 1717 1718```tablegen 1719 let builders = [ 1720 TypeBuilderWithInferredContext<(ins "Type":$typeParam), [{ 1721 // This builder states that it can infer an MLIRContext instance from 1722 // its arguments. 1723 return Base::get(typeParam.getContext(), ...); 1724 }]>, 1725 ]; 1726``` 1727 1728```c++ 1729class MyType : /*...*/ { 1730 /*...*/ 1731 static MyType get(Type typeParam); 1732}; 1733 1734MyType MyType::get(Type typeParam) { 1735 // This builder states that it can infer an MLIRContext instance from its 1736 // arguments. 1737 return Base::get(typeParam.getContext(), ...); 1738} 1739``` 1740 1741In this builder example, the main difference from the third builder example 1742three is that the `MLIRContext` parameter is no longer added. This is because 1743the builder type used `TypeBuilderWithInferredContext` implies that the context 1744parameter is not necessary as it can be inferred from the arguments to the 1745builder. 1746 1747## Debugging Tips 1748 1749### Run `mlir-tblgen` to see the generated content 1750 1751TableGen syntax sometimes can be obscure; reading the generated content can be a 1752very helpful way to understand and debug issues. To build `mlir-tblgen`, run 1753`cmake --build . --target mlir-tblgen` in your build directory and find the 1754`mlir-tblgen` binary in the `bin/` subdirectory. All the supported generators 1755can be found via `mlir-tblgen --help`. For example, `--gen-op-decls` and 1756`--gen-op-defs` as explained in [Generated C++ code](#generated-c++-code). 1757 1758To see the generated code, invoke `mlir-tblgen` with a specific generator by 1759providing include paths via `-I`. For example, 1760 1761```sh 1762# To see op C++ class declaration 1763mlir-tblgen --gen-op-decls -I /path/to/mlir/include /path/to/input/td/file 1764# To see op C++ class definition 1765mlir-tblgen --gen-op-defs -I /path/to/mlir/include /path/to/input/td/file 1766# To see op documentation 1767mlir-tblgen --gen-dialect-doc -I /path/to/mlir/include /path/to/input/td/file 1768 1769# To see op interface C++ class declaration 1770mlir-tblgen --gen-op-interface-decls -I /path/to/mlir/include /path/to/input/td/file 1771# To see op interface C++ class definition 1772mlir-tblgen --gen-op-interface-defs -I /path/to/mlir/include /path/to/input/td/file 1773# To see op interface documentation 1774mlir-tblgen --gen-op-interface-doc -I /path/to/mlir/include /path/to/input/td/file 1775``` 1776 1777## Appendix 1778 1779### Requirements and existing mechanisms analysis 1780 1781The op description should as declarative as possible to allow a wide range of 1782tools to work with them and query methods generated from them. In particular 1783this means specifying traits, constraints and shape inference information in a 1784way that is easily analyzable (e.g., avoid opaque calls to C++ functions where 1785possible). 1786 1787We considered the approaches of several contemporary systems and focused on 1788requirements that were desirable: 1789 1790* Ops registered using a registry separate from C++ code. 1791 * Unknown ops are allowed in MLIR, so ops need not be registered. The 1792 ability of the compiler to optimize those ops or graphs containing those 1793 ops is constrained but correct. 1794 * The current proposal does not include a runtime op description, but it 1795 does not preclude such description, it can be added later. 1796 * The op registry is essential for generating C++ classes that make 1797 manipulating ops, verifying correct construction etc. in C++ easier by 1798 providing a typed representation and accessors. 1799* The op registry will be defined in 1800 [TableGen](https://llvm.org/docs/TableGen/index.html) and be used to 1801 generate C++ classes and utility functions 1802 (builder/verifier/parser/printer). 1803 * TableGen is a modelling specification language used by LLVM's backends 1804 and fits in well with trait-based modelling. This is an implementation 1805 decision and there are alternative ways of doing this. But the 1806 specification language is good for the requirements of modelling the 1807 traits (as seen from usage in LLVM processor backend modelling) and easy 1808 to extend, so a practical choice. If another good option comes up, we 1809 will consider it. 1810* MLIR allows both defined and undefined ops. 1811 * Defined ops should have fixed semantics and could have a corresponding 1812 reference implementation defined using, for example, EDSC. 1813 * Dialects are under full control of the dialect owner and normally live 1814 with the framework of the dialect. 1815* The op's traits (e.g., commutative) are modelled along with the op in the 1816 registry. 1817* The op's operand/return type constraints are modelled along with the op in 1818 the registry (see [Shape inference](ShapeInference.md) discussion below), 1819 this allows (e.g.) optimized concise syntax in textual dumps. 1820* Behavior of the op is documented along with the op with a summary and a 1821 description. The description is written in markdown and extracted for 1822 inclusion in the generated LangRef section of the dialect. 1823* The generic assembly form of printing and parsing is available as normal, 1824 but a custom parser and printer can either be specified or automatically 1825 generated from an optional string representation showing the mapping of the 1826 "assembly" string to operands/type. 1827 * Parser-level remappings (e.g., `eq` to enum) will be supported as part 1828 of the parser generation. 1829* Matching patterns are specified separately from the op description. 1830 * Contrasted with LLVM there is no "base" set of ops that every backend 1831 needs to be aware of. Instead there are many different dialects and the 1832 transformations/legalizations between these dialects form a graph of 1833 transformations. 1834* Reference implementation may be provided along with the op definition. 1835 1836 * The reference implementation may be in terms of either standard ops or 1837 other reference implementations. 1838 1839 TODO: document expectation if the dependent op's definition changes. 1840 1841[TableGen]: https://llvm.org/docs/TableGen/index.html 1842[TableGenProgRef]: https://llvm.org/docs/TableGen/ProgRef.html 1843[TableGenBackend]: https://llvm.org/docs/TableGen/BackEnds.html#introduction 1844[OpBase]: https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/OpBase.td 1845[OpDefinitionsGen]: https://github.com/llvm/llvm-project/blob/main/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp 1846[EnumsGen]: https://github.com/llvm/llvm-project/blob/main/mlir/tools/mlir-tblgen/EnumsGen.cpp 1847[StringAttr]: LangRef.md#string-attribute 1848[IntegerAttr]: LangRef.md#integer-attribute 1849[AttrClasses]: https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/Attributes.h 1850