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