llvm-project/mlir/tools/mlir-tblgen/OpFormatGen.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1962 lines
72 KiB
C++
Raw Normal View History

//===- OpFormatGen.cpp - MLIR operation asm format generator --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "OpFormatGen.h"
#include "mlir/ADT/TypeSwitch.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Support/STLExtras.h"
#include "mlir/TableGen/Format.h"
#include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/OpClass.h"
#include "mlir/TableGen/OpInterfaces.h"
#include "mlir/TableGen/OpTrait.h"
#include "mlir/TableGen/Operator.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Signals.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#define DEBUG_TYPE "mlir-tblgen-opformatgen"
using namespace mlir;
using namespace mlir::tblgen;
static llvm::cl::opt<bool> formatErrorIsFatal(
"asmformat-error-is-fatal",
llvm::cl::desc("Emit a fatal error if format parsing fails"),
llvm::cl::init(true));
//===----------------------------------------------------------------------===//
// Element
//===----------------------------------------------------------------------===//
namespace {
/// This class represents a single format element.
class Element {
public:
enum class Kind {
/// This element is a directive.
AttrDictDirective,
FunctionalTypeDirective,
OperandsDirective,
ResultsDirective,
SuccessorsDirective,
TypeDirective,
/// This element is a literal.
Literal,
/// This element is an variable value.
AttributeVariable,
OperandVariable,
ResultVariable,
SuccessorVariable,
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
/// This element is an optional element.
Optional,
};
Element(Kind kind) : kind(kind) {}
virtual ~Element() = default;
/// Return the kind of this element.
Kind getKind() const { return kind; }
private:
/// The kind of this element.
Kind kind;
};
} // namespace
//===----------------------------------------------------------------------===//
// VariableElement
namespace {
/// This class represents an instance of an variable element. A variable refers
/// to something registered on the operation itself, e.g. an argument, result,
/// etc.
template <typename VarT, Element::Kind kindVal>
class VariableElement : public Element {
public:
VariableElement(const VarT *var) : Element(kindVal), var(var) {}
static bool classof(const Element *element) {
return element->getKind() == kindVal;
}
const VarT *getVar() { return var; }
protected:
const VarT *var;
};
/// This class represents a variable that refers to an attribute argument.
struct AttributeVariable
: public VariableElement<NamedAttribute, Element::Kind::AttributeVariable> {
using VariableElement<NamedAttribute,
Element::Kind::AttributeVariable>::VariableElement;
/// Return the constant builder call for the type of this attribute, or None
/// if it doesn't have one.
Optional<StringRef> getTypeBuilder() const {
Optional<Type> attrType = var->attr.getValueType();
return attrType ? attrType->getBuilderCall() : llvm::None;
}
};
/// This class represents a variable that refers to an operand argument.
using OperandVariable =
VariableElement<NamedTypeConstraint, Element::Kind::OperandVariable>;
/// This class represents a variable that refers to a result.
using ResultVariable =
VariableElement<NamedTypeConstraint, Element::Kind::ResultVariable>;
/// This class represents a variable that refers to a successor.
using SuccessorVariable =
VariableElement<NamedSuccessor, Element::Kind::SuccessorVariable>;
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// DirectiveElement
namespace {
/// This class implements single kind directives.
template <Element::Kind type>
class DirectiveElement : public Element {
public:
DirectiveElement() : Element(type){};
static bool classof(const Element *ele) { return ele->getKind() == type; }
};
/// This class represents the `operands` directive. This directive represents
/// all of the operands of an operation.
using OperandsDirective = DirectiveElement<Element::Kind::OperandsDirective>;
/// This class represents the `results` directive. This directive represents
/// all of the results of an operation.
using ResultsDirective = DirectiveElement<Element::Kind::ResultsDirective>;
/// This class represents the `successors` directive. This directive represents
/// all of the successors of an operation.
using SuccessorsDirective =
DirectiveElement<Element::Kind::SuccessorsDirective>;
/// 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
/// two arguments and formats them, respectively, as the inputs and results of a
/// FunctionType.
class FunctionalTypeDirective
: public DirectiveElement<Element::Kind::FunctionalTypeDirective> {
public:
FunctionalTypeDirective(std::unique_ptr<Element> inputs,
std::unique_ptr<Element> results)
: inputs(std::move(inputs)), results(std::move(results)) {}
Element *getInputs() const { return inputs.get(); }
Element *getResults() const { return results.get(); }
private:
/// The input and result arguments.
std::unique_ptr<Element> inputs, results;
};
/// This class represents the `type` directive.
class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> {
public:
TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
Element *getOperand() const { return operand.get(); }
private:
/// The operand that is used to format the directive.
std::unique_ptr<Element> operand;
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// LiteralElement
namespace {
/// This class represents an instance of a literal element.
class LiteralElement : public Element {
public:
LiteralElement(StringRef literal)
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
: Element{Kind::Literal}, literal(literal) {}
static bool classof(const Element *element) {
return element->getKind() == Kind::Literal;
}
/// Return the literal for this element.
StringRef getLiteral() const { return literal; }
/// Returns true if the given string is a valid literal.
static bool isValidLiteral(StringRef value);
private:
/// The spelling of the literal for this element.
StringRef literal;
};
} // end anonymous namespace
bool LiteralElement::isValidLiteral(StringRef value) {
if (value.empty())
return false;
char front = value.front();
// If there is only one character, this must either be punctuation or a
// single character bare identifier.
if (value.size() == 1)
return isalpha(front) || StringRef("_:,=<>()[]").contains(front);
// Check the punctuation that are larger than a single character.
if (value == "->")
return true;
// Otherwise, this must be an identifier.
if (!isalpha(front) && front != '_')
return false;
return llvm::all_of(value.drop_front(), [](char c) {
return isalnum(c) || c == '_' || c == '$' || c == '.';
});
}
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
//===----------------------------------------------------------------------===//
// OptionalElement
namespace {
/// This class represents a group of elements that are optionally emitted based
/// upon an optional variable of the operation.
class OptionalElement : public Element {
public:
OptionalElement(std::vector<std::unique_ptr<Element>> &&elements,
unsigned anchor)
: Element{Kind::Optional}, elements(std::move(elements)), anchor(anchor) {
}
static bool classof(const Element *element) {
return element->getKind() == Kind::Optional;
}
/// Return the nested elements of this grouping.
auto getElements() const { return llvm::make_pointee_range(elements); }
/// Return the anchor of this optional group.
Element *getAnchor() const { return elements[anchor].get(); }
private:
/// The child elements of this optional.
std::vector<std::unique_ptr<Element>> elements;
/// The index of the element that acts as the anchor for the optional group.
unsigned anchor;
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// OperationFormat
//===----------------------------------------------------------------------===//
namespace {
struct OperationFormat {
/// This class represents a specific resolver for an operand or result type.
class TypeResolution {
public:
TypeResolution() = default;
/// Get the index into the buildable types for this type, or None.
Optional<int> getBuilderIdx() const { return builderIdx; }
void setBuilderIdx(int idx) { builderIdx = idx; }
/// Get the variable this type is resolved to, or None.
const NamedTypeConstraint *getVariable() const { return variable; }
Optional<StringRef> getVarTransformer() const {
return variableTransformer;
}
void setVariable(const NamedTypeConstraint *var,
Optional<StringRef> transformer) {
variable = var;
variableTransformer = transformer;
}
private:
/// If the type is resolved with a buildable type, this is the index into
/// 'buildableTypes' in the parent format.
Optional<int> builderIdx;
/// If the type is resolved based upon another operand or result, this is
/// the variable that this type is resolved to.
const NamedTypeConstraint *variable;
/// If the type is resolved based upon another operand or result, this is
/// a transformer to apply to the variable when resolving.
Optional<StringRef> variableTransformer;
};
OperationFormat(const Operator &op)
: allOperands(false), allOperandTypes(false), allResultTypes(false) {
operandTypes.resize(op.getNumOperands(), TypeResolution());
resultTypes.resize(op.getNumResults(), TypeResolution());
}
/// Generate the operation parser from this format.
void genParser(Operator &op, OpClass &opClass);
/// Generate the c++ to resolve the types of operands and results during
/// parsing.
void genParserTypeResolution(Operator &op, OpMethodBody &body);
/// Generate the c++ to resolve successors during parsing.
void genParserSuccessorResolution(Operator &op, OpMethodBody &body);
/// Generate the c++ to handling variadic segment size traits.
void genParserVariadicSegmentResolution(Operator &op, OpMethodBody &body);
/// Generate the operation printer from this format.
void genPrinter(Operator &op, OpClass &opClass);
/// The various elements in this format.
std::vector<std::unique_ptr<Element>> elements;
/// A flag indicating if all operand/result types were seen. If the format
/// contains these, it can not contain individual type resolvers.
bool allOperands, allOperandTypes, allResultTypes;
/// A map of buildable types to indices.
llvm::MapVector<StringRef, int, llvm::StringMap<int>> buildableTypes;
/// The index of the buildable type, if valid, for every operand and result.
std::vector<TypeResolution> operandTypes, resultTypes;
};
} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Parser Gen
/// Returns if we can format the given attribute as an EnumAttr in the parser
/// format.
static bool canFormatEnumAttr(const NamedAttribute *attr) {
const EnumAttr *enumAttr = dyn_cast<EnumAttr>(&attr->attr);
if (!enumAttr)
return false;
// The attribute must have a valid underlying type and a constant builder.
return !enumAttr->getUnderlyingType().empty() &&
!enumAttr->getConstBuilderTemplate().empty();
}
/// The code snippet used to generate a parser call for an attribute.
///
/// {0}: The storage type of the attribute.
/// {1}: The name of the attribute.
/// {2}: The type for the attribute.
const char *const attrParserCode = R"(
{0} {1}Attr;
if (parser.parseAttribute({1}Attr{2}, "{1}", result.attributes))
return failure();
)";
/// The code snippet used to generate a parser call for an enum attribute.
///
/// {0}: The name of the attribute.
/// {1}: The c++ namespace for the enum symbolize functions.
/// {2}: The function to symbolize a string of the enum.
/// {3}: The constant builder call to create an attribute of the enum type.
const char *const enumAttrParserCode = R"(
{
StringAttr attrVal;
SmallVector<NamedAttribute, 1> attrStorage;
auto loc = parser.getCurrentLocation();
if (parser.parseAttribute(attrVal, parser.getBuilder().getNoneType(),
"{0}", attrStorage))
return failure();
auto attrOptional = {1}::{2}(attrVal.getValue());
if (!attrOptional)
return parser.emitError(loc, "invalid ")
<< "{0} attribute specification: " << attrVal;
result.addAttribute("{0}", {3});
}
)";
/// The code snippet used to generate a parser call for an operand.
///
/// {0}: The name of the operand.
const char *const variadicOperandParserCode = R"(
if (parser.parseOperandList({0}Operands))
return failure();
)";
const char *const operandParserCode = R"(
if (parser.parseOperand({0}RawOperands[0]))
return failure();
)";
/// The code snippet used to generate a parser call for a type list.
///
/// {0}: The name for the type list.
const char *const variadicTypeParserCode = R"(
if (parser.parseTypeList({0}Types))
return failure();
)";
const char *const typeParserCode = R"(
if (parser.parseType({0}RawTypes[0]))
return failure();
)";
/// The code snippet used to generate a parser call for a functional type.
///
/// {0}: The name for the input type list.
/// {1}: The name for the result type list.
const char *const functionalTypeParserCode = R"(
FunctionType {0}__{1}_functionType;
if (parser.parseType({0}__{1}_functionType))
return failure();
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
{0}Types = {0}__{1}_functionType.getInputs();
{1}Types = {0}__{1}_functionType.getResults();
)";
/// The code snippet used to generate a parser call for a successor list.
///
/// {0}: The name for the successor list.
const char *successorListParserCode = R"(
SmallVector<Block *, 2> {0}Successors;
{
Block *succ;
auto firstSucc = parser.parseOptionalSuccessor(succ);
if (firstSucc.hasValue()) {
if (failed(*firstSucc))
return failure();
{0}Successors.emplace_back(succ);
// Parse any trailing successors.
while (succeeded(parser.parseOptionalComma())) {
if (parser.parseSuccessor(succ))
return failure();
{0}Successors.emplace_back(succ);
}
}
}
)";
/// The code snippet used to generate a parser call for a successor.
///
/// {0}: The name of the successor.
const char *successorParserCode = R"(
Block *{0}Successor = nullptr;
if (parser.parseSuccessor({0}Successor))
return failure();
)";
/// Get the name used for the type list for the given type directive operand.
/// 'isVariadic' is set to true if the operand has variadic types.
static StringRef getTypeListName(Element *arg, bool &isVariadic) {
if (auto *operand = dyn_cast<OperandVariable>(arg)) {
isVariadic = operand->getVar()->isVariadic();
return operand->getVar()->name;
}
if (auto *result = dyn_cast<ResultVariable>(arg)) {
isVariadic = result->getVar()->isVariadic();
return result->getVar()->name;
}
isVariadic = true;
if (isa<OperandsDirective>(arg))
return "allOperand";
if (isa<ResultsDirective>(arg))
return "allResult";
llvm_unreachable("unknown 'type' directive argument");
}
/// Generate the parser for a literal value.
static void genLiteralParser(StringRef value, OpMethodBody &body) {
// Handle the case of a keyword/identifier.
if (value.front() == '_' || isalpha(value.front())) {
body << "Keyword(\"" << value << "\")";
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
return;
}
body << (StringRef)llvm::StringSwitch<StringRef>(value)
.Case("->", "Arrow()")
.Case(":", "Colon()")
.Case(",", "Comma()")
.Case("=", "Equal()")
.Case("<", "Less()")
.Case(">", "Greater()")
.Case("(", "LParen()")
.Case(")", "RParen()")
.Case("[", "LSquare()")
.Case("]", "RSquare()");
}
/// Generate the storage code required for parsing the given element.
static void genElementParserStorage(Element *element, OpMethodBody &body) {
if (auto *optional = dyn_cast<OptionalElement>(element)) {
for (auto &childElement : optional->getElements())
genElementParserStorage(&childElement, body);
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
StringRef name = operand->getVar()->name;
if (operand->getVar()->isVariadic()) {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
body << " SmallVector<OpAsmParser::OperandType, 4> " << name
<< "Operands;\n";
} else {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
body << " OpAsmParser::OperandType " << name << "RawOperands[1];\n"
<< " ArrayRef<OpAsmParser::OperandType> " << name << "Operands("
<< name << "RawOperands);";
}
body << llvm::formatv(
" llvm::SMLoc {0}OperandsLoc = parser.getCurrentLocation();\n"
" (void){0}OperandsLoc;\n",
name);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
bool variadic = false;
StringRef name = getTypeListName(dir->getOperand(), variadic);
if (variadic)
body << " SmallVector<Type, 1> " << name << "Types;\n";
else
body << llvm::formatv(" Type {0}RawTypes[1];\n", name)
<< llvm::formatv(" ArrayRef<Type> {0}Types({0}RawTypes);\n", name);
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
bool ignored = false;
body << " ArrayRef<Type> " << getTypeListName(dir->getInputs(), ignored)
<< "Types;\n";
body << " ArrayRef<Type> " << getTypeListName(dir->getResults(), ignored)
<< "Types;\n";
}
}
/// Generate the parser for a single format element.
static void genElementParser(Element *element, OpMethodBody &body,
FmtContext &attrTypeCtx) {
/// Optional Group.
if (auto *optional = dyn_cast<OptionalElement>(element)) {
auto elements = optional->getElements();
// Generate a special optional parser for the first element to gate the
// parsing of the rest of the elements.
if (auto *literal = dyn_cast<LiteralElement>(&*elements.begin())) {
body << " if (succeeded(parser.parseOptional";
genLiteralParser(literal->getLiteral(), body);
body << ")) {\n";
} else if (auto *opVar = dyn_cast<OperandVariable>(&*elements.begin())) {
genElementParser(opVar, body, attrTypeCtx);
body << " if (!" << opVar->getVar()->name << "Operands.empty()) {\n";
}
// Generate the rest of the elements normally.
for (auto &childElement : llvm::drop_begin(elements, 1))
genElementParser(&childElement, body, attrTypeCtx);
body << " }\n";
/// Literals.
} else if (LiteralElement *literal = dyn_cast<LiteralElement>(element)) {
body << " if (parser.parse";
genLiteralParser(literal->getLiteral(), body);
body << ")\n return failure();\n";
/// Arguments.
} else if (auto *attr = dyn_cast<AttributeVariable>(element)) {
const NamedAttribute *var = attr->getVar();
// Check to see if we can parse this as an enum attribute.
if (canFormatEnumAttr(var)) {
const EnumAttr &enumAttr = cast<EnumAttr>(var->attr);
// Generate the code for building an attribute for this enum.
std::string attrBuilderStr;
{
llvm::raw_string_ostream os(attrBuilderStr);
os << tgfmt(enumAttr.getConstBuilderTemplate(), &attrTypeCtx,
"attrOptional.getValue()");
}
body << formatv(enumAttrParserCode, var->name, enumAttr.getCppNamespace(),
enumAttr.getStringToSymbolFnName(), attrBuilderStr);
return;
}
// If this attribute has a buildable type, use that when parsing the
// attribute.
std::string attrTypeStr;
if (Optional<StringRef> typeBuilder = attr->getTypeBuilder()) {
llvm::raw_string_ostream os(attrTypeStr);
os << ", " << tgfmt(*typeBuilder, &attrTypeCtx);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
}
body << formatv(attrParserCode, var->attr.getStorageType(), var->name,
attrTypeStr);
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
bool isVariadic = operand->getVar()->isVariadic();
body << formatv(isVariadic ? variadicOperandParserCode : operandParserCode,
operand->getVar()->name);
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
bool isVariadic = successor->getVar()->isVariadic();
body << formatv(isVariadic ? successorListParserCode : successorParserCode,
successor->getVar()->name);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
/// Directives.
} else if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
body << " if (parser.parseOptionalAttrDict"
<< (attrDict->isWithKeyword() ? "WithKeyword" : "")
<< "(result.attributes))\n"
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
<< " return failure();\n";
} else if (isa<OperandsDirective>(element)) {
body << " llvm::SMLoc allOperandLoc = parser.getCurrentLocation();\n"
<< " SmallVector<OpAsmParser::OperandType, 4> allOperands;\n"
<< " if (parser.parseOperandList(allOperands))\n"
<< " return failure();\n";
} else if (isa<SuccessorsDirective>(element)) {
body << llvm::formatv(successorListParserCode, "full");
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
bool isVariadic = false;
StringRef listName = getTypeListName(dir->getOperand(), isVariadic);
body << formatv(isVariadic ? variadicTypeParserCode : typeParserCode,
listName);
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
bool ignored = false;
body << formatv(functionalTypeParserCode,
getTypeListName(dir->getInputs(), ignored),
getTypeListName(dir->getResults(), ignored));
} else {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
llvm_unreachable("unknown format element");
}
}
void OperationFormat::genParser(Operator &op, OpClass &opClass) {
auto &method = opClass.newMethod(
"ParseResult", "parse", "OpAsmParser &parser, OperationState &result",
OpMethod::MP_Static);
auto &body = method.body();
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
// Generate variables to store the operands and type within the format. This
// allows for referencing these variables in the presence of optional
// groupings.
for (auto &element : elements)
genElementParserStorage(&*element, body);
// A format context used when parsing attributes with buildable types.
FmtContext attrTypeCtx;
attrTypeCtx.withBuilder("parser.getBuilder()");
// Generate parsers for each of the elements.
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
for (auto &element : elements)
genElementParser(element.get(), body, attrTypeCtx);
// Generate the code to resolve the operand/result types and successors now
// that they have been parsed.
genParserTypeResolution(op, body);
genParserSuccessorResolution(op, body);
genParserVariadicSegmentResolution(op, body);
body << " return success();\n";
}
void OperationFormat::genParserTypeResolution(Operator &op,
OpMethodBody &body) {
// If any of type resolutions use transformed variables, make sure that the
// types of those variables are resolved.
SmallPtrSet<const NamedTypeConstraint *, 8> verifiedVariables;
FmtContext verifierFCtx;
for (TypeResolution &resolver :
llvm::concat<TypeResolution>(resultTypes, operandTypes)) {
Optional<StringRef> transformer = resolver.getVarTransformer();
if (!transformer)
continue;
// Ensure that we don't verify the same variables twice.
const NamedTypeConstraint *variable = resolver.getVariable();
if (!verifiedVariables.insert(variable).second)
continue;
auto constraint = variable->constraint;
body << " for (Type type : " << variable->name << "Types) {\n"
<< " (void)type;\n"
<< " if (!("
<< tgfmt(constraint.getConditionTemplate(),
&verifierFCtx.withSelf("type"))
<< ")) {\n"
<< formatv(" return parser.emitError(parser.getNameLoc()) << "
"\"'{0}' must be {1}, but got \" << type;\n",
variable->name, constraint.getDescription())
<< " }\n"
<< " }\n";
}
// Initialize the set of buildable types.
if (!buildableTypes.empty()) {
body << " Builder &builder = parser.getBuilder();\n";
FmtContext typeBuilderCtx;
typeBuilderCtx.withBuilder("builder");
for (auto &it : buildableTypes)
body << " Type odsBuildableType" << it.second << " = "
<< tgfmt(it.first, &typeBuilderCtx) << ";\n";
}
// Emit the code necessary for a type resolver.
auto emitTypeResolver = [&](TypeResolution &resolver, StringRef curVar) {
if (Optional<int> val = resolver.getBuilderIdx()) {
body << "odsBuildableType" << *val;
} else if (const NamedTypeConstraint *var = resolver.getVariable()) {
if (Optional<StringRef> tform = resolver.getVarTransformer())
body << tgfmt(*tform, &FmtContext().withSelf(var->name + "Types[0]"));
else
body << var->name << "Types";
} else {
body << curVar << "Types";
}
};
// Resolve each of the result types.
if (allResultTypes) {
body << " result.addTypes(allResultTypes);\n";
} else {
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) {
body << " result.addTypes(";
emitTypeResolver(resultTypes[i], op.getResultName(i));
body << ");\n";
}
}
// Early exit if there are no operands.
if (op.getNumOperands() == 0)
return;
// Handle the case where all operand types are in one group.
if (allOperandTypes) {
// If we have all operands together, use the full operand list directly.
if (allOperands) {
body << " if (parser.resolveOperands(allOperands, allOperandTypes, "
"allOperandLoc, result.operands))\n"
" return failure();\n";
return;
}
// Otherwise, use llvm::concat to merge the disjoint operand lists together.
// llvm::concat does not allow the case of a single range, so guard it here.
body << " if (parser.resolveOperands(";
if (op.getNumOperands() > 1) {
body << "llvm::concat<const OpAsmParser::OperandType>(";
interleaveComma(op.getOperands(), body, [&](auto &operand) {
body << operand.name << "Operands";
});
body << ")";
} else {
body << op.operand_begin()->name << "Operands";
}
body << ", allOperandTypes, parser.getNameLoc(), result.operands))\n"
<< " return failure();\n";
return;
}
// Handle the case where all of the operands were grouped together.
if (allOperands) {
body << " if (parser.resolveOperands(allOperands, ";
// Group all of the operand types together to perform the resolution all at
// once. Use llvm::concat to perform the merge. llvm::concat does not allow
// the case of a single range, so guard it here.
if (op.getNumOperands() > 1) {
body << "llvm::concat<const Type>(";
interleaveComma(llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
body << "ArrayRef<Type>(";
emitTypeResolver(operandTypes[i], op.getOperand(i).name);
body << ")";
});
body << ")";
} else {
emitTypeResolver(operandTypes.front(), op.getOperand(0).name);
}
body << ", allOperandLoc, result.operands))\n"
<< " return failure();\n";
return;
}
// The final case is the one where each of the operands types are resolved
// separately.
for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i) {
NamedTypeConstraint &operand = op.getOperand(i);
body << " if (parser.resolveOperands(" << operand.name << "Operands, ";
emitTypeResolver(operandTypes[i], operand.name);
// If this isn't a buildable type, verify the sizes match by adding the loc.
if (!operandTypes[i].getBuilderIdx())
body << ", " << operand.name << "OperandsLoc";
body << ", result.operands))\n return failure();\n";
}
}
void OperationFormat::genParserSuccessorResolution(Operator &op,
OpMethodBody &body) {
// Check for the case where all successors were parsed.
bool hasAllSuccessors = llvm::any_of(
elements, [](auto &elt) { return isa<SuccessorsDirective>(elt.get()); });
if (hasAllSuccessors) {
body << " result.addSuccessors(fullSuccessors);\n";
return;
}
// Otherwise, handle each successor individually.
for (const NamedSuccessor &successor : op.getSuccessors()) {
if (successor.isVariadic())
body << " result.addSuccessors(" << successor.name << "Successors);\n";
else
body << " result.addSuccessors(" << successor.name << "Successor);\n";
}
}
void OperationFormat::genParserVariadicSegmentResolution(Operator &op,
OpMethodBody &body) {
if (!allOperands && op.getTrait("OpTrait::AttrSizedOperandSegments")) {
body << " result.addAttribute(\"operand_segment_sizes\", "
<< "builder.getI32VectorAttr({";
auto interleaveFn = [&](const NamedTypeConstraint &operand) {
// If the operand is variadic emit the parsed size.
if (operand.isVariadic())
body << "static_cast<int32_t>(" << operand.name << "Operands.size())";
else
body << "1";
};
interleaveComma(op.getOperands(), body, interleaveFn);
body << "}));\n";
}
}
//===----------------------------------------------------------------------===//
// PrinterGen
/// Generate the printer for the 'attr-dict' directive.
static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
OpMethodBody &body, bool withKeyword) {
// Collect all of the attributes used in the format, these will be elided.
SmallVector<const NamedAttribute *, 1> usedAttributes;
for (auto &it : fmt.elements)
if (auto *attr = dyn_cast<AttributeVariable>(it.get()))
usedAttributes.push_back(attr->getVar());
body << " p.printOptionalAttrDict" << (withKeyword ? "WithKeyword" : "")
<< "(getAttrs(), /*elidedAttrs=*/{";
// Elide the variadic segment size attributes if necessary.
if (!fmt.allOperands && op.getTrait("OpTrait::AttrSizedOperandSegments"))
body << "\"operand_segment_sizes\", ";
interleaveComma(usedAttributes, body, [&](const NamedAttribute *attr) {
body << "\"" << attr->name << "\"";
});
body << "});\n";
}
/// Generate the printer for a literal value. `shouldEmitSpace` is true if a
/// space should be emitted before this element. `lastWasPunctuation` is true if
/// the previous element was a punctuation literal.
static void genLiteralPrinter(StringRef value, OpMethodBody &body,
bool &shouldEmitSpace, bool &lastWasPunctuation) {
body << " p";
// Don't insert a space for certain punctuation.
auto shouldPrintSpaceBeforeLiteral = [&] {
if (value.size() != 1 && value != "->")
return true;
if (lastWasPunctuation)
return !StringRef(">)}],").contains(value.front());
return !StringRef("<>(){}[],").contains(value.front());
};
if (shouldEmitSpace && shouldPrintSpaceBeforeLiteral())
body << " << \" \"";
body << " << \"" << value << "\";\n";
// Insert a space after certain literals.
shouldEmitSpace =
value.size() != 1 || !StringRef("<({[").contains(value.front());
lastWasPunctuation = !(value.front() == '_' || isalpha(value.front()));
}
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
/// Generate the C++ for an operand to a (*-)type directive.
static OpMethodBody &genTypeOperandPrinter(Element *arg, OpMethodBody &body) {
if (isa<OperandsDirective>(arg))
return body << "getOperation()->getOperandTypes()";
if (isa<ResultsDirective>(arg))
return body << "getOperation()->getResultTypes()";
auto *operand = dyn_cast<OperandVariable>(arg);
auto *var = operand ? operand->getVar() : cast<ResultVariable>(arg)->getVar();
if (var->isVariadic())
return body << var->name << "().getTypes()";
return body << "ArrayRef<Type>(" << var->name << "().getType())";
}
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
/// Generate the code for printing the given element.
static void genElementPrinter(Element *element, OpMethodBody &body,
OperationFormat &fmt, Operator &op,
bool &shouldEmitSpace, bool &lastWasPunctuation) {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
if (LiteralElement *literal = dyn_cast<LiteralElement>(element))
return genLiteralPrinter(literal->getLiteral(), body, shouldEmitSpace,
lastWasPunctuation);
// Emit an optional group.
if (OptionalElement *optional = dyn_cast<OptionalElement>(element)) {
// Emit the check for the presence of the anchor element.
Element *anchor = optional->getAnchor();
if (AttributeVariable *attrVar = dyn_cast<AttributeVariable>(anchor))
body << " if (getAttr(\"" << attrVar->getVar()->name << "\")) {\n";
else
body << " if (!" << cast<OperandVariable>(anchor)->getVar()->name
<< "().empty()) {\n";
// Emit each of the elements.
for (Element &childElement : optional->getElements())
genElementPrinter(&childElement, body, fmt, op, shouldEmitSpace,
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
lastWasPunctuation);
body << " }\n";
return;
}
// Emit the attribute dictionary.
if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
genAttrDictPrinter(fmt, op, body, attrDict->isWithKeyword());
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
lastWasPunctuation = false;
return;
}
// Optionally insert a space before the next element. The AttrDict printer
// already adds a space as necessary.
if (shouldEmitSpace || !lastWasPunctuation)
body << " p << \" \";\n";
lastWasPunctuation = false;
shouldEmitSpace = true;
if (auto *attr = dyn_cast<AttributeVariable>(element)) {
const NamedAttribute *var = attr->getVar();
// If we are formatting as an enum, symbolize the attribute as a string.
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
if (canFormatEnumAttr(var)) {
const EnumAttr &enumAttr = cast<EnumAttr>(var->attr);
body << " p << \"\\\"\" << " << enumAttr.getSymbolToStringFnName() << "("
<< var->name << "()) << \"\\\"\";\n";
return;
}
// Elide the attribute type if it is buildable.
if (attr->getTypeBuilder())
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
body << " p.printAttributeWithoutType(" << var->name << "Attr());\n";
else
body << " p.printAttribute(" << var->name << "Attr());\n";
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
body << " p << " << operand->getVar()->name << "();\n";
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
const NamedSuccessor *var = successor->getVar();
if (var->isVariadic())
body << " interleaveComma(" << var->name << "(), p);\n";
else
body << " p << " << var->name << "();\n";
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
} else if (isa<OperandsDirective>(element)) {
body << " p << getOperation()->getOperands();\n";
} else if (isa<SuccessorsDirective>(element)) {
body << " interleaveComma(getOperation()->getSuccessors(), p);\n";
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
body << " p << ";
genTypeOperandPrinter(dir->getOperand(), body) << ";\n";
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
body << " p.printFunctionalType(";
genTypeOperandPrinter(dir->getInputs(), body) << ", ";
genTypeOperandPrinter(dir->getResults(), body) << ");\n";
} else {
llvm_unreachable("unknown format element");
}
}
void OperationFormat::genPrinter(Operator &op, OpClass &opClass) {
auto &method = opClass.newMethod("void", "print", "OpAsmPrinter &p");
auto &body = method.body();
// Emit the operation name, trimming the prefix if this is the standard
// dialect.
body << " p << \"";
std::string opName = op.getOperationName();
if (op.getDialectName() == "std")
body << StringRef(opName).drop_front(4);
else
body << opName;
body << "\";\n";
// Flags for if we should emit a space, and if the last element was
// punctuation.
bool shouldEmitSpace = true, lastWasPunctuation = false;
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
for (auto &element : elements)
genElementPrinter(element.get(), body, *this, op, shouldEmitSpace,
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
lastWasPunctuation);
}
//===----------------------------------------------------------------------===//
// FormatLexer
//===----------------------------------------------------------------------===//
namespace {
/// This class represents a specific token in the input format.
class Token {
public:
enum Kind {
// Markers.
eof,
error,
// Tokens with no info.
l_paren,
r_paren,
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
caret,
comma,
equal,
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
question,
// Keywords.
keyword_start,
kw_attr_dict,
kw_attr_dict_w_keyword,
kw_functional_type,
kw_operands,
kw_results,
kw_successors,
kw_type,
keyword_end,
// String valued tokens.
identifier,
literal,
variable,
};
Token(Kind kind, StringRef spelling) : kind(kind), spelling(spelling) {}
/// Return the bytes that make up this token.
StringRef getSpelling() const { return spelling; }
/// Return the kind of this token.
Kind getKind() const { return kind; }
/// Return a location for this token.
llvm::SMLoc getLoc() const {
return llvm::SMLoc::getFromPointer(spelling.data());
}
/// Return if this token is a keyword.
bool isKeyword() const { return kind > keyword_start && kind < keyword_end; }
private:
/// Discriminator that indicates the kind of token this is.
Kind kind;
/// A reference to the entire token contents; this is always a pointer into
/// a memory buffer owned by the source manager.
StringRef spelling;
};
/// This class implements a simple lexer for operation assembly format strings.
class FormatLexer {
public:
FormatLexer(llvm::SourceMgr &mgr);
/// Lex the next token and return it.
Token lexToken();
/// Emit an error to the lexer with the given location and message.
Token emitError(llvm::SMLoc loc, const Twine &msg);
Token emitError(const char *loc, const Twine &msg);
private:
Token formToken(Token::Kind kind, const char *tokStart) {
return Token(kind, StringRef(tokStart, curPtr - tokStart));
}
/// Return the next character in the stream.
int getNextChar();
/// Lex an identifier, literal, or variable.
Token lexIdentifier(const char *tokStart);
Token lexLiteral(const char *tokStart);
Token lexVariable(const char *tokStart);
llvm::SourceMgr &srcMgr;
StringRef curBuffer;
const char *curPtr;
};
} // end anonymous namespace
FormatLexer::FormatLexer(llvm::SourceMgr &mgr) : srcMgr(mgr) {
curBuffer = srcMgr.getMemoryBuffer(mgr.getMainFileID())->getBuffer();
curPtr = curBuffer.begin();
}
Token FormatLexer::emitError(llvm::SMLoc loc, const Twine &msg) {
srcMgr.PrintMessage(loc, llvm::SourceMgr::DK_Error, msg);
return formToken(Token::error, loc.getPointer());
}
Token FormatLexer::emitError(const char *loc, const Twine &msg) {
return emitError(llvm::SMLoc::getFromPointer(loc), msg);
}
int FormatLexer::getNextChar() {
char curChar = *curPtr++;
switch (curChar) {
default:
return (unsigned char)curChar;
case 0: {
// A nul character in the stream is either the end of the current buffer or
// a random nul in the file. Disambiguate that here.
if (curPtr - 1 != curBuffer.end())
return 0;
// Otherwise, return end of file.
--curPtr;
return EOF;
}
case '\n':
case '\r':
// Handle the newline character by ignoring it and incrementing the line
// count. However, be careful about 'dos style' files with \n\r in them.
// Only treat a \n\r or \r\n as a single line.
if ((*curPtr == '\n' || (*curPtr == '\r')) && *curPtr != curChar)
++curPtr;
return '\n';
}
}
Token FormatLexer::lexToken() {
const char *tokStart = curPtr;
// This always consumes at least one character.
int curChar = getNextChar();
switch (curChar) {
default:
// Handle identifiers: [a-zA-Z_]
if (isalpha(curChar) || curChar == '_')
return lexIdentifier(tokStart);
// Unknown character, emit an error.
return emitError(tokStart, "unexpected character");
case EOF:
// Return EOF denoting the end of lexing.
return formToken(Token::eof, tokStart);
// Lex punctuation.
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
case '^':
return formToken(Token::caret, tokStart);
case ',':
return formToken(Token::comma, tokStart);
case '=':
return formToken(Token::equal, tokStart);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
case '?':
return formToken(Token::question, tokStart);
case '(':
return formToken(Token::l_paren, tokStart);
case ')':
return formToken(Token::r_paren, tokStart);
// Ignore whitespace characters.
case 0:
case ' ':
case '\t':
case '\n':
return lexToken();
case '`':
return lexLiteral(tokStart);
case '$':
return lexVariable(tokStart);
}
}
Token FormatLexer::lexLiteral(const char *tokStart) {
assert(curPtr[-1] == '`');
// Lex a literal surrounded by ``.
while (const char curChar = *curPtr++) {
if (curChar == '`')
return formToken(Token::literal, tokStart);
}
return emitError(curPtr - 1, "unexpected end of file in literal");
}
Token FormatLexer::lexVariable(const char *tokStart) {
if (!isalpha(curPtr[0]) && curPtr[0] != '_')
return emitError(curPtr - 1, "expected variable name");
// Otherwise, consume the rest of the characters.
while (isalnum(*curPtr) || *curPtr == '_')
++curPtr;
return formToken(Token::variable, tokStart);
}
Token FormatLexer::lexIdentifier(const char *tokStart) {
// Match the rest of the identifier regex: [0-9a-zA-Z_\-]*
while (isalnum(*curPtr) || *curPtr == '_' || *curPtr == '-')
++curPtr;
// Check to see if this identifier is a keyword.
StringRef str(tokStart, curPtr - tokStart);
Token::Kind kind =
llvm::StringSwitch<Token::Kind>(str)
.Case("attr-dict", Token::kw_attr_dict)
.Case("attr-dict-with-keyword", Token::kw_attr_dict_w_keyword)
.Case("functional-type", Token::kw_functional_type)
.Case("operands", Token::kw_operands)
.Case("results", Token::kw_results)
.Case("successors", Token::kw_successors)
.Case("type", Token::kw_type)
.Default(Token::identifier);
return Token(kind, str);
}
//===----------------------------------------------------------------------===//
// FormatParser
//===----------------------------------------------------------------------===//
/// Function to find an element within the given range that has the same name as
/// 'name'.
template <typename RangeT> static auto findArg(RangeT &&range, StringRef name) {
auto it = llvm::find_if(range, [=](auto &arg) { return arg.name == name; });
return it != range.end() ? &*it : nullptr;
}
namespace {
/// This class implements a parser for an instance of an operation assembly
/// format.
class FormatParser {
public:
FormatParser(llvm::SourceMgr &mgr, OperationFormat &format, Operator &op)
: lexer(mgr), curToken(lexer.lexToken()), fmt(format), op(op),
seenOperandTypes(op.getNumOperands()),
seenResultTypes(op.getNumResults()) {}
/// Parse the operation assembly format.
LogicalResult parse();
private:
/// This struct represents a type resolution instance. It includes a specific
/// type as well as an optional transformer to apply to that type in order to
/// properly resolve the type of a variable.
struct TypeResolutionInstance {
const NamedTypeConstraint *type;
Optional<StringRef> transformer;
};
/// Verify the state of operation attributes within the format.
LogicalResult verifyAttributes(llvm::SMLoc loc);
/// Verify the state of operation operands within the format.
LogicalResult
verifyOperands(llvm::SMLoc loc,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
/// Verify the state of operation results within the format.
LogicalResult
verifyResults(llvm::SMLoc loc,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
/// Verify the state of operation successors within the format.
LogicalResult verifySuccessors(llvm::SMLoc loc);
/// Given the values of an `AllTypesMatch` trait, check for inferable type
/// resolution.
void handleAllTypesMatchConstraint(
ArrayRef<StringRef> values,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
/// Check for inferable type resolution given all operands, and or results,
/// have the same type. If 'includeResults' is true, the results also have the
/// same type as all of the operands.
void handleSameTypesConstraint(
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
bool includeResults);
/// Returns an argument with the given name that has been seen within the
/// format.
const NamedTypeConstraint *findSeenArg(StringRef name);
/// Parse a specific element.
LogicalResult parseElement(std::unique_ptr<Element> &element,
bool isTopLevel);
LogicalResult parseVariable(std::unique_ptr<Element> &element,
bool isTopLevel);
LogicalResult parseDirective(std::unique_ptr<Element> &element,
bool isTopLevel);
LogicalResult parseLiteral(std::unique_ptr<Element> &element);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
LogicalResult parseOptional(std::unique_ptr<Element> &element,
bool isTopLevel);
LogicalResult parseOptionalChildElement(
std::vector<std::unique_ptr<Element>> &childElements,
SmallPtrSetImpl<const NamedTypeConstraint *> &seenVariables,
Optional<unsigned> &anchorIdx);
/// Parse the various different directives.
LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel,
bool withKeyword);
LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
Token tok, bool isTopLevel);
LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel);
LogicalResult parseResultsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel);
LogicalResult parseSuccessorsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel);
LogicalResult parseTypeDirective(std::unique_ptr<Element> &element, Token tok,
bool isTopLevel);
LogicalResult parseTypeDirectiveOperand(std::unique_ptr<Element> &element);
//===--------------------------------------------------------------------===//
// Lexer Utilities
//===--------------------------------------------------------------------===//
/// Advance the current lexer onto the next token.
void consumeToken() {
assert(curToken.getKind() != Token::eof &&
curToken.getKind() != Token::error &&
"shouldn't advance past EOF or errors");
curToken = lexer.lexToken();
}
LogicalResult parseToken(Token::Kind kind, const Twine &msg) {
if (curToken.getKind() != kind)
return emitError(curToken.getLoc(), msg);
consumeToken();
return success();
}
LogicalResult emitError(llvm::SMLoc loc, const Twine &msg) {
lexer.emitError(loc, msg);
return failure();
}
//===--------------------------------------------------------------------===//
// Fields
//===--------------------------------------------------------------------===//
FormatLexer lexer;
Token curToken;
OperationFormat &fmt;
Operator &op;
// The following are various bits of format state used for verification
// during parsing.
bool hasAllOperands = false, hasAttrDict = false;
bool hasAllSuccessors = false;
llvm::SmallBitVector seenOperandTypes, seenResultTypes;
llvm::DenseSet<const NamedTypeConstraint *> seenOperands;
llvm::DenseSet<const NamedAttribute *> seenAttrs;
llvm::DenseSet<const NamedSuccessor *> seenSuccessors;
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
llvm::DenseSet<const NamedTypeConstraint *> optionalVariables;
};
} // end anonymous namespace
LogicalResult FormatParser::parse() {
llvm::SMLoc loc = curToken.getLoc();
// Parse each of the format elements into the main format.
while (curToken.getKind() != Token::eof) {
std::unique_ptr<Element> element;
if (failed(parseElement(element, /*isTopLevel=*/true)))
return failure();
fmt.elements.push_back(std::move(element));
}
// Check that the attribute dictionary is in the format.
if (!hasAttrDict)
return emitError(loc, "format missing 'attr-dict' directive");
// Check for any type traits that we can use for inferring types.
llvm::StringMap<TypeResolutionInstance> variableTyResolver;
for (const OpTrait &trait : op.getTraits()) {
const llvm::Record &def = trait.getDef();
if (def.isSubClassOf("AllTypesMatch")) {
handleAllTypesMatchConstraint(def.getValueAsListOfStrings("values"),
variableTyResolver);
} else if (def.getName() == "SameTypeOperands") {
handleSameTypesConstraint(variableTyResolver, /*includeResults=*/false);
} else if (def.getName() == "SameOperandsAndResultType") {
handleSameTypesConstraint(variableTyResolver, /*includeResults=*/true);
} else if (def.isSubClassOf("TypesMatchWith")) {
if (const auto *lhsArg = findSeenArg(def.getValueAsString("lhs")))
variableTyResolver[def.getValueAsString("rhs")] = {
lhsArg, def.getValueAsString("transformer")};
}
}
// Verify the state of the various operation components.
if (failed(verifyAttributes(loc)) ||
failed(verifyResults(loc, variableTyResolver)) ||
failed(verifyOperands(loc, variableTyResolver)) ||
failed(verifySuccessors(loc)))
return failure();
// Check to see if we are formatting all of the operands.
fmt.allOperands = llvm::any_of(fmt.elements, [](auto &elt) {
return isa<OperandsDirective>(elt.get());
});
return success();
}
LogicalResult FormatParser::verifyAttributes(llvm::SMLoc loc) {
// Check that there are no `:` literals after an attribute without a constant
// type. The attribute grammar contains an optional trailing colon type, which
// can lead to unexpected and generally unintended behavior. Given that, it is
// better to just error out here instead.
using ElementsIterT = llvm::pointee_iterator<
std::vector<std::unique_ptr<Element>>::const_iterator>;
SmallVector<std::pair<ElementsIterT, ElementsIterT>, 1> iteratorStack;
iteratorStack.emplace_back(fmt.elements.begin(), fmt.elements.end());
while (!iteratorStack.empty()) {
auto &stackIt = iteratorStack.back();
ElementsIterT &it = stackIt.first, e = stackIt.second;
while (it != e) {
Element *element = &*(it++);
// Traverse into optional groups.
if (auto *optional = dyn_cast<OptionalElement>(element)) {
auto elements = optional->getElements();
iteratorStack.emplace_back(elements.begin(), elements.end());
break;
}
// We are checking for an attribute element followed by a `:`, so there is
// no need to check the end.
if (it == e && iteratorStack.size() == 1)
break;
// Check for an attribute with a constant type builder, followed by a `:`.
auto *prevAttr = dyn_cast<AttributeVariable>(element);
if (!prevAttr || prevAttr->getTypeBuilder())
continue;
// Check the next iterator within the stack for literal elements.
for (auto &nextItPair : iteratorStack) {
ElementsIterT nextIt = nextItPair.first, nextE = nextItPair.second;
for (; nextIt != nextE; ++nextIt) {
// Skip any trailing optional groups or attribute dictionaries.
if (isa<AttrDictDirective>(*nextIt) || isa<OptionalElement>(*nextIt))
continue;
// We are only interested in `:` literals.
auto *literal = dyn_cast<LiteralElement>(&*nextIt);
if (!literal || literal->getLiteral() != ":")
break;
// TODO: Use the location of the literal element itself.
return emitError(
loc, llvm::formatv("format ambiguity caused by `:` literal found "
"after attribute `{0}` which does not have "
"a buildable type",
prevAttr->getVar()->name));
}
}
}
if (it == e)
iteratorStack.pop_back();
}
return success();
}
LogicalResult FormatParser::verifyOperands(
llvm::SMLoc loc,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
// Check that all of the operands are within the format, and their types can
// be inferred.
auto &buildableTypes = fmt.buildableTypes;
for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i) {
NamedTypeConstraint &operand = op.getOperand(i);
// Check that the operand itself is in the format.
if (!hasAllOperands && !seenOperands.count(&operand)) {
return emitError(loc, "format missing instance of operand #" + Twine(i) +
"('" + operand.name + "')");
}
// Check that the operand type is in the format, or that it can be inferred.
if (fmt.allOperandTypes || seenOperandTypes.test(i))
continue;
// Check to see if we can infer this type from another variable.
auto varResolverIt = variableTyResolver.find(op.getOperand(i).name);
if (varResolverIt != variableTyResolver.end()) {
fmt.operandTypes[i].setVariable(varResolverIt->second.type,
varResolverIt->second.transformer);
continue;
}
// Similarly to results, allow a custom builder for resolving the type if
// we aren't using the 'operands' directive.
Optional<StringRef> builder = operand.constraint.getBuilderCall();
if (!builder || (hasAllOperands && operand.isVariadic())) {
return emitError(loc, "format missing instance of operand #" + Twine(i) +
"('" + operand.name + "') type");
}
auto it = buildableTypes.insert({*builder, buildableTypes.size()});
fmt.operandTypes[i].setBuilderIdx(it.first->second);
}
return success();
}
LogicalResult FormatParser::verifyResults(
llvm::SMLoc loc,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
// If we format all of the types together, there is nothing to check.
if (fmt.allResultTypes)
return success();
// Check that all of the result types can be inferred.
auto &buildableTypes = fmt.buildableTypes;
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) {
if (seenResultTypes.test(i))
continue;
// Check to see if we can infer this type from another variable.
auto varResolverIt = variableTyResolver.find(op.getResultName(i));
if (varResolverIt != variableTyResolver.end()) {
fmt.resultTypes[i].setVariable(varResolverIt->second.type,
varResolverIt->second.transformer);
continue;
}
// If the result is not variadic, allow for the case where the type has a
// builder that we can use.
NamedTypeConstraint &result = op.getResult(i);
Optional<StringRef> builder = result.constraint.getBuilderCall();
if (!builder || result.constraint.isVariadic()) {
return emitError(loc, "format missing instance of result #" + Twine(i) +
"('" + result.name + "') type");
}
// Note in the format that this result uses the custom builder.
auto it = buildableTypes.insert({*builder, buildableTypes.size()});
fmt.resultTypes[i].setBuilderIdx(it.first->second);
}
return success();
}
LogicalResult FormatParser::verifySuccessors(llvm::SMLoc loc) {
// Check that all of the successors are within the format.
if (hasAllSuccessors)
return success();
for (unsigned i = 0, e = op.getNumSuccessors(); i != e; ++i) {
const NamedSuccessor &successor = op.getSuccessor(i);
if (!seenSuccessors.count(&successor)) {
return emitError(loc, "format missing instance of successor #" +
Twine(i) + "('" + successor.name + "')");
}
}
return success();
}
void FormatParser::handleAllTypesMatchConstraint(
ArrayRef<StringRef> values,
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
for (unsigned i = 0, e = values.size(); i != e; ++i) {
// Check to see if this value matches a resolved operand or result type.
const NamedTypeConstraint *arg = findSeenArg(values[i]);
if (!arg)
continue;
// Mark this value as the type resolver for the other variables.
for (unsigned j = 0; j != i; ++j)
variableTyResolver[values[j]] = {arg, llvm::None};
for (unsigned j = i + 1; j != e; ++j)
variableTyResolver[values[j]] = {arg, llvm::None};
}
}
void FormatParser::handleSameTypesConstraint(
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
bool includeResults) {
const NamedTypeConstraint *resolver = nullptr;
int resolvedIt = -1;
// Check to see if there is an operand or result to use for the resolution.
if ((resolvedIt = seenOperandTypes.find_first()) != -1)
resolver = &op.getOperand(resolvedIt);
else if (includeResults && (resolvedIt = seenResultTypes.find_first()) != -1)
resolver = &op.getResult(resolvedIt);
else
return;
// Set the resolvers for each operand and result.
for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i)
if (!seenOperandTypes.test(i) && !op.getOperand(i).name.empty())
variableTyResolver[op.getOperand(i).name] = {resolver, llvm::None};
if (includeResults) {
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)
if (!seenResultTypes.test(i) && !op.getResultName(i).empty())
variableTyResolver[op.getResultName(i)] = {resolver, llvm::None};
}
}
const NamedTypeConstraint *FormatParser::findSeenArg(StringRef name) {
if (auto *arg = findArg(op.getOperands(), name))
return seenOperandTypes.test(arg - op.operand_begin()) ? arg : nullptr;
if (auto *arg = findArg(op.getResults(), name))
return seenResultTypes.test(arg - op.result_begin()) ? arg : nullptr;
return nullptr;
}
LogicalResult FormatParser::parseElement(std::unique_ptr<Element> &element,
bool isTopLevel) {
// Directives.
if (curToken.isKeyword())
return parseDirective(element, isTopLevel);
// Literals.
if (curToken.getKind() == Token::literal)
return parseLiteral(element);
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
// Optionals.
if (curToken.getKind() == Token::l_paren)
return parseOptional(element, isTopLevel);
// Variables.
if (curToken.getKind() == Token::variable)
return parseVariable(element, isTopLevel);
return emitError(curToken.getLoc(),
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
"expected directive, literal, variable, or optional group");
}
LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element,
bool isTopLevel) {
Token varTok = curToken;
consumeToken();
StringRef name = varTok.getSpelling().drop_front();
llvm::SMLoc loc = varTok.getLoc();
// Check that the parsed argument is something actually registered on the
// op.
/// Attributes
if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {
if (isTopLevel && !seenAttrs.insert(attr).second)
return emitError(loc, "attribute '" + name + "' is already bound");
element = std::make_unique<AttributeVariable>(attr);
return success();
}
/// Operands
if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {
if (isTopLevel) {
if (hasAllOperands || !seenOperands.insert(operand).second)
return emitError(loc, "operand '" + name + "' is already bound");
}
element = std::make_unique<OperandVariable>(operand);
return success();
}
/// Results.
if (const auto *result = findArg(op.getResults(), name)) {
if (isTopLevel)
return emitError(loc, "results can not be used at the top level");
element = std::make_unique<ResultVariable>(result);
return success();
}
/// Successors.
if (const auto *successor = findArg(op.getSuccessors(), name)) {
if (!isTopLevel)
return emitError(loc, "successors can only be used at the top level");
if (hasAllSuccessors || !seenSuccessors.insert(successor).second)
return emitError(loc, "successor '" + name + "' is already bound");
element = std::make_unique<SuccessorVariable>(successor);
return success();
}
return emitError(
loc, "expected variable to refer to an argument, result, or successor");
}
LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element,
bool isTopLevel) {
Token dirTok = curToken;
consumeToken();
switch (dirTok.getKind()) {
case Token::kw_attr_dict:
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:
return parseFunctionalTypeDirective(element, dirTok, isTopLevel);
case Token::kw_operands:
return parseOperandsDirective(element, dirTok.getLoc(), isTopLevel);
case Token::kw_results:
return parseResultsDirective(element, dirTok.getLoc(), isTopLevel);
case Token::kw_successors:
return parseSuccessorsDirective(element, dirTok.getLoc(), isTopLevel);
case Token::kw_type:
return parseTypeDirective(element, dirTok, isTopLevel);
default:
llvm_unreachable("unknown directive token");
}
}
LogicalResult FormatParser::parseLiteral(std::unique_ptr<Element> &element) {
Token literalTok = curToken;
consumeToken();
// Check that the parsed literal is valid.
StringRef value = literalTok.getSpelling().drop_front().drop_back();
if (!LiteralElement::isValidLiteral(value))
return emitError(literalTok.getLoc(), "expected valid literal");
element = std::make_unique<LiteralElement>(value);
return success();
}
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
LogicalResult FormatParser::parseOptional(std::unique_ptr<Element> &element,
bool isTopLevel) {
llvm::SMLoc curLoc = curToken.getLoc();
if (!isTopLevel)
return emitError(curLoc, "optional groups can only be used as top-level "
"elements");
consumeToken();
// Parse the child elements for this optional group.
std::vector<std::unique_ptr<Element>> elements;
SmallPtrSet<const NamedTypeConstraint *, 8> seenVariables;
Optional<unsigned> anchorIdx;
do {
if (failed(parseOptionalChildElement(elements, seenVariables, anchorIdx)))
return failure();
} while (curToken.getKind() != Token::r_paren);
consumeToken();
if (failed(parseToken(Token::question, "expected '?' after optional group")))
return failure();
// The optional group is required to have an anchor.
if (!anchorIdx)
return emitError(curLoc, "optional group specified no anchor element");
// The first element of the group must be one that can be parsed/printed in an
// optional fashion.
if (!isa<LiteralElement>(&*elements.front()) &&
!isa<OperandVariable>(&*elements.front()))
return emitError(curLoc, "first element of an operand group must be a "
"literal or operand");
// After parsing all of the elements, ensure that all type directives refer
// only to elements within the group.
auto checkTypeOperand = [&](Element *typeEle) {
auto *opVar = dyn_cast<OperandVariable>(typeEle);
const NamedTypeConstraint *var = opVar ? opVar->getVar() : nullptr;
if (!seenVariables.count(var))
return emitError(curLoc, "type directive can only refer to variables "
"within the optional group");
return success();
};
for (auto &ele : elements) {
if (auto *typeEle = dyn_cast<TypeDirective>(ele.get())) {
if (failed(checkTypeOperand(typeEle->getOperand())))
return failure();
} else if (auto *typeEle = dyn_cast<FunctionalTypeDirective>(ele.get())) {
if (failed(checkTypeOperand(typeEle->getInputs())) ||
failed(checkTypeOperand(typeEle->getResults())))
return failure();
}
}
optionalVariables.insert(seenVariables.begin(), seenVariables.end());
element = std::make_unique<OptionalElement>(std::move(elements), *anchorIdx);
return success();
}
LogicalResult FormatParser::parseOptionalChildElement(
std::vector<std::unique_ptr<Element>> &childElements,
SmallPtrSetImpl<const NamedTypeConstraint *> &seenVariables,
Optional<unsigned> &anchorIdx) {
llvm::SMLoc childLoc = curToken.getLoc();
childElements.push_back({});
if (failed(parseElement(childElements.back(), /*isTopLevel=*/true)))
return failure();
// Check to see if this element is the anchor of the optional group.
bool isAnchor = curToken.getKind() == Token::caret;
if (isAnchor) {
if (anchorIdx)
return emitError(childLoc, "only one element can be marked as the anchor "
"of an optional group");
anchorIdx = childElements.size() - 1;
consumeToken();
}
return TypeSwitch<Element *, LogicalResult>(childElements.back().get())
// All attributes can be within the optional group, but only optional
// attributes can be the anchor.
.Case([&](AttributeVariable *attrEle) {
if (isAnchor && !attrEle->getVar()->attr.isOptional())
return emitError(childLoc, "only optional attributes can be used to "
"anchor an optional group");
return success();
})
// Only optional-like(i.e. variadic) operands can be within an optional
// group.
.Case<OperandVariable>([&](OperandVariable *ele) {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
if (!ele->getVar()->isVariadic())
return emitError(childLoc, "only variadic operands can be used within"
" an optional group");
seenVariables.insert(ele->getVar());
return success();
})
// Literals and type directives may be used, but they can't anchor the
// group.
.Case<LiteralElement, TypeDirective, FunctionalTypeDirective>(
[&](Element *) {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
if (isAnchor)
return emitError(childLoc, "only variables can be used to anchor "
"an optional group");
return success();
})
.Default([&](Element *) {
[mlir][DeclarativeParser] Add basic support for optional groups in the assembly format. When operations have optional attributes, or optional operands(i.e. empty variadic operands), the assembly format often has an optional section to represent these arguments. This revision adds basic support for defining an "optional group" in the assembly format to support this. An optional group is defined by wrapping a set of elements in `()` followed by `?` and requires the following: * The first element of the group must be either a literal or an operand argument. - This is because the first element must be optionally parsable. * There must be exactly one argument variable within the group that is marked as the anchor of the group. The anchor is the element whose presence controls whether the group should be printed/parsed. An element is marked as the anchor by adding a trailing `^`. * The group must only contain literals, variables, and type directives. - Any attribute variables may be used, but only optional attributes can be marked as the anchor. - Only variadic, i.e. optional, operand arguments can be used. - The elements of a type directive must be defined within the same optional group. An example of this can be seen with the assembly format for ReturnOp, which has a variadic number of operands. ``` def ReturnOp : ... { let arguments = (ins Variadic<AnyType>:$operands); // We only print the operands+types if there are a non-zero number // of operands. let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?"; } ``` Differential Revision: https://reviews.llvm.org/D74681
2020-02-22 05:19:15 +08:00
return emitError(childLoc, "only literals, types, and variables can be "
"used within an optional group");
});
}
LogicalResult
FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel,
bool withKeyword) {
if (!isTopLevel)
return emitError(loc, "'attr-dict' directive can only be used as a "
"top-level directive");
if (hasAttrDict)
return emitError(loc, "'attr-dict' directive has already been seen");
hasAttrDict = true;
element = std::make_unique<AttrDictDirective>(withKeyword);
return success();
}
LogicalResult
FormatParser::parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
Token tok, bool isTopLevel) {
llvm::SMLoc loc = tok.getLoc();
if (!isTopLevel)
return emitError(
loc, "'functional-type' is only valid as a top-level directive");
// Parse the main operand.
std::unique_ptr<Element> inputs, results;
if (failed(parseToken(Token::l_paren, "expected '(' before argument list")) ||
failed(parseTypeDirectiveOperand(inputs)) ||
failed(parseToken(Token::comma, "expected ',' after inputs argument")) ||
failed(parseTypeDirectiveOperand(results)) ||
failed(parseToken(Token::r_paren, "expected ')' after argument list")))
return failure();
element = std::make_unique<FunctionalTypeDirective>(std::move(inputs),
std::move(results));
return success();
}
LogicalResult
FormatParser::parseOperandsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) {
if (isTopLevel && (hasAllOperands || !seenOperands.empty()))
return emitError(loc, "'operands' directive creates overlap in format");
hasAllOperands = true;
element = std::make_unique<OperandsDirective>();
return success();
}
LogicalResult
FormatParser::parseResultsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) {
if (isTopLevel)
return emitError(loc, "'results' directive can not be used as a "
"top-level directive");
element = std::make_unique<ResultsDirective>();
return success();
}
LogicalResult
FormatParser::parseSuccessorsDirective(std::unique_ptr<Element> &element,
llvm::SMLoc loc, bool isTopLevel) {
if (!isTopLevel)
return emitError(loc,
"'successors' is only valid as a top-level directive");
if (hasAllSuccessors || !seenSuccessors.empty())
return emitError(loc, "'successors' directive creates overlap in format");
hasAllSuccessors = true;
element = std::make_unique<SuccessorsDirective>();
return success();
}
LogicalResult
FormatParser::parseTypeDirective(std::unique_ptr<Element> &element, Token tok,
bool isTopLevel) {
llvm::SMLoc loc = tok.getLoc();
if (!isTopLevel)
return emitError(loc, "'type' is only valid as a top-level directive");
std::unique_ptr<Element> operand;
if (failed(parseToken(Token::l_paren, "expected '(' before argument list")) ||
failed(parseTypeDirectiveOperand(operand)) ||
failed(parseToken(Token::r_paren, "expected ')' after argument list")))
return failure();
element = std::make_unique<TypeDirective>(std::move(operand));
return success();
}
LogicalResult
FormatParser::parseTypeDirectiveOperand(std::unique_ptr<Element> &element) {
llvm::SMLoc loc = curToken.getLoc();
if (failed(parseElement(element, /*isTopLevel=*/false)))
return failure();
if (isa<LiteralElement>(element.get()))
return emitError(
loc, "'type' directive operand expects variable or directive operand");
if (auto *var = dyn_cast<OperandVariable>(element.get())) {
unsigned opIdx = var->getVar() - op.operand_begin();
if (fmt.allOperandTypes || seenOperandTypes.test(opIdx))
return emitError(loc, "'type' of '" + var->getVar()->name +
"' is already bound");
seenOperandTypes.set(opIdx);
} else if (auto *var = dyn_cast<ResultVariable>(element.get())) {
unsigned resIdx = var->getVar() - op.result_begin();
if (fmt.allResultTypes || seenResultTypes.test(resIdx))
return emitError(loc, "'type' of '" + var->getVar()->name +
"' is already bound");
seenResultTypes.set(resIdx);
} else if (isa<OperandsDirective>(&*element)) {
if (fmt.allOperandTypes || seenOperandTypes.any())
return emitError(loc, "'operands' 'type' is already bound");
fmt.allOperandTypes = true;
} else if (isa<ResultsDirective>(&*element)) {
if (fmt.allResultTypes || seenResultTypes.any())
return emitError(loc, "'results' 'type' is already bound");
fmt.allResultTypes = true;
} else {
return emitError(loc, "invalid argument to 'type' directive");
}
return success();
}
//===----------------------------------------------------------------------===//
// Interface
//===----------------------------------------------------------------------===//
void mlir::tblgen::generateOpFormat(const Operator &constOp, OpClass &opClass) {
// TODO(riverriddle) Operator doesn't expose all necessary functionality via
// the const interface.
Operator &op = const_cast<Operator &>(constOp);
if (!op.hasAssemblyFormat())
return;
// Parse the format description.
llvm::SourceMgr mgr;
mgr.AddNewSourceBuffer(
llvm::MemoryBuffer::getMemBuffer(op.getAssemblyFormat()), llvm::SMLoc());
OperationFormat format(op);
if (failed(FormatParser(mgr, format, op).parse())) {
// Exit the process if format errors are treated as fatal.
if (formatErrorIsFatal) {
// Invoke the interrupt handlers to run the file cleanup handlers.
llvm::sys::RunInterruptHandlers();
std::exit(1);
}
return;
}
// Generate the printer and parser based on the parsed format.
format.genParser(op, opClass);
format.genPrinter(op, opClass);
}