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