1# Defining Dialect Attributes and Types
2
3This document describes how to define dialect
4[attributes](LangRef.md/#attributes) and [types](LangRef.md/#type-system).
5
6[TOC]
7
8## LangRef Refresher
9
10Before diving into how to define these constructs, below is a quick refresher
11from the [MLIR LangRef](LangRef.md).
12
13### Attributes
14
15Attributes are the mechanism for specifying constant data on operations in
16places where a variable is never allowed - e.g. the comparison predicate of a
17[`arith.cmpi` operation](Dialects/ArithmeticOps.md#arithcmpi-mlirarithcmpiop), or
18the underlying value of a [`arith.constant` operation](Dialects/ArithmeticOps.md#arithconstant-mlirarithconstantop).
19Each operation has an attribute dictionary, which associates a set of attribute
20names to attribute values.
21
22### Types
23
24Every SSA value, such as operation results or block arguments, in MLIR has a type
25defined by the type system. MLIR has an open type system with no fixed list of types,
26and there are no restrictions on the abstractions they represent. For example, take
27the following [Arithmetic AddI operation](Dialects/ArithmeticOps.md#arithaddi-mlirarithaddiop):
28
29```mlir
30  %result = arith.addi %lhs, %rhs : i64
31```
32
33It takes two input SSA values (`%lhs` and `%rhs`), and returns a single SSA
34value (`%result`). The inputs and outputs of this operation are of type `i64`,
35which is an instance of the [Builtin IntegerType](Dialects/Builtin.md#integertype).
36
37## Attributes and Types
38
39The C++ Attribute and Type classes in MLIR (like Ops, and many other things) are
40value-typed. This means that instances of `Attribute` or `Type` are passed
41around by-value, as opposed to by-pointer or by-reference. The `Attribute` and
42`Type` classes act as wrappers around internal storage objects that are uniqued
43within an instance of an `MLIRContext`.
44
45The structure for defining Attributes and Types is nearly identical, with only a
46few differences depending on the context. As such, a majority of this document
47describes the process for defining both Attributes and Types side-by-side with
48examples for both. If necessary, a section will explicitly call out any
49distinct differences.
50
51### Adding a new Attribute or Type definition
52
53As described above, C++ Attribute and Type objects in MLIR are value-typed and
54essentially function as helpful wrappers around an internal storage object that
55holds the actual data for the type. Similarly to Operations, Attributes and Types
56are defined declaratively via [TableGen](https://llvm.org/docs/TableGen/index.html);
57a generic language with tooling to maintain records of domain-specific information.
58It is highly recommended that users review the
59[TableGen Programmer's Reference](https://llvm.org/docs/TableGen/ProgRef.html)
60for an introduction to its syntax and constructs.
61
62Starting the definition of a new attribute or type simply requires adding a
63specialization for either the `AttrDef` or `TypeDef` class respectively. Instances
64of the classes correspond to unqiue Attribute or Type classes.
65
66Below show cases an example Attribute and Type definition. We generally recommend
67defining Attribute and Type classes in different `.td` files to better encapsulate
68the different constructs, and define a proper layering between them. This
69recommendation extends to all of the MLIR constructs, including [Interfaces](Interfaces.md),
70Operations, etc.
71
72```tablegen
73// Include the definition of the necessary tablegen constructs for defining
74// our types.
75include "mlir/IR/AttrTypeBase.td"
76
77// It's common to define a base classes for types in the same dialect. This
78// removes the need to pass in the dialect for each type, and can also be used
79// to define a few fields ahead of time.
80class MyDialect_Type<string name, string typeMnemonic, list<Trait> traits = []>
81    : TypeDef<My_Dialect, name, traits> {
82  let mnemonic = typeMnemonic;
83}
84
85// Here is a simple definition of an "integer" type, with a width parameter.
86def My_IntegerType : MyDialect_Type<"Integer", "int"> {
87  let summary = "Integer type with arbitrary precision up to a fixed limit";
88  let description = [{
89    Integer types have a designated bit width.
90  }];
91  /// Here we defined a single parameter for the type, which is the bitwidth.
92  let parameters = (ins "unsigned":$width);
93
94  /// Here we define the textual format of the type declaratively, which will
95  /// automatically generate parser and printer logic. This will allow for
96  /// instances of the type to be output as, for example:
97  ///
98  ///    !my.int<10> // a 10-bit integer.
99  ///
100  let assemblyFormat = "`<` $width `>`";
101
102  /// Indicate that our type will add additional verification to the parameters.
103  let genVerifyDecl = 1;
104}
105```
106
107Below is an example of an Attribute:
108
109```tablegen
110// Include the definition of the necessary tablegen constructs for defining
111// our attributes.
112include "mlir/IR/AttrTypeBase.td"
113
114// It's common to define a base classes for attributes in the same dialect. This
115// removes the need to pass in the dialect for each attribute, and can also be used
116// to define a few fields ahead of time.
117class MyDialect_Attr<string name, string attrMnemonic, list<Trait> traits = []>
118    : AttrDef<My_Dialect, name, traits> {
119  let mnemonic = attrMnemonic;
120}
121
122// Here is a simple definition of an "integer" attribute, with a type and value parameter.
123def My_IntegerAttr : MyDialect_Attr<"Integer", "int"> {
124  let summary = "An Attribute containing a integer value";
125  let description = [{
126    An integer attribute is a literal attribute that represents an integral
127    value of the specified integer type.
128  }];
129  /// Here we've defined two parameters, one is the `self` type of the attribute
130  /// (i.e. the type of the Attribute itself), and the other is the integer value
131  /// of the attribute.
132  let parameters = (ins AttributeSelfTypeParameter<"">:$type, "APInt":$value);
133
134  /// Here we've defined a custom builder for the type, that removes the need to pass
135  /// in an MLIRContext instance; as it can be infered from the `type`.
136  let builders = [
137    AttrBuilderWithInferredContext<(ins "Type":$type,
138                                        "const APInt &":$value), [{
139      return $_get(type.getContext(), type, value);
140    }]>
141  ];
142
143  /// Here we define the textual format of the attribute declaratively, which will
144  /// automatically generate parser and printer logic. This will allow for
145  /// instances of the attribute to be output as, for example:
146  ///
147  ///    #my.int<50> : !my.int<32> // a 32-bit integer of value 50.
148  ///
149  let assemblyFormat = "`<` $value `>`";
150
151  /// Indicate that our attribute will add additional verification to the parameters.
152  let genVerifyDecl = 1;
153
154  /// Indicate to the ODS generator that we do not want the default builders,
155  /// as we have defined our own simpler ones.
156  let skipDefaultBuilders = 1;
157}
158```
159
160### Class Name
161
162The name of the C++ class which gets generated defaults to
163`<classParamName>Attr` or `<classParamName>Type` for attributes and types
164respectively. In the examples above, this was the `name` template parameter that
165was provided to `MyDialect_Attr` and `MyDialect_Type`. For the definitions we
166added above, we would get C++ classes named `IntegerType` and `IntegerAttr`
167respectively. This can be explicitly overridden via the `cppClassName` field.
168
169### Documentation
170
171The `summary` and `description` fields allow for providing user documentation
172for the attribute or type. The `summary` field expects a simple single-line
173string, with the `description` field used for long and extensive documentation.
174This documentation can be used to generate markdown documentation for the
175dialect and is used by upstream
176[MLIR dialects](https://mlir.llvm.org/docs/Dialects/).
177
178### Mnemonic
179
180The `mnemonic` field, i.e. the template parameters `attrMnemonic` and
181`typeMnemonic` we specified above, are used to specify a name for use during
182parsing. This allows for more easily dispatching to the current attribute or
183type class when parsing IR. This field is generally optional, and custom
184parsing/printing logic can be added without defining it, though most classes
185will want to take advantage of the convenience it provides. This is why we
186added it as a template parameter in the examples above.
187
188### Parameters
189
190The `parameters` field is a variable length list containing the attribute or
191type's parameters. If no parameters are specified (the default), this type is
192considered a singleton type (meaning there is only one possible instance).
193Parameters in this list take the form: `"c++Type":$paramName`. Parameter types
194with a C++ type that requires allocation when constructing the storage instance
195in the context require one of the following:
196
197- Utilize the `AttrParameter` or `TypeParameter` classes instead of the raw
198  "c++Type" string. This allows for providing custom allocation code when using
199  that parameter. `StringRefParameter` and `ArrayRefParameter` are examples of
200  common parameter types that require allocation.
201- Set the `genAccessors` field to 1 (the default) to generate accessor methods
202  for each parameter (e.g. `int getWidth() const` in the Type example above).
203- Set the `hasCustomStorageConstructor` field to `1` to generate a storage class
204  that only declares the constructor, allowing for you to specialize it with
205  whatever allocation code necessary.
206
207#### AttrParameter, TypeParameter, and AttrOrTypeParameter
208
209As hinted at above, these classes allow for specifying parameter types with
210additional functionality. This is generally useful for complex parameters, or those
211with additional invariants that prevent using the raw C++ class. Examples
212include documentation (e.g. the `summary` and `syntax` field), the C++ type, a
213custom allocator to use in the storage constructor method, a custom comparator
214to decide if two instances of the parameter type are equal, etc. As the names
215may suggest, `AttrParameter` is intended for parameters on Attributes,
216`TypeParameter` for Type parameters, and `AttrOrTypeParameters` for either.
217
218Below is an easy parameter pitfall, and highlights when to use these parameter
219classes.
220
221```tablegen
222let parameters = (ins "ArrayRef<int>":$dims);
223```
224
225The above seems innocuous, but it is often a bug! The default storage
226constructor blindly copies parameters by value. It does not know anything about
227the types, meaning that the data of this ArrayRef will be copied as-is and is
228likely to lead to use-after-free errors when using the created Attribute or
229Type if the underlying does not have a lifetime exceeding that of the MLIRContext.
230If the lifetime of the data can't be guaranteed, the `ArrayRef<int>` requires
231allocation to ensure that its elements reside within the MLIRContext, e.g. with
232`dims = allocator.copyInto(dims)`.
233
234Here is a simple example for the exact situation above:
235
236```tablegen
237def ArrayRefIntParam : TypeParameter<"::llvm::ArrayRef<int>", "Array of int"> {
238  let allocator = "$_dst = $_allocator.copyInto($_self);";
239}
240
241The parameter can then be used as so:
242
243...
244let parameters = (ins ArrayRefIntParam:$dims);
245```
246
247Below contains descriptions for other various available fields:
248
249The `allocator` code block has the following substitutions:
250
251- `$_allocator` is the TypeStorageAllocator in which to allocate objects.
252- `$_dst` is the variable in which to place the allocated data.
253
254The `comparator` code block has the following substitutions:
255
256- `$_lhs` is an instance of the parameter type.
257- `$_rhs` is an instance of the parameter type.
258
259MLIR includes several specialized classes for common situations:
260
261- `APFloatParameter` for APFloats.
262
263- `StringRefParameter<descriptionOfParam>` for StringRefs.
264
265- `ArrayRefParameter<arrayOf, descriptionOfParam>` for ArrayRefs of value types.
266
267- `SelfAllocationParameter<descriptionOfParam>` for C++ classes which contain a
268  method called `allocateInto(StorageAllocator &allocator)` to allocate itself
269  into `allocator`.
270
271- `ArrayRefOfSelfAllocationParameter<arrayOf, descriptionOfParam>` for arrays of
272  objects which self-allocate as per the last specialization.
273
274- `AttributeSelfTypeParameter` is a special AttrParameter that corresponds to
275  the `Type` of the attribute. Only one parameter of the attribute may be of
276  this parameter type.
277
278### Traits
279
280Similarly to operations, Attribute and Type classes may attach `Traits` that
281provide additional mixin methods and other data. `Trait`s may be attached via
282the trailing template argument, i.e. the `traits` list parameter in the example
283above. See the main [`Trait`](Traits.md) documentation for more information
284on defining and using traits.
285
286### Interfaces
287
288Attribute and Type classes may attach `Interfaces` to provide an virtual
289interface into the Attribute or Type. `Interfaces` are added in the same way as
290[Traits](#Traits), by using the `traits` list template parameter of the
291`AttrDef` or `TypeDef`. See the main [`Interface`](Interfaces.md)
292documentation for more information on defining and using interfaces.
293
294### Builders
295
296For each attribute or type, there are a few builders(`get`/`getChecked`)
297automatically generated based on the parameters of the type. These are used to
298construct instances of the corresponding attribute or type. For example, given
299the following definition:
300
301```tablegen
302def MyAttrOrType : ... {
303  let parameters = (ins "int":$intParam);
304}
305```
306
307The following builders are generated:
308
309```c++
310// Builders are named `get`, and return a new instance for a given set of parameters.
311static MyAttrOrType get(MLIRContext *context, int intParam);
312
313// If `genVerifyDecl` is set to 1, the following method is also generated. This method
314// is similar to `get`, but is failable and on error will return nullptr.
315static MyAttrOrType getChecked(function_ref<InFlightDiagnostic()> emitError,
316                               MLIRContext *context, int intParam);
317```
318
319If these autogenerated methods are not desired, such as when they conflict with
320a custom builder method, the `skipDefaultBuilders` field may be set to 1 to
321signal that the default builders should not be generated.
322
323#### Custom builder methods
324
325The default builder methods may cover a majority of the simple cases related to
326construction, but when they cannot satisfy all of an attribute or type's needs,
327additional builders may be defined via the `builders` field. The `builders`
328field is a list of custom builders, either using `TypeBuilder` for types or
329`AttrBuilder` for attributes, that are added to the attribute or type class. The
330following will showcase several examples for defining builders for a custom type
331`MyType`, the process is the same for attributes except that attributes use
332`AttrBuilder` instead of `TypeBuilder`.
333
334```tablegen
335def MyType : ... {
336  let parameters = (ins "int":$intParam);
337
338  let builders = [
339    TypeBuilder<(ins "int":$intParam)>,
340    TypeBuilder<(ins CArg<"int", "0">:$intParam)>,
341    TypeBuilder<(ins CArg<"int", "0">:$intParam), [{
342      // Write the body of the `get` builder inline here.
343      return Base::get($_ctxt, intParam);
344    }]>,
345    TypeBuilderWithInferredContext<(ins "Type":$typeParam), [{
346      // This builder states that it can infer an MLIRContext instance from
347      // its arguments.
348      return Base::get(typeParam.getContext(), ...);
349    }]>,
350    TypeBuilder<(ins "int":$intParam), [{}], "IntegerType">,
351  ];
352}
353```
354
355In this example, we provide several different convenience builders that are
356useful in different scenarios. The `ins` prefix is common to many function
357declarations in ODS, which use a TableGen [`dag`](#tablegen-syntax). What
358follows is a comma-separated list of types (quoted string or `CArg`) and names
359prefixed with the `$` sign. The use of `CArg` allows for providing a default
360value to that argument. Let's take a look at each of these builders individually
361
362The first builder will generate the declaration of a builder method that looks
363like:
364
365```tablegen
366  let builders = [
367    TypeBuilder<(ins "int":$intParam)>,
368  ];
369```
370
371```c++
372class MyType : /*...*/ {
373  /*...*/
374  static MyType get(::mlir::MLIRContext *context, int intParam);
375};
376```
377
378This builder is identical to the one that will be automatically generated for
379`MyType`. The `context` parameter is implicitly added by the generator, and is
380used when building the Type instance (with `Base::get`). The distinction here is
381that we can provide the implementation of this `get` method. With this style of
382builder definition only the declaration is generated, the implementor of
383`MyType` will need to provide a definition of `MyType::get`.
384
385The second builder will generate the declaration of a builder method that looks
386like:
387
388```tablegen
389  let builders = [
390    TypeBuilder<(ins CArg<"int", "0">:$intParam)>,
391  ];
392```
393
394```c++
395class MyType : /*...*/ {
396  /*...*/
397  static MyType get(::mlir::MLIRContext *context, int intParam = 0);
398};
399```
400
401The constraints here are identical to the first builder example except for the
402fact that `intParam` now has a default value attached.
403
404The third builder will generate the declaration of a builder method that looks
405like:
406
407```tablegen
408  let builders = [
409    TypeBuilder<(ins CArg<"int", "0">:$intParam), [{
410      // Write the body of the `get` builder inline here.
411      return Base::get($_ctxt, intParam);
412    }]>,
413  ];
414```
415
416```c++
417class MyType : /*...*/ {
418  /*...*/
419  static MyType get(::mlir::MLIRContext *context, int intParam = 0);
420};
421
422MyType MyType::get(::mlir::MLIRContext *context, int intParam) {
423  // Write the body of the `get` builder inline here.
424  return Base::get(context, intParam);
425}
426```
427
428This is identical to the second builder example. The difference is that now, a
429definition for the builder method will be generated automatically using the
430provided code block as the body. When specifying the body inline, `$_ctxt` may
431be used to access the `MLIRContext *` parameter.
432
433The fourth builder will generate the declaration of a builder method that looks
434like:
435
436```tablegen
437  let builders = [
438    TypeBuilderWithInferredContext<(ins "Type":$typeParam), [{
439      // This builder states that it can infer an MLIRContext instance from
440      // its arguments.
441      return Base::get(typeParam.getContext(), ...);
442    }]>,
443  ];
444```
445
446```c++
447class MyType : /*...*/ {
448  /*...*/
449  static MyType get(Type typeParam);
450};
451
452MyType MyType::get(Type typeParam) {
453  // This builder states that it can infer an MLIRContext instance from its
454  // arguments.
455  return Base::get(typeParam.getContext(), ...);
456}
457```
458
459In this builder example, the main difference from the third builder example
460there is that the `MLIRContext` parameter is no longer added. This is because
461the builder used `TypeBuilderWithInferredContext` implies that the context
462parameter is not necessary as it can be inferred from the arguments to the
463builder.
464
465The fifth builder will generate the declaration of a builder method with a
466custom return type, like:
467
468```tablegen
469  let builders = [
470    TypeBuilder<(ins "int":$intParam), [{}], "IntegerType">,
471  ]
472```
473
474```c++
475class MyType : /*...*/ {
476  /*...*/
477  static IntegerType get(::mlir::MLIRContext *context, int intParam);
478
479};
480```
481
482This generates a builder declaration the same as the first three examples, but
483the return type of the builder is user-specified instead of the attribute or
484type class. This is useful for defining builders of attributes and types that
485may fold or canonicalize on construction.
486
487### Parsing and Printing
488
489If a mnemonic was specified, the `hasCustomAssemblyFormat` and `assemblyFormat`
490fields may be used to specify the assembly format of an attribute or type. Attributes
491and Types with no parameters need not use either of these fields, in which case
492the syntax for the Attribute or Type is simply the mnemonic.
493
494For each dialect, two "dispatch" functions will be created: one for parsing and
495one for printing. These static functions placed alongside the class definitions
496and have the following function signatures:
497
498```c++
499static ParseResult generatedAttributeParser(DialectAsmParser& parser, StringRef *mnemonic, Type attrType, Attribute &result);
500static LogicalResult generatedAttributePrinter(Attribute attr, DialectAsmPrinter& printer);
501
502static ParseResult generatedTypeParser(DialectAsmParser& parser, StringRef *mnemonic, Type &result);
503static LogicalResult generatedTypePrinter(Type type, DialectAsmPrinter& printer);
504```
505
506The above functions should be added to the respective in your
507`Dialect::printType` and `Dialect::parseType` methods, or consider using the
508`useDefaultAttributePrinterParser` and `useDefaultTypePrinterParser` ODS Dialect
509options if all attributes or types define a mnemonic.
510
511The mnemonic, hasCustomAssemblyFormat, and assemblyFormat fields are optional.
512If none are defined, the generated code will not include any parsing or printing
513code and omit the attribute or type from the dispatch functions above. In this
514case, the dialect author is responsible for parsing/printing in the respective
515`Dialect::parseAttribute`/`Dialect::printAttribute` and
516`Dialect::parseType`/`Dialect::printType` methods.
517
518#### Using `hasCustomAssemblyFormat`
519
520Attributes and types defined in ODS with a mnemonic can define an
521`hasCustomAssemblyFormat` to specify custom parsers and printers defined in C++.
522When set to `1` a corresponding `parse` and `print` method will be declared on
523the Attribute or Type class to be defined by the user.
524
525For Types, these methods will have the form:
526
527- `static Type MyType::parse(AsmParser &parser)`
528
529- `Type MyType::print(AsmPrinter &p) const`
530
531For Attributes, these methods will have the form:
532
533- `static Attribute MyAttr::parse(AsmParser &parser, Type attrType)`
534
535- `Attribute MyAttr::print(AsmPrinter &p) const`
536
537#### Using `assemblyFormat`
538
539Attributes and types defined in ODS with a mnemonic can define an
540`assemblyFormat` to declaratively describe custom parsers and printers. The
541assembly format consists of literals, variables, and directives.
542
543- A literal is a keyword or valid punctuation enclosed in backticks, e.g.
544  `` `keyword` `` or `` `<` ``.
545- A variable is a parameter name preceded by a dollar sign, e.g. `$param0`,
546  which captures one attribute or type parameter.
547- A directive is a keyword followed by an optional argument list that defines
548  special parser and printer behaviour.
549
550```tablegen
551// An example type with an assembly format.
552def MyType : TypeDef<My_Dialect, "MyType"> {
553  // Define a mnemonic to allow the dialect's parser hook to call into the
554  // generated parser.
555  let mnemonic = "my_type";
556
557  // Define two parameters whose C++ types are indicated in string literals.
558  let parameters = (ins "int":$count, "AffineMap":$map);
559
560  // Define the assembly format. Surround the format with less `<` and greater
561  // `>` so that MLIR's printer uses the pretty format.
562  let assemblyFormat = "`<` $count `,` `map` `=` $map `>`";
563}
564```
565
566The declarative assembly format for `MyType` results in the following format in
567the IR:
568
569```mlir
570!my_dialect.my_type<42, map = affine_map<(i, j) -> (j, i)>>
571```
572
573##### Parameter Parsing and Printing
574
575For many basic parameter types, no additional work is needed to define how these
576parameters are parsed or printed.
577
578- The default printer for any parameter is `$_printer << $_self`, where `$_self`
579  is the C++ value of the parameter and `$_printer` is an `AsmPrinter`.
580- The default parser for a parameter is
581  `FieldParser<$cppClass>::parse($_parser)`, where `$cppClass` is the C++ type
582  of the parameter and `$_parser` is an `AsmParser`.
583
584Printing and parsing behaviour can be added to additional C++ types by
585overloading these functions or by defining a `parser` and `printer` in an ODS
586parameter class.
587
588Example of overloading:
589
590```c++
591using MyParameter = std::pair<int, int>;
592
593AsmPrinter &operator<<(AsmPrinter &printer, MyParameter param) {
594  printer << param.first << " * " << param.second;
595}
596
597template <> struct FieldParser<MyParameter> {
598  static FailureOr<MyParameter> parse(AsmParser &parser) {
599    int a, b;
600    if (parser.parseInteger(a) || parser.parseStar() ||
601        parser.parseInteger(b))
602      return failure();
603    return MyParameter(a, b);
604  }
605};
606```
607
608Example of using ODS parameter classes:
609
610```tablegen
611def MyParameter : TypeParameter<"std::pair<int, int>", "pair of ints"> {
612  let printer = [{ $_printer << $_self.first << " * " << $_self.second }];
613  let parser = [{ [&] -> FailureOr<std::pair<int, int>> {
614    int a, b;
615    if ($_parser.parseInteger(a) || $_parser.parseStar() ||
616        $_parser.parseInteger(b))
617      return failure();
618    return std::make_pair(a, b);
619  }() }];
620}
621```
622
623A type using this parameter with the assembly format `` `<` $myParam `>` `` will
624look as follows in the IR:
625
626```mlir
627!my_dialect.my_type<42 * 24>
628```
629
630###### Non-POD Parameters
631
632Parameters that aren't plain-old-data (e.g. references) may need to define a
633`cppStorageType` to contain the data until it is copied into the allocator. For
634example, `StringRefParameter` uses `std::string` as its storage type, whereas
635`ArrayRefParameter` uses `SmallVector` as its storage type. The parsers for
636these parameters are expected to return `FailureOr<$cppStorageType>`.
637
638To add a custom conversion between the `cppStorageType` and the C++ type of the
639parameter, parameters can override `convertFromStorage`, which by default is
640`"$_self"` (i.e., it attempts an implicit conversion from `cppStorageType`).
641
642###### Optional Parameters
643
644Optional parameters in the assembly format can be indicated by setting
645`isOptional`. The C++ type of an optional parameter is required to satisfy the
646following requirements:
647
648- is default-constructible
649- is contextually convertible to `bool`
650- only the default-constructed value is `false`
651
652The parameter parser should return the default-constructed value to indicate "no
653value present". The printer will guard on the presence of a value to print the
654parameter.
655
656If a value was not parsed for an optional parameter, then the parameter will be
657set to its default-constructed C++ value. For example, `Optional<int>` will be
658set to `llvm::None` and `Attribute` will be set to `nullptr`.
659
660Only optional parameters or directives that only capture optional parameters can
661be used in optional groups. An optional group is a set of elements optionally
662printed based on the presence of an anchor. Suppose parameter `a` is an
663`IntegerAttr`.
664
665```
666( `(` $a^ `)` ) : (`x`)?
667```
668
669In the above assembly format, if `a` is present (non-null), then it will be
670printed as `(5 : i32)`. If it is not present, it will be `x`. Directives that
671are used inside optional groups are allowed only if all captured parameters are
672also optional.
673
674###### Default-Valued Parameters
675
676Optional parameters can be given default values by setting `defaultValue`, a
677string of the C++ default value, or by using `DefaultValuedParameter`. If a
678value for the parameter was not encountered during parsing, it is set to this
679default value. If a parameter is equal to its default value, it is not printed.
680The `comparator` field of the parameter is used, but if one is not specified,
681the equality operator is used.
682
683For example:
684
685```tablegen
686let parameters = (ins DefaultValuedParameter<"Optional<int>", "5">:$a)
687let mnemonic = "default_valued";
688let assemblyFormat = "(`<` $a^ `>`)?";
689```
690
691Which will look like:
692
693```mlir
694!test.default_valued     // a = 5
695!test.default_valued<10> // a = 10
696```
697
698For optional `Attribute` or `Type` parameters, the current MLIR context is
699available through `$_ctxt`. E.g.
700
701```tablegen
702DefaultValuedParameter<"IntegerType", "IntegerType::get($_ctxt, 32)">
703```
704
705##### Assembly Format Directives
706
707Attribute and type assembly formats have the following directives:
708
709- `params`: capture all parameters of an attribute or type.
710- `qualified`: mark a parameter to be printed with its leading dialect and
711  mnemonic.
712- `struct`: generate a "struct-like" parser and printer for a list of key-value
713  pairs.
714- `custom`: dispatch a call to user-define parser and printer functions
715- `ref`: in a custom directive, references a previously bound variable
716
717###### `params` Directive
718
719This directive is used to refer to all parameters of an attribute or type. When
720used as a top-level directive, `params` generates a parser and printer for a
721comma-separated list of the parameters. For example:
722
723```tablegen
724def MyPairType : TypeDef<My_Dialect, "MyPairType"> {
725  let parameters = (ins "int":$a, "int":$b);
726  let mnemonic = "pair";
727  let assemblyFormat = "`<` params `>`";
728}
729```
730
731In the IR, this type will appear as:
732
733```mlir
734!my_dialect.pair<42, 24>
735```
736
737The `params` directive can also be passed to other directives, such as `struct`,
738as an argument that refers to all parameters in place of explicitly listing all
739parameters as variables.
740
741###### `qualified` Directive
742
743This directive can be used to wrap attribute or type parameters such that they
744are printed in a fully qualified form, i.e., they include the dialect name and
745mnemonic prefix.
746
747For example:
748
749```tablegen
750def OuterType : TypeDef<My_Dialect, "MyOuterType"> {
751  let parameters = (ins MyPairType:$inner);
752  let mnemonic = "outer";
753  let assemblyFormat = "`<` pair `:` $inner `>`";
754}
755def OuterQualifiedType : TypeDef<My_Dialect, "MyOuterQualifiedType"> {
756  let parameters = (ins MyPairType:$inner);
757  let mnemonic = "outer_qual";
758  let assemblyFormat = "`<` pair `:` qualified($inner) `>`";
759}
760```
761
762In the IR, the types will appear as:
763
764```mlir
765!my_dialect.outer<pair : <42, 24>>
766!my_dialect.outer_qual<pair : !mydialect.pair<42, 24>>
767```
768
769If optional parameters are present, they are not printed in the parameter list
770if they are not present.
771
772###### `struct` Directive
773
774The `struct` directive accepts a list of variables to capture and will generate
775a parser and printer for a comma-separated list of key-value pairs. If an
776optional parameter is included in the `struct`, it can be elided. The variables
777are printed in the order they are specified in the argument list **but can be
778parsed in any order**. For example:
779
780```tablegen
781def MyStructType : TypeDef<My_Dialect, "MyStructType"> {
782  let parameters = (ins StringRefParameter<>:$sym_name,
783                        "int":$a, "int":$b, "int":$c);
784  let mnemonic = "struct";
785  let assemblyFormat = "`<` $sym_name `->` struct($a, $b, $c) `>`";
786}
787```
788
789In the IR, this type can appear with any permutation of the order of the
790parameters captured in the directive.
791
792```mlir
793!my_dialect.struct<"foo" -> a = 1, b = 2, c = 3>
794!my_dialect.struct<"foo" -> b = 2, c = 3, a = 1>
795```
796
797Passing `params` as the only argument to `struct` makes the directive capture
798all the parameters of the attribute or type. For the same type above, an
799assembly format of `` `<` struct(params) `>` `` will result in:
800
801```mlir
802!my_dialect.struct<b = 2, sym_name = "foo", c = 3, a = 1>
803```
804
805The order in which the parameters are printed is the order in which they are
806declared in the attribute's or type's `parameter` list.
807
808###### `custom` and `ref` directive
809
810The `custom` directive is used to dispatch calls to user-defined printer and
811parser functions. For example, suppose we had the following type:
812
813```tablegen
814let parameters = (ins "int":$foo, "int":$bar);
815let assemblyFormat = "custom<Foo>($foo) custom<Bar>($bar, ref($foo))";
816```
817
818The `custom` directive `custom<Foo>($foo)` will in the parser and printer
819respectively generate calls to:
820
821```c++
822LogicalResult parseFoo(AsmParser &parser, FailureOr<int> &foo);
823void printFoo(AsmPrinter &printer, int foo);
824```
825
826A previously bound variable can be passed as a parameter to a `custom` directive
827by wrapping it in a `ref` directive. In the previous example, `$foo` is bound by
828the first directive. The second directive references it and expects the
829following printer and parser signatures:
830
831```c++
832LogicalResult parseBar(AsmParser &parser, FailureOr<int> &bar, int foo);
833void printBar(AsmPrinter &printer, int bar, int foo);
834```
835
836More complex C++ types can be used with the `custom` directive. The only caveat
837is that the parameter for the parser must use the storage type of the parameter.
838For example, `StringRefParameter` expects the parser and printer signatures as:
839
840```c++
841LogicalResult parseStringParam(AsmParser &parser,
842                               FailureOr<std::string> &value);
843void printStringParam(AsmPrinter &printer, StringRef value);
844```
845
846The custom parser is considered to have failed if it returns failure or if any
847bound parameters have failure values afterwards.
848
849### Verification
850
851If the `genVerifyDecl` field is set, additional verification methods are
852generated on the class.
853
854- `static LogicalResult verify(function_ref<InFlightDiagnostic()> emitError, parameters...)`
855
856These methods are used to verify the parameters provided to the attribute or
857type class on construction, and emit any necessary diagnostics. This method is
858automatically invoked from the builders of the attribute or type class.
859
860- `AttrOrType getChecked(function_ref<InFlightDiagnostic()> emitError, parameters...)`
861
862As noted in the [Builders](#Builders) section, these methods are companions to
863`get` builders that are failable. If the `verify` invocation fails when these
864methods are called, they return nullptr instead of asserting.
865
866### Storage Classes
867
868Somewhat alluded to in the sections above is the concept of a "storage class"
869(often abbreviated to "storage"). Storage classes contain all of the data
870necessary to construct and unique a attribute or type instance. These classes
871are the "immortal" objects that get uniqued within an MLIRContext and get
872wrapped by the `Attribute` and `Type` classes. Every Attribute or Type class has
873a corresponding storage class, that can be accessed via the protected
874`getImpl()` method.
875
876In most cases the storage class is auto generated, but if necessary it can be
877manually defined by setting the `genStorageClass` field to 0. The name and
878namespace (defaults to `detail`) can additionally be controlled via the The
879`storageClass` and `storageNamespace` fields.
880
881#### Defining a storage class
882
883User defined storage classes must adhere to the following:
884
885- Inherit from the base type storage class of `AttributeStorage` or
886  `TypeStorage` respectively.
887- Define a type alias, `KeyTy`, that maps to a type that uniquely identifies an
888  instance of the derived type. For example, this could be a `std::tuple` of all
889  of the storage parameters.
890- Provide a construction method that is used to allocate a new instance of the
891  storage class.
892  - `static Storage *construct(StorageAllocator &allocator, const KeyTy &key)`
893- Provide a comparison method between an instance of the storage and the
894  `KeyTy`.
895  - `bool operator==(const KeyTy &) const`
896- Provide a method to generate the `KeyTy` from a list of arguments passed to
897  the uniquer when building an Attribute or Type. (Note: This is only necessary
898  if the `KeyTy` cannot be default constructed from these arguments).
899  - `static KeyTy getKey(Args...&& args)`
900- Provide a method to hash an instance of the `KeyTy`. (Note: This is not
901  necessary if an `llvm::DenseMapInfo<KeyTy>` specialization exists)
902  - `static llvm::hash_code hashKey(const KeyTy &)`
903
904Let's look at an example:
905
906```c++
907/// Here we define a storage class for a ComplexType, that holds a non-zero
908/// integer and an integer type.
909struct ComplexTypeStorage : public TypeStorage {
910  ComplexTypeStorage(unsigned nonZeroParam, Type integerType)
911      : nonZeroParam(nonZeroParam), integerType(integerType) {}
912
913  /// The hash key for this storage is a pair of the integer and type params.
914  using KeyTy = std::pair<unsigned, Type>;
915
916  /// Define the comparison function for the key type.
917  bool operator==(const KeyTy &key) const {
918    return key == KeyTy(nonZeroParam, integerType);
919  }
920
921  /// Define a hash function for the key type.
922  /// Note: This isn't necessary because std::pair, unsigned, and Type all have
923  /// hash functions already available.
924  static llvm::hash_code hashKey(const KeyTy &key) {
925    return llvm::hash_combine(key.first, key.second);
926  }
927
928  /// Define a construction function for the key type.
929  /// Note: This isn't necessary because KeyTy can be directly constructed with
930  /// the given parameters.
931  static KeyTy getKey(unsigned nonZeroParam, Type integerType) {
932    return KeyTy(nonZeroParam, integerType);
933  }
934
935  /// Define a construction method for creating a new instance of this storage.
936  static ComplexTypeStorage *construct(StorageAllocator &allocator, const KeyTy &key) {
937    return new (allocator.allocate<ComplexTypeStorage>())
938        ComplexTypeStorage(key.first, key.second);
939  }
940
941  /// The parametric data held by the storage class.
942  unsigned nonZeroParam;
943  Type integerType;
944};
945```
946
947### Mutable attributes and types
948
949Attributes and Types are immutable objects uniqued within an MLIRContext. That
950being said, some parameters may be treated as "mutable" and modified after
951construction. Mutable parameters should be reserved for parameters that can not
952be reasonably initialized during construction time. Given the mutable component,
953these parameters do not take part in the uniquing of the Attribute or Type.
954
955TODO: Mutable parameters are currently not supported in the declarative
956specification of attributes and types, and thus requires defining the Attribute
957or Type class in C++.
958
959#### Defining a mutable storage
960
961In addition to the base requirements for a storage class, instances with a
962mutable component must additionally adhere to the following:
963
964- The mutable component must not participate in the storage `KeyTy`.
965- Provide a mutation method that is used to modify an existing instance of the
966  storage. This method modifies the mutable component based on arguments, using
967  `allocator` for any newly dynamically-allocated storage, and indicates whether
968  the modification was successful.
969  - `LogicalResult mutate(StorageAllocator &allocator, Args ...&& args)`
970
971Let's define a simple storage for recursive types, where a type is identified by
972its name and may contain another type including itself.
973
974```c++
975/// Here we define a storage class for a RecursiveType that is identified by its
976/// name and contains another type.
977struct RecursiveTypeStorage : public TypeStorage {
978  /// The type is uniquely identified by its name. Note that the contained type
979  /// is _not_ a part of the key.
980  using KeyTy = StringRef;
981
982  /// Construct the storage from the type name. Explicitly initialize the
983  /// containedType to nullptr, which is used as marker for the mutable
984  /// component being not yet initialized.
985  RecursiveTypeStorage(StringRef name) : name(name), containedType(nullptr) {}
986
987  /// Define the comparison function.
988  bool operator==(const KeyTy &key) const { return key == name; }
989
990  /// Define a construction method for creating a new instance of the storage.
991  static RecursiveTypeStorage *construct(StorageAllocator &allocator,
992                                         const KeyTy &key) {
993    // Note that the key string is copied into the allocator to ensure it
994    // remains live as long as the storage itself.
995    return new (allocator.allocate<RecursiveTypeStorage>())
996        RecursiveTypeStorage(allocator.copyInto(key));
997  }
998
999  /// Define a mutation method for changing the type after it is created. In
1000  /// many cases, we only want to set the mutable component once and reject
1001  /// any further modification, which can be achieved by returning failure from
1002  /// this function.
1003  LogicalResult mutate(StorageAllocator &, Type body) {
1004    // If the contained type has been initialized already, and the call tries
1005    // to change it, reject the change.
1006    if (containedType && containedType != body)
1007      return failure();
1008
1009    // Change the body successfully.
1010    containedType = body;
1011    return success();
1012  }
1013
1014  StringRef name;
1015  Type containedType;
1016};
1017```
1018
1019#### Type class definition
1020
1021Having defined the storage class, we can define the type class itself.
1022`Type::TypeBase` provides a `mutate` method that forwards its arguments to the
1023`mutate` method of the storage and ensures the mutation happens safely.
1024
1025```c++
1026class RecursiveType : public Type::TypeBase<RecursiveType, Type,
1027                                            RecursiveTypeStorage> {
1028public:
1029  /// Inherit parent constructors.
1030  using Base::Base;
1031
1032  /// Creates an instance of the Recursive type. This only takes the type name
1033  /// and returns the type with uninitialized body.
1034  static RecursiveType get(MLIRContext *ctx, StringRef name) {
1035    // Call into the base to get a uniqued instance of this type. The parameter
1036    // (name) is passed after the context.
1037    return Base::get(ctx, name);
1038  }
1039
1040  /// Now we can change the mutable component of the type. This is an instance
1041  /// method callable on an already existing RecursiveType.
1042  void setBody(Type body) {
1043    // Call into the base to mutate the type.
1044    LogicalResult result = Base::mutate(body);
1045
1046    // Most types expect the mutation to always succeed, but types can implement
1047    // custom logic for handling mutation failures.
1048    assert(succeeded(result) &&
1049           "attempting to change the body of an already-initialized type");
1050
1051    // Avoid unused-variable warning when building without assertions.
1052    (void) result;
1053  }
1054
1055  /// Returns the contained type, which may be null if it has not been
1056  /// initialized yet.
1057  Type getBody() { return getImpl()->containedType; }
1058
1059  /// Returns the name.
1060  StringRef getName() { return getImpl()->name; }
1061};
1062```
1063
1064### Extra declarations
1065
1066The declarative Attribute and Type definitions try to auto-generate as much
1067logic and methods as possible. With that said, there will always be long-tail
1068cases that won't be covered. For such cases, `extraClassDeclaration` and
1069`extraClassDefinition` can be used. Code within the `extraClassDeclaration`
1070field will be copied literally to the generated C++ Attribute or Type class.
1071Code within `extraClassDefinition` will be added to the generated source file
1072inside the class's C++ namespace. The substitution `$cppClass` will be replaced
1073by the Attribute or Type's C++ class name.
1074
1075Note that these are mechanisms intended for long-tail cases by power users; for
1076not-yet-implemented widely-applicable cases, improving the infrastructure is
1077preferable.
1078
1079### Registering with the Dialect
1080
1081Once the attributes and types have been defined, they must then be registered
1082with the parent `Dialect`. This is done via the `addAttributes` and `addTypes`
1083methods. Note that when registering, the full definition of the storage classes
1084must be visible.
1085
1086```c++
1087void MyDialect::initialize() {
1088    /// Add the defined attributes to the dialect.
1089  addAttributes<
1090#define GET_ATTRDEF_LIST
1091#include "MyDialect/Attributes.cpp.inc"
1092  >();
1093
1094    /// Add the defined types to the dialect.
1095  addTypes<
1096#define GET_TYPEDEF_LIST
1097#include "MyDialect/Types.cpp.inc"
1098  >();
1099}
1100```
1101