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