``` {.ebnf}
attribute-dict ::= `{` `}`
| `{` attribute-entry (`,` attribute-entry)* `}`
-attribute-entry ::= `:`? bare-id `:` attribute-value
+attribute-entry ::= dialect-attribute-entry | dependent-attribute-entry
+dialect-attribute-entry ::= dialect-namespace `.` bare-id `:` attribute-value
+dependent-attribute-entry ::= dependent-attribute-name `:` attribute-value
+dependent-attribute-name ::= (letter|[_]) (letter|digit|[_$])*
```
Attributes are the mechanism for specifying constant data in MLIR in places
where a variable is never allowed - e.g. the index of a
-[`dim` operation](#'dim'-operation), or the stride of a convolution.
+[`dim` operation](#'dim'-operation), or the stride of a convolution. They
+consist of a name and a [concrete attribute value](#attribute-values). It is
+possible to attach attributes to operations, functions, and function arguments.
+The set of expected attributes, their structure, and their interpretation are
+all contextually dependent on what they are attached to.
-Attributes have a name, and their values are represented by the following forms:
+There are two main classes of attributes; dependent and dialect. Dependent
+attributes derive their structure and meaning from what they are attached to,
+e.g the meaning of the `index` attribute on a `dim` operation is defined by the
+`dim` operation. Dialect attributes, on the other hand, derive their context and
+meaning from a specific dialect. An example of a dialect attribute may be a
+`swift.self` function argument attribute that indicates an argument is the
+self/context parameter. The context of this attribute is defined by the `swift`
+dialect and not the function argument.
+
+### Function and Argument Attributes
+
+Functions and function arguments in MLIR may have optional attributes attached
+to them. The sole constraint for these attributes is that they must be dialect
+specific attributes. This is because functions, and function arguments, are a
+generic entities and thus cannot apply any meaningful context necessary for
+dependent attributes. This has the added benefit of avoiding collisions between
+common attribute names, such as `noalias`.
+
+### Operation Attributes
+
+Operations, unlike functions and function arguments, may include both dialect
+specific and dependent attributes. This is because an operation represents a
+distinct semantic context, and can thus provide a single source of meaning to
+dependent attributes.
+
+### Attribute Values {#attribute-values}
+
+Attributes values are represented by the following forms:
``` {.ebnf}
attribute-value ::= affine-map-attribute
| type-attribute
```
-It is possible to attach attributes to instructions and functions, and the set
-of expected attributes, their structure, and the definition of that meaning is
-contextually dependent on the operation they are attached to.
-
-### AffineMap Attribute {#affine-map-attribute}
+#### AffineMap Attribute {#affine-map-attribute}
Syntax:
An affine-map attribute is an attribute that represents a affine-map object.
-### Array Attribute {#array-attribute}
+#### Array Attribute {#array-attribute}
Syntax:
An array attribute is an attribute that represents a collection of attribute
values.
-### Boolean Attribute {#bool-attribute}
+#### Boolean Attribute {#bool-attribute}
Syntax:
A boolean attribute is a literal attribute that represents a one-bit boolean
value, true or false.
-### Elements Attributes {#elements-attributes}
+#### Elements Attributes {#elements-attributes}
Syntax:
An elements attribute is a literal attribute that represents a constant
[vector](#vector-type) or [tensor](#tensor-type) value.
-#### Dense Elements Attribute {#dense-elements-attribute}
+##### Dense Elements Attribute {#dense-elements-attribute}
Syntax:
element type of the vector or tensor constant must be of integer, index, or
floating point type.
-#### Opaque Elements Attribute {#opaque-elements-attribute}
+##### Opaque Elements Attribute {#opaque-elements-attribute}
Syntax:
Note: The parsed string literal must be in hexadecimal form.
-#### Sparse Elements Attribute {#sparse-elements-attribute}
+##### Sparse Elements Attribute {#sparse-elements-attribute}
Syntax:
/// [0, 0, 0, 0]]
```
-#### Splat Elements Attribute {#splat-elements-attribute}
+##### Splat Elements Attribute {#splat-elements-attribute}
Syntax:
A splat elements attribute is an elements attribute that represents a tensor or
vector constant where all elements have the same value.
-### Integer Attribute {#integer-attribute}
+#### Integer Attribute {#integer-attribute}
Syntax:
the specified integer or index type. The default type for this attribute, if one
is not specified, is a 64-bit integer.
-### Integer Set Attribute {#integer-set-attribute}
+#### Integer Set Attribute {#integer-set-attribute}
Syntax:
An integer-set attribute is an attribute that represents a integer-set object.
-### Float Attribute {#float-attribute}
+#### Float Attribute {#float-attribute}
Syntax:
A float attribute is a literal attribute that represents a floating point value
of the specified [float type](#floating-point-types).
-### Function Attribute {#function-attribute}
+#### Function Attribute {#function-attribute}
Syntax:
A function attribute is a literal attribute that represents a reference to the
given function object.
-### String Attribute {#string-attribute}
+#### String Attribute {#string-attribute}
Syntax:
A string attribute is an attribute that represents a string literal value.
-### Type Attribute {#type-attribute}
+#### Type Attribute {#type-attribute}
Syntax:
virtual void
getTypeAliases(SmallVectorImpl<std::pair<StringRef, Type>> &aliases) {}
+ /// Verify an attribute from this dialect on the given function. Returns true
+ /// if the verification failed, false otherwise.
+ virtual bool verifyFunctionAttribute(const Function *, Attribute) {
+ return false;
+ }
+
+ /// Verify an attribute from this dialect on the argument at 'argIndex' for
+ /// the given function. Returns true if the verification failed, false
+ /// otherwise.
+ virtual bool verifyFunctionArgAttribute(const Function *, unsigned argIndex,
+ Attribute) {
+ return false;
+ }
+
+ /// Verify an attribute from this dialect on the given instruction. Returns
+ /// true if the verification failed, false otherwise.
+ virtual bool verifyInstructionAttribute(const Instruction *, Attribute) {
+ return false;
+ }
+
virtual ~Dialect();
/// Utility function that returns if the given string is a valid dialect
#include "mlir/Analysis/Dominance.h"
#include "mlir/IR/Attributes.h"
+#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Module.h"
return failure(message, fn);
}
+ /// Returns the registered dialect for a dialect-specific attribute.
+ template <typename ErrorContext>
+ Dialect *getDialectForAttribute(const NamedAttribute &attr,
+ const ErrorContext &ctx) {
+ assert(attr.first.strref().contains('.') && "expected dialect attribute");
+ auto dialectNamePair = attr.first.strref().split('.');
+ return fn.getContext()->getRegisteredDialect(dialectNamePair.first);
+ }
+
template <typename ErrorContext>
bool verifyAttribute(Attribute attr, const ErrorContext &ctx) {
if (!attr.isOrContainsFunction())
bool verifyInstDominance(const Instruction &inst);
explicit FuncVerifier(const Function &fn)
- : fn(fn), attrNameRegex("^:?[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
+ : fn(fn), identifierRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
private:
/// The function being checked.
DominanceInfo *domInfo = nullptr;
/// Regex checker for attribute names.
- llvm::Regex attrNameRegex;
+ llvm::Regex identifierRegex;
};
} // end anonymous namespace
fn.getName().c_str());
// Check that the function name is valid.
- llvm::Regex funcNameRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$");
- if (!funcNameRegex.match(fn.getName().strref()))
+ if (!identifierRegex.match(fn.getName().strref()))
return failure("invalid function name '" + fn.getName().strref() + "'", fn);
/// Verify that all of the attributes are okay.
for (auto attr : fn.getAttrs()) {
- if (!attrNameRegex.match(attr.first))
+ if (!identifierRegex.match(attr.first))
return failure("invalid attribute name '" + attr.first.strref() + "'",
fn);
if (verifyAttribute(attr.second, fn))
return true;
+
+ /// Check that the attribute is a dialect attribute, i.e. contains a '.' for
+ /// the namespace.
+ if (!attr.first.strref().contains('.')) {
+ // TODO: Remove the remaining usages of non dialect attributes on
+ // functions and then enable this check.
+ // return failure("functions may only have dialect attributes", fn);
+ continue;
+ }
+
+ // Verify this attribute with the defining dialect.
+ if (auto *dialect = getDialectForAttribute(attr, fn))
+ if (dialect->verifyFunctionAttribute(&fn, attr.second))
+ return true;
}
/// Verify that all of the argument attributes are okay.
for (unsigned i = 0, e = fn.getNumArguments(); i != e; ++i) {
for (auto attr : fn.getArgAttrs(i)) {
- if (!attrNameRegex.match(attr.first))
+ if (!identifierRegex.match(attr.first))
return failure(
llvm::formatv("invalid attribute name '{0}' on argument {1}",
attr.first.strref(), i),
fn);
if (verifyAttribute(attr.second, fn))
return true;
+
+ /// Check that the attribute is a dialect attribute, i.e. contains a '.'
+ /// for the namespace.
+ if (!attr.first.strref().contains('.'))
+ return failure("function arguments may only have dialect attributes",
+ fn);
+
+ // Verify this attribute with the defining dialect.
+ if (auto *dialect = getDialectForAttribute(attr, fn))
+ if (dialect->verifyFunctionArgAttribute(&fn, i, attr.second))
+ return true;
}
}
/// Verify that all of the attributes are okay.
for (auto attr : op.getAttrs()) {
- if (!attrNameRegex.match(attr.first))
+ if (!identifierRegex.match(attr.first))
return failure("invalid attribute name '" + attr.first.strref() + "'",
op);
if (verifyAttribute(attr.second, op))
return true;
+
+ // Check for any optional dialect specific attributes.
+ if (!attr.first.strref().contains('.'))
+ continue;
+ if (auto *dialect = getDialectForAttribute(attr, op))
+ if (dialect->verifyInstructionAttribute(&op, attr.second))
+ return true;
}
// If we can get operation info for this, check the custom hook.
llvm::cl::desc("Print the generic op form"),
llvm::cl::init(false), llvm::cl::Hidden);
-static llvm::cl::opt<bool> printInternalAttributes(
- "mlir-print-internal-attributes",
- llvm::cl::desc(
- "Print internal function and instruction attributes (':' prefix)."),
- llvm::cl::init(false));
-
namespace {
class ModuleState {
public:
// Filter out any attributes that shouldn't be included.
SmallVector<NamedAttribute, 8> filteredAttrs;
for (auto attr : attrs) {
- auto attrName = attr.first.strref();
- // By default, never print attributes that start with a colon. These are
- // attributes represent location or other internal metadata.
- if (!printInternalAttributes && attrName.startswith(":"))
- return;
-
// If the caller has requested that this attribute be ignored, then drop it.
- bool ignore = false;
- for (auto elide : elidedAttrs)
- ignore |= attrName == elide;
+ if (llvm::any_of(elidedAttrs,
+ [&](StringRef elided) { return attr.first.is(elided); }))
+ continue;
// Otherwise add it to our filteredAttrs list.
- if (!ignore) {
- filteredAttrs.push_back(attr);
- }
+ filteredAttrs.push_back(attr);
}
// If there are no attributes left to print after filtering, then we're done.
///
/// attribute-dict ::= `{` `}`
/// | `{` attribute-entry (`,` attribute-entry)* `}`
-/// attribute-entry ::= `:`? bare-id `:` attribute-value
+/// attribute-entry ::= bare-id `:` attribute-value
///
ParseResult
Parser::parseAttributeDict(SmallVectorImpl<NamedAttribute> &attributes) {
return ParseFailure;
auto parseElt = [&]() -> ParseResult {
- // Check for an internal attribute.
- bool isInternalAttr = consumeIf(Token::colon);
-
// We allow keywords as attribute names.
if (getToken().isNot(Token::bare_identifier, Token::inttype) &&
!getToken().isKeyword())
return emitError("expected attribute name");
- Identifier nameId =
- isInternalAttr
- ? builder.getIdentifier(Twine(":" + getTokenSpelling()).str())
- : builder.getIdentifier(getTokenSpelling());
+ Identifier nameId = builder.getIdentifier(getTokenSpelling());
consumeToken();
if (parseToken(Token::colon, "expected ':' in attribute list"))
// expected-error @+1 {{@ identifier expected to start with letter or '_'}}
func @$invalid_function_name()
+
+// -----
+
+// expected-error @+1 {{function arguments may only have dialect attributes}}
+func @invalid_func_arg_attr(i1 {non_dialect_attr: 10})
-// RUN: mlir-opt -mlir-print-internal-attributes %s | FileCheck %s
+// RUN: mlir-opt %s | FileCheck %s
// CHECK-DAG: #map{{[0-9]+}} = (d0, d1, d2, d3, d4)[s0] -> (d0, d1, d2, d4, d3)
#map0 = (d0, d1, d2, d3, d4)[s0] -> (d0, d1, d2, d4, d3)
// CHECK-LABEL: func @externalfuncattr
func @externalfuncattr() -> ()
- // CHECK: attributes {a: "a\22quoted\22string", b: 4.000000e+00, c: tensor<*xf32>}
- attributes {a: "a\"quoted\"string", b: 4.0, c: tensor<*xf32>}
+ // CHECK: attributes {dialect.a: "a\22quoted\22string", dialect.b: 4.000000e+00, dialect.c: tensor<*xf32>}
+ attributes {dialect.a: "a\"quoted\"string", dialect.b: 4.0, dialect.c: tensor<*xf32>}
// CHECK-LABEL: func @funcattrempty
func @funcattrempty() -> ()
// CHECK-LABEL: func @funcattr
func @funcattr() -> ()
- // CHECK: attributes {a: "a\22quoted\22string", b: 4.000000e+00, c: tensor<*xf32>}
- attributes {a: "a\"quoted\"string", b: 4.0, c: tensor<*xf32>} {
+ // CHECK: attributes {dialect.a: "a\22quoted\22string", dialect.b: 4.000000e+00, dialect.c: tensor<*xf32>}
+ attributes {dialect.a: "a\"quoted\"string", dialect.b: 4.0, dialect.c: tensor<*xf32>} {
^bb0:
return
}
return %arg1 : i1
}
-// CHECK-LABEL: func @internal_attrs
-func @internal_attrs()
- // CHECK-NEXT: attributes {:internal.attr: 10
- attributes {:internal.attr: 10} {
+// CHECK-LABEL: func @dialect_attrs
+func @dialect_attrs()
+ // CHECK-NEXT: attributes {dialect.attr: 10
+ attributes {dialect.attr: 10} {
return
}
// CHECK-LABEL: func @_valid.function$name
func @_valid.function$name()
-// CHECK-LABEL: func @external_func_arg_attrs(i32, i1 {arg.attr: 10}, i32)
-func @external_func_arg_attrs(i32, i1 {arg.attr: 10}, i32)
+// CHECK-LABEL: func @external_func_arg_attrs(i32, i1 {dialect.attr: 10}, i32)
+func @external_func_arg_attrs(i32, i1 {dialect.attr: 10}, i32)
-// CHECK-LABEL: func @func_arg_attrs(%arg0: i1 {arg.attr: 10})
-func @func_arg_attrs(%arg0: i1 {arg.attr: 10}) {
+// CHECK-LABEL: func @func_arg_attrs(%arg0: i1 {dialect.attr: 10})
+func @func_arg_attrs(%arg0: i1 {dialect.attr: 10}) {
return
}