[mlir][DeclarativeParser] Add an 'attr-dict-with-keyword' directive

This matches the '(print|parse)OptionalAttrDictWithKeyword' functionality provided by the assembly parser/printer.

Differential Revision: https://reviews.llvm.org/D74682
This commit is contained in:
River Riddle 2020-02-21 13:19:26 -08:00
parent 2d0477a003
commit ca4ea51c0a
5 changed files with 88 additions and 40 deletions
mlir
docs
test
tools/mlir-tblgen

View File

@ -583,25 +583,35 @@ The format is comprised of three components:
A directive is a type of builtin function, with an optional set of arguments. A directive is a type of builtin function, with an optional set of arguments.
The available directives are as follows: The available directives are as follows:
* `attr-dict` * `attr-dict`
- Represents the attribute dictionary of the operation.
* `functional-type` ( inputs , results ) - Represents the attribute dictionary of the operation.
- Formats the `inputs` and `results` arguments as a
[function type](LangRef.md#function-type).
- The constraints on `inputs` and `results` are the same as the `input` of
the `type` directive.
* `operands` * `attr-dict-with-keyword`
- Represents all of the operands of an operation.
* `results` - Represents the attribute dictionary of the operation, but prefixes the
- Represents all of the results of an operation. dictionary with an `attributes` keyword.
* `type` ( input ) * `functional-type` ( inputs , results )
- Represents the type of the given input.
- `input` must be either an operand or result [variable](#variables), the - Formats the `inputs` and `results` arguments as a
`operands` directive, or the `results` directive. [function type](LangRef.md#function-type).
- The constraints on `inputs` and `results` are the same as the `input` of
the `type` directive.
* `operands`
- Represents all of the operands of an operation.
* `results`
- Represents all of the results of an operation.
* `type` ( input )
- Represents the type of the given input.
- `input` must be either an operand or result [variable](#variables), the
`operands` directive, or the `results` directive.
#### Literals #### Literals

View File

@ -1089,6 +1089,12 @@ def FormatAttrOp : TEST_Op<"format_attr_op"> {
let assemblyFormat = "$attr attr-dict"; let assemblyFormat = "$attr attr-dict";
} }
// Test that we elide attributes that are within the syntax.
def FormatAttrDictWithKeywordOp : TEST_Op<"format_attr_dict_w_keyword"> {
let arguments = (ins I64Attr:$attr);
let assemblyFormat = "attr-dict-with-keyword";
}
// Test that we don't need to provide types in the format if they are buildable. // Test that we don't need to provide types in the format if they are buildable.
def FormatBuildableTypeOp : TEST_Op<"format_buildable_type_op"> { def FormatBuildableTypeOp : TEST_Op<"format_buildable_type_op"> {
let arguments = (ins I64:$buildable); let arguments = (ins I64:$buildable);

View File

@ -26,14 +26,21 @@ def DirectiveAttrDictInvalidA : TestFormat_Op<"attrdict_invalid_a", [{
def DirectiveAttrDictInvalidB : TestFormat_Op<"attrdict_invalid_b", [{ def DirectiveAttrDictInvalidB : TestFormat_Op<"attrdict_invalid_b", [{
attr-dict attr-dict attr-dict attr-dict
}]>; }]>;
// CHECK: error: 'attr-dict' directive can only be used as a top-level directive // CHECK: error: 'attr-dict' directive has already been seen
def DirectiveAttrDictInvalidC : TestFormat_Op<"attrdict_invalid_c", [{ def DirectiveAttrDictInvalidC : TestFormat_Op<"attrdict_invalid_c", [{
attr-dict attr-dict-with-keyword
}]>;
// CHECK: error: 'attr-dict' directive can only be used as a top-level directive
def DirectiveAttrDictInvalidD : TestFormat_Op<"attrdict_invalid_d", [{
type(attr-dict) type(attr-dict)
}]>; }]>;
// CHECK-NOT: error // CHECK-NOT: error
def DirectiveAttrDictValid : TestFormat_Op<"attrdict_valid", [{ def DirectiveAttrDictValidA : TestFormat_Op<"attrdict_valid_a", [{
attr-dict attr-dict
}]>; }]>;
def DirectiveAttrDictValidB : TestFormat_Op<"attrdict_valid_b", [{
attr-dict-with-keyword
}]>;
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// functional-type // functional-type

View File

@ -12,6 +12,9 @@ test.format_literal_op keyword_$. -> :, = <> () [] {foo.some_attr}
// CHECK-NOT: {attr // CHECK-NOT: {attr
test.format_attr_op 10 test.format_attr_op 10
// CHECK: test.format_attr_dict_w_keyword attributes {attr = 10 : i64}
test.format_attr_dict_w_keyword attributes {attr = 10 : i64}
// CHECK: test.format_buildable_type_op %[[I64]] // CHECK: test.format_buildable_type_op %[[I64]]
%ignored = test.format_buildable_type_op %i64 %ignored = test.format_buildable_type_op %i64

View File

@ -118,10 +118,6 @@ public:
DirectiveElement() : Element(type){}; DirectiveElement() : Element(type){};
static bool classof(const Element *ele) { return ele->getKind() == type; } static bool classof(const Element *ele) { return ele->getKind() == type; }
}; };
/// This class represents the `attr-dict` directive. This directive represents
/// the attribute dictionary of the operation.
using AttrDictDirective = DirectiveElement<Element::Kind::AttrDictDirective>;
/// This class represents the `operands` directive. This directive represents /// This class represents the `operands` directive. This directive represents
/// all of the operands of an operation. /// all of the operands of an operation.
using OperandsDirective = DirectiveElement<Element::Kind::OperandsDirective>; using OperandsDirective = DirectiveElement<Element::Kind::OperandsDirective>;
@ -130,10 +126,23 @@ using OperandsDirective = DirectiveElement<Element::Kind::OperandsDirective>;
/// all of the results of an operation. /// all of the results of an operation.
using ResultsDirective = DirectiveElement<Element::Kind::ResultsDirective>; using ResultsDirective = DirectiveElement<Element::Kind::ResultsDirective>;
/// This class represents the `attr-dict` directive. This directive represents
/// the attribute dictionary of the operation.
class AttrDictDirective
: public DirectiveElement<Element::Kind::AttrDictDirective> {
public:
explicit AttrDictDirective(bool withKeyword) : withKeyword(withKeyword) {}
bool isWithKeyword() const { return withKeyword; }
private:
/// If the dictionary should be printed with the 'attributes' keyword.
bool withKeyword;
};
/// This class represents the `functional-type` directive. This directive takes /// This class represents the `functional-type` directive. This directive takes
/// two arguments and formats them, respectively, as the inputs and results of a /// two arguments and formats them, respectively, as the inputs and results of a
/// FunctionType. /// FunctionType.
struct FunctionalTypeDirective class FunctionalTypeDirective
: public DirectiveElement<Element::Kind::FunctionalTypeDirective> { : public DirectiveElement<Element::Kind::FunctionalTypeDirective> {
public: public:
FunctionalTypeDirective(std::unique_ptr<Element> inputs, FunctionalTypeDirective(std::unique_ptr<Element> inputs,
@ -148,7 +157,7 @@ private:
}; };
/// This class represents the `type` directive. /// This class represents the `type` directive.
struct TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> { class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> {
public: public:
TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {} TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
Element *getOperand() const { return operand.get(); } Element *getOperand() const { return operand.get(); }
@ -532,8 +541,10 @@ static void genElementParser(Element *element, OpMethodBody &body,
operand->getVar()->name); operand->getVar()->name);
/// Directives. /// Directives.
} else if (isa<AttrDictDirective>(element)) { } else if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
body << " if (parser.parseOptionalAttrDict(result.attributes))\n" body << " if (parser.parseOptionalAttrDict"
<< (attrDict->isWithKeyword() ? "WithKeyword" : "")
<< "(result.attributes))\n"
<< " return failure();\n"; << " return failure();\n";
} else if (isa<OperandsDirective>(element)) { } else if (isa<OperandsDirective>(element)) {
body << " llvm::SMLoc allOperandLoc = parser.getCurrentLocation();\n" body << " llvm::SMLoc allOperandLoc = parser.getCurrentLocation();\n"
@ -723,14 +734,16 @@ void OperationFormat::genParserTypeResolution(Operator &op,
// PrinterGen // PrinterGen
/// Generate the printer for the 'attr-dict' directive. /// Generate the printer for the 'attr-dict' directive.
static void genAttrDictPrinter(OperationFormat &fmt, OpMethodBody &body) { static void genAttrDictPrinter(OperationFormat &fmt, OpMethodBody &body,
bool withKeyword) {
// Collect all of the attributes used in the format, these will be elided. // Collect all of the attributes used in the format, these will be elided.
SmallVector<const NamedAttribute *, 1> usedAttributes; SmallVector<const NamedAttribute *, 1> usedAttributes;
for (auto &it : fmt.elements) for (auto &it : fmt.elements)
if (auto *attr = dyn_cast<AttributeVariable>(it.get())) if (auto *attr = dyn_cast<AttributeVariable>(it.get()))
usedAttributes.push_back(attr->getVar()); usedAttributes.push_back(attr->getVar());
body << " p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{"; body << " p.printOptionalAttrDict" << (withKeyword ? "WithKeyword" : "")
<< "(getAttrs(), /*elidedAttrs=*/{";
interleaveComma(usedAttributes, body, [&](const NamedAttribute *attr) { interleaveComma(usedAttributes, body, [&](const NamedAttribute *attr) {
body << "\"" << attr->name << "\""; body << "\"" << attr->name << "\"";
}); });
@ -802,8 +815,8 @@ static void genElementPrinter(Element *element, OpMethodBody &body,
} }
// Emit the attribute dictionary. // Emit the attribute dictionary.
if (isa<AttrDictDirective>(element)) { if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
genAttrDictPrinter(fmt, body); genAttrDictPrinter(fmt, body, attrDict->isWithKeyword());
lastWasPunctuation = false; lastWasPunctuation = false;
return; return;
} }
@ -894,6 +907,7 @@ public:
// Keywords. // Keywords.
keyword_start, keyword_start,
kw_attr_dict, kw_attr_dict,
kw_attr_dict_w_keyword,
kw_functional_type, kw_functional_type,
kw_operands, kw_operands,
kw_results, kw_results,
@ -1073,13 +1087,15 @@ Token FormatLexer::lexIdentifier(const char *tokStart) {
// Check to see if this identifier is a keyword. // Check to see if this identifier is a keyword.
StringRef str(tokStart, curPtr - tokStart); StringRef str(tokStart, curPtr - tokStart);
Token::Kind kind = llvm::StringSwitch<Token::Kind>(str) Token::Kind kind =
.Case("attr-dict", Token::kw_attr_dict) llvm::StringSwitch<Token::Kind>(str)
.Case("functional-type", Token::kw_functional_type) .Case("attr-dict", Token::kw_attr_dict)
.Case("operands", Token::kw_operands) .Case("attr-dict-with-keyword", Token::kw_attr_dict_w_keyword)
.Case("results", Token::kw_results) .Case("functional-type", Token::kw_functional_type)
.Case("type", Token::kw_type) .Case("operands", Token::kw_operands)
.Default(Token::identifier); .Case("results", Token::kw_results)
.Case("type", Token::kw_type)
.Default(Token::identifier);
return Token(kind, str); return Token(kind, str);
} }
@ -1149,7 +1165,8 @@ private:
/// Parse the various different directives. /// Parse the various different directives.
LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element, LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel); llvm::SMLoc loc, bool isTopLevel,
bool withKeyword);
LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element, LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
Token tok, bool isTopLevel); Token tok, bool isTopLevel);
LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element, LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element,
@ -1410,7 +1427,11 @@ LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element,
switch (dirTok.getKind()) { switch (dirTok.getKind()) {
case Token::kw_attr_dict: case Token::kw_attr_dict:
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel); return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel,
/*withKeyword=*/false);
case Token::kw_attr_dict_w_keyword:
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel,
/*withKeyword=*/true);
case Token::kw_functional_type: case Token::kw_functional_type:
return parseFunctionalTypeDirective(element, dirTok, isTopLevel); return parseFunctionalTypeDirective(element, dirTok, isTopLevel);
case Token::kw_operands: case Token::kw_operands:
@ -1549,7 +1570,8 @@ LogicalResult FormatParser::parseOptionalChildElement(
LogicalResult LogicalResult
FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element, FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) { llvm::SMLoc loc, bool isTopLevel,
bool withKeyword) {
if (!isTopLevel) if (!isTopLevel)
return emitError(loc, "'attr-dict' directive can only be used as a " return emitError(loc, "'attr-dict' directive can only be used as a "
"top-level directive"); "top-level directive");
@ -1557,7 +1579,7 @@ FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
return emitError(loc, "'attr-dict' directive has already been seen"); return emitError(loc, "'attr-dict' directive has already been seen");
hasAttrDict = true; hasAttrDict = true;
element = std::make_unique<AttrDictDirective>(); element = std::make_unique<AttrDictDirective>(withKeyword);
return success(); return success();
} }