Attribute and type defs in ODS don’t provide a declarative way to describe parsers and printers in the same way that ops do. This RFC proposes an assemblyFormat
field for TypeDef
s and AttrDef
s to generate parsers and printers.
Defining a Custom Assembly Format
Attributes and type defs that define an assemblyFormat
must specify mnemonic
. Then, the assembly format is specified in a manner very similar to op assembly formats:
def MyAttr : AttrDef<MyDialect, "MyAttr"> {
let parameters = (ins "int32_t":$value);
let mnemonic = "my_attr";
let assemblyFormat = "`<` $value `>`";
}
The above attribute will have an assembly format #my_dialect.my_attr<42>
, since it meets the requirements for pretty-printing format.
let assemblyFormat = "`(` $value `)`";
The above format will result in the ugly form: #my_dialect<"my_attr(42)">
Special Directives
The only available directive is struct
. The directive struct($one, $two, $three, ...)
matches a comma-separated list of key-value pairs for the parameters one
, two
, and three
in any order (but all must be present).
def MyStructType : TypeDef<MyDialect, "MyStructType"> {
let parameters = (ins "int":$one, "int":$two, "int":$three);
let mnemonic = "my_struct_type";
let assemblyFormat = "`<` struct($two, $three) `,` $one `>`";
}
The above attribute will have the format !my_dialect.my_struct_type<two = 24, three = 16, 42>
.
Using struct(*)
will capture all the parameters in an attribute or type.
Parameter Parsing and Printing
The default parser for any parameter is
MyParamT myParam;
::mlir::ParseResult result = ::mlir::parseField(parser, myParam);
The default printer is
printer << getMyParam();
Both parseField
and the stream operator can be overloaded to provide parsers and printers for custom types. One goal is to provide enough of these in MLIR core that must common types (integers, strings, etc.) won’t require work on the user’s part.
Just as non-POD parameters require defining an AttrParameter
or TypeParameter
to provide a custom allocator, they must also specify a cppStorageType
; for StringRef
it is std::string
and for ArrayRef<T>
it is SmallVector<T>
.
For more complex parsing or printing behaviour, define parser
and/or printer
to override the default parser and printer.
def MyParam : AttrParameter<"MyParam"> {
let parser = [{ parseMyParam($_parser, $_self, $_type) }];
let printer = [{ printMyParam($_printer, $_self) }];
}
For attribute parameters, $_type
refers to the expected type of the attribute.
Limitations
Parameter types whose cppStorageType
does not have a default constructor cannot be parsed as part of an assembly format, because they are declared at the top of the function – notably, APFloat
.
Example: Ditching StructAttr
StructAttr
is convenient and concise, but it can be slow (attributes inside DictAttr
).
def SlowAttr : StructAttr<"SlowAttr", MyDialect, [
StructFieldAttr<"count", I32Attr>,
StructFieldAttr<"dims", I32ArrayAttr>,
StructFieldAttr<"limits", I32ArrayAttr>,
StructFieldAttr<"map", AffineMapAttr>]>;
The above example can be replaced with an AttrDef
now as simply:
def FasterAttr : AttrDef<"FasterAttr", MyDialect> {
let parameters = (ins
"int32_t":$count,
ArrayRefParameter<"int32_t">:$dims,
ArrayRefParameter<"int32_t">:$limits,
"::mlir::AffineMap":$map
);
let mnemonic = "faster";
let assemblyFormat = "`<` struct(*) `>`";
}