[MLIR] ODS TypeDef documentation

Adds documentation for the new ODS TypeDef support.

Reviewed By: rriddle

Differential Revision: https://reviews.llvm.org/D89440
This commit is contained in:
John Demme 2020-10-19 00:20:53 +00:00
parent 094e9f4779
commit 6f87809664
1 changed files with 171 additions and 4 deletions

View File

@ -1,11 +1,11 @@
# Operation Definition Specification (ODS)
In addition to specializing the `mlir::Op` C++ template, MLIR also supports
defining operations in a table-driven manner. This is achieved via
[TableGen][TableGen], which is both a generic language and its tooling to
defining operations and data types in a table-driven manner. This is achieved
via [TableGen][TableGen], which is both a generic language and its tooling to
maintain records of domain-specific information. Facts regarding an operation
are specified concisely into a TableGen record, which will be expanded into an
equivalent `mlir::Op` C++ template specialization at compiler build time.
are specified concisely into a TableGen record, which will be expanded into
an equivalent `mlir::Op` C++ template specialization at compiler build time.
This manual explains in detail all the available mechanisms for defining
operations in such a table-driven manner. It aims to be a specification instead
@ -1412,6 +1412,173 @@ llvm::Optional<MyBitEnum> symbolizeMyBitEnum(uint32_t value) {
}
```
## Type Definitions
MLIR defines the TypeDef class hierarchy to enable generation of data types
from their specifications. A type is defined by specializing the TypeDef
class with concrete contents for all the fields it requires. For example, an
integer type could be defined as:
```tablegen
// All of the types will extend this class.
class Test_Type<string name> : TypeDef<Test_Dialect, name> { }
// An alternate int type.
def IntegerType : Test_Type<"TestInteger"> {
let mnemonic = "int";
let summary = "An integer type with special semantics";
let description = [{
An alternate integer type. This type differentiates itself from the
standard integer type by not having a SignednessSemantics parameter, just
a width.
}];
let parameters = (ins "unsigned":$width);
// We define the printer inline.
let printer = [{
$_printer << "int<" << getImpl()->width << ">";
}];
// The parser is defined here also.
let parser = [{
if (parser.parseLess())
return Type();
int width;
if ($_parser.parseInteger(width))
return Type();
if ($_parser.parseGreater())
return Type();
return get(ctxt, width);
}];
```
### Type name
The name of the C++ class which gets generated defaults to
`<classParamName>Type` (e.g. `TestIntegerType` in the above example). This
can be overridden via the the `cppClassName` field. The field `mnemonic` is
to specify the asm name for parsing. It is optional and not specifying it
will imply that no parser or printer methods are attached to this class.
### Type documentation
The `summary` and `description` fields exist and are to be used the same way
as in Operations. Namely, the summary should be a one-liner and `description`
should be a longer explanation.
### Type parameters
The `parameters` field is a list of the types parameters. If no parameters
are specified (the default), this type is considered a singleton type.
Parameters are in the `"c++Type":$paramName` format.
To use C++ types as parameters which need allocation in the storage
constructor, there are two options:
- Set `hasCustomStorageConstructor` to generate the TypeStorage class with
a constructor which is just declared -- no definition -- so you can write it
yourself.
- Use the `TypeParameter` tablegen class instead of the "c++Type" string.
### TypeParameter tablegen class
This is used to further specify attributes about each of the types
parameters. It includes documentation (`description` and `syntax`), the C++
type to use, and a custom allocator to use in the storage constructor method.
```tablegen
// DO NOT DO THIS!
let parameters = (ins
"ArrayRef<int>":$dims);
```
The default storage constructor blindly copies fields by value. It does not
know anything about the types. In this case, the ArrayRef<int> requires
allocation with `dims = allocator.copyInto(dims)`.
You can specify the necessary constuctor by specializing the `TypeParameter`
tblgen class:
```tablegen
class ArrayRefIntParam :
TypeParameter<"::llvm::ArrayRef<int>", "Array of ints"> {
let allocator = [{$_dst = $_allocator.copyInto($_self);}];
}
...
let parameters = (ins
ArrayRefIntParam:$dims);
```
The `allocator` code block has the following substitutions:
- `$_allocator` is the TypeStorageAllocator in which to allocate objects.
- `$_dst` is the variable in which to place the allocated data.
MLIR includes several specialized classes for common situations:
- `StringRefParameter<descriptionOfParam>` for StringRefs.
- `ArrayRefParameter<arrayOf, descriptionOfParam>` for ArrayRefs of value
types
- `SelfAllocationParameter<descriptionOfParam>` for C++ classes which contain
a method called `allocateInto(StorageAllocator &allocator)` to allocate
itself into `allocator`.
- `ArrayRefOfSelfAllocationParameter<arrayOf, descriptionOfParam>` for arrays
of objects which self-allocate as per the last specialization.
If we were to use one of these included specializations:
```tablegen
let parameters = (ins
ArrayRefParameter<"int", "The dimensions">:$dims
);
```
### Parsing and printing
If a mnemonic is specified, the `printer` and `parser` code fields are active.
The rules for both are:
- If null, generate just the declaration.
- If non-null and non-empty, use the code in the definition. The `$_printer`
or `$_parser` substitutions are valid and should be used.
- It is an error to have an empty code block.
For each dialect, two "dispatch" functions will be created: one for parsing
and one for printing. You should add calls to these in your
`Dialect::printType` and `Dialect::parseType` methods. They are created in
the dialect's namespace and their function signatures are:
```c++
Type generatedTypeParser(MLIRContext* ctxt, DialectAsmParser& parser,
StringRef mnemonic);
LogicalResult generatedTypePrinter(Type type, DialectAsmPrinter& printer);
```
The mnemonic, parser, and printer fields are optional. If they're not
defined, the generated code will not include any parsing or printing code and
omit the type from the dispatch functions above. In this case, the dialect
author is responsible for parsing/printing the types in `Dialect::printType`
and `Dialect::parseType`.
### Other fields
- If the `genStorageClass` field is set to 1 (the default) a storage class is
generated with member variables corresponding to each of the specified
`parameters`.
- If the `genAccessors` field is 1 (the default) accessor methods will be
generated on the Type class (e.g. `int getWidth() const` in the example
above).
- If the `genVerifyInvariantsDecl` field is set, a declaration for a method
`static LogicalResult verifyConstructionInvariants(Location, parameters...)`
is added to the class as well as a `getChecked(Location, parameters...)`
method which gets the result of `verifyConstructionInvariants` before calling
`get`.
- The `storageClass` field can be used to set the name of the storage class.
- The `storageNamespace` field is used to set the namespace where the storage
class should sit. Defaults to "detail".
- The `extraClassDeclaration` field is used to include extra code in the
class declaration.
## Debugging Tips
### Run `mlir-tblgen` to see the generated content