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