Skip to content

Commit a495f96

Browse files
River707jpienaar
authored andcommitted
Introduce the notion of dialect attributes and dependent attributes. A dialect attribute derives its context from a specific dialect, whereas a dependent attribute derives context from what it is attached to. Following this, we now enforce that functions and function arguments may only contain dialect specific attributes. These are generic entities and cannot provide any specific context for a dependent attribute.
Dialect attributes are defined as: dialect-namespace `.` attr-name `:` attribute-value Dialects can override any of the following hooks to verify the validity of a given attribute: * verifyFunctionAttribute * verifyFunctionArgAttribute * verifyInstructionAttribute PiperOrigin-RevId: 236507970
1 parent 485746f commit a495f96

File tree

7 files changed

+143
-67
lines changed

7 files changed

+143
-67
lines changed

mlir/g3doc/LangRef.md

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -867,14 +867,48 @@ Syntax:
867867
``` {.ebnf}
868868
attribute-dict ::= `{` `}`
869869
| `{` attribute-entry (`,` attribute-entry)* `}`
870-
attribute-entry ::= `:`? bare-id `:` attribute-value
870+
attribute-entry ::= dialect-attribute-entry | dependent-attribute-entry
871+
dialect-attribute-entry ::= dialect-namespace `.` bare-id `:` attribute-value
872+
dependent-attribute-entry ::= dependent-attribute-name `:` attribute-value
873+
dependent-attribute-name ::= (letter|[_]) (letter|digit|[_$])*
871874
```
872875

873876
Attributes are the mechanism for specifying constant data in MLIR in places
874877
where a variable is never allowed - e.g. the index of a
875-
[`dim` operation](#'dim'-operation), or the stride of a convolution.
878+
[`dim` operation](#'dim'-operation), or the stride of a convolution. They
879+
consist of a name and a [concrete attribute value](#attribute-values). It is
880+
possible to attach attributes to operations, functions, and function arguments.
881+
The set of expected attributes, their structure, and their interpretation are
882+
all contextually dependent on what they are attached to.
876883

877-
Attributes have a name, and their values are represented by the following forms:
884+
There are two main classes of attributes; dependent and dialect. Dependent
885+
attributes derive their structure and meaning from what they are attached to,
886+
e.g the meaning of the `index` attribute on a `dim` operation is defined by the
887+
`dim` operation. Dialect attributes, on the other hand, derive their context and
888+
meaning from a specific dialect. An example of a dialect attribute may be a
889+
`swift.self` function argument attribute that indicates an argument is the
890+
self/context parameter. The context of this attribute is defined by the `swift`
891+
dialect and not the function argument.
892+
893+
### Function and Argument Attributes
894+
895+
Functions and function arguments in MLIR may have optional attributes attached
896+
to them. The sole constraint for these attributes is that they must be dialect
897+
specific attributes. This is because functions, and function arguments, are a
898+
generic entities and thus cannot apply any meaningful context necessary for
899+
dependent attributes. This has the added benefit of avoiding collisions between
900+
common attribute names, such as `noalias`.
901+
902+
### Operation Attributes
903+
904+
Operations, unlike functions and function arguments, may include both dialect
905+
specific and dependent attributes. This is because an operation represents a
906+
distinct semantic context, and can thus provide a single source of meaning to
907+
dependent attributes.
908+
909+
### Attribute Values {#attribute-values}
910+
911+
Attributes values are represented by the following forms:
878912

879913
``` {.ebnf}
880914
attribute-value ::= affine-map-attribute
@@ -889,11 +923,7 @@ attribute-value ::= affine-map-attribute
889923
| type-attribute
890924
```
891925

892-
It is possible to attach attributes to instructions and functions, and the set
893-
of expected attributes, their structure, and the definition of that meaning is
894-
contextually dependent on the operation they are attached to.
895-
896-
### AffineMap Attribute {#affine-map-attribute}
926+
#### AffineMap Attribute {#affine-map-attribute}
897927

898928
Syntax:
899929

@@ -903,7 +933,7 @@ affine-map-attribute ::= affine-map
903933

904934
An affine-map attribute is an attribute that represents a affine-map object.
905935

906-
### Array Attribute {#array-attribute}
936+
#### Array Attribute {#array-attribute}
907937

908938
Syntax:
909939

@@ -914,7 +944,7 @@ array-attribute ::= `[` (attribute-value (`,` attribute-value)*)? `]`
914944
An array attribute is an attribute that represents a collection of attribute
915945
values.
916946

917-
### Boolean Attribute {#bool-attribute}
947+
#### Boolean Attribute {#bool-attribute}
918948

919949
Syntax:
920950

@@ -925,7 +955,7 @@ bool-attribute ::= bool-literal
925955
A boolean attribute is a literal attribute that represents a one-bit boolean
926956
value, true or false.
927957

928-
### Elements Attributes {#elements-attributes}
958+
#### Elements Attributes {#elements-attributes}
929959

930960
Syntax:
931961

@@ -939,7 +969,7 @@ elements-attribute ::= dense-elements-attribute
939969
An elements attribute is a literal attribute that represents a constant
940970
[vector](#vector-type) or [tensor](#tensor-type) value.
941971

942-
#### Dense Elements Attribute {#dense-elements-attribute}
972+
##### Dense Elements Attribute {#dense-elements-attribute}
943973

944974
Syntax:
945975

@@ -953,7 +983,7 @@ constant vector or tensor value has been packed to the element bitwidth. The
953983
element type of the vector or tensor constant must be of integer, index, or
954984
floating point type.
955985

956-
#### Opaque Elements Attribute {#opaque-elements-attribute}
986+
##### Opaque Elements Attribute {#opaque-elements-attribute}
957987

958988
Syntax:
959989

@@ -970,7 +1000,7 @@ it.
9701000

9711001
Note: The parsed string literal must be in hexadecimal form.
9721002

973-
#### Sparse Elements Attribute {#sparse-elements-attribute}
1003+
##### Sparse Elements Attribute {#sparse-elements-attribute}
9741004

9751005
Syntax:
9761006

@@ -1000,7 +1030,7 @@ Example:
10001030
/// [0, 0, 0, 0]]
10011031
```
10021032

1003-
#### Splat Elements Attribute {#splat-elements-attribute}
1033+
##### Splat Elements Attribute {#splat-elements-attribute}
10041034

10051035
Syntax:
10061036

@@ -1012,7 +1042,7 @@ splat-elements-attribute ::= `splat` `<` ( tensor-type | vector-type ) `,`
10121042
A splat elements attribute is an elements attribute that represents a tensor or
10131043
vector constant where all elements have the same value.
10141044

1015-
### Integer Attribute {#integer-attribute}
1045+
#### Integer Attribute {#integer-attribute}
10161046

10171047
Syntax:
10181048

@@ -1024,7 +1054,7 @@ An integer attribute is a literal attribute that represents an integral value of
10241054
the specified integer or index type. The default type for this attribute, if one
10251055
is not specified, is a 64-bit integer.
10261056

1027-
### Integer Set Attribute {#integer-set-attribute}
1057+
#### Integer Set Attribute {#integer-set-attribute}
10281058

10291059
Syntax:
10301060

@@ -1034,7 +1064,7 @@ integer-set-attribute ::= affine-map
10341064

10351065
An integer-set attribute is an attribute that represents a integer-set object.
10361066

1037-
### Float Attribute {#float-attribute}
1067+
#### Float Attribute {#float-attribute}
10381068

10391069
Syntax:
10401070

@@ -1045,7 +1075,7 @@ float-attribute ::= float-literal (`:` float-type)?
10451075
A float attribute is a literal attribute that represents a floating point value
10461076
of the specified [float type](#floating-point-types).
10471077

1048-
### Function Attribute {#function-attribute}
1078+
#### Function Attribute {#function-attribute}
10491079

10501080
Syntax:
10511081

@@ -1056,7 +1086,7 @@ function-attribute ::= function-id `:` function-type
10561086
A function attribute is a literal attribute that represents a reference to the
10571087
given function object.
10581088

1059-
### String Attribute {#string-attribute}
1089+
#### String Attribute {#string-attribute}
10601090

10611091
Syntax:
10621092

@@ -1066,7 +1096,7 @@ string-attribute ::= string-literal
10661096

10671097
A string attribute is an attribute that represents a string literal value.
10681098

1069-
### Type Attribute {#type-attribute}
1099+
#### Type Attribute {#type-attribute}
10701100

10711101
Syntax:
10721102

mlir/include/mlir/IR/Dialect.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,26 @@ class Dialect {
101101
virtual void
102102
getTypeAliases(SmallVectorImpl<std::pair<StringRef, Type>> &aliases) {}
103103

104+
/// Verify an attribute from this dialect on the given function. Returns true
105+
/// if the verification failed, false otherwise.
106+
virtual bool verifyFunctionAttribute(const Function *, Attribute) {
107+
return false;
108+
}
109+
110+
/// Verify an attribute from this dialect on the argument at 'argIndex' for
111+
/// the given function. Returns true if the verification failed, false
112+
/// otherwise.
113+
virtual bool verifyFunctionArgAttribute(const Function *, unsigned argIndex,
114+
Attribute) {
115+
return false;
116+
}
117+
118+
/// Verify an attribute from this dialect on the given instruction. Returns
119+
/// true if the verification failed, false otherwise.
120+
virtual bool verifyInstructionAttribute(const Instruction *, Attribute) {
121+
return false;
122+
}
123+
104124
virtual ~Dialect();
105125

106126
/// Utility function that returns if the given string is a valid dialect

mlir/lib/Analysis/Verifier.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
#include "mlir/Analysis/Dominance.h"
3737
#include "mlir/IR/Attributes.h"
38+
#include "mlir/IR/Dialect.h"
3839
#include "mlir/IR/Function.h"
3940
#include "mlir/IR/Instruction.h"
4041
#include "mlir/IR/Module.h"
@@ -68,6 +69,15 @@ class FuncVerifier {
6869
return failure(message, fn);
6970
}
7071

72+
/// Returns the registered dialect for a dialect-specific attribute.
73+
template <typename ErrorContext>
74+
Dialect *getDialectForAttribute(const NamedAttribute &attr,
75+
const ErrorContext &ctx) {
76+
assert(attr.first.strref().contains('.') && "expected dialect attribute");
77+
auto dialectNamePair = attr.first.strref().split('.');
78+
return fn.getContext()->getRegisteredDialect(dialectNamePair.first);
79+
}
80+
7181
template <typename ErrorContext>
7282
bool verifyAttribute(Attribute attr, const ErrorContext &ctx) {
7383
if (!attr.isOrContainsFunction())
@@ -103,7 +113,7 @@ class FuncVerifier {
103113
bool verifyInstDominance(const Instruction &inst);
104114

105115
explicit FuncVerifier(const Function &fn)
106-
: fn(fn), attrNameRegex("^:?[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
116+
: fn(fn), identifierRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
107117

108118
private:
109119
/// The function being checked.
@@ -113,7 +123,7 @@ class FuncVerifier {
113123
DominanceInfo *domInfo = nullptr;
114124

115125
/// Regex checker for attribute names.
116-
llvm::Regex attrNameRegex;
126+
llvm::Regex identifierRegex;
117127
};
118128
} // end anonymous namespace
119129

@@ -122,29 +132,53 @@ bool FuncVerifier::verify() {
122132
fn.getName().c_str());
123133

124134
// Check that the function name is valid.
125-
llvm::Regex funcNameRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$");
126-
if (!funcNameRegex.match(fn.getName().strref()))
135+
if (!identifierRegex.match(fn.getName().strref()))
127136
return failure("invalid function name '" + fn.getName().strref() + "'", fn);
128137

129138
/// Verify that all of the attributes are okay.
130139
for (auto attr : fn.getAttrs()) {
131-
if (!attrNameRegex.match(attr.first))
140+
if (!identifierRegex.match(attr.first))
132141
return failure("invalid attribute name '" + attr.first.strref() + "'",
133142
fn);
134143
if (verifyAttribute(attr.second, fn))
135144
return true;
145+
146+
/// Check that the attribute is a dialect attribute, i.e. contains a '.' for
147+
/// the namespace.
148+
if (!attr.first.strref().contains('.')) {
149+
// TODO: Remove the remaining usages of non dialect attributes on
150+
// functions and then enable this check.
151+
// return failure("functions may only have dialect attributes", fn);
152+
continue;
153+
}
154+
155+
// Verify this attribute with the defining dialect.
156+
if (auto *dialect = getDialectForAttribute(attr, fn))
157+
if (dialect->verifyFunctionAttribute(&fn, attr.second))
158+
return true;
136159
}
137160

138161
/// Verify that all of the argument attributes are okay.
139162
for (unsigned i = 0, e = fn.getNumArguments(); i != e; ++i) {
140163
for (auto attr : fn.getArgAttrs(i)) {
141-
if (!attrNameRegex.match(attr.first))
164+
if (!identifierRegex.match(attr.first))
142165
return failure(
143166
llvm::formatv("invalid attribute name '{0}' on argument {1}",
144167
attr.first.strref(), i),
145168
fn);
146169
if (verifyAttribute(attr.second, fn))
147170
return true;
171+
172+
/// Check that the attribute is a dialect attribute, i.e. contains a '.'
173+
/// for the namespace.
174+
if (!attr.first.strref().contains('.'))
175+
return failure("function arguments may only have dialect attributes",
176+
fn);
177+
178+
// Verify this attribute with the defining dialect.
179+
if (auto *dialect = getDialectForAttribute(attr, fn))
180+
if (dialect->verifyFunctionArgAttribute(&fn, i, attr.second))
181+
return true;
148182
}
149183
}
150184

@@ -257,11 +291,18 @@ bool FuncVerifier::verifyOperation(const Instruction &op) {
257291

258292
/// Verify that all of the attributes are okay.
259293
for (auto attr : op.getAttrs()) {
260-
if (!attrNameRegex.match(attr.first))
294+
if (!identifierRegex.match(attr.first))
261295
return failure("invalid attribute name '" + attr.first.strref() + "'",
262296
op);
263297
if (verifyAttribute(attr.second, op))
264298
return true;
299+
300+
// Check for any optional dialect specific attributes.
301+
if (!attr.first.strref().contains('.'))
302+
continue;
303+
if (auto *dialect = getDialectForAttribute(attr, op))
304+
if (dialect->verifyInstructionAttribute(&op, attr.second))
305+
return true;
265306
}
266307

267308
// If we can get operation info for this, check the custom hook.

mlir/lib/IR/AsmPrinter.cpp

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,6 @@ static llvm::cl::opt<bool>
7777
llvm::cl::desc("Print the generic op form"),
7878
llvm::cl::init(false), llvm::cl::Hidden);
7979

80-
static llvm::cl::opt<bool> printInternalAttributes(
81-
"mlir-print-internal-attributes",
82-
llvm::cl::desc(
83-
"Print internal function and instruction attributes (':' prefix)."),
84-
llvm::cl::init(false));
85-
8680
namespace {
8781
class ModuleState {
8882
public:
@@ -1022,21 +1016,13 @@ void ModulePrinter::printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
10221016
// Filter out any attributes that shouldn't be included.
10231017
SmallVector<NamedAttribute, 8> filteredAttrs;
10241018
for (auto attr : attrs) {
1025-
auto attrName = attr.first.strref();
1026-
// By default, never print attributes that start with a colon. These are
1027-
// attributes represent location or other internal metadata.
1028-
if (!printInternalAttributes && attrName.startswith(":"))
1029-
return;
1030-
10311019
// If the caller has requested that this attribute be ignored, then drop it.
1032-
bool ignore = false;
1033-
for (auto elide : elidedAttrs)
1034-
ignore |= attrName == elide;
1020+
if (llvm::any_of(elidedAttrs,
1021+
[&](StringRef elided) { return attr.first.is(elided); }))
1022+
continue;
10351023

10361024
// Otherwise add it to our filteredAttrs list.
1037-
if (!ignore) {
1038-
filteredAttrs.push_back(attr);
1039-
}
1025+
filteredAttrs.push_back(attr);
10401026
}
10411027

10421028
// If there are no attributes left to print after filtering, then we're done.

mlir/lib/Parser/Parser.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,25 +1432,19 @@ ParseResult Parser::parseLocationInstance(llvm::Optional<Location> *loc) {
14321432
///
14331433
/// attribute-dict ::= `{` `}`
14341434
/// | `{` attribute-entry (`,` attribute-entry)* `}`
1435-
/// attribute-entry ::= `:`? bare-id `:` attribute-value
1435+
/// attribute-entry ::= bare-id `:` attribute-value
14361436
///
14371437
ParseResult
14381438
Parser::parseAttributeDict(SmallVectorImpl<NamedAttribute> &attributes) {
14391439
if (!consumeIf(Token::l_brace))
14401440
return ParseFailure;
14411441

14421442
auto parseElt = [&]() -> ParseResult {
1443-
// Check for an internal attribute.
1444-
bool isInternalAttr = consumeIf(Token::colon);
1445-
14461443
// We allow keywords as attribute names.
14471444
if (getToken().isNot(Token::bare_identifier, Token::inttype) &&
14481445
!getToken().isKeyword())
14491446
return emitError("expected attribute name");
1450-
Identifier nameId =
1451-
isInternalAttr
1452-
? builder.getIdentifier(Twine(":" + getTokenSpelling()).str())
1453-
: builder.getIdentifier(getTokenSpelling());
1447+
Identifier nameId = builder.getIdentifier(getTokenSpelling());
14541448
consumeToken();
14551449

14561450
if (parseToken(Token::colon, "expected ':' in attribute list"))

0 commit comments

Comments
 (0)