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