Skip to content

Commit d572cd1

Browse files
committed
Introduce MLIR Op Properties
This new features enabled to dedicate custom storage inline within operations. This storage can be used as an alternative to attributes to store data that is specific to an operation. Attribute can also be stored inside the properties storage if desired, but any kind of data can be present as well. This offers a way to store and mutate data without uniquing in the Context like Attribute. See the OpPropertiesTest.cpp for an example where a struct with a std::vector<> is attached to an operation and mutated in-place: struct TestProperties { int a = -1; float b = -1.; std::vector<int64_t> array = {-33}; }; More complex scheme (including reference-counting) are also possible. The only constraint to enable storing a C++ object as "properties" on an operation is to implement three functions: - convert from the candidate object to an Attribute - convert from the Attribute to the candidate object - hash the object Optional the parsing and printing can also be customized with 2 extra functions. A new options is introduced to ODS to allow dialects to specify: let usePropertiesForAttributes = 1; When set to true, the inherent attributes for all the ops in this dialect will be using properties instead of being stored alongside discardable attributes. The TestDialect showcases this feature. Another change is that we introduce new APIs on the Operation class to access separately the inherent attributes from the discardable ones. We envision deprecating and removing the `getAttr()`, `getAttrsDictionary()`, and other similar method which don't make the distinction explicit, leading to an entirely separate namespace for discardable attributes. Differential Revision: https://reviews.llvm.org/D141742
1 parent 04fc02e commit d572cd1

File tree

84 files changed

+3081
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+3081
-478
lines changed

mlir/docs/LangRef.md

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,14 @@ Syntax:
290290
operation ::= op-result-list? (generic-operation | custom-operation)
291291
trailing-location?
292292
generic-operation ::= string-literal `(` value-use-list? `)` successor-list?
293-
region-list? dictionary-attribute? `:` function-type
293+
dictionary-properties? region-list? dictionary-attribute?
294+
`:` function-type
294295
custom-operation ::= bare-id custom-operation-format
295296
op-result-list ::= op-result (`,` op-result)* `=`
296297
op-result ::= value-id (`:` integer-literal)
297298
successor-list ::= `[` successor (`,` successor)* `]`
298299
successor ::= caret-id (`:` block-arg-list)?
300+
dictionary-propertes ::= `<` dictionary-attribute `>`
299301
region-list ::= `(` region (`,` region)* `)`
300302
dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)? `}`
301303
trailing-location ::= (`loc` `(` location `)`)?
@@ -312,9 +314,10 @@ semantics. For example, MLIR supports
312314
The internal representation of an operation is simple: an operation is
313315
identified by a unique string (e.g. `dim`, `tf.Conv2d`, `x86.repmovsb`,
314316
`ppc.eieio`, etc), can return zero or more results, take zero or more operands,
315-
has a dictionary of [attributes](#attributes), has zero or more successors, and
316-
zero or more enclosed [regions](#regions). The generic printing form includes
317-
all these elements literally, with a function type to indicate the types of the
317+
has storage for [properties](#properties), has a dictionary of
318+
[attributes](#attributes), has zero or more successors, and zero or more
319+
enclosed [regions](#regions). The generic printing form includes all these
320+
elements literally, with a function type to indicate the types of the
318321
results and operands.
319322

320323
Example:
@@ -328,8 +331,11 @@ Example:
328331
%foo, %bar = "foo_div"() : () -> (f32, i32)
329332
330333
// Invoke a TensorFlow function called tf.scramble with two inputs
331-
// and an attribute "fruit".
332-
%2 = "tf.scramble"(%result#0, %bar) {fruit = "banana"} : (f32, i32) -> f32
334+
// and an attribute "fruit" stored in properties.
335+
%2 = "tf.scramble"(%result#0, %bar) <{fruit = "banana"}> : (f32, i32) -> f32
336+
337+
// Invoke an operation with some discardable attributes
338+
%foo, %bar = "foo_div"() {some_attr = "value", other_attr = 42 : i64} : () -> (f32, i32)
333339
```
334340

335341
In addition to the basic syntax above, dialects may register known operations.
@@ -733,6 +739,15 @@ The [builtin dialect](Dialects/Builtin.md) defines a set of types that are
733739
directly usable by any other dialect in MLIR. These types cover a range from
734740
primitive integer and floating-point types, function types, and more.
735741

742+
## Properties
743+
744+
Properties are extra data members stored directly on an Operation class. They
745+
provide a way to store [inherent attributes](#attributes) and other arbitrary
746+
data. The semantics of the data is specific to a given operation, and may be
747+
exposed through [Interfaces](Interfaces.md) accessors and other methods.
748+
Properties can always be serialized to Attribute in order to be printed
749+
generically.
750+
736751
## Attributes
737752

738753
Syntax:
@@ -751,9 +766,10 @@ values. MLIR's builtin dialect provides a rich set of
751766
arrays, dictionaries, strings, etc.). Additionally, dialects can define their
752767
own [dialect attribute values](#dialect-attribute-values).
753768

754-
The top-level attribute dictionary attached to an operation has special
755-
semantics. The attribute entries are considered to be of two different kinds
756-
based on whether their dictionary key has a dialect prefix:
769+
For dialects which haven't adopted properties yet, the top-level attribute
770+
dictionary attached to an operation has special semantics. The attribute
771+
entries are considered to be of two different kinds based on whether their
772+
dictionary key has a dialect prefix:
757773

758774
- *inherent attributes* are inherent to the definition of an operation's
759775
semantics. The operation itself is expected to verify the consistency of
@@ -771,6 +787,10 @@ Note that attribute values are allowed to themselves be dictionary attributes,
771787
but only the top-level dictionary attribute attached to the operation is subject
772788
to the classification above.
773789

790+
When properties are adopted, only discardable attributes are stored in the
791+
top-level dictionary, while inherent attributes are stored in the properties
792+
storage.
793+
774794
### Attribute Value Aliases
775795

776796
```

mlir/include/mlir/Conversion/LLVMCommon/Pattern.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ class ConvertOpToLLVMPattern : public ConvertToLLVMPattern {
145145
/// Wrappers around the RewritePattern methods that pass the derived op type.
146146
void rewrite(Operation *op, ArrayRef<Value> operands,
147147
ConversionPatternRewriter &rewriter) const final {
148+
if constexpr (SourceOp::hasProperties())
149+
rewrite(cast<SourceOp>(op),
150+
OpAdaptor(operands, op->getAttrDictionary(),
151+
cast<SourceOp>(op).getProperties()),
152+
rewriter);
148153
rewrite(cast<SourceOp>(op), OpAdaptor(operands, op->getAttrDictionary()),
149154
rewriter);
150155
}
@@ -154,6 +159,11 @@ class ConvertOpToLLVMPattern : public ConvertToLLVMPattern {
154159
LogicalResult
155160
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
156161
ConversionPatternRewriter &rewriter) const final {
162+
if constexpr (SourceOp::hasProperties())
163+
return matchAndRewrite(cast<SourceOp>(op),
164+
OpAdaptor(operands, op->getAttrDictionary(),
165+
cast<SourceOp>(op).getProperties()),
166+
rewriter);
157167
return matchAndRewrite(cast<SourceOp>(op),
158168
OpAdaptor(operands, op->getAttrDictionary()),
159169
rewriter);

mlir/include/mlir/IR/DialectBase.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ class Dialect {
103103

104104
// If this dialect can be extended at runtime with new operations or types.
105105
bit isExtensible = 0;
106+
107+
// Whether inherent Attributes defined in ODS will be stored as Properties.
108+
bit usePropertiesForAttributes = 0;
106109
}
107110

108111
#endif // DIALECTBASE_TD

mlir/include/mlir/IR/ExtensibleDialect.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "mlir/IR/OpDefinition.h"
2727
#include "mlir/Support/TypeID.h"
2828
#include "llvm/ADT/StringMap.h"
29+
#include "llvm/Support/ErrorHandling.h"
30+
#include <optional>
2931

3032
namespace mlir {
3133
class AsmParser;
@@ -462,6 +464,35 @@ class DynamicOpDefinition : public OperationName::Impl {
462464
return verifyRegionFn(op);
463465
}
464466

467+
/// Implementation for properties (unsupported right now here).
468+
std::optional<Attribute> getInherentAttr(Operation *op,
469+
StringRef name) final {
470+
llvm::report_fatal_error("Unsupported getInherentAttr on Dynamic dialects");
471+
}
472+
void setInherentAttr(Operation *op, StringAttr name, Attribute value) final {
473+
llvm::report_fatal_error("Unsupported setInherentAttr on Dynamic dialects");
474+
}
475+
void populateInherentAttrs(Operation *op, NamedAttrList &attrs) final {}
476+
LogicalResult
477+
verifyInherentAttrs(OperationName opName, NamedAttrList &attributes,
478+
function_ref<InFlightDiagnostic()> getDiag) final {
479+
return success();
480+
}
481+
int getOpPropertyByteSize() final { return 0; }
482+
void initProperties(OperationName opName, OpaqueProperties storage,
483+
OpaqueProperties init) final {}
484+
void deleteProperties(OpaqueProperties prop) final {}
485+
void populateDefaultProperties(OperationName opName,
486+
OpaqueProperties properties) final {}
487+
488+
LogicalResult setPropertiesFromAttr(Operation *op, Attribute attr,
489+
InFlightDiagnostic *diag) final {
490+
return failure();
491+
}
492+
Attribute getPropertiesAsAttr(Operation *op) final { return {}; }
493+
void copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) final {}
494+
llvm::hash_code hashProperties(OpaqueProperties prop) final { return {}; }
495+
465496
private:
466497
DynamicOpDefinition(
467498
StringRef name, ExtensibleDialect *dialect,

mlir/include/mlir/IR/ODSSupport.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//===- ODSSupport.h ---------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines a number of support method for ODS generated code.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef MLIR_IR_ODSSUPPORT_H
14+
#define MLIR_IR_ODSSUPPORT_H
15+
16+
#include "mlir/IR/Attributes.h"
17+
18+
namespace mlir {
19+
20+
//===----------------------------------------------------------------------===//
21+
// Support for properties
22+
//===----------------------------------------------------------------------===//
23+
24+
/// Convert an IntegerAttr attribute to an int64_t, or return an error if the
25+
/// attribute isn't an IntegerAttr. If the optional diagnostic is provided an
26+
/// error message is also emitted.
27+
LogicalResult convertFromAttribute(int64_t &storage, Attribute attr,
28+
InFlightDiagnostic *diag);
29+
30+
/// Convert the provided int64_t to an IntegerAttr attribute.
31+
Attribute convertToAttribute(MLIRContext *ctx, int64_t storage);
32+
33+
/// Convert a DenseI64ArrayAttr to the provided storage. It is expected that the
34+
/// storage has the same size as the array. An error is returned if the
35+
/// attribute isn't a DenseI64ArrayAttr or it does not have the same size. If
36+
/// the optional diagnostic is provided an error message is also emitted.
37+
LogicalResult convertFromAttribute(MutableArrayRef<int64_t> storage,
38+
Attribute attr, InFlightDiagnostic *diag);
39+
40+
/// Convert the provided ArrayRef<int64_t> to a DenseI64ArrayAttr attribute.
41+
Attribute convertToAttribute(MLIRContext *ctx, ArrayRef<int64_t> storage);
42+
43+
} // namespace mlir
44+
45+
#endif // MLIR_IR_ODSSUPPORT_H

mlir/include/mlir/IR/OpBase.td

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,69 @@ class TypeConstraint<Pred predicate, string summary = "",
179179
string cppClassName = cppClassNameParam;
180180
}
181181

182+
// Base class for defining properties.
183+
class Property<string storageTypeParam = "", string desc = ""> {
184+
// User-readable one line summary used in error reporting messages. If empty,
185+
// a generic message will be used.
186+
string summary = desc;
187+
// The full description of this property.
188+
string description = "";
189+
code storageType = storageTypeParam;
190+
code interfaceType = storageTypeParam;
191+
192+
// The expression to convert from the storage type to the Interface
193+
// type. For example, an enum can be stored as an int but returned as an
194+
// enum class.
195+
//
196+
// Format:
197+
// - `$_storage` will contain the property in the storage type.
198+
// - `$_ctxt` will contain an `MLIRContext *`.
199+
code convertFromStorage = "$_storage";
200+
201+
// The call expression to build a property storage from the interface type.
202+
//
203+
// Format:
204+
// - `$_storage` will contain the property in the storage type.
205+
// - `$_value` will contain the property in the user interface type.
206+
code assignToStorage = "$_storage = $_value";
207+
208+
// The call expression to convert from the storage type to an attribute.
209+
//
210+
// Format:
211+
// - `$_storage` is the storage type value.
212+
// - `$_ctxt` is a `MLIRContext *`.
213+
//
214+
// The expression must result in an Attribute.
215+
code convertToAttribute = [{
216+
convertToAttribute($_ctxt, $_storage)
217+
}];
218+
219+
// The call expression to convert from an Attribute to the storage type.
220+
//
221+
// Format:
222+
// - `$_storage` is the storage type value.
223+
// - `$_attr` is the attribute.
224+
// - `$_diag` is an optional Diagnostic pointer to emit error.
225+
//
226+
// The expression must return a LogicalResult
227+
code convertFromAttribute = [{
228+
return convertFromAttribute($_storage, $_attr, $_diag);
229+
}];
230+
231+
// The call expression to hash the property.
232+
//
233+
// Format:
234+
// - `$_storage` is the variable to hash.
235+
//
236+
// The expression should define a llvm::hash_code.
237+
code hashProperty = [{
238+
llvm::hash_value($_storage);
239+
}];
240+
241+
// Default value for the property.
242+
string defaultValue = ?;
243+
}
244+
182245
// Subclass for constraints on an attribute.
183246
class AttrConstraint<Pred predicate, string summary = ""> :
184247
Constraint<predicate, summary>;
@@ -1090,6 +1153,16 @@ class DefaultValuedStrAttr<Attr attr, string val>
10901153
class DefaultValuedOptionalStrAttr<Attr attr, string val>
10911154
: DefaultValuedOptionalAttr<attr, "\"" # val # "\"">;
10921155

1156+
//===----------------------------------------------------------------------===//
1157+
// Primitive property kinds
1158+
1159+
class ArrayProperty<string storageTypeParam = "", int n, string desc = ""> :
1160+
Property<storageTypeParam # "[" # n # "]", desc> {
1161+
let interfaceType = "::llvm::ArrayRef<" # storageTypeParam # ">";
1162+
let convertFromStorage = "$_storage";
1163+
let assignToStorage = "::llvm::copy($_value, $_storage)";
1164+
}
1165+
10931166
//===----------------------------------------------------------------------===//
10941167
// Primitive attribute kinds
10951168

0 commit comments

Comments
 (0)