Skip to content

Commit 8955e28

Browse files
krzysz00joker-ephDinistro
authored
[mlir] Add property combinators, initial ODS support (llvm#94732)
While we have had a Properties.td that allowed for defining non-attribute-backed properties, such properties were not plumbed through the basic autogeneration facilities available to attributes, forcing those who want to migrate to the new system to write such code by hand. ## Potentially breaking changes - The `setFoo()` methods on `Properties` struct no longer take their inputs by const reference. Those wishing to pass non-owned values of a property by reference to constructors and setters should set the interface type to `const [storageType]&` - Adapters and operations now define getters and setters for properties listed in ODS, which may conflict with custom getters. - Builders now include properties listed in ODS specifications, potentially conflicting with custom builders with the same type signature. ## Extensions to the `Property` class This commit adds several fields to the `Property` class, including: - `parser`, `optionalParser`, and `printer` (for parsing/printing properties of a given type in ODS syntax) - `storageTypeValueOverride`, an extension of `defaultValue` to allow the storage and interface type defaults to differ - `baseProperty` (allowing for classes like `DefaultValuedProperty`) Existing fields have also had their documentation comments updated. This commit does not add a `PropertyConstraint` analogous to `AttrConstraint`, but this is a natural evolution of the work here. This commit also adds the concrete property kinds `I32Property`, `I64Property`, `UnitProperty` (and special handling for it like for UnitAttr), and `BoolProperty`. ## Property combinators `Properties.td` also now includes several ways to combine properties. One is `ArrayProperty<Property elem>`, which now stores a variable-length array of some property as `SmallVector<elem.storageType>` and uses `ArrayRef<elem.storageType>` as its interface type. It has `IntArrayProperty` subclasses that change its conversion to attributes to use `DenseI[N]Attr`s instead of an `ArrayAttr`. Similarly, `OptionalProperty<Property p>` wraps a property's storage in `std::optional<>` and adds a `std::nullopt` default value. In the case where the underlying property can be parsed optionally but doesn't have its own default value, `OptionalProperty` can piggyback off the optional parser to produce a cleaner syntax, as opposed to its general form, which is either `none` or `some<[value]>`. (Note that `OptionalProperty` can be nested if desired). ## Autogeneration changes Operations and adaptors now support getters and setters for properties like those for attributes. Unlike for attributes, there aren't separate value and attribute forms, since there is no `FooAttr()` available for a `getFooAttr()` to return. The largest change is to operation formats. Previously, properties could only be used in custom directives. Now, they can be used anywhere an attribute could be used, and have parsers and printers defined in their tablegen records. These updates include special `UnitProperty` logic like that used for `UnitAttr`. ## Misc. Some attempt has been made to test the new functionality. This commit takes tentative steps towards updating the documentation to account for properties. A full update will be in order once any followup work has been completed and the interfaces have stabilized. --------- Co-authored-by: Mehdi Amini <[email protected]> Co-authored-by: Christian Ulmann <[email protected]>
1 parent 65361ff commit 8955e28

File tree

20 files changed

+1486
-203
lines changed

20 files changed

+1486
-203
lines changed

mlir/docs/DefiningDialects/Operations.md

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ their semantics via a special [TableGen backend][TableGenBackend]:
101101
* The `AttrConstraint` class hierarchy: They are used to specify the
102102
constraints over attributes. A notable subclass hierarchy is `Attr`, which
103103
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. This will be expanded to a
106+
`PropertyConstraint` class or something similar in the future.
104107

105108
An operation is defined by specializing the `Op` class with concrete contents
106109
for all the fields it requires. For example, `tf.AvgPool` is defined as
@@ -172,9 +175,9 @@ understanding the operation.
172175
173176
### Operation arguments
174177

175-
There are two kinds of arguments: operands and attributes. Operands are runtime
176-
values produced by other ops; while attributes are compile-time known constant
177-
values, including two categories:
178+
There are three kinds of arguments: operands, attributes, and properties.
179+
Operands are runtime values produced by other ops; while attributes and properties
180+
are compile-time known constant values, including two categories:
178181

179182
1. Natural attributes: these attributes affect the behavior of the operations
180183
(e.g., padding for convolution);
@@ -187,22 +190,27 @@ values, including two categories:
187190
even though they are not materialized, it should be possible to store as an
188191
attribute.
189192

190-
Both operands and attributes are specified inside the `dag`-typed `arguments`,
191-
led by `ins`:
193+
Properties are similar to attributes, except that they are not stored within
194+
the MLIR context but are stored inline with the operation.
195+
196+
Operands, attributes, and properties are specified inside the `dag`-typed
197+
`arguments`, led by `ins`:
192198

193199
```tablegen
194200
let arguments = (ins
195201
<type-constraint>:$<operand-name>,
196202
...
197203
<attr-constraint>:$<attr-name>,
198204
...
205+
<property-constraint>:$<property-name>,
199206
);
200207
```
201208

202209
Here `<type-constraint>` is a TableGen `def` from the `TypeConstraint` class
203210
hierarchy. Similarly, `<attr-constraint>` is a TableGen `def` from the
204-
`AttrConstraint` class hierarchy. See [Constraints](#constraints) for more
205-
information.
211+
`AttrConstraint` class hierarchy and `<property-constraint>` is a subclass
212+
of `Property` (though a `PropertyConstraint` hierarchy is planned).
213+
See [Constraints](#constraints) for more information.
206214

207215
There is no requirements on the relative order of operands and attributes; they
208216
can mix freely. The relative order of operands themselves matters. From each
@@ -324,6 +332,18 @@ Right now, the following primitive constraints are supported:
324332

325333
TODO: Design and implement more primitive constraints
326334

335+
#### Optional and default-valued properties
336+
337+
To declare a property with a default value, use `DefaultValuedProperty<..., "...">`.
338+
If the property's storage data type is different from its interface type,
339+
for example, in the case of array properties (which are stored as `SmallVector`s
340+
but use `ArrayRef` as an interface type), add the storage-type equivalent
341+
of the default value as the third argument.
342+
343+
To declare an optional property, use `OptionalProperty<...>`.
344+
This wraps the underlying property in an `std::optional` and gives it a
345+
default value of `std::nullopt`.
346+
327347
#### Combining constraints
328348

329349
`AllAttrOf` is provided to allow combination of multiple constraints which
@@ -429,6 +449,8 @@ def MyOp : ... {
429449
I32Attr:$i32_attr,
430450
F32Attr:$f32_attr,
431451
...
452+
I32Property:$i32_prop,
453+
...
432454
);
433455
434456
let results = (outs
@@ -453,7 +475,8 @@ static void build(OpBuilder &odsBuilder, OperationState &odsState,
453475
static void build(OpBuilder &odsBuilder, OperationState &odsState,
454476
Type i32_result, Type f32_result, ...,
455477
Value i32_operand, Value f32_operand, ...,
456-
IntegerAttr i32_attr, FloatAttr f32_attr, ...);
478+
IntegerAttr i32_attr, FloatAttr f32_attr, ...,
479+
int32_t i32_prop);
457480

458481
// Each result-type/operand/attribute has a separate parameter. The parameters
459482
// for attributes are raw values unwrapped with mlir::Attribute instances.
@@ -462,13 +485,15 @@ static void build(OpBuilder &odsBuilder, OperationState &odsState,
462485
static void build(OpBuilder &odsBuilder, OperationState &odsState,
463486
Type i32_result, Type f32_result, ...,
464487
Value i32_operand, Value f32_operand, ...,
465-
APInt i32_attr, StringRef f32_attr, ...);
488+
APInt i32_attr, StringRef f32_attr, ...,
489+
int32_t i32_prop, ...);
466490

467491
// Each operand/attribute has a separate parameter but result type is aggregate.
468492
static void build(OpBuilder &odsBuilder, OperationState &odsState,
469493
TypeRange resultTypes,
470494
Value i32_operand, Value f32_operand, ...,
471-
IntegerAttr i32_attr, FloatAttr f32_attr, ...);
495+
IntegerAttr i32_attr, FloatAttr f32_attr, ...,
496+
int32_t i32_prop, ...);
472497

473498
// All operands/attributes have aggregate parameters.
474499
// Generated if return type can be inferred.
@@ -921,8 +946,10 @@ optional-group: `(` then-elements `)` (`:` `(` else-elements `)`)? `?`
921946
The elements of an optional group have the following requirements:
922947

923948
* The first element of `then-elements` must either be a attribute, literal,
924-
operand, or region.
949+
operand, property, or region.
925950
- This is because the first element must be optionally parsable.
951+
- If a property is used, it must have an `optionalParser` defined and have a
952+
default value.
926953
* Exactly one argument variable or type directive within either
927954
`then-elements` or `else-elements` must be marked as the anchor of the
928955
group.
@@ -984,6 +1011,8 @@ foo.op is_read_only
9841011
foo.op
9851012
```
9861013

1014+
The same logic applies to a `UnitProperty`.
1015+
9871016
##### Optional "else" Group
9881017

9891018
Optional groups also have support for an "else" group of elements. These are
@@ -1026,6 +1055,8 @@ to:
10261055
1. All operand and result types must appear within the format using the various
10271056
`type` directives, either individually or with the `operands` or `results`
10281057
directives.
1058+
1. Unless all non-attribute properties appear in the format, the `prop-dict`
1059+
directive must be present.
10291060
1. The `attr-dict` directive must always be present.
10301061
1. Must not contain overlapping information; e.g. multiple instances of
10311062
'attr-dict', types, operands, etc.

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,9 @@ class LLVM_IntArithmeticOpWithOverflowFlag<string mnemonic, string instName,
5959
list<Trait> traits = []> :
6060
LLVM_ArithmeticOpBase<AnySignlessInteger, mnemonic, instName,
6161
!listconcat([DeclareOpInterfaceMethods<IntegerOverflowFlagsInterface>], traits)> {
62-
dag iofArg = (ins EnumProperty<"IntegerOverflowFlags">:$overflowFlags);
62+
dag iofArg = (ins EnumProperty<"IntegerOverflowFlags", "", "IntegerOverflowFlags::none">:$overflowFlags);
6363
let arguments = !con(commonArgs, iofArg);
6464

65-
let builders = [
66-
OpBuilder<(ins "Type":$type, "Value":$lhs, "Value":$rhs,
67-
"IntegerOverflowFlags":$overflowFlags), [{
68-
$_state.getOrAddProperties<Properties>().overflowFlags = overflowFlags;
69-
build($_builder, $_state, type, lhs, rhs);
70-
}]>,
71-
OpBuilder<(ins "Value":$lhs, "Value":$rhs,
72-
"IntegerOverflowFlags":$overflowFlags), [{
73-
$_state.getOrAddProperties<Properties>().overflowFlags = overflowFlags;
74-
build($_builder, $_state, lhs, rhs);
75-
}]>
76-
];
77-
7865
string mlirBuilder = [{
7966
auto op = $_builder.create<$_qualCppClassName>($_location, $lhs, $rhs);
8067
moduleImport.setIntegerOverflowFlags(inst, op);

mlir/include/mlir/IR/ODSSupport.h

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,37 @@ convertFromAttribute(int64_t &storage, Attribute attr,
3333
/// Convert the provided int64_t to an IntegerAttr attribute.
3434
Attribute convertToAttribute(MLIRContext *ctx, int64_t storage);
3535

36+
/// Convert an IntegerAttr attribute to an int32_t, or return an error if the
37+
/// attribute isn't an IntegerAttr. If the optional diagnostic is provided an
38+
/// error message is also emitted.
39+
LogicalResult
40+
convertFromAttribute(int32_t &storage, Attribute attr,
41+
function_ref<InFlightDiagnostic()> emitError);
42+
43+
/// Convert the provided int32_t to an IntegerAttr attribute.
44+
Attribute convertToAttribute(MLIRContext *ctx, int32_t storage);
45+
46+
/// Extract the string from `attr` into `storage`. If `attr` is not a
47+
/// `StringAttr`, return failure and emit an error into the diagnostic from
48+
/// `emitError`.
49+
LogicalResult
50+
convertFromAttribute(std::string &storage, Attribute attr,
51+
function_ref<InFlightDiagnostic()> emitError);
52+
53+
/// Convert the given string into a StringAttr. Note that this takes a reference
54+
/// to the storage of a string property, which is an std::string.
55+
Attribute convertToAttribute(MLIRContext *ctx, const std::string &storage);
56+
57+
/// Extract the boolean from `attr` into `storage`. If `attr` is not a
58+
/// `BoolAttr`, return failure and emit an error into the diagnostic from
59+
/// `emitError`.
60+
LogicalResult
61+
convertFromAttribute(bool &storage, Attribute attr,
62+
function_ref<InFlightDiagnostic()> emitError);
63+
64+
/// Convert the given string into a BooleanAttr.
65+
Attribute convertToAttribute(MLIRContext *ctx, bool storage);
66+
3667
/// Convert a DenseI64ArrayAttr to the provided storage. It is expected that the
3768
/// storage has the same size as the array. An error is returned if the
3869
/// attribute isn't a DenseI64ArrayAttr or it does not have the same size. If
@@ -49,9 +80,24 @@ LogicalResult
4980
convertFromAttribute(MutableArrayRef<int32_t> storage, Attribute attr,
5081
function_ref<InFlightDiagnostic()> emitError);
5182

83+
/// Convert a DenseI64ArrayAttr to the provided storage, which will be
84+
/// cleared before writing. An error is returned and emitted to the optional
85+
/// `emitError` function if the attribute isn't a DenseI64ArrayAttr.
86+
LogicalResult
87+
convertFromAttribute(SmallVectorImpl<int64_t> &storage, Attribute attr,
88+
function_ref<InFlightDiagnostic()> emitError);
89+
90+
/// Convert a DenseI32ArrayAttr to the provided storage, which will be
91+
/// cleared before writing. It is expected that the storage has the same size as
92+
/// the array. An error is returned and emitted to the optional `emitError`
93+
/// function if the attribute isn't a DenseI32ArrayAttr.
94+
LogicalResult
95+
convertFromAttribute(SmallVectorImpl<int32_t> &storage, Attribute attr,
96+
function_ref<InFlightDiagnostic()> emitError);
97+
5298
/// Convert the provided ArrayRef<int64_t> to a DenseI64ArrayAttr attribute.
5399
Attribute convertToAttribute(MLIRContext *ctx, ArrayRef<int64_t> storage);
54100

55101
} // namespace mlir
56102

57-
#endif // MLIR_IR_ODSSUPPORT_H
103+
#endif // MLIR_IR_ODSSUPPORT_H

0 commit comments

Comments
 (0)