2020-01-31 03:30:23 +08:00
|
|
|
//===- 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/Support/LogicalResult.h"
|
|
|
|
#include "mlir/TableGen/Format.h"
|
|
|
|
#include "mlir/TableGen/GenInfo.h"
|
2020-07-01 06:42:52 +08:00
|
|
|
#include "mlir/TableGen/Interfaces.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#include "mlir/TableGen/OpClass.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"
|
2020-04-15 05:53:50 +08:00
|
|
|
#include "llvm/ADT/TypeSwitch.h"
|
2020-02-04 14:14:33 +08:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Signals.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#include "llvm/TableGen/Error.h"
|
|
|
|
#include "llvm/TableGen/Record.h"
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "mlir-tblgen-opformatgen"
|
|
|
|
|
|
|
|
using namespace mlir;
|
|
|
|
using namespace mlir::tblgen;
|
|
|
|
|
2020-02-04 14:14:33 +08:00
|
|
|
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));
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Element
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents a single format element.
|
|
|
|
class Element {
|
|
|
|
public:
|
|
|
|
enum class Kind {
|
|
|
|
/// This element is a directive.
|
|
|
|
AttrDictDirective,
|
|
|
|
FunctionalTypeDirective,
|
|
|
|
OperandsDirective,
|
|
|
|
ResultsDirective,
|
2020-02-22 05:20:06 +08:00
|
|
|
SuccessorsDirective,
|
2020-01-31 03:30:23 +08:00
|
|
|
TypeDirective,
|
|
|
|
|
|
|
|
/// This element is a literal.
|
|
|
|
Literal,
|
|
|
|
|
|
|
|
/// This element is an variable value.
|
|
|
|
AttributeVariable,
|
|
|
|
OperandVariable,
|
|
|
|
ResultVariable,
|
2020-02-22 05:20:06 +08:00
|
|
|
SuccessorVariable,
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
/// This element is an optional element.
|
|
|
|
Optional,
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
|
|
|
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; }
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
protected:
|
2020-01-31 03:30:23 +08:00
|
|
|
const VarT *var;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This class represents a variable that refers to an attribute argument.
|
2020-04-04 10:20:33 +08:00
|
|
|
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;
|
|
|
|
}
|
2020-08-04 05:20:50 +08:00
|
|
|
|
|
|
|
/// Return if this attribute refers to a UnitAttr.
|
|
|
|
bool isUnitAttr() const {
|
|
|
|
return var->attr.getBaseAttr().getAttrDefName() == "UnitAttr";
|
|
|
|
}
|
2020-04-04 10:20:33 +08:00
|
|
|
};
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// 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>;
|
2020-02-22 05:20:06 +08:00
|
|
|
|
|
|
|
/// This class represents a variable that refers to a successor.
|
|
|
|
using SuccessorVariable =
|
|
|
|
VariableElement<NamedSuccessor, Element::Kind::SuccessorVariable>;
|
2020-01-31 03:30:23 +08:00
|
|
|
} // 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>;
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
/// This class represents the `successors` directive. This directive represents
|
|
|
|
/// all of the successors of an operation.
|
|
|
|
using SuccessorsDirective =
|
|
|
|
DirectiveElement<Element::Kind::SuccessorsDirective>;
|
|
|
|
|
2020-02-22 05:19:26 +08:00
|
|
|
/// 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;
|
|
|
|
};
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This class represents the `functional-type` directive. This directive takes
|
|
|
|
/// two arguments and formats them, respectively, as the inputs and results of a
|
|
|
|
/// FunctionType.
|
2020-02-22 05:19:26 +08:00
|
|
|
class FunctionalTypeDirective
|
2020-01-31 03:30:23 +08:00
|
|
|
: 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.
|
2020-02-22 05:19:26 +08:00
|
|
|
class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> {
|
2020-01-31 03:30:23 +08:00
|
|
|
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)
|
2020-02-22 05:19:15 +08:00
|
|
|
: Element{Kind::Literal}, literal(literal) {}
|
2020-01-31 03:30:23 +08:00
|
|
|
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)
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
return isalpha(front) || StringRef("_:,=<>()[]?").contains(front);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
// 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 == '.';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OperationFormat
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2020-07-07 12:40:01 +08:00
|
|
|
|
|
|
|
using ConstArgument =
|
|
|
|
llvm::PointerUnion<const NamedAttribute *, const NamedTypeConstraint *>;
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
struct OperationFormat {
|
2020-02-04 13:52:38 +08:00
|
|
|
/// 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; }
|
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Get the variable this type is resolved to, or nullptr.
|
|
|
|
const NamedTypeConstraint *getVariable() const {
|
|
|
|
return resolver.dyn_cast<const NamedTypeConstraint *>();
|
|
|
|
}
|
|
|
|
/// Get the attribute this type is resolved to, or nullptr.
|
|
|
|
const NamedAttribute *getAttribute() const {
|
|
|
|
return resolver.dyn_cast<const NamedAttribute *>();
|
|
|
|
}
|
|
|
|
/// Get the transformer for the type of the variable, or None.
|
2020-02-22 05:19:03 +08:00
|
|
|
Optional<StringRef> getVarTransformer() const {
|
|
|
|
return variableTransformer;
|
|
|
|
}
|
2020-07-07 12:40:01 +08:00
|
|
|
void setResolver(ConstArgument arg, Optional<StringRef> transformer) {
|
|
|
|
resolver = arg;
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTransformer = transformer;
|
2020-07-07 12:40:01 +08:00
|
|
|
assert(getVariable() || getAttribute());
|
2020-02-22 05:19:03 +08:00
|
|
|
}
|
2020-02-04 13:52:38 +08:00
|
|
|
|
|
|
|
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
|
2020-07-07 12:40:01 +08:00
|
|
|
/// the variable or the attribute that this type is resolved to.
|
|
|
|
ConstArgument resolver;
|
2020-02-22 05:19:03 +08:00
|
|
|
/// 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;
|
2020-02-04 13:52:38 +08:00
|
|
|
};
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat(const Operator &op)
|
2020-03-06 04:40:53 +08:00
|
|
|
: allOperands(false), allOperandTypes(false), allResultTypes(false) {
|
2020-02-04 13:52:38 +08:00
|
|
|
operandTypes.resize(op.getNumOperands(), TypeResolution());
|
|
|
|
resultTypes.resize(op.getNumResults(), TypeResolution());
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
/// 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);
|
2020-02-22 05:20:06 +08:00
|
|
|
/// Generate the c++ to resolve successors during parsing.
|
|
|
|
void genParserSuccessorResolution(Operator &op, OpMethodBody &body);
|
2020-03-06 04:40:53 +08:00
|
|
|
/// Generate the c++ to handling variadic segment size traits.
|
|
|
|
void genParserVariadicSegmentResolution(Operator &op, OpMethodBody &body);
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
/// Generate the operation printer from this format.
|
|
|
|
void genPrinter(Operator &op, OpClass &opClass);
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// 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.
|
2020-03-06 04:40:53 +08:00
|
|
|
bool allOperands, allOperandTypes, allResultTypes;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// 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.
|
2020-02-04 13:52:38 +08:00
|
|
|
std::vector<TypeResolution> operandTypes, resultTypes;
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Parser Gen
|
|
|
|
|
2020-02-14 09:11:01 +08:00
|
|
|
/// 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();
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
/// 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.
|
2020-02-09 02:01:17 +08:00
|
|
|
/// {2}: The type for the attribute.
|
2020-01-31 03:31:21 +08:00
|
|
|
const char *const attrParserCode = R"(
|
|
|
|
{0} {1}Attr;
|
2020-02-09 02:01:17 +08:00
|
|
|
if (parser.parseAttribute({1}Attr{2}, "{1}", result.attributes))
|
2020-01-31 03:31:21 +08:00
|
|
|
return failure();
|
|
|
|
)";
|
2020-07-15 04:14:14 +08:00
|
|
|
const char *const optionalAttrParserCode = R"(
|
|
|
|
{0} {1}Attr;
|
|
|
|
{
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalAttribute({1}Attr{2}, "{1}", result.attributes);
|
|
|
|
if (parseResult.hasValue() && failed(*parseResult))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2020-02-14 09:11:01 +08:00
|
|
|
/// 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"(
|
|
|
|
{
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::StringAttr attrVal;
|
|
|
|
::mlir::NamedAttrList attrStorage;
|
2020-02-14 09:11:01 +08:00
|
|
|
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});
|
|
|
|
}
|
|
|
|
)";
|
2020-07-15 04:14:14 +08:00
|
|
|
const char *const optionalEnumAttrParserCode = R"(
|
|
|
|
Attribute {0}Attr;
|
|
|
|
{
|
|
|
|
::mlir::StringAttr attrVal;
|
|
|
|
::mlir::NamedAttrList attrStorage;
|
|
|
|
auto loc = parser.getCurrentLocation();
|
|
|
|
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalAttribute(attrVal, parser.getBuilder().getNoneType(),
|
|
|
|
"{0}", attrStorage);
|
|
|
|
if (parseResult.hasValue()) {
|
|
|
|
if (failed(*parseResult))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
auto attrOptional = {1}::{2}(attrVal.getValue());
|
|
|
|
if (!attrOptional)
|
|
|
|
return parser.emitError(loc, "invalid ")
|
|
|
|
<< "{0} attribute specification: " << attrVal;
|
|
|
|
|
|
|
|
{0}Attr = {3};
|
|
|
|
result.addAttribute("{0}", {0}Attr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
2020-02-14 09:11:01 +08:00
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
/// 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();
|
|
|
|
)";
|
2020-04-11 05:11:45 +08:00
|
|
|
const char *const optionalOperandParserCode = R"(
|
|
|
|
{
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::OpAsmParser::OperandType operand;
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalOperand(operand);
|
2020-04-11 05:11:45 +08:00
|
|
|
if (parseResult.hasValue()) {
|
|
|
|
if (failed(*parseResult))
|
|
|
|
return failure();
|
|
|
|
{0}Operands.push_back(operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
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();
|
|
|
|
)";
|
2020-04-11 05:11:45 +08:00
|
|
|
const char *const optionalTypeParserCode = R"(
|
|
|
|
{
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::Type optionalType;
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalType(optionalType);
|
2020-04-11 05:11:45 +08:00
|
|
|
if (parseResult.hasValue()) {
|
|
|
|
if (failed(*parseResult))
|
|
|
|
return failure();
|
|
|
|
{0}Types.push_back(optionalType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
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"(
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::FunctionType {0}__{1}_functionType;
|
2020-01-31 03:31:21 +08:00
|
|
|
if (parser.parseType({0}__{1}_functionType))
|
|
|
|
return failure();
|
2020-02-22 05:19:15 +08:00
|
|
|
{0}Types = {0}__{1}_functionType.getInputs();
|
|
|
|
{1}Types = {0}__{1}_functionType.getResults();
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
/// The code snippet used to generate a parser call for a successor list.
|
|
|
|
///
|
|
|
|
/// {0}: The name for the successor list.
|
|
|
|
const char *successorListParserCode = R"(
|
2020-06-26 19:20:44 +08:00
|
|
|
::llvm::SmallVector<::mlir::Block *, 2> {0}Successors;
|
2020-02-22 05:20:06 +08:00
|
|
|
{
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::Block *succ;
|
2020-03-06 04:48:28 +08:00
|
|
|
auto firstSucc = parser.parseOptionalSuccessor(succ);
|
2020-02-22 05:20:06 +08:00
|
|
|
if (firstSucc.hasValue()) {
|
|
|
|
if (failed(*firstSucc))
|
|
|
|
return failure();
|
2020-03-06 04:48:28 +08:00
|
|
|
{0}Successors.emplace_back(succ);
|
2020-02-22 05:20:06 +08:00
|
|
|
|
|
|
|
// Parse any trailing successors.
|
|
|
|
while (succeeded(parser.parseOptionalComma())) {
|
2020-03-06 04:48:28 +08:00
|
|
|
if (parser.parseSuccessor(succ))
|
2020-02-22 05:20:06 +08:00
|
|
|
return failure();
|
2020-03-06 04:48:28 +08:00
|
|
|
{0}Successors.emplace_back(succ);
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
/// The code snippet used to generate a parser call for a successor.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the successor.
|
|
|
|
const char *successorParserCode = R"(
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::Block *{0}Successor = nullptr;
|
2020-03-06 04:48:28 +08:00
|
|
|
if (parser.parseSuccessor({0}Successor))
|
2020-02-22 05:20:06 +08:00
|
|
|
return failure();
|
|
|
|
)";
|
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
namespace {
|
|
|
|
/// The type of length for a given parse argument.
|
|
|
|
enum class ArgumentLengthKind {
|
|
|
|
/// The argument is variadic, and may contain 0->N elements.
|
|
|
|
Variadic,
|
|
|
|
/// The argument is optional, and may contain 0 or 1 elements.
|
|
|
|
Optional,
|
|
|
|
/// The argument is a single element, i.e. always represents 1 element.
|
|
|
|
Single
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
/// Get the length kind for the given constraint.
|
|
|
|
static ArgumentLengthKind
|
|
|
|
getArgumentLengthKind(const NamedTypeConstraint *var) {
|
|
|
|
if (var->isOptional())
|
|
|
|
return ArgumentLengthKind::Optional;
|
|
|
|
if (var->isVariadic())
|
|
|
|
return ArgumentLengthKind::Variadic;
|
|
|
|
return ArgumentLengthKind::Single;
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
/// Get the name used for the type list for the given type directive operand.
|
2020-04-11 05:11:45 +08:00
|
|
|
/// 'lengthKind' to the corresponding kind for the given argument.
|
|
|
|
static StringRef getTypeListName(Element *arg, ArgumentLengthKind &lengthKind) {
|
2020-01-31 03:31:21 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(arg)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = getArgumentLengthKind(operand->getVar());
|
2020-01-31 03:31:21 +08:00
|
|
|
return operand->getVar()->name;
|
|
|
|
}
|
|
|
|
if (auto *result = dyn_cast<ResultVariable>(arg)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = getArgumentLengthKind(result->getVar());
|
2020-01-31 03:31:21 +08:00
|
|
|
return result->getVar()->name;
|
|
|
|
}
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = ArgumentLengthKind::Variadic;
|
2020-01-31 03:31:21 +08:00
|
|
|
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 << "\")";
|
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()")
|
[mlir] Add a new "Pattern Descriptor Language" (PDL) dialect.
PDL presents a high level abstraction for the rewrite pattern infrastructure available in MLIR. This abstraction allows for representing patterns transforming MLIR, as MLIR. This allows for applying all of the benefits that the general MLIR infrastructure provides, to the infrastructure itself. This means that pattern matching can be more easily verified for correctness, targeted by frontends, and optimized.
PDL abstracts over various different aspects of patterns and core MLIR data structures. Patterns are specified via a `pdl.pattern` operation. These operations contain a region body for the "matcher" code, and terminate with a `pdl.rewrite` that either dispatches to an external rewriter or contains a region for the rewrite specified via `pdl`. The types of values in `pdl` are handle types to MLIR C++ types, with `!pdl.attribute`, `!pdl.operation`, and `!pdl.type` directly mapping to `mlir::Attribute`, `mlir::Operation*`, and `mlir::Value` respectively.
An example pattern is shown below:
```mlir
// pdl.pattern contains metadata similarly to a `RewritePattern`.
pdl.pattern : benefit(1) {
// External input operand values are specified via `pdl.input` operations.
// Result types are constrainted via `pdl.type` operations.
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite(%root) {
pdl.replace %root with (%inputOperand)
}
}
```
This is a culmination of the work originally discussed here: https://groups.google.com/a/tensorflow.org/g/mlir/c/j_bn74ByxlQ
Differential Revision: https://reviews.llvm.org/D84578
2020-08-20 03:57:45 +08:00
|
|
|
.Case("]", "RSquare()")
|
|
|
|
.Case("?", "Question()");
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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;
|
2020-04-11 05:11:45 +08:00
|
|
|
if (operand->getVar()->isVariableLength()) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::SmallVector<::mlir::OpAsmParser::OperandType, 4> "
|
|
|
|
<< name << "Operands;\n";
|
2020-03-06 04:40:53 +08:00
|
|
|
} else {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::OpAsmParser::OperandType " << name
|
|
|
|
<< "RawOperands[1];\n"
|
|
|
|
<< " ::llvm::ArrayRef<::mlir::OpAsmParser::OperandType> " << name
|
|
|
|
<< "Operands(" << name << "RawOperands);";
|
2020-03-06 04:40:53 +08:00
|
|
|
}
|
|
|
|
body << llvm::formatv(
|
2020-06-26 19:20:44 +08:00
|
|
|
" ::llvm::SMLoc {0}OperandsLoc = parser.getCurrentLocation();\n"
|
2020-03-06 04:40:53 +08:00
|
|
|
" (void){0}OperandsLoc;\n",
|
|
|
|
name);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef name = getTypeListName(dir->getOperand(), lengthKind);
|
|
|
|
if (lengthKind != ArgumentLengthKind::Single)
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::SmallVector<::mlir::Type, 1> " << name << "Types;\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
else
|
2020-06-26 19:20:44 +08:00
|
|
|
body << llvm::formatv(" ::mlir::Type {0}RawTypes[1];\n", name)
|
|
|
|
<< llvm::formatv(
|
|
|
|
" ::llvm::ArrayRef<::mlir::Type> {0}Types({0}RawTypes);\n",
|
|
|
|
name);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind ignored;
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::ArrayRef<::mlir::Type> "
|
|
|
|
<< getTypeListName(dir->getInputs(), ignored) << "Types;\n";
|
|
|
|
body << " ::llvm::ArrayRef<::mlir::Type> "
|
|
|
|
<< getTypeListName(dir->getResults(), ignored) << "Types;\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2020-07-15 04:14:14 +08:00
|
|
|
Element *firstElement = &*elements.begin();
|
|
|
|
if (auto *attrVar = dyn_cast<AttributeVariable>(firstElement)) {
|
|
|
|
genElementParser(attrVar, body, attrTypeCtx);
|
|
|
|
body << " if (" << attrVar->getVar()->name << "Attr) {\n";
|
|
|
|
} else if (auto *literal = dyn_cast<LiteralElement>(firstElement)) {
|
2020-02-22 05:19:15 +08:00
|
|
|
body << " if (succeeded(parser.parseOptional";
|
|
|
|
genLiteralParser(literal->getLiteral(), body);
|
|
|
|
body << ")) {\n";
|
2020-07-15 04:14:14 +08:00
|
|
|
} else if (auto *opVar = dyn_cast<OperandVariable>(firstElement)) {
|
2020-02-22 05:19:15 +08:00
|
|
|
genElementParser(opVar, body, attrTypeCtx);
|
|
|
|
body << " if (!" << opVar->getVar()->name << "Operands.empty()) {\n";
|
|
|
|
}
|
|
|
|
|
2020-08-04 05:20:50 +08:00
|
|
|
// If the anchor is a unit attribute, we don't need to print it. When
|
|
|
|
// parsing, we will add this attribute if this group is present.
|
|
|
|
Element *elidedAnchorElement = nullptr;
|
|
|
|
auto *anchorAttr = dyn_cast<AttributeVariable>(optional->getAnchor());
|
|
|
|
if (anchorAttr && anchorAttr != firstElement && anchorAttr->isUnitAttr()) {
|
|
|
|
elidedAnchorElement = anchorAttr;
|
|
|
|
|
|
|
|
// Add the anchor unit attribute to the operation state.
|
|
|
|
body << " result.addAttribute(\"" << anchorAttr->getVar()->name
|
|
|
|
<< "\", parser.getBuilder().getUnitAttr());\n";
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Generate the rest of the elements normally.
|
2020-08-04 05:20:50 +08:00
|
|
|
for (Element &childElement : llvm::drop_begin(elements, 1)) {
|
|
|
|
if (&childElement != elidedAnchorElement)
|
|
|
|
genElementParser(&childElement, body, attrTypeCtx);
|
|
|
|
}
|
2020-02-22 05:19:15 +08:00
|
|
|
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()");
|
|
|
|
}
|
|
|
|
|
2020-07-15 04:14:14 +08:00
|
|
|
body << formatv(var->attr.isOptional() ? optionalEnumAttrParserCode
|
|
|
|
: enumAttrParserCode,
|
|
|
|
var->name, enumAttr.getCppNamespace(),
|
2020-02-22 05:19:15 +08:00
|
|
|
enumAttr.getStringToSymbolFnName(), attrBuilderStr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this attribute has a buildable type, use that when parsing the
|
|
|
|
// attribute.
|
|
|
|
std::string attrTypeStr;
|
2020-04-04 10:20:33 +08:00
|
|
|
if (Optional<StringRef> typeBuilder = attr->getTypeBuilder()) {
|
|
|
|
llvm::raw_string_ostream os(attrTypeStr);
|
|
|
|
os << ", " << tgfmt(*typeBuilder, &attrTypeCtx);
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
|
2020-07-15 04:14:14 +08:00
|
|
|
body << formatv(var->attr.isOptional() ? optionalAttrParserCode
|
|
|
|
: attrParserCode,
|
|
|
|
var->attr.getStorageType(), var->name, attrTypeStr);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind lengthKind = getArgumentLengthKind(operand->getVar());
|
|
|
|
StringRef name = operand->getVar()->name;
|
|
|
|
if (lengthKind == ArgumentLengthKind::Variadic)
|
|
|
|
body << llvm::formatv(variadicOperandParserCode, name);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Optional)
|
|
|
|
body << llvm::formatv(optionalOperandParserCode, name);
|
|
|
|
else
|
|
|
|
body << formatv(operandParserCode, name);
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
|
|
|
|
bool isVariadic = successor->getVar()->isVariadic();
|
|
|
|
body << formatv(isVariadic ? successorListParserCode : successorParserCode,
|
|
|
|
successor->getVar()->name);
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
/// Directives.
|
2020-02-22 05:19:26 +08:00
|
|
|
} else if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
|
|
|
|
body << " if (parser.parseOptionalAttrDict"
|
|
|
|
<< (attrDict->isWithKeyword() ? "WithKeyword" : "")
|
|
|
|
<< "(result.attributes))\n"
|
2020-02-22 05:19:15 +08:00
|
|
|
<< " return failure();\n";
|
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::SMLoc allOperandLoc = parser.getCurrentLocation();\n"
|
|
|
|
<< " ::mlir::SmallVector<::mlir::OpAsmParser::OperandType, 4> "
|
|
|
|
"allOperands;\n"
|
2020-02-22 05:19:15 +08:00
|
|
|
<< " if (parser.parseOperandList(allOperands))\n"
|
|
|
|
<< " return failure();\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (isa<SuccessorsDirective>(element)) {
|
|
|
|
body << llvm::formatv(successorListParserCode, "full");
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
|
|
|
|
if (lengthKind == ArgumentLengthKind::Variadic)
|
|
|
|
body << llvm::formatv(variadicTypeParserCode, listName);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Optional)
|
|
|
|
body << llvm::formatv(optionalTypeParserCode, listName);
|
|
|
|
else
|
|
|
|
body << formatv(typeParserCode, listName);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind ignored;
|
2020-02-22 05:19:15 +08:00
|
|
|
body << formatv(functionalTypeParserCode,
|
|
|
|
getTypeListName(dir->getInputs(), ignored),
|
|
|
|
getTypeListName(dir->getResults(), ignored));
|
2020-01-31 03:31:21 +08:00
|
|
|
} else {
|
2020-02-22 05:19:15 +08:00
|
|
|
llvm_unreachable("unknown format element");
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void OperationFormat::genParser(Operator &op, OpClass &opClass) {
|
|
|
|
auto &method = opClass.newMethod(
|
2020-06-26 19:20:44 +08:00
|
|
|
"::mlir::ParseResult", "parse",
|
|
|
|
"::mlir::OpAsmParser &parser, ::mlir::OperationState &result",
|
2020-01-31 03:31:21 +08:00
|
|
|
OpMethod::MP_Static);
|
|
|
|
auto &body = method.body();
|
|
|
|
|
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);
|
|
|
|
|
2020-02-09 02:01:17 +08:00
|
|
|
// A format context used when parsing attributes with buildable types.
|
|
|
|
FmtContext attrTypeCtx;
|
|
|
|
attrTypeCtx.withBuilder("parser.getBuilder()");
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
// Generate parsers for each of the elements.
|
2020-02-22 05:19:15 +08:00
|
|
|
for (auto &element : elements)
|
|
|
|
genElementParser(element.get(), body, attrTypeCtx);
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
// Generate the code to resolve the operand/result types and successors now
|
|
|
|
// that they have been parsed.
|
2020-01-31 03:31:21 +08:00
|
|
|
genParserTypeResolution(op, body);
|
2020-02-22 05:20:06 +08:00
|
|
|
genParserSuccessorResolution(op, body);
|
2020-03-06 04:40:53 +08:00
|
|
|
genParserVariadicSegmentResolution(op, body);
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
body << " return success();\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void OperationFormat::genParserTypeResolution(Operator &op,
|
|
|
|
OpMethodBody &body) {
|
2020-02-22 05:19:03 +08:00
|
|
|
// 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();
|
2020-07-07 12:40:01 +08:00
|
|
|
if (!variable || !verifiedVariables.insert(variable).second)
|
2020-02-22 05:19:03 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
auto constraint = variable->constraint;
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " for (::mlir::Type type : " << variable->name << "Types) {\n"
|
2020-02-22 05:19:03 +08:00
|
|
|
<< " (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";
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
// Initialize the set of buildable types.
|
2020-02-04 13:52:43 +08:00
|
|
|
if (!buildableTypes.empty()) {
|
|
|
|
FmtContext typeBuilderCtx;
|
[mlir][assemblyFormat] Fix bug when using AttrSizedOperandSegments trait with only non-buildable operand types
Summary:
When creating an operation with
* `AttrSizedOperandSegments` trait
* Variadic operands of only non-buildable types
* assemblyFormat to automatically generate the parser
the `builder` local variable is used, but never declared.
This adds a fix as well as a test for this case as existing ones use buildable types only.
Reviewers: rriddle, Kayjukh, grosser
Reviewed By: Kayjukh
Subscribers: mehdi_amini, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, grosul1, frgossen, llvm-commits
Tags: #mlir, #llvm
Differential Revision: https://reviews.llvm.org/D79004
2020-04-29 00:25:04 +08:00
|
|
|
typeBuilderCtx.withBuilder("parser.getBuilder()");
|
2020-02-04 13:52:43 +08:00
|
|
|
for (auto &it : buildableTypes)
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::Type odsBuildableType" << it.second << " = "
|
2020-02-04 13:52:43 +08:00
|
|
|
<< tgfmt(it.first, &typeBuilderCtx) << ";\n";
|
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2020-02-22 05:19:03 +08:00
|
|
|
// 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";
|
2020-07-07 12:40:01 +08:00
|
|
|
} else if (const NamedAttribute *attr = resolver.getAttribute()) {
|
|
|
|
if (Optional<StringRef> tform = resolver.getVarTransformer())
|
|
|
|
body << tgfmt(*tform,
|
|
|
|
&FmtContext().withSelf(attr->name + "Attr.getType()"));
|
|
|
|
else
|
|
|
|
body << attr->name << "Attr.getType()";
|
2020-02-22 05:19:03 +08:00
|
|
|
} else {
|
|
|
|
body << curVar << "Types";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
// 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(";
|
2020-02-22 05:19:03 +08:00
|
|
|
emitTypeResolver(resultTypes[i], op.getResultName(i));
|
2020-01-31 03:31:21 +08:00
|
|
|
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.
|
2020-03-06 04:40:53 +08:00
|
|
|
if (allOperands) {
|
2020-01-31 03:31:21 +08:00
|
|
|
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) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::concat<const ::mlir::OpAsmParser::OperandType>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(op.getOperands(), body, [&](auto &operand) {
|
2020-01-31 03:31:21 +08:00
|
|
|
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.
|
2020-03-06 04:40:53 +08:00
|
|
|
if (allOperands) {
|
2020-01-31 03:31:21 +08:00
|
|
|
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) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::concat<const Type>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(
|
|
|
|
llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::ArrayRef<::mlir::Type>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
emitTypeResolver(operandTypes[i], op.getOperand(i).name);
|
|
|
|
body << ")";
|
|
|
|
});
|
2020-01-31 03:31:21 +08:00
|
|
|
body << ")";
|
|
|
|
} else {
|
2020-02-22 05:19:03 +08:00
|
|
|
emitTypeResolver(operandTypes.front(), op.getOperand(0).name);
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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, ";
|
2020-02-22 05:19:03 +08:00
|
|
|
|
2020-07-02 13:24:36 +08:00
|
|
|
// Resolve the type of this operand.
|
|
|
|
TypeResolution &operandType = operandTypes[i];
|
|
|
|
emitTypeResolver(operandType, operand.name);
|
|
|
|
|
|
|
|
// If the type is resolved by a non-variadic variable, index into the
|
|
|
|
// resolved type list. This allows for resolving the types of a variadic
|
|
|
|
// operand list from a non-variadic variable.
|
|
|
|
bool verifyOperandAndTypeSize = true;
|
|
|
|
if (auto *resolverVar = operandType.getVariable()) {
|
|
|
|
if (!resolverVar->isVariadic() && !operandType.getVarTransformer()) {
|
|
|
|
body << "[0]";
|
|
|
|
verifyOperandAndTypeSize = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
verifyOperandAndTypeSize = !operandType.getBuilderIdx();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check to see if the sizes between the types and operands must match. If
|
|
|
|
// they do, provide the operand location to select the proper resolution
|
|
|
|
// overload.
|
|
|
|
if (verifyOperandAndTypeSize)
|
2020-02-22 05:19:03 +08:00
|
|
|
body << ", " << operand.name << "OperandsLoc";
|
|
|
|
body << ", result.operands))\n return failure();\n";
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
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) {
|
2020-03-06 04:48:28 +08:00
|
|
|
body << " result.addSuccessors(fullSuccessors);\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, handle each successor individually.
|
|
|
|
for (const NamedSuccessor &successor : op.getSuccessors()) {
|
2020-03-06 04:48:28 +08:00
|
|
|
if (successor.isVariadic())
|
|
|
|
body << " result.addSuccessors(" << successor.name << "Successors);\n";
|
|
|
|
else
|
|
|
|
body << " result.addSuccessors(" << successor.name << "Successor);\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 04:40:53 +08:00
|
|
|
void OperationFormat::genParserVariadicSegmentResolution(Operator &op,
|
|
|
|
OpMethodBody &body) {
|
|
|
|
if (!allOperands && op.getTrait("OpTrait::AttrSizedOperandSegments")) {
|
|
|
|
body << " result.addAttribute(\"operand_segment_sizes\", "
|
[mlir][assemblyFormat] Fix bug when using AttrSizedOperandSegments trait with only non-buildable operand types
Summary:
When creating an operation with
* `AttrSizedOperandSegments` trait
* Variadic operands of only non-buildable types
* assemblyFormat to automatically generate the parser
the `builder` local variable is used, but never declared.
This adds a fix as well as a test for this case as existing ones use buildable types only.
Reviewers: rriddle, Kayjukh, grosser
Reviewed By: Kayjukh
Subscribers: mehdi_amini, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, Joonsoo, grosul1, frgossen, llvm-commits
Tags: #mlir, #llvm
Differential Revision: https://reviews.llvm.org/D79004
2020-04-29 00:25:04 +08:00
|
|
|
<< "parser.getBuilder().getI32VectorAttr({";
|
2020-03-06 04:40:53 +08:00
|
|
|
auto interleaveFn = [&](const NamedTypeConstraint &operand) {
|
|
|
|
// If the operand is variadic emit the parsed size.
|
2020-04-11 05:11:45 +08:00
|
|
|
if (operand.isVariableLength())
|
2020-03-06 04:40:53 +08:00
|
|
|
body << "static_cast<int32_t>(" << operand.name << "Operands.size())";
|
|
|
|
else
|
|
|
|
body << "1";
|
|
|
|
};
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(op.getOperands(), body, interleaveFn);
|
2020-03-06 04:40:53 +08:00
|
|
|
body << "}));\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PrinterGen
|
|
|
|
|
|
|
|
/// Generate the printer for the 'attr-dict' directive.
|
2020-03-06 04:40:53 +08:00
|
|
|
static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
|
|
|
|
OpMethodBody &body, bool withKeyword) {
|
2020-01-31 03:31:21 +08:00
|
|
|
// Collect all of the attributes used in the format, these will be elided.
|
|
|
|
SmallVector<const NamedAttribute *, 1> usedAttributes;
|
[mlir] Support optional attributes in assembly formats
Summary: This revision adds support for assembly formats with optional attributes. It elides optional attributes that are part of the syntax from the attribute dictionary.
Reviewers: ftynse, Kayjukh
Reviewed By: ftynse, Kayjukh
Subscribers: mehdi_amini, rriddle, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, stephenneuendorffer, Joonsoo, grosul1, frgossen, jurahul, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D80113
2020-05-19 00:30:39 +08:00
|
|
|
for (auto &it : fmt.elements) {
|
2020-01-31 03:31:21 +08:00
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(it.get()))
|
|
|
|
usedAttributes.push_back(attr->getVar());
|
[mlir] Support optional attributes in assembly formats
Summary: This revision adds support for assembly formats with optional attributes. It elides optional attributes that are part of the syntax from the attribute dictionary.
Reviewers: ftynse, Kayjukh
Reviewed By: ftynse, Kayjukh
Subscribers: mehdi_amini, rriddle, jpienaar, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, liufengdb, stephenneuendorffer, Joonsoo, grosul1, frgossen, jurahul, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D80113
2020-05-19 00:30:39 +08:00
|
|
|
// Collect the optional attributes.
|
|
|
|
if (auto *opt = dyn_cast<OptionalElement>(it.get())) {
|
|
|
|
for (auto &elem : opt->getElements()) {
|
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(&elem))
|
|
|
|
usedAttributes.push_back(attr->getVar());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2020-02-22 05:19:26 +08:00
|
|
|
body << " p.printOptionalAttrDict" << (withKeyword ? "WithKeyword" : "")
|
|
|
|
<< "(getAttrs(), /*elidedAttrs=*/{";
|
2020-03-06 04:40:53 +08:00
|
|
|
// Elide the variadic segment size attributes if necessary.
|
|
|
|
if (!fmt.allOperands && op.getTrait("OpTrait::AttrSizedOperandSegments"))
|
|
|
|
body << "\"operand_segment_sizes\", ";
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(usedAttributes, body, [&](const NamedAttribute *attr) {
|
2020-01-31 03:31:21 +08:00
|
|
|
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()));
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Generate the C++ for an operand to a (*-)type directive.
|
2020-01-31 03:31:21 +08:00
|
|
|
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()";
|
2020-04-11 05:11:45 +08:00
|
|
|
if (var->isOptional())
|
|
|
|
return body << llvm::formatv(
|
2020-06-26 19:20:44 +08:00
|
|
|
"({0}() ? ::llvm::ArrayRef<::mlir::Type>({0}().getType()) : "
|
|
|
|
"::llvm::ArrayRef<::mlir::Type>())",
|
2020-04-11 05:11:45 +08:00
|
|
|
var->name);
|
2020-06-26 19:20:44 +08:00
|
|
|
return body << "::llvm::ArrayRef<::mlir::Type>(" << var->name
|
|
|
|
<< "().getType())";
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Generate the code for printing the given element.
|
|
|
|
static void genElementPrinter(Element *element, OpMethodBody &body,
|
2020-02-22 05:20:06 +08:00
|
|
|
OperationFormat &fmt, Operator &op,
|
|
|
|
bool &shouldEmitSpace, bool &lastWasPunctuation) {
|
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();
|
2020-04-11 05:11:45 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(anchor)) {
|
|
|
|
const NamedTypeConstraint *var = operand->getVar();
|
|
|
|
if (var->isOptional())
|
|
|
|
body << " if (" << var->name << "()) {\n";
|
|
|
|
else if (var->isVariadic())
|
|
|
|
body << " if (!" << var->name << "().empty()) {\n";
|
|
|
|
} else {
|
|
|
|
body << " if (getAttr(\""
|
|
|
|
<< cast<AttributeVariable>(anchor)->getVar()->name << "\")) {\n";
|
|
|
|
}
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-08-04 05:20:50 +08:00
|
|
|
// If the anchor is a unit attribute, we don't need to print it. When
|
|
|
|
// parsing, we will add this attribute if this group is present.
|
|
|
|
auto elements = optional->getElements();
|
|
|
|
Element *elidedAnchorElement = nullptr;
|
|
|
|
auto *anchorAttr = dyn_cast<AttributeVariable>(anchor);
|
|
|
|
if (anchorAttr && anchorAttr != &*elements.begin() &&
|
|
|
|
anchorAttr->isUnitAttr()) {
|
|
|
|
elidedAnchorElement = anchorAttr;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Emit each of the elements.
|
2020-08-04 05:20:50 +08:00
|
|
|
for (Element &childElement : elements) {
|
|
|
|
if (&childElement != elidedAnchorElement) {
|
|
|
|
genElementPrinter(&childElement, body, fmt, op, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
|
|
|
}
|
|
|
|
}
|
2020-02-22 05:19:15 +08:00
|
|
|
body << " }\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit the attribute dictionary.
|
2020-02-22 05:19:26 +08:00
|
|
|
if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
|
2020-03-06 04:40:53 +08:00
|
|
|
genAttrDictPrinter(fmt, op, body, attrDict->isWithKeyword());
|
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();
|
|
|
|
|
2020-04-05 10:30:01 +08:00
|
|
|
// If we are formatting as an enum, symbolize the attribute as a string.
|
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.
|
2020-04-04 10:20:33 +08:00
|
|
|
if (attr->getTypeBuilder())
|
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)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
if (operand->getVar()->isOptional()) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " if (::mlir::Value value = " << operand->getVar()->name
|
|
|
|
<< "())\n"
|
2020-04-11 05:11:45 +08:00
|
|
|
<< " p << value;\n";
|
|
|
|
} else {
|
|
|
|
body << " p << " << operand->getVar()->name << "();\n";
|
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
|
|
|
|
const NamedSuccessor *var = successor->getVar();
|
2020-03-06 04:48:28 +08:00
|
|
|
if (var->isVariadic())
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::interleaveComma(" << var->name << "(), p);\n";
|
2020-03-06 04:48:28 +08:00
|
|
|
else
|
|
|
|
body << " p << " << var->name << "();\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
|
|
|
body << " p << getOperation()->getOperands();\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (isa<SuccessorsDirective>(element)) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::interleaveComma(getOperation()->getSuccessors(), p);\n";
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
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;
|
2020-02-22 05:19:15 +08:00
|
|
|
for (auto &element : elements)
|
2020-02-22 05:20:06 +08:00
|
|
|
genElementPrinter(element.get(), body, *this, op, shouldEmitSpace,
|
2020-02-22 05:19:15 +08:00
|
|
|
lastWasPunctuation);
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// 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,
|
2020-02-22 05:19:15 +08:00
|
|
|
caret,
|
2020-01-31 03:30:23 +08:00
|
|
|
comma,
|
|
|
|
equal,
|
2020-02-22 05:19:15 +08:00
|
|
|
question,
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
// Keywords.
|
|
|
|
keyword_start,
|
|
|
|
kw_attr_dict,
|
2020-02-22 05:19:26 +08:00
|
|
|
kw_attr_dict_w_keyword,
|
2020-01-31 03:30:23 +08:00
|
|
|
kw_functional_type,
|
|
|
|
kw_operands,
|
|
|
|
kw_results,
|
2020-02-22 05:20:06 +08:00
|
|
|
kw_successors,
|
2020-01-31 03:30:23 +08:00
|
|
|
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:
|
2020-04-05 13:54:35 +08:00
|
|
|
FormatLexer(llvm::SourceMgr &mgr, Operator &op);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// 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);
|
|
|
|
|
2020-04-04 09:17:51 +08:00
|
|
|
Token emitErrorAndNote(llvm::SMLoc loc, const Twine &msg, const Twine ¬e);
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
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;
|
2020-04-05 13:54:35 +08:00
|
|
|
Operator &op;
|
2020-01-31 03:30:23 +08:00
|
|
|
StringRef curBuffer;
|
|
|
|
const char *curPtr;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2020-04-05 13:54:35 +08:00
|
|
|
FormatLexer::FormatLexer(llvm::SourceMgr &mgr, Operator &op)
|
|
|
|
: srcMgr(mgr), op(op) {
|
2020-01-31 03:30:23 +08:00
|
|
|
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);
|
2020-04-05 13:54:35 +08:00
|
|
|
llvm::SrcMgr.PrintMessage(op.getLoc()[0], llvm::SourceMgr::DK_Note,
|
|
|
|
"in custom assembly format for this operation");
|
2020-01-31 03:30:23 +08:00
|
|
|
return formToken(Token::error, loc.getPointer());
|
|
|
|
}
|
2020-04-04 09:17:51 +08:00
|
|
|
Token FormatLexer::emitErrorAndNote(llvm::SMLoc loc, const Twine &msg,
|
|
|
|
const Twine ¬e) {
|
|
|
|
srcMgr.PrintMessage(loc, llvm::SourceMgr::DK_Error, msg);
|
2020-04-05 13:54:35 +08:00
|
|
|
llvm::SrcMgr.PrintMessage(op.getLoc()[0], llvm::SourceMgr::DK_Note,
|
|
|
|
"in custom assembly format for this operation");
|
2020-04-04 09:17:51 +08:00
|
|
|
srcMgr.PrintMessage(loc, llvm::SourceMgr::DK_Note, note);
|
|
|
|
return formToken(Token::error, loc.getPointer());
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
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.
|
2020-02-22 05:19:15 +08:00
|
|
|
case '^':
|
|
|
|
return formToken(Token::caret, tokStart);
|
2020-01-31 03:30:23 +08:00
|
|
|
case ',':
|
|
|
|
return formToken(Token::comma, tokStart);
|
|
|
|
case '=':
|
|
|
|
return formToken(Token::equal, tokStart);
|
2020-02-22 05:19:15 +08:00
|
|
|
case '?':
|
|
|
|
return formToken(Token::question, tokStart);
|
2020-01-31 03:30:23 +08:00
|
|
|
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);
|
2020-02-22 05:19:26 +08:00
|
|
|
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)
|
2020-02-22 05:20:06 +08:00
|
|
|
.Case("successors", Token::kw_successors)
|
2020-02-22 05:19:26 +08:00
|
|
|
.Case("type", Token::kw_type)
|
|
|
|
.Default(Token::identifier);
|
2020-01-31 03:30:23 +08:00
|
|
|
return Token(kind, str);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// FormatParser
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
/// Function to find an element within the given range that has the same name as
|
|
|
|
/// 'name'.
|
2020-06-26 19:20:44 +08:00
|
|
|
template <typename RangeT>
|
|
|
|
static auto findArg(RangeT &&range, StringRef name) {
|
2020-02-04 13:52:38 +08:00
|
|
|
auto it = llvm::find_if(range, [=](auto &arg) { return arg.name == name; });
|
|
|
|
return it != range.end() ? &*it : nullptr;
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
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)
|
2020-04-05 13:54:35 +08:00
|
|
|
: lexer(mgr, op), curToken(lexer.lexToken()), fmt(format), op(op),
|
2020-01-31 03:30:23 +08:00
|
|
|
seenOperandTypes(op.getNumOperands()),
|
|
|
|
seenResultTypes(op.getNumResults()) {}
|
|
|
|
|
|
|
|
/// Parse the operation assembly format.
|
|
|
|
LogicalResult parse();
|
|
|
|
|
|
|
|
private:
|
2020-02-22 05:19:03 +08:00
|
|
|
/// 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 {
|
2020-07-07 12:40:01 +08:00
|
|
|
ConstArgument resolver;
|
2020-02-22 05:19:03 +08:00
|
|
|
Optional<StringRef> transformer;
|
|
|
|
};
|
|
|
|
|
2020-04-08 06:51:42 +08:00
|
|
|
/// An iterator over the elements of a format group.
|
|
|
|
using ElementsIterT = llvm::pointee_iterator<
|
|
|
|
std::vector<std::unique_ptr<Element>>::const_iterator>;
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
/// Verify the state of operation attributes within the format.
|
|
|
|
LogicalResult verifyAttributes(llvm::SMLoc loc);
|
2020-04-08 06:51:42 +08:00
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
|
|
|
LogicalResult verifyAttributes(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
SmallVectorImpl<std::pair<ElementsIterT, ElementsIterT>> &iteratorStack);
|
2020-04-04 10:20:33 +08:00
|
|
|
|
|
|
|
/// 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);
|
|
|
|
|
[mlir] NFC: fix trivial typo in source files
Summary: fix trivial typos in the source files
Reviewers: mravishankar, antiagainst, nicolasvasilache, herhut, rriddle, aartbik
Reviewed By: antiagainst, rriddle
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, csigg, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, bader, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76876
2020-03-27 02:51:37 +08:00
|
|
|
/// Given the values of an `AllTypesMatch` trait, check for inferable type
|
2020-02-04 13:52:38 +08:00
|
|
|
/// resolution.
|
|
|
|
void handleAllTypesMatchConstraint(
|
|
|
|
ArrayRef<StringRef> values,
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
[mlir] NFC: fix trivial typo in source files
Summary: fix trivial typos in the source files
Reviewers: mravishankar, antiagainst, nicolasvasilache, herhut, rriddle, aartbik
Reviewed By: antiagainst, rriddle
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, csigg, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, bader, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76876
2020-03-27 02:51:37 +08:00
|
|
|
/// Check for inferable type resolution given all operands, and or results,
|
2020-02-04 13:52:38 +08:00
|
|
|
/// have the same type. If 'includeResults' is true, the results also have the
|
|
|
|
/// same type as all of the operands.
|
|
|
|
void handleSameTypesConstraint(
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
2020-02-04 13:52:38 +08:00
|
|
|
bool includeResults);
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Check for inferable type resolution based on another operand, result, or
|
|
|
|
/// attribute.
|
|
|
|
void handleTypesMatchConstraint(
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
|
|
|
llvm::Record def);
|
2020-02-04 13:52:38 +08:00
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Returns an argument or attribute with the given name that has been seen
|
|
|
|
/// within the format.
|
|
|
|
ConstArgument findSeenArg(StringRef name);
|
2020-02-22 05:19:03 +08:00
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// 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);
|
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);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// Parse the various different directives.
|
|
|
|
LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element,
|
2020-02-22 05:19:26 +08:00
|
|
|
llvm::SMLoc loc, bool isTopLevel,
|
|
|
|
bool withKeyword);
|
2020-01-31 03:30:23 +08:00
|
|
|
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);
|
2020-02-22 05:20:06 +08:00
|
|
|
LogicalResult parseSuccessorsDirective(std::unique_ptr<Element> &element,
|
|
|
|
llvm::SMLoc loc, bool isTopLevel);
|
2020-01-31 03:30:23 +08:00
|
|
|
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();
|
|
|
|
}
|
2020-04-04 09:17:51 +08:00
|
|
|
LogicalResult emitErrorAndNote(llvm::SMLoc loc, const Twine &msg,
|
|
|
|
const Twine ¬e) {
|
|
|
|
lexer.emitErrorAndNote(loc, msg, note);
|
|
|
|
return failure();
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Fields
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
FormatLexer lexer;
|
|
|
|
Token curToken;
|
|
|
|
OperationFormat &fmt;
|
|
|
|
Operator &op;
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
// The following are various bits of format state used for verification
|
|
|
|
// during parsing.
|
2020-01-31 03:30:23 +08:00
|
|
|
bool hasAllOperands = false, hasAttrDict = false;
|
2020-02-22 05:20:06 +08:00
|
|
|
bool hasAllSuccessors = false;
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::SmallBitVector seenOperandTypes, seenResultTypes;
|
|
|
|
llvm::DenseSet<const NamedTypeConstraint *> seenOperands;
|
|
|
|
llvm::DenseSet<const NamedAttribute *> seenAttrs;
|
2020-02-22 05:20:06 +08:00
|
|
|
llvm::DenseSet<const NamedSuccessor *> seenSuccessors;
|
2020-02-22 05:19:15 +08:00
|
|
|
llvm::DenseSet<const NamedTypeConstraint *> optionalVariables;
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
|
|
|
} // 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)
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitError(loc, "'attr-dict' directive not found in "
|
|
|
|
"custom assembly format");
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
// Check for any type traits that we can use for inferring types.
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> variableTyResolver;
|
2020-02-04 13:52:38 +08:00
|
|
|
for (const OpTrait &trait : op.getTraits()) {
|
|
|
|
const llvm::Record &def = trait.getDef();
|
2020-02-22 05:19:03 +08:00
|
|
|
if (def.isSubClassOf("AllTypesMatch")) {
|
2020-02-04 13:52:38 +08:00
|
|
|
handleAllTypesMatchConstraint(def.getValueAsListOfStrings("values"),
|
|
|
|
variableTyResolver);
|
2020-02-22 05:19:03 +08:00
|
|
|
} else if (def.getName() == "SameTypeOperands") {
|
2020-02-04 13:52:38 +08:00
|
|
|
handleSameTypesConstraint(variableTyResolver, /*includeResults=*/false);
|
2020-02-22 05:19:03 +08:00
|
|
|
} else if (def.getName() == "SameOperandsAndResultType") {
|
2020-02-04 13:52:38 +08:00
|
|
|
handleSameTypesConstraint(variableTyResolver, /*includeResults=*/true);
|
2020-02-22 05:19:03 +08:00
|
|
|
} else if (def.isSubClassOf("TypesMatchWith")) {
|
2020-07-07 12:40:01 +08:00
|
|
|
handleTypesMatchConstraint(variableTyResolver, def);
|
2020-02-22 05:19:03 +08:00
|
|
|
}
|
2020-02-04 13:52:38 +08:00
|
|
|
}
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
// 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();
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
// 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());
|
2020-04-08 06:51:42 +08:00
|
|
|
while (!iteratorStack.empty())
|
|
|
|
if (failed(verifyAttributes(loc, iteratorStack)))
|
|
|
|
return failure();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
|
|
|
LogicalResult FormatParser::verifyAttributes(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
SmallVectorImpl<std::pair<ElementsIterT, ElementsIterT>> &iteratorStack) {
|
|
|
|
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());
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2020-02-04 13:52:38 +08:00
|
|
|
|
2020-04-08 06:51:42 +08:00
|
|
|
// 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));
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-08 06:51:42 +08:00
|
|
|
iteratorStack.pop_back();
|
2020-04-04 10:20:33 +08:00
|
|
|
return success();
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
LogicalResult FormatParser::verifyOperands(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
2020-01-31 03:30:23 +08:00
|
|
|
// Check that all of the operands are within the format, and their types can
|
|
|
|
// be inferred.
|
2020-04-04 10:20:33 +08:00
|
|
|
auto &buildableTypes = fmt.buildableTypes;
|
2020-01-31 03:30:23 +08:00
|
|
|
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)) {
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitErrorAndNote(loc,
|
|
|
|
"operand #" + Twine(i) + ", named '" +
|
2020-04-05 13:54:35 +08:00
|
|
|
operand.name + "', not found",
|
2020-04-04 09:17:51 +08:00
|
|
|
"suggest adding a '$" + operand.name +
|
|
|
|
"' directive to the custom assembly format");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the operand type is in the format, or that it can be inferred.
|
2020-02-04 13:52:38 +08:00
|
|
|
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()) {
|
2020-07-07 12:40:01 +08:00
|
|
|
TypeResolutionInstance &resolver = varResolverIt->second;
|
|
|
|
fmt.operandTypes[i].setResolver(resolver.resolver, resolver.transformer);
|
2020-02-04 13:52:38 +08:00
|
|
|
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();
|
2020-04-11 05:11:45 +08:00
|
|
|
if (!builder || (hasAllOperands && operand.isVariableLength())) {
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitErrorAndNote(
|
|
|
|
loc,
|
|
|
|
"type of operand #" + Twine(i) + ", named '" + operand.name +
|
2020-04-11 05:11:45 +08:00
|
|
|
"', is not buildable and a buildable type cannot be inferred",
|
|
|
|
"suggest adding a type constraint to the operation or adding a "
|
2020-04-04 09:17:51 +08:00
|
|
|
"'type($" +
|
|
|
|
operand.name + ")' directive to the " + "custom assembly format");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2020-02-04 13:52:38 +08:00
|
|
|
auto it = buildableTypes.insert({*builder, buildableTypes.size()});
|
|
|
|
fmt.operandTypes[i].setBuilderIdx(it.first->second);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2020-04-04 10:20:33 +08:00
|
|
|
return success();
|
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
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()) {
|
2020-07-07 12:40:01 +08:00
|
|
|
TypeResolutionInstance resolver = varResolverIt->second;
|
|
|
|
fmt.resultTypes[i].setResolver(resolver.resolver, resolver.transformer);
|
2020-04-04 10:20:33 +08:00
|
|
|
continue;
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
2020-04-04 10:20:33 +08:00
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
// If the result is not variable length, allow for the case where the type
|
|
|
|
// has a builder that we can use.
|
2020-04-04 10:20:33 +08:00
|
|
|
NamedTypeConstraint &result = op.getResult(i);
|
|
|
|
Optional<StringRef> builder = result.constraint.getBuilderCall();
|
2020-04-11 05:11:45 +08:00
|
|
|
if (!builder || result.isVariableLength()) {
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitErrorAndNote(
|
|
|
|
loc,
|
|
|
|
"type of result #" + Twine(i) + ", named '" + result.name +
|
2020-04-11 05:11:45 +08:00
|
|
|
"', is not buildable and a buildable type cannot be inferred",
|
|
|
|
"suggest adding a type constraint to the operation or adding a "
|
2020-04-04 09:17:51 +08:00
|
|
|
"'type($" +
|
|
|
|
result.name + ")' directive to the " + "custom assembly format");
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
|
|
|
// 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);
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
2020-04-04 10:20:33 +08:00
|
|
|
return success();
|
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
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)) {
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitErrorAndNote(loc,
|
|
|
|
"successor #" + Twine(i) + ", named '" +
|
2020-04-05 13:54:35 +08:00
|
|
|
successor.name + "', not found",
|
2020-04-04 09:17:51 +08:00
|
|
|
"suggest adding a '$" + successor.name +
|
|
|
|
"' directive to the custom assembly format");
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
void FormatParser::handleAllTypesMatchConstraint(
|
|
|
|
ArrayRef<StringRef> values,
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
2020-02-04 13:52:38 +08:00
|
|
|
for (unsigned i = 0, e = values.size(); i != e; ++i) {
|
|
|
|
// Check to see if this value matches a resolved operand or result type.
|
2020-07-07 12:40:01 +08:00
|
|
|
ConstArgument arg = findSeenArg(values[i]);
|
2020-02-22 05:19:03 +08:00
|
|
|
if (!arg)
|
2020-02-04 13:52:38 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Mark this value as the type resolver for the other variables.
|
|
|
|
for (unsigned j = 0; j != i; ++j)
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[values[j]] = {arg, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
for (unsigned j = i + 1; j != e; ++j)
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[values[j]] = {arg, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FormatParser::handleSameTypesConstraint(
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
2020-02-04 13:52:38 +08:00
|
|
|
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())
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[op.getOperand(i).name] = {resolver, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
if (includeResults) {
|
|
|
|
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)
|
|
|
|
if (!seenResultTypes.test(i) && !op.getResultName(i).empty())
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[op.getResultName(i)] = {resolver, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
void FormatParser::handleTypesMatchConstraint(
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
|
|
|
llvm::Record def) {
|
|
|
|
StringRef lhsName = def.getValueAsString("lhs");
|
|
|
|
StringRef rhsName = def.getValueAsString("rhs");
|
|
|
|
StringRef transformer = def.getValueAsString("transformer");
|
|
|
|
if (ConstArgument arg = findSeenArg(lhsName))
|
|
|
|
variableTyResolver[rhsName] = {arg, transformer};
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstArgument FormatParser::findSeenArg(StringRef name) {
|
|
|
|
if (const NamedTypeConstraint *arg = findArg(op.getOperands(), name))
|
2020-02-22 05:19:03 +08:00
|
|
|
return seenOperandTypes.test(arg - op.operand_begin()) ? arg : nullptr;
|
2020-07-07 12:40:01 +08:00
|
|
|
if (const NamedTypeConstraint *arg = findArg(op.getResults(), name))
|
2020-02-22 05:19:03 +08:00
|
|
|
return seenResultTypes.test(arg - op.result_begin()) ? arg : nullptr;
|
2020-07-07 12:40:01 +08:00
|
|
|
if (const NamedAttribute *attr = findArg(op.getAttributes(), name))
|
|
|
|
return seenAttrs.find_as(attr) != seenAttrs.end() ? attr : nullptr;
|
2020-02-22 05:19:03 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
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);
|
2020-02-22 05:19:15 +08:00
|
|
|
// Optionals.
|
|
|
|
if (curToken.getKind() == Token::l_paren)
|
|
|
|
return parseOptional(element, isTopLevel);
|
2020-01-31 03:30:23 +08:00
|
|
|
// Variables.
|
|
|
|
if (curToken.getKind() == Token::variable)
|
|
|
|
return parseVariable(element, isTopLevel);
|
|
|
|
return emitError(curToken.getLoc(),
|
2020-02-22 05:19:15 +08:00
|
|
|
"expected directive, literal, variable, or optional group");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
2020-02-22 05:19:03 +08:00
|
|
|
// Check that the parsed argument is something actually registered on the
|
|
|
|
// op.
|
2020-01-31 03:30:23 +08:00
|
|
|
/// Attributes
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
if (isTopLevel && !seenAttrs.insert(attr).second)
|
|
|
|
return emitError(loc, "attribute '" + name + "' is already bound");
|
|
|
|
element = std::make_unique<AttributeVariable>(attr);
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
/// Operands
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
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.
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const auto *result = findArg(op.getResults(), name)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
if (isTopLevel)
|
|
|
|
return emitError(loc, "results can not be used at the top level");
|
|
|
|
element = std::make_unique<ResultVariable>(result);
|
|
|
|
return success();
|
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
/// 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(
|
2020-04-05 10:30:01 +08:00
|
|
|
loc, "expected variable to refer to an argument, result, or successor");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element,
|
|
|
|
bool isTopLevel) {
|
|
|
|
Token dirTok = curToken;
|
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
switch (dirTok.getKind()) {
|
|
|
|
case Token::kw_attr_dict:
|
2020-02-22 05:19:26 +08:00
|
|
|
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel,
|
|
|
|
/*withKeyword=*/false);
|
|
|
|
case Token::kw_attr_dict_w_keyword:
|
|
|
|
return parseAttrDictDirective(element, dirTok.getLoc(), isTopLevel,
|
|
|
|
/*withKeyword=*/true);
|
2020-01-31 03:30:23 +08:00
|
|
|
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);
|
2020-02-22 05:20:06 +08:00
|
|
|
case Token::kw_successors:
|
|
|
|
return parseSuccessorsDirective(element, dirTok.getLoc(), isTopLevel);
|
2020-01-31 03:30:23 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
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.
|
2020-07-15 04:14:14 +08:00
|
|
|
Element *firstElement = &*elements.front();
|
|
|
|
if (!isa<AttributeVariable>(firstElement) &&
|
|
|
|
!isa<LiteralElement>(firstElement) && !isa<OperandVariable>(firstElement))
|
|
|
|
return emitError(curLoc, "first element of an operand group must be an "
|
|
|
|
"attribute, literal, or operand");
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-02-22 08:09:58 +08:00
|
|
|
.Case<OperandVariable>([&](OperandVariable *ele) {
|
2020-04-11 05:11:45 +08:00
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(childLoc, "only variable length operands can be "
|
|
|
|
"used within an optional group");
|
2020-02-22 05:19:15 +08:00
|
|
|
seenVariables.insert(ele->getVar());
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
// Literals and type directives may be used, but they can't anchor the
|
|
|
|
// group.
|
|
|
|
.Case<LiteralElement, TypeDirective, FunctionalTypeDirective>(
|
2020-02-22 08:09:58 +08:00
|
|
|
[&](Element *) {
|
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();
|
|
|
|
})
|
2020-02-22 08:09:58 +08:00
|
|
|
.Default([&](Element *) {
|
2020-02-22 05:19:15 +08:00
|
|
|
return emitError(childLoc, "only literals, types, and variables can be "
|
|
|
|
"used within an optional group");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
|
2020-02-22 05:19:26 +08:00
|
|
|
llvm::SMLoc loc, bool isTopLevel,
|
|
|
|
bool withKeyword) {
|
2020-01-31 03:30:23 +08:00
|
|
|
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;
|
2020-02-22 05:19:26 +08:00
|
|
|
element = std::make_unique<AttrDictDirective>(withKeyword);
|
2020-01-31 03:30:23 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
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) {
|
2020-07-07 16:35:23 +08:00
|
|
|
// TODO: Operator doesn't expose all necessary functionality via
|
2020-01-31 03:30:23 +08:00
|
|
|
// the const interface.
|
|
|
|
Operator &op = const_cast<Operator &>(constOp);
|
2020-03-25 02:57:13 +08:00
|
|
|
if (!op.hasAssemblyFormat())
|
2020-01-31 03:30:23 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Parse the format description.
|
|
|
|
llvm::SourceMgr mgr;
|
2020-03-25 02:57:13 +08:00
|
|
|
mgr.AddNewSourceBuffer(
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(op.getAssemblyFormat()), llvm::SMLoc());
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat format(op);
|
2020-02-04 14:14:33 +08:00
|
|
|
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);
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
return;
|
2020-02-04 14:14:33 +08:00
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
// Generate the printer and parser based on the parsed format.
|
|
|
|
format.genParser(op, opClass);
|
|
|
|
format.genPrinter(op, opClass);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|