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"
|
2021-10-16 05:39:07 +08:00
|
|
|
#include "FormatGen.h"
|
2021-11-30 22:09:00 +08:00
|
|
|
#include "OpClass.h"
|
2022-02-01 14:58:02 +08:00
|
|
|
#include "mlir/Support/LLVM.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#include "mlir/Support/LogicalResult.h"
|
2021-11-12 09:17:05 +08:00
|
|
|
#include "mlir/TableGen/Class.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#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/Operator.h"
|
2021-04-16 02:29:23 +08:00
|
|
|
#include "mlir/TableGen/Trait.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#include "llvm/ADT/MapVector.h"
|
|
|
|
#include "llvm/ADT/Sequence.h"
|
2020-09-01 03:33:55 +08:00
|
|
|
#include "llvm/ADT/SetVector.h"
|
2020-01-31 03:30:23 +08:00
|
|
|
#include "llvm/ADT/SmallBitVector.h"
|
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2022-01-20 19:55:14 +08:00
|
|
|
#include "llvm/ADT/StringSwitch.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/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;
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2022-02-01 14:58:02 +08:00
|
|
|
// VariableElement
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
namespace {
|
2022-02-01 14:58:02 +08:00
|
|
|
/// This class represents an instance of an op variable element. A variable
|
|
|
|
/// refers to something registered on the operation itself, e.g. an operand,
|
|
|
|
/// result, attribute, region, or successor.
|
|
|
|
template <typename VarT, VariableElement::Kind VariableKind>
|
|
|
|
class OpVariableElement : public VariableElementBase<VariableKind> {
|
2020-01-31 03:30:23 +08:00
|
|
|
public:
|
2022-02-01 14:58:02 +08:00
|
|
|
using Base = OpVariableElement<VarT, VariableKind>;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
/// Create an op variable element with the variable value.
|
|
|
|
OpVariableElement(const VarT *var) : var(var) {}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
/// Get the variable.
|
2020-01-31 03:30:23 +08:00
|
|
|
const VarT *getVar() { return var; }
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
protected:
|
2022-02-01 14:58:02 +08:00
|
|
|
/// The op variable, e.g. a type or attribute constraint.
|
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
|
2022-02-01 14:58:02 +08:00
|
|
|
: public OpVariableElement<NamedAttribute, VariableElement::Attribute> {
|
|
|
|
using Base::Base;
|
2020-04-04 10:20:33 +08:00
|
|
|
|
|
|
|
/// Return the constant builder call for the type of this attribute, or None
|
|
|
|
/// if it doesn't have one.
|
2022-02-01 14:58:02 +08:00
|
|
|
llvm::Optional<StringRef> getTypeBuilder() const {
|
|
|
|
llvm::Optional<Type> attrType = var->attr.getValueType();
|
2020-04-04 10:20:33 +08:00
|
|
|
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";
|
|
|
|
}
|
2022-01-11 09:26:44 +08:00
|
|
|
|
|
|
|
/// Indicate if this attribute is printed "qualified" (that is it is
|
|
|
|
/// prefixed with the `#dialect.mnemonic`).
|
|
|
|
bool shouldBeQualified() { return shouldBeQualifiedFlag; }
|
|
|
|
void setShouldBeQualified(bool qualified = true) {
|
|
|
|
shouldBeQualifiedFlag = qualified;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool shouldBeQualifiedFlag = false;
|
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 =
|
2022-02-01 14:58:02 +08:00
|
|
|
OpVariableElement<NamedTypeConstraint, VariableElement::Operand>;
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This class represents a variable that refers to a result.
|
|
|
|
using ResultVariable =
|
2022-02-01 14:58:02 +08:00
|
|
|
OpVariableElement<NamedTypeConstraint, VariableElement::Result>;
|
|
|
|
|
|
|
|
/// This class represents a variable that refers to a region.
|
|
|
|
using RegionVariable = OpVariableElement<NamedRegion, VariableElement::Region>;
|
2020-02-22 05:20:06 +08:00
|
|
|
|
|
|
|
/// This class represents a variable that refers to a successor.
|
|
|
|
using SuccessorVariable =
|
2022-02-01 14:58:02 +08:00
|
|
|
OpVariableElement<NamedSuccessor, VariableElement::Successor>;
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DirectiveElement
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents the `operands` directive. This directive represents
|
|
|
|
/// all of the operands of an operation.
|
2022-02-01 14:58:02 +08:00
|
|
|
using OperandsDirective = DirectiveElementBase<DirectiveElement::Operands>;
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This class represents the `results` directive. This directive represents
|
|
|
|
/// all of the results of an operation.
|
2022-02-01 14:58:02 +08:00
|
|
|
using ResultsDirective = DirectiveElementBase<DirectiveElement::Results>;
|
|
|
|
|
|
|
|
/// This class represents the `regions` directive. This directive represents
|
|
|
|
/// all of the regions of an operation.
|
|
|
|
using RegionsDirective = DirectiveElementBase<DirectiveElement::Regions>;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
/// This class represents the `successors` directive. This directive represents
|
|
|
|
/// all of the successors of an operation.
|
2022-02-01 14:58:02 +08:00
|
|
|
using SuccessorsDirective = DirectiveElementBase<DirectiveElement::Successors>;
|
2020-02-22 05:20:06 +08:00
|
|
|
|
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
|
2022-02-01 14:58:02 +08:00
|
|
|
: public DirectiveElementBase<DirectiveElement::AttrDict> {
|
2020-02-22 05:19:26 +08:00
|
|
|
public:
|
|
|
|
explicit AttrDictDirective(bool withKeyword) : withKeyword(withKeyword) {}
|
2022-02-01 14:58:02 +08:00
|
|
|
|
|
|
|
/// Return whether the dictionary should be printed with the 'attributes'
|
|
|
|
/// keyword.
|
2020-02-22 05:19:26 +08:00
|
|
|
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
|
2022-02-01 14:58:02 +08:00
|
|
|
: public DirectiveElementBase<DirectiveElement::FunctionalType> {
|
2020-01-31 03:30:23 +08:00
|
|
|
public:
|
2022-02-01 14:58:02 +08:00
|
|
|
FunctionalTypeDirective(FormatElement *inputs, FormatElement *results)
|
|
|
|
: inputs(inputs), results(results) {}
|
|
|
|
|
|
|
|
FormatElement *getInputs() const { return inputs; }
|
|
|
|
FormatElement *getResults() const { return results; }
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
/// The input and result arguments.
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *inputs, *results;
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
/// This class represents the `type` directive.
|
2022-02-01 14:58:02 +08:00
|
|
|
class TypeDirective : public DirectiveElementBase<DirectiveElement::Type> {
|
2020-09-18 18:13:25 +08:00
|
|
|
public:
|
2022-02-01 14:58:02 +08:00
|
|
|
TypeDirective(FormatElement *arg) : arg(arg) {}
|
|
|
|
|
|
|
|
FormatElement *getArg() const { return arg; }
|
2020-09-18 18:13:25 +08:00
|
|
|
|
2022-01-11 09:26:44 +08:00
|
|
|
/// Indicate if this type is printed "qualified" (that is it is
|
|
|
|
/// prefixed with the `!dialect.mnemonic`).
|
|
|
|
bool shouldBeQualified() { return shouldBeQualifiedFlag; }
|
|
|
|
void setShouldBeQualified(bool qualified = true) {
|
|
|
|
shouldBeQualifiedFlag = qualified;
|
|
|
|
}
|
|
|
|
|
2020-09-18 18:13:25 +08:00
|
|
|
private:
|
2022-02-01 14:58:02 +08:00
|
|
|
/// The argument that is used to format the directive.
|
|
|
|
FormatElement *arg;
|
2022-01-11 09:26:44 +08:00
|
|
|
|
|
|
|
bool shouldBeQualifiedFlag = false;
|
2020-09-18 18:13:25 +08:00
|
|
|
};
|
2022-02-17 12:54:10 +08:00
|
|
|
|
|
|
|
/// This class represents a group of order-independent optional clauses. Each
|
|
|
|
/// clause starts with a literal element and has a coressponding parsing
|
|
|
|
/// element. A parsing element is a continous sequence of format elements.
|
|
|
|
/// Each clause can appear 0 or 1 time.
|
|
|
|
class OIListElement : public DirectiveElementBase<DirectiveElement::OIList> {
|
|
|
|
public:
|
|
|
|
OIListElement(std::vector<FormatElement *> &&literalElements,
|
|
|
|
std::vector<std::vector<FormatElement *>> &&parsingElements)
|
|
|
|
: literalElements(std::move(literalElements)),
|
|
|
|
parsingElements(std::move(parsingElements)) {}
|
|
|
|
|
|
|
|
/// Returns a range to iterate over the LiteralElements.
|
|
|
|
auto getLiteralElements() const {
|
|
|
|
// The use of std::function is unfortunate but necessary here. Lambda
|
|
|
|
// functions cannot be copied but std::function can be copied. This copy
|
|
|
|
// constructor is used in llvm::zip.
|
|
|
|
std::function<LiteralElement *(FormatElement * el)>
|
|
|
|
literalElementCastConverter =
|
|
|
|
[](FormatElement *el) { return cast<LiteralElement>(el); };
|
|
|
|
return llvm::map_range(literalElements, literalElementCastConverter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a range to iterate over the parsing elements corresponding to the
|
|
|
|
/// clauses.
|
|
|
|
ArrayRef<std::vector<FormatElement *>> getParsingElements() const {
|
|
|
|
return parsingElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a range to iterate over tuples of parsing and literal elements.
|
|
|
|
auto getClauses() const {
|
|
|
|
return llvm::zip(getLiteralElements(), getParsingElements());
|
|
|
|
}
|
|
|
|
|
2022-03-22 12:38:33 +08:00
|
|
|
/// If the parsing element is a single UnitAttr element, then it returns the
|
|
|
|
/// attribute variable. Otherwise, returns nullptr.
|
|
|
|
AttributeVariable *
|
|
|
|
getUnitAttrParsingElement(ArrayRef<FormatElement *> pelement) {
|
|
|
|
if (pelement.size() == 1) {
|
|
|
|
auto attrElem = dyn_cast<AttributeVariable>(pelement[0]);
|
|
|
|
if (attrElem && attrElem->isUnitAttr())
|
|
|
|
return attrElem;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
private:
|
|
|
|
/// A vector of `LiteralElement` objects. Each element stores the keyword
|
|
|
|
/// for one case of oilist element. For example, an oilist element along with
|
|
|
|
/// the `literalElements` vector:
|
|
|
|
/// ```
|
|
|
|
/// oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]
|
|
|
|
/// literalElements = { `keyword`, `otherKeyword` }
|
|
|
|
/// ```
|
|
|
|
std::vector<FormatElement *> literalElements;
|
|
|
|
|
|
|
|
/// A vector of valid declarative assembly format vectors. Each object in
|
|
|
|
/// parsing elements is a vector of elements in assembly format syntax.
|
|
|
|
/// For example, an oilist element along with the parsingElements vector:
|
|
|
|
/// ```
|
|
|
|
/// oilist [ `keyword` `=` `(` $arg0 `)` | `otherKeyword` `<` $arg1 `>`]
|
|
|
|
/// parsingElements = {
|
|
|
|
/// { `=`, `(`, $arg0, `)` },
|
|
|
|
/// { `<`, $arg1, `>` }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
std::vector<std::vector<FormatElement *>> parsingElements;
|
|
|
|
};
|
2020-09-18 18:13:25 +08:00
|
|
|
} // 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
|
|
|
};
|
|
|
|
|
2022-01-19 00:53:55 +08:00
|
|
|
/// The context in which an element is generated.
|
|
|
|
enum class GenContext {
|
|
|
|
/// The element is generated at the top-level or with the same behaviour.
|
|
|
|
Normal,
|
|
|
|
/// The element is generated inside an optional group.
|
|
|
|
Optional
|
|
|
|
};
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat(const Operator &op)
|
2022-03-07 18:12:43 +08:00
|
|
|
|
|
|
|
{
|
2020-02-04 13:52:38 +08:00
|
|
|
operandTypes.resize(op.getNumOperands(), TypeResolution());
|
|
|
|
resultTypes.resize(op.getNumResults(), TypeResolution());
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2021-04-16 02:29:23 +08:00
|
|
|
hasImplicitTermTrait = llvm::any_of(op.getTraits(), [](const Trait &trait) {
|
|
|
|
return trait.getDef().isSubClassOf("SingleBlockImplicitTerminator");
|
|
|
|
});
|
2021-03-12 07:58:02 +08:00
|
|
|
|
|
|
|
hasSingleBlockTrait =
|
2021-04-16 02:29:23 +08:00
|
|
|
hasImplicitTermTrait || op.getTrait("::mlir::OpTrait::SingleBlock");
|
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);
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Generate the parser code for a specific format element.
|
2022-02-01 14:58:02 +08:00
|
|
|
void genElementParser(FormatElement *element, MethodBody &body,
|
2022-01-19 00:53:55 +08:00
|
|
|
FmtContext &attrTypeCtx,
|
|
|
|
GenContext genCtx = GenContext::Normal);
|
2021-12-10 23:04:46 +08:00
|
|
|
/// Generate the C++ to resolve the types of operands and results during
|
2020-01-31 03:31:21 +08:00
|
|
|
/// parsing.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genParserTypeResolution(Operator &op, MethodBody &body);
|
2021-12-10 23:04:46 +08:00
|
|
|
/// Generate the C++ to resolve the types of the operands during parsing.
|
|
|
|
void genParserOperandTypeResolution(
|
|
|
|
Operator &op, MethodBody &body,
|
|
|
|
function_ref<void(TypeResolution &, StringRef)> emitTypeResolver);
|
|
|
|
/// Generate the C++ to resolve regions during parsing.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genParserRegionResolution(Operator &op, MethodBody &body);
|
2021-12-10 23:04:46 +08:00
|
|
|
/// Generate the C++ to resolve successors during parsing.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genParserSuccessorResolution(Operator &op, MethodBody &body);
|
2021-12-10 23:04:46 +08:00
|
|
|
/// Generate the C++ to handling variadic segment size traits.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genParserVariadicSegmentResolution(Operator &op, MethodBody &body);
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
/// Generate the operation printer from this format.
|
|
|
|
void genPrinter(Operator &op, OpClass &opClass);
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Generate the printer code for a specific format element.
|
2022-02-01 14:58:02 +08:00
|
|
|
void genElementPrinter(FormatElement *element, MethodBody &body, Operator &op,
|
2020-09-01 03:33:55 +08:00
|
|
|
bool &shouldEmitSpace, bool &lastWasPunctuation);
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// The various elements in this format.
|
2022-02-01 14:58:02 +08:00
|
|
|
std::vector<FormatElement *> elements;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// A flag indicating if all operand/result types were seen. If the format
|
|
|
|
/// contains these, it can not contain individual type resolvers.
|
2022-03-07 18:12:43 +08:00
|
|
|
bool allOperands = false, allOperandTypes = false, allResultTypes = false;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2021-10-07 08:50:38 +08:00
|
|
|
/// A flag indicating if this operation infers its result types
|
2022-03-07 18:12:43 +08:00
|
|
|
bool infersResultTypes = false;
|
2021-10-07 08:50:38 +08:00
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// A flag indicating if this operation has the SingleBlockImplicitTerminator
|
|
|
|
/// trait.
|
|
|
|
bool hasImplicitTermTrait;
|
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
/// A flag indicating if this operation has the SingleBlock trait.
|
|
|
|
bool hasSingleBlockTrait;
|
|
|
|
|
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-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
/// The set of attributes explicitly used within the format.
|
|
|
|
SmallVector<const NamedAttribute *, 8> usedAttributes;
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
llvm::StringSet<> inferredAttributes;
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Parser Gen
|
|
|
|
|
2020-08-30 15:59:53 +08:00
|
|
|
/// Returns true if we can format the given attribute as an EnumAttr in the
|
|
|
|
/// parser format.
|
2020-02-14 09:11:01 +08:00
|
|
|
static bool canFormatEnumAttr(const NamedAttribute *attr) {
|
2020-12-05 12:54:23 +08:00
|
|
|
Attribute baseAttr = attr->attr.getBaseAttr();
|
|
|
|
const EnumAttr *enumAttr = dyn_cast<EnumAttr>(&baseAttr);
|
2020-02-14 09:11:01 +08:00
|
|
|
if (!enumAttr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// The attribute must have a valid underlying type and a constant builder.
|
|
|
|
return !enumAttr->getUnderlyingType().empty() &&
|
|
|
|
!enumAttr->getConstBuilderTemplate().empty();
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:46 +08:00
|
|
|
/// Returns if we should format the given attribute as an SymbolNameAttr.
|
|
|
|
static bool shouldFormatSymbolNameAttr(const NamedAttribute *attr) {
|
|
|
|
return attr->attr.getBaseAttr().getAttrDefName() == "SymbolNameAttr";
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
/// The code snippet used to generate a parser call for an attribute.
|
|
|
|
///
|
2020-09-01 03:33:36 +08:00
|
|
|
/// {0}: The name of the attribute.
|
|
|
|
/// {1}: The type for the attribute.
|
2020-01-31 03:31:21 +08:00
|
|
|
const char *const attrParserCode = R"(
|
2021-12-08 09:24:51 +08:00
|
|
|
if (parser.parseCustomAttributeWithFallback({0}Attr, {1}, "{0}",
|
|
|
|
result.attributes)) {{
|
|
|
|
return ::mlir::failure();
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
/// The code snippet used to generate a parser call for an attribute.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the attribute.
|
|
|
|
/// {1}: The type for the attribute.
|
|
|
|
const char *const genericAttrParserCode = R"(
|
|
|
|
if (parser.parseAttribute({0}Attr, {1}, "{0}", result.attributes))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
2021-12-08 09:24:51 +08:00
|
|
|
|
2020-07-15 04:14:14 +08:00
|
|
|
const char *const optionalAttrParserCode = R"(
|
|
|
|
{
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
2021-12-08 09:24:51 +08:00
|
|
|
parser.parseOptionalAttribute({0}Attr, {1}, "{0}", result.attributes);
|
2020-07-15 04:14:14 +08:00
|
|
|
if (parseResult.hasValue() && failed(*parseResult))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-07-15 04:14:14 +08:00
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2020-09-01 03:33:46 +08:00
|
|
|
/// The code snippet used to generate a parser call for a symbol name attribute.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the attribute.
|
|
|
|
const char *const symbolNameAttrParserCode = R"(
|
|
|
|
if (parser.parseSymbolName({0}Attr, "{0}", result.attributes))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:46 +08:00
|
|
|
)";
|
|
|
|
const char *const optionalSymbolNameAttrParserCode = R"(
|
|
|
|
// Parsing an optional symbol name doesn't fail, so no need to check the
|
|
|
|
// result.
|
|
|
|
(void)parser.parseOptionalSymbolName({0}Attr, "{0}", result.attributes);
|
|
|
|
)";
|
|
|
|
|
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.
|
2021-01-15 03:35:15 +08:00
|
|
|
/// {4}: The set of allowed enum keywords.
|
|
|
|
/// {5}: The error message on failure when the enum isn't present.
|
2020-02-14 09:11:01 +08:00
|
|
|
const char *const enumAttrParserCode = R"(
|
|
|
|
{
|
2021-01-15 03:35:15 +08:00
|
|
|
::llvm::StringRef attrStr;
|
2020-07-15 04:14:14 +08:00
|
|
|
::mlir::NamedAttrList attrStorage;
|
|
|
|
auto loc = parser.getCurrentLocation();
|
2021-01-15 03:35:15 +08:00
|
|
|
if (parser.parseOptionalKeyword(&attrStr, {4})) {
|
|
|
|
::mlir::StringAttr attrVal;
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalAttribute(attrVal,
|
|
|
|
parser.getBuilder().getNoneType(),
|
|
|
|
"{0}", attrStorage);
|
|
|
|
if (parseResult.hasValue()) {{
|
|
|
|
if (failed(*parseResult))
|
|
|
|
return ::mlir::failure();
|
|
|
|
attrStr = attrVal.getValue();
|
|
|
|
} else {
|
|
|
|
{5}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!attrStr.empty()) {
|
|
|
|
auto attrOptional = {1}::{2}(attrStr);
|
2020-07-15 04:14:14 +08:00
|
|
|
if (!attrOptional)
|
|
|
|
return parser.emitError(loc, "invalid ")
|
2021-01-15 03:35:15 +08:00
|
|
|
<< "{0} attribute specification: \"" << attrStr << '"';;
|
2020-07-15 04:14:14 +08:00
|
|
|
|
|
|
|
{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"(
|
2020-09-01 03:33:36 +08:00
|
|
|
{0}OperandsLoc = parser.getCurrentLocation();
|
2020-01-31 03:31:21 +08:00
|
|
|
if (parser.parseOperandList({0}Operands))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
2020-04-11 05:11:45 +08:00
|
|
|
const char *const optionalOperandParserCode = R"(
|
|
|
|
{
|
2020-09-01 03:33:36 +08:00
|
|
|
{0}OperandsLoc = parser.getCurrentLocation();
|
2022-03-22 04:42:13 +08:00
|
|
|
::mlir::OpAsmParser::UnresolvedOperand operand;
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalOperand(operand);
|
2020-04-11 05:11:45 +08:00
|
|
|
if (parseResult.hasValue()) {
|
|
|
|
if (failed(*parseResult))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-04-11 05:11:45 +08:00
|
|
|
{0}Operands.push_back(operand);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
const char *const operandParserCode = R"(
|
2020-09-01 03:33:36 +08:00
|
|
|
{0}OperandsLoc = parser.getCurrentLocation();
|
2020-01-31 03:31:21 +08:00
|
|
|
if (parser.parseOperand({0}RawOperands[0]))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
/// The code snippet used to generate a parser call for a VariadicOfVariadic
|
|
|
|
/// operand.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the operand.
|
|
|
|
/// {1}: The name of segment size attribute.
|
|
|
|
const char *const variadicOfVariadicOperandParserCode = R"(
|
|
|
|
{
|
|
|
|
{0}OperandsLoc = parser.getCurrentLocation();
|
|
|
|
int32_t curSize = 0;
|
|
|
|
do {
|
|
|
|
if (parser.parseOptionalLParen())
|
|
|
|
break;
|
|
|
|
if (parser.parseOperandList({0}Operands) || parser.parseRParen())
|
|
|
|
return ::mlir::failure();
|
|
|
|
{0}OperandGroupSizes.push_back({0}Operands.size() - curSize);
|
|
|
|
curSize = {0}Operands.size();
|
|
|
|
} while (succeeded(parser.parseOptionalComma()));
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
/// The code snippet used to generate a parser call for a type list.
|
|
|
|
///
|
|
|
|
/// {0}: The name for the type list.
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
const char *const variadicOfVariadicTypeParserCode = R"(
|
|
|
|
do {
|
|
|
|
if (parser.parseOptionalLParen())
|
|
|
|
break;
|
|
|
|
if (parser.parseOptionalRParen() &&
|
|
|
|
(parser.parseTypeList({0}Types) || parser.parseRParen()))
|
|
|
|
return ::mlir::failure();
|
|
|
|
} while (succeeded(parser.parseOptionalComma()));
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
const char *const variadicTypeParserCode = R"(
|
|
|
|
if (parser.parseTypeList({0}Types))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
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))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-04-11 05:11:45 +08:00
|
|
|
{0}Types.push_back(optionalType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
const char *const typeParserCode = R"(
|
2021-12-08 09:24:51 +08:00
|
|
|
{
|
|
|
|
{0} type;
|
|
|
|
if (parser.parseCustomTypeWithFallback(type))
|
|
|
|
return ::mlir::failure();
|
|
|
|
{1}RawTypes[0] = type;
|
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
)";
|
2022-01-11 09:26:44 +08:00
|
|
|
const char *const qualifiedTypeParserCode = R"(
|
|
|
|
if (parser.parseType({1}RawTypes[0]))
|
|
|
|
return ::mlir::failure();
|
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
/// 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))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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
|
|
|
)";
|
|
|
|
|
2021-10-07 08:50:38 +08:00
|
|
|
/// The code snippet used to generate a parser call to infer return types.
|
|
|
|
///
|
|
|
|
/// {0}: The operation class name
|
|
|
|
const char *const inferReturnTypesParserCode = R"(
|
|
|
|
::llvm::SmallVector<::mlir::Type> inferredReturnTypes;
|
|
|
|
if (::mlir::failed({0}::inferReturnTypes(parser.getContext(),
|
|
|
|
result.location, result.operands,
|
|
|
|
result.attributes.getDictionary(parser.getContext()),
|
|
|
|
result.regions, inferredReturnTypes)))
|
|
|
|
return ::mlir::failure();
|
|
|
|
result.addTypes(inferredReturnTypes);
|
|
|
|
)";
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// The code snippet used to generate a parser call for a region list.
|
|
|
|
///
|
|
|
|
/// {0}: The name for the region list.
|
|
|
|
const char *regionListParserCode = R"(
|
|
|
|
{
|
|
|
|
std::unique_ptr<::mlir::Region> region;
|
|
|
|
auto firstRegionResult = parser.parseOptionalRegion(region);
|
|
|
|
if (firstRegionResult.hasValue()) {
|
|
|
|
if (failed(*firstRegionResult))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:55 +08:00
|
|
|
{0}Regions.emplace_back(std::move(region));
|
|
|
|
|
|
|
|
// Parse any trailing regions.
|
|
|
|
while (succeeded(parser.parseOptionalComma())) {
|
|
|
|
region = std::make_unique<::mlir::Region>();
|
|
|
|
if (parser.parseRegion(*region))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:55 +08:00
|
|
|
{0}Regions.emplace_back(std::move(region));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)";
|
|
|
|
|
|
|
|
/// The code snippet used to ensure a list of regions have terminators.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region list.
|
|
|
|
const char *regionListEnsureTerminatorParserCode = R"(
|
|
|
|
for (auto ®ion : {0}Regions)
|
|
|
|
ensureTerminator(*region, parser.getBuilder(), result.location);
|
|
|
|
)";
|
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
/// The code snippet used to ensure a list of regions have a block.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region list.
|
|
|
|
const char *regionListEnsureSingleBlockParserCode = R"(
|
|
|
|
for (auto ®ion : {0}Regions)
|
2021-03-25 20:02:41 +08:00
|
|
|
if (region->empty()) region->emplaceBlock();
|
2021-03-12 07:58:02 +08:00
|
|
|
)";
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// The code snippet used to generate a parser call for an optional region.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region.
|
|
|
|
const char *optionalRegionParserCode = R"(
|
2020-12-03 08:49:47 +08:00
|
|
|
{
|
|
|
|
auto parseResult = parser.parseOptionalRegion(*{0}Region);
|
|
|
|
if (parseResult.hasValue() && failed(*parseResult))
|
|
|
|
return ::mlir::failure();
|
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
)";
|
|
|
|
|
|
|
|
/// The code snippet used to generate a parser call for a region.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region.
|
|
|
|
const char *regionParserCode = R"(
|
|
|
|
if (parser.parseRegion(*{0}Region))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:55 +08:00
|
|
|
)";
|
|
|
|
|
|
|
|
/// The code snippet used to ensure a region has a terminator.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region.
|
|
|
|
const char *regionEnsureTerminatorParserCode = R"(
|
|
|
|
ensureTerminator(*{0}Region, parser.getBuilder(), result.location);
|
|
|
|
)";
|
|
|
|
|
2021-03-12 07:58:02 +08:00
|
|
|
/// The code snippet used to ensure a region has a block.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region.
|
|
|
|
const char *regionEnsureSingleBlockParserCode = R"(
|
|
|
|
if ({0}Region->empty()) {0}Region->emplaceBlock();
|
|
|
|
)";
|
|
|
|
|
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
|
|
|
::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))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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-03-06 04:48:28 +08:00
|
|
|
if (parser.parseSuccessor({0}Successor))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-02-22 05:20:06 +08:00
|
|
|
)";
|
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
/// The code snippet used to generate a parser for OIList
|
|
|
|
///
|
|
|
|
/// {0}: literal keyword corresponding to a case for oilist
|
|
|
|
const char *oilistParserCode = R"(
|
|
|
|
if ({0}Clause) {
|
|
|
|
return parser.emitError(parser.getNameLoc())
|
|
|
|
<< "`{0}` clause can appear at most once in the expansion of the "
|
|
|
|
"oilist directive";
|
|
|
|
}
|
|
|
|
{0}Clause = true;
|
|
|
|
)";
|
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
namespace {
|
|
|
|
/// The type of length for a given parse argument.
|
|
|
|
enum class ArgumentLengthKind {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
/// The argument is a variadic of a variadic, and may contain 0->N range
|
|
|
|
/// elements.
|
|
|
|
VariadicOfVariadic,
|
2020-04-11 05:11:45 +08:00
|
|
|
/// 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
|
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-04-11 05:11:45 +08:00
|
|
|
|
|
|
|
/// Get the length kind for the given constraint.
|
|
|
|
static ArgumentLengthKind
|
|
|
|
getArgumentLengthKind(const NamedTypeConstraint *var) {
|
|
|
|
if (var->isOptional())
|
|
|
|
return ArgumentLengthKind::Optional;
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (var->isVariadicOfVariadic())
|
|
|
|
return ArgumentLengthKind::VariadicOfVariadic;
|
2020-04-11 05:11:45 +08:00
|
|
|
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.
|
2022-02-01 14:58:02 +08:00
|
|
|
static StringRef getTypeListName(FormatElement *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.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genLiteralParser(StringRef value, MethodBody &body) {
|
2020-01-31 03:31:21 +08:00
|
|
|
// 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;
|
|
|
|
}
|
2020-10-07 22:17:35 +08:00
|
|
|
body << (StringRef)StringSwitch<StringRef>(value)
|
2020-02-22 05:19:15 +08:00
|
|
|
.Case("->", "Arrow()")
|
|
|
|
.Case(":", "Colon()")
|
|
|
|
.Case(",", "Comma()")
|
|
|
|
.Case("=", "Equal()")
|
|
|
|
.Case("<", "Less()")
|
|
|
|
.Case(">", "Greater()")
|
[mlir][PDL] Add a PDL Interpreter Dialect
The PDL Interpreter dialect provides a lower level abstraction compared to the PDL dialect, and is targeted towards low level optimization and interpreter code generation. The dialect operations encapsulates low-level pattern match and rewrite "primitives", such as navigating the IR (Operation::getOperand), creating new operations (OpBuilder::create), etc. Many of the operations within this dialect also fuse branching control flow with some form of a predicate comparison operation. This type of fusion reduces the amount of work that an interpreter must do when executing.
An example of this representation is shown below:
```mlir
// The following high level PDL pattern:
pdl.pattern : benefit(1) {
%resultType = pdl.type
%inputOperand = pdl.input
%root, %results = pdl.operation "foo.op"(%inputOperand) -> %resultType
pdl.rewrite %root {
pdl.replace %root with (%inputOperand)
}
}
// May be represented in the interpreter dialect as follows:
module {
func @matcher(%arg0: !pdl.operation) {
pdl_interp.check_operation_name of %arg0 is "foo.op" -> ^bb2, ^bb1
^bb1:
pdl_interp.return
^bb2:
pdl_interp.check_operand_count of %arg0 is 1 -> ^bb3, ^bb1
^bb3:
pdl_interp.check_result_count of %arg0 is 1 -> ^bb4, ^bb1
^bb4:
%0 = pdl_interp.get_operand 0 of %arg0
pdl_interp.is_not_null %0 : !pdl.value -> ^bb5, ^bb1
^bb5:
%1 = pdl_interp.get_result 0 of %arg0
pdl_interp.is_not_null %1 : !pdl.value -> ^bb6, ^bb1
^bb6:
pdl_interp.record_match @rewriters::@rewriter(%0, %arg0 : !pdl.value, !pdl.operation) : benefit(1), loc([%arg0]), root("foo.op") -> ^bb1
}
module @rewriters {
func @rewriter(%arg0: !pdl.value, %arg1: !pdl.operation) {
pdl_interp.replace %arg1 with(%arg0)
pdl_interp.return
}
}
}
```
Differential Revision: https://reviews.llvm.org/D84579
2020-08-26 20:12:07 +08:00
|
|
|
.Case("{", "LBrace()")
|
|
|
|
.Case("}", "RBrace()")
|
2020-02-22 05:19:15 +08:00
|
|
|
.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()")
|
2020-11-12 01:01:39 +08:00
|
|
|
.Case("?", "Question()")
|
|
|
|
.Case("+", "Plus()")
|
|
|
|
.Case("*", "Star()");
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate the storage code required for parsing the given element.
|
2022-02-01 14:58:02 +08:00
|
|
|
static void genElementParserStorage(FormatElement *element, const Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2020-02-22 05:19:15 +08:00
|
|
|
if (auto *optional = dyn_cast<OptionalElement>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
ArrayRef<FormatElement *> elements = optional->getThenElements();
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// If the anchor is a unit attribute, it won't be parsed directly so elide
|
|
|
|
// it.
|
|
|
|
auto *anchor = dyn_cast<AttributeVariable>(optional->getAnchor());
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *elidedAnchorElement = nullptr;
|
|
|
|
if (anchor && anchor != elements.front() && anchor->isUnitAttr())
|
2020-09-01 03:33:36 +08:00
|
|
|
elidedAnchorElement = anchor;
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *childElement : elements)
|
|
|
|
if (childElement != elidedAnchorElement)
|
|
|
|
genElementParserStorage(childElement, op, body);
|
|
|
|
for (FormatElement *childElement : optional->getElseElements())
|
|
|
|
genElementParserStorage(childElement, op, body);
|
2020-09-01 03:33:36 +08:00
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
} else if (auto *oilist = dyn_cast<OIListElement>(element)) {
|
2022-03-22 12:38:33 +08:00
|
|
|
for (ArrayRef<FormatElement *> pelement : oilist->getParsingElements()) {
|
|
|
|
if (!oilist->getUnitAttrParsingElement(pelement))
|
|
|
|
for (FormatElement *element : pelement)
|
|
|
|
genElementParserStorage(element, op, body);
|
|
|
|
}
|
2022-02-17 12:54:10 +08:00
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *custom = dyn_cast<CustomDirective>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *paramElement : custom->getArguments())
|
|
|
|
genElementParserStorage(paramElement, op, body);
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
2022-03-22 04:42:13 +08:00
|
|
|
body << " ::mlir::SmallVector<::mlir::OpAsmParser::UnresolvedOperand, 4> "
|
2020-09-01 03:33:36 +08:00
|
|
|
"allOperands;\n";
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
} else if (isa<RegionsDirective>(element)) {
|
|
|
|
body << " ::llvm::SmallVector<std::unique_ptr<::mlir::Region>, 2> "
|
|
|
|
"fullRegions;\n";
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (isa<SuccessorsDirective>(element)) {
|
|
|
|
body << " ::llvm::SmallVector<::mlir::Block *, 2> fullSuccessors;\n";
|
|
|
|
|
|
|
|
} else if (auto *attr = dyn_cast<AttributeVariable>(element)) {
|
|
|
|
const NamedAttribute *var = attr->getVar();
|
|
|
|
body << llvm::formatv(" {0} {1}Attr;\n", var->attr.getStorageType(),
|
|
|
|
var->name);
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
} 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()) {
|
2022-03-22 04:42:13 +08:00
|
|
|
body
|
|
|
|
<< " ::mlir::SmallVector<::mlir::OpAsmParser::UnresolvedOperand, 4> "
|
|
|
|
<< name << "Operands;\n";
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (operand->getVar()->isVariadicOfVariadic()) {
|
|
|
|
body << " llvm::SmallVector<int32_t> " << name
|
|
|
|
<< "OperandGroupSizes;\n";
|
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
} else {
|
2022-03-22 04:42:13 +08:00
|
|
|
body << " ::mlir::OpAsmParser::UnresolvedOperand " << name
|
2020-06-26 19:20:44 +08:00
|
|
|
<< "RawOperands[1];\n"
|
2022-03-22 04:42:13 +08:00
|
|
|
<< " ::llvm::ArrayRef<::mlir::OpAsmParser::UnresolvedOperand> "
|
|
|
|
<< name << "Operands(" << name << "RawOperands);";
|
2020-03-06 04:40:53 +08:00
|
|
|
}
|
2020-09-01 03:33:36 +08:00
|
|
|
body << llvm::formatv(" ::llvm::SMLoc {0}OperandsLoc;\n"
|
|
|
|
" (void){0}OperandsLoc;\n",
|
|
|
|
name);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(element)) {
|
|
|
|
StringRef name = region->getVar()->name;
|
|
|
|
if (region->getVar()->isVariadic()) {
|
|
|
|
body << llvm::formatv(
|
|
|
|
" ::llvm::SmallVector<std::unique_ptr<::mlir::Region>, 2> "
|
|
|
|
"{0}Regions;\n",
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
body << llvm::formatv(" std::unique_ptr<::mlir::Region> {0}Region = "
|
|
|
|
"std::make_unique<::mlir::Region>();\n",
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
|
|
|
|
StringRef name = successor->getVar()->name;
|
|
|
|
if (successor->getVar()->isVariadic()) {
|
|
|
|
body << llvm::formatv(" ::llvm::SmallVector<::mlir::Block *, 2> "
|
|
|
|
"{0}Successors;\n",
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
body << llvm::formatv(" ::mlir::Block *{0}Successor = nullptr;\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;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef name = getTypeListName(dir->getArg(), lengthKind);
|
2020-04-11 05:11:45 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
/// Generate the parser for a parameter to a custom directive.
|
2022-02-01 14:58:02 +08:00
|
|
|
static void genCustomParameterParser(FormatElement *param, MethodBody &body) {
|
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << attr->getVar()->name << "Attr";
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (isa<AttrDictDirective>(param)) {
|
2020-10-28 09:01:44 +08:00
|
|
|
body << "result.attributes";
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
StringRef name = operand->getVar()->name;
|
|
|
|
ArgumentLengthKind lengthKind = getArgumentLengthKind(operand->getVar());
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)
|
|
|
|
body << llvm::formatv("{0}OperandGroups", name);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Variadic)
|
2020-09-01 03:33:36 +08:00
|
|
|
body << llvm::formatv("{0}Operands", name);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Optional)
|
|
|
|
body << llvm::formatv("{0}Operand", name);
|
|
|
|
else
|
|
|
|
body << formatv("{0}RawOperands[0]", name);
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(param)) {
|
2020-09-01 03:33:55 +08:00
|
|
|
StringRef name = region->getVar()->name;
|
|
|
|
if (region->getVar()->isVariadic())
|
|
|
|
body << llvm::formatv("{0}Regions", name);
|
|
|
|
else
|
|
|
|
body << llvm::formatv("*{0}Region", name);
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
StringRef name = successor->getVar()->name;
|
|
|
|
if (successor->getVar()->isVariadic())
|
|
|
|
body << llvm::formatv("{0}Successors", name);
|
|
|
|
else
|
|
|
|
body << llvm::formatv("{0}Successor", name);
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *dir = dyn_cast<RefDirective>(param)) {
|
|
|
|
genCustomParameterParser(dir->getArg(), body);
|
2021-02-10 06:32:15 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
ArgumentLengthKind lengthKind;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef listName = getTypeListName(dir->getArg(), lengthKind);
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)
|
|
|
|
body << llvm::formatv("{0}TypeGroups", listName);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Variadic)
|
2020-09-01 03:33:36 +08:00
|
|
|
body << llvm::formatv("{0}Types", listName);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Optional)
|
|
|
|
body << llvm::formatv("{0}Type", listName);
|
|
|
|
else
|
|
|
|
body << formatv("{0}RawTypes[0]", listName);
|
|
|
|
} else {
|
|
|
|
llvm_unreachable("unknown custom directive parameter");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate the parser for a custom directive.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genCustomDirectiveParser(CustomDirective *dir, MethodBody &body) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << " {\n";
|
|
|
|
|
|
|
|
// Preprocess the directive variables.
|
|
|
|
// * Add a local variable for optional operands and types. This provides a
|
|
|
|
// better API to the user defined parser methods.
|
|
|
|
// * Set the location of operand variables.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *param : dir->getArguments()) {
|
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(param)) {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
auto *var = operand->getVar();
|
|
|
|
body << " " << var->name
|
2020-09-01 03:33:36 +08:00
|
|
|
<< "OperandsLoc = parser.getCurrentLocation();\n";
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (var->isOptional()) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << llvm::formatv(
|
2022-03-22 04:42:13 +08:00
|
|
|
" ::llvm::Optional<::mlir::OpAsmParser::UnresolvedOperand> "
|
2020-09-01 03:33:36 +08:00
|
|
|
"{0}Operand;\n",
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
var->name);
|
|
|
|
} else if (var->isVariadicOfVariadic()) {
|
|
|
|
body << llvm::formatv(" "
|
2022-02-01 14:58:02 +08:00
|
|
|
"::llvm::SmallVector<::llvm::SmallVector<::mlir::"
|
2022-03-22 04:42:13 +08:00
|
|
|
"OpAsmParser::UnresolvedOperand>> "
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
"{0}OperandGroups;\n",
|
|
|
|
var->name);
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
ArgumentLengthKind lengthKind;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef listName = getTypeListName(dir->getArg(), lengthKind);
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::Optional) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << llvm::formatv(" ::mlir::Type {0}Type;\n", listName);
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
} else if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {
|
|
|
|
body << llvm::formatv(
|
2022-02-01 14:58:02 +08:00
|
|
|
" ::llvm::SmallVector<llvm::SmallVector<::mlir::Type>> "
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
"{0}TypeGroups;\n",
|
|
|
|
listName);
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *dir = dyn_cast<RefDirective>(param)) {
|
|
|
|
FormatElement *input = dir->getArg();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(input)) {
|
|
|
|
if (!operand->getVar()->isOptional())
|
|
|
|
continue;
|
|
|
|
body << llvm::formatv(
|
|
|
|
" {0} {1}Operand = {1}Operands.empty() ? {0}() : "
|
|
|
|
"{1}Operands[0];\n",
|
2022-03-22 04:42:13 +08:00
|
|
|
"::llvm::Optional<::mlir::OpAsmParser::UnresolvedOperand>",
|
2021-02-10 06:32:15 +08:00
|
|
|
operand->getVar()->name);
|
|
|
|
|
|
|
|
} else if (auto *type = dyn_cast<TypeDirective>(input)) {
|
|
|
|
ArgumentLengthKind lengthKind;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef listName = getTypeListName(type->getArg(), lengthKind);
|
2021-02-10 06:32:15 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::Optional) {
|
|
|
|
body << llvm::formatv(" ::mlir::Type {0}Type = {0}Types.empty() ? "
|
|
|
|
"::mlir::Type() : {0}Types[0];\n",
|
|
|
|
listName);
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
body << " if (parse" << dir->getName() << "(parser";
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *param : dir->getArguments()) {
|
2021-02-10 06:32:15 +08:00
|
|
|
body << ", ";
|
2020-09-01 03:33:36 +08:00
|
|
|
genCustomParameterParser(param, body);
|
2021-02-10 06:32:15 +08:00
|
|
|
}
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
body << "))\n"
|
2020-09-15 04:01:07 +08:00
|
|
|
<< " return ::mlir::failure();\n";
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// After parsing, add handling for any of the optional constructs.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *param : dir->getArguments()) {
|
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
const NamedAttribute *var = attr->getVar();
|
|
|
|
if (var->attr.isOptional())
|
|
|
|
body << llvm::formatv(" if ({0}Attr)\n ", var->name);
|
|
|
|
|
2020-09-24 02:01:39 +08:00
|
|
|
body << llvm::formatv(" result.addAttribute(\"{0}\", {0}Attr);\n",
|
|
|
|
var->name);
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
const NamedTypeConstraint *var = operand->getVar();
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (var->isOptional()) {
|
|
|
|
body << llvm::formatv(" if ({0}Operand.hasValue())\n"
|
|
|
|
" {0}Operands.push_back(*{0}Operand);\n",
|
|
|
|
var->name);
|
|
|
|
} else if (var->isVariadicOfVariadic()) {
|
|
|
|
body << llvm::formatv(
|
|
|
|
" for (const auto &subRange : {0}OperandGroups) {{\n"
|
|
|
|
" {0}Operands.append(subRange.begin(), subRange.end());\n"
|
|
|
|
" {0}OperandGroupSizes.push_back(subRange.size());\n"
|
|
|
|
" }\n",
|
|
|
|
var->name, var->constraint.getVariadicOfVariadicSegmentSizeAttr());
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(param)) {
|
2020-09-01 03:33:36 +08:00
|
|
|
ArgumentLengthKind lengthKind;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef listName = getTypeListName(dir->getArg(), lengthKind);
|
2020-09-01 03:33:36 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::Optional) {
|
|
|
|
body << llvm::formatv(" if ({0}Type)\n"
|
|
|
|
" {0}Types.push_back({0}Type);\n",
|
|
|
|
listName);
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
} else if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {
|
|
|
|
body << llvm::formatv(
|
|
|
|
" for (const auto &subRange : {0}TypeGroups)\n"
|
|
|
|
" {0}Types.append(subRange.begin(), subRange.end());\n",
|
|
|
|
listName);
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
body << " }\n";
|
|
|
|
}
|
|
|
|
|
2021-01-15 03:35:15 +08:00
|
|
|
/// Generate the parser for a enum attribute.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genEnumAttrParser(const NamedAttribute *var, MethodBody &body,
|
2021-01-15 03:35:15 +08:00
|
|
|
FmtContext &attrTypeCtx) {
|
|
|
|
Attribute baseAttr = var->attr.getBaseAttr();
|
|
|
|
const EnumAttr &enumAttr = cast<EnumAttr>(baseAttr);
|
|
|
|
std::vector<EnumAttrCase> cases = enumAttr.getAllCases();
|
|
|
|
|
|
|
|
// 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()");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build a string containing the cases that can be formatted as a keyword.
|
|
|
|
std::string validCaseKeywordsStr = "{";
|
|
|
|
llvm::raw_string_ostream validCaseKeywordsOS(validCaseKeywordsStr);
|
|
|
|
for (const EnumAttrCase &attrCase : cases)
|
|
|
|
if (canFormatStringAsKeyword(attrCase.getStr()))
|
|
|
|
validCaseKeywordsOS << '"' << attrCase.getStr() << "\",";
|
|
|
|
validCaseKeywordsOS.str().back() = '}';
|
|
|
|
|
|
|
|
// If the attribute is not optional, build an error message for the missing
|
|
|
|
// attribute.
|
|
|
|
std::string errorMessage;
|
|
|
|
if (!var->attr.isOptional()) {
|
|
|
|
llvm::raw_string_ostream errorMessageOS(errorMessage);
|
|
|
|
errorMessageOS
|
|
|
|
<< "return parser.emitError(loc, \"expected string or "
|
|
|
|
"keyword containing one of the following enum values for attribute '"
|
|
|
|
<< var->name << "' [";
|
|
|
|
llvm::interleaveComma(cases, errorMessageOS, [&](const auto &attrCase) {
|
|
|
|
errorMessageOS << attrCase.getStr();
|
|
|
|
});
|
|
|
|
errorMessageOS << "]\");";
|
|
|
|
}
|
|
|
|
|
|
|
|
body << formatv(enumAttrParserCode, var->name, enumAttr.getCppNamespace(),
|
|
|
|
enumAttr.getStringToSymbolFnName(), attrBuilderStr,
|
|
|
|
validCaseKeywordsStr, errorMessage);
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
void OperationFormat::genParser(Operator &op, OpClass &opClass) {
|
2021-11-12 09:17:05 +08:00
|
|
|
SmallVector<MethodParameter> paramList;
|
2020-09-18 04:18:09 +08:00
|
|
|
paramList.emplace_back("::mlir::OpAsmParser &", "parser");
|
|
|
|
paramList.emplace_back("::mlir::OperationState &", "result");
|
|
|
|
|
2021-11-12 09:17:05 +08:00
|
|
|
auto *method = opClass.addStaticMethod("::mlir::ParseResult", "parse",
|
|
|
|
std::move(paramList));
|
2020-09-18 04:18:09 +08:00
|
|
|
auto &body = method->body();
|
2020-09-01 03:33:55 +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.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *element : elements)
|
|
|
|
genElementParserStorage(element, op, body);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
// A format context used when parsing attributes with buildable types.
|
|
|
|
FmtContext attrTypeCtx;
|
|
|
|
attrTypeCtx.withBuilder("parser.getBuilder()");
|
|
|
|
|
|
|
|
// Generate parsers for each of the elements.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *element : elements)
|
|
|
|
genElementParser(element, body, attrTypeCtx);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
// Generate the code to resolve the operand/result types and successors now
|
|
|
|
// that they have been parsed.
|
|
|
|
genParserRegionResolution(op, body);
|
|
|
|
genParserSuccessorResolution(op, body);
|
|
|
|
genParserVariadicSegmentResolution(op, body);
|
2021-12-15 14:22:04 +08:00
|
|
|
genParserTypeResolution(op, body);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2020-09-15 04:01:07 +08:00
|
|
|
body << " return ::mlir::success();\n";
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
void OperationFormat::genElementParser(FormatElement *element, MethodBody &body,
|
2022-01-19 00:53:55 +08:00
|
|
|
FmtContext &attrTypeCtx,
|
|
|
|
GenContext genCtx) {
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Optional Group.
|
|
|
|
if (auto *optional = dyn_cast<OptionalElement>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
ArrayRef<FormatElement *> elements =
|
|
|
|
optional->getThenElements().drop_front(optional->getParseStart());
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
// Generate a special optional parser for the first element to gate the
|
|
|
|
// parsing of the rest of the elements.
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *firstElement = elements.front();
|
2020-07-15 04:14:14 +08:00
|
|
|
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";
|
2022-02-01 14:58:02 +08:00
|
|
|
genLiteralParser(literal->getSpelling(), body);
|
2020-02-22 05:19:15 +08:00
|
|
|
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-09-01 03:33:55 +08:00
|
|
|
} else if (auto *regionVar = dyn_cast<RegionVariable>(firstElement)) {
|
|
|
|
const NamedRegion *region = regionVar->getVar();
|
|
|
|
if (region->isVariadic()) {
|
|
|
|
genElementParser(regionVar, body, attrTypeCtx);
|
|
|
|
body << " if (!" << region->name << "Regions.empty()) {\n";
|
|
|
|
} else {
|
|
|
|
body << llvm::formatv(optionalRegionParserCode, region->name);
|
|
|
|
body << " if (!" << region->name << "Region->empty()) {\n ";
|
|
|
|
if (hasImplicitTermTrait)
|
|
|
|
body << llvm::formatv(regionEnsureTerminatorParserCode, region->name);
|
2021-03-12 07:58:02 +08:00
|
|
|
else if (hasSingleBlockTrait)
|
|
|
|
body << llvm::formatv(regionEnsureSingleBlockParserCode,
|
|
|
|
region->name);
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
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.
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *elidedAnchorElement = nullptr;
|
2020-08-04 05:20:50 +08:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2022-01-19 00:53:55 +08:00
|
|
|
// Generate the rest of the elements inside an optional group. Elements in
|
|
|
|
// an optional group after the guard are parsed as required.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *childElement : llvm::drop_begin(elements, 1))
|
|
|
|
if (childElement != elidedAnchorElement)
|
|
|
|
genElementParser(childElement, body, attrTypeCtx, GenContext::Optional);
|
2021-03-23 09:07:09 +08:00
|
|
|
body << " }";
|
|
|
|
|
|
|
|
// Generate the else elements.
|
|
|
|
auto elseElements = optional->getElseElements();
|
|
|
|
if (!elseElements.empty()) {
|
|
|
|
body << " else {\n";
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *childElement : elseElements)
|
|
|
|
genElementParser(childElement, body, attrTypeCtx);
|
2021-03-23 09:07:09 +08:00
|
|
|
body << " }";
|
|
|
|
}
|
|
|
|
body << "\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
/// OIList Directive
|
|
|
|
} else if (OIListElement *oilist = dyn_cast<OIListElement>(element)) {
|
|
|
|
for (LiteralElement *le : oilist->getLiteralElements())
|
|
|
|
body << " bool " << le->getSpelling() << "Clause = false;\n";
|
|
|
|
|
|
|
|
// Generate the parsing loop
|
|
|
|
body << " while(true) {\n";
|
|
|
|
for (auto clause : oilist->getClauses()) {
|
|
|
|
LiteralElement *lelement = std::get<0>(clause);
|
|
|
|
ArrayRef<FormatElement *> pelement = std::get<1>(clause);
|
|
|
|
body << "if (succeeded(parser.parseOptional";
|
|
|
|
genLiteralParser(lelement->getSpelling(), body);
|
|
|
|
body << ")) {\n";
|
2022-03-22 12:38:33 +08:00
|
|
|
StringRef lelementName = lelement->getSpelling();
|
|
|
|
body << formatv(oilistParserCode, lelementName);
|
|
|
|
if (AttributeVariable *unitAttrElem =
|
|
|
|
oilist->getUnitAttrParsingElement(pelement)) {
|
|
|
|
body << " result.addAttribute(\"" << unitAttrElem->getVar()->name
|
|
|
|
<< "\", UnitAttr::get(parser.getContext()));\n";
|
|
|
|
} else {
|
|
|
|
for (FormatElement *el : pelement)
|
|
|
|
genElementParser(el, body, attrTypeCtx);
|
|
|
|
}
|
2022-02-17 12:54:10 +08:00
|
|
|
body << " } else ";
|
|
|
|
}
|
|
|
|
body << " {\n";
|
|
|
|
body << " break;\n";
|
|
|
|
body << " }\n";
|
|
|
|
body << "}\n";
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Literals.
|
|
|
|
} else if (LiteralElement *literal = dyn_cast<LiteralElement>(element)) {
|
|
|
|
body << " if (parser.parse";
|
2022-02-01 14:58:02 +08:00
|
|
|
genLiteralParser(literal->getSpelling(), body);
|
2020-09-15 04:01:07 +08:00
|
|
|
body << ")\n return ::mlir::failure();\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-12-15 03:53:34 +08:00
|
|
|
/// Whitespaces.
|
|
|
|
} else if (isa<WhitespaceElement>(element)) {
|
2020-10-18 13:40:42 +08:00
|
|
|
// Nothing to parse.
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
/// 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.
|
2021-01-15 03:35:15 +08:00
|
|
|
if (canFormatEnumAttr(var))
|
|
|
|
return genEnumAttrParser(var, body, attrTypeCtx);
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-09-01 03:33:46 +08:00
|
|
|
// Check to see if we should parse this as a symbol name attribute.
|
|
|
|
if (shouldFormatSymbolNameAttr(var)) {
|
|
|
|
body << formatv(var->attr.isOptional() ? optionalSymbolNameAttrParserCode
|
|
|
|
: symbolNameAttrParserCode,
|
|
|
|
var->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// 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);
|
2021-12-08 09:24:51 +08:00
|
|
|
os << tgfmt(*typeBuilder, &attrTypeCtx);
|
|
|
|
} else {
|
2021-12-25 05:25:32 +08:00
|
|
|
attrTypeStr = "::mlir::Type{}";
|
2021-12-08 09:24:51 +08:00
|
|
|
}
|
2022-01-19 00:53:55 +08:00
|
|
|
if (genCtx == GenContext::Normal && var->attr.isOptional()) {
|
2021-12-08 09:24:51 +08:00
|
|
|
body << formatv(optionalAttrParserCode, var->name, attrTypeStr);
|
|
|
|
} else {
|
2022-01-11 09:26:44 +08:00
|
|
|
if (attr->shouldBeQualified() ||
|
|
|
|
var->attr.getStorageType() == "::mlir::Attribute")
|
2021-12-08 09:24:51 +08:00
|
|
|
body << formatv(genericAttrParserCode, var->name, attrTypeStr);
|
|
|
|
else
|
|
|
|
body << formatv(attrParserCode, 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;
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::VariadicOfVariadic)
|
|
|
|
body << llvm::formatv(
|
|
|
|
variadicOfVariadicOperandParserCode, name,
|
|
|
|
operand->getVar()->constraint.getVariadicOfVariadicSegmentSizeAttr());
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Variadic)
|
2020-04-11 05:11:45 +08:00
|
|
|
body << llvm::formatv(variadicOperandParserCode, name);
|
|
|
|
else if (lengthKind == ArgumentLengthKind::Optional)
|
|
|
|
body << llvm::formatv(optionalOperandParserCode, name);
|
|
|
|
else
|
|
|
|
body << formatv(operandParserCode, name);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(element)) {
|
|
|
|
bool isVariadic = region->getVar()->isVariadic();
|
|
|
|
body << llvm::formatv(isVariadic ? regionListParserCode : regionParserCode,
|
|
|
|
region->getVar()->name);
|
2021-03-12 07:58:02 +08:00
|
|
|
if (hasImplicitTermTrait)
|
2020-09-01 03:33:55 +08:00
|
|
|
body << llvm::formatv(isVariadic ? regionListEnsureTerminatorParserCode
|
|
|
|
: regionEnsureTerminatorParserCode,
|
|
|
|
region->getVar()->name);
|
2021-03-12 07:58:02 +08:00
|
|
|
else if (hasSingleBlockTrait)
|
|
|
|
body << llvm::formatv(isVariadic ? regionListEnsureSingleBlockParserCode
|
|
|
|
: regionEnsureSingleBlockParserCode,
|
|
|
|
region->getVar()->name);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
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-09-15 04:01:07 +08:00
|
|
|
<< " return ::mlir::failure();\n";
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *customDir = dyn_cast<CustomDirective>(element)) {
|
|
|
|
genCustomDirectiveParser(customDir, body);
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::SMLoc allOperandLoc = parser.getCurrentLocation();\n"
|
2020-02-22 05:19:15 +08:00
|
|
|
<< " if (parser.parseOperandList(allOperands))\n"
|
2020-09-15 04:01:07 +08:00
|
|
|
<< " return ::mlir::failure();\n";
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
} else if (isa<RegionsDirective>(element)) {
|
|
|
|
body << llvm::formatv(regionListParserCode, "full");
|
|
|
|
if (hasImplicitTermTrait)
|
|
|
|
body << llvm::formatv(regionListEnsureTerminatorParserCode, "full");
|
2021-03-12 07:58:02 +08:00
|
|
|
else if (hasSingleBlockTrait)
|
|
|
|
body << llvm::formatv(regionListEnsureSingleBlockParserCode, "full");
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (isa<SuccessorsDirective>(element)) {
|
|
|
|
body << llvm::formatv(successorListParserCode, "full");
|
2020-09-01 03:33:55 +08:00
|
|
|
|
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;
|
2022-02-01 14:58:02 +08:00
|
|
|
StringRef listName = getTypeListName(dir->getArg(), lengthKind);
|
2021-12-08 09:24:51 +08:00
|
|
|
if (lengthKind == ArgumentLengthKind::VariadicOfVariadic) {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
body << llvm::formatv(variadicOfVariadicTypeParserCode, listName);
|
2021-12-08 09:24:51 +08:00
|
|
|
} else if (lengthKind == ArgumentLengthKind::Variadic) {
|
2020-04-11 05:11:45 +08:00
|
|
|
body << llvm::formatv(variadicTypeParserCode, listName);
|
2021-12-08 09:24:51 +08:00
|
|
|
} else if (lengthKind == ArgumentLengthKind::Optional) {
|
2020-04-11 05:11:45 +08:00
|
|
|
body << llvm::formatv(optionalTypeParserCode, listName);
|
2021-12-08 09:24:51 +08:00
|
|
|
} else {
|
2022-01-11 09:26:44 +08:00
|
|
|
const char *parserCode =
|
|
|
|
dir->shouldBeQualified() ? qualifiedTypeParserCode : typeParserCode;
|
2022-02-01 14:58:02 +08:00
|
|
|
TypeSwitch<FormatElement *>(dir->getArg())
|
2021-12-08 09:24:51 +08:00
|
|
|
.Case<OperandVariable, ResultVariable>([&](auto operand) {
|
2022-01-11 09:26:44 +08:00
|
|
|
body << formatv(parserCode,
|
2021-12-08 09:24:51 +08:00
|
|
|
operand->getVar()->constraint.getCPPClassName(),
|
|
|
|
listName);
|
|
|
|
})
|
|
|
|
.Default([&](auto operand) {
|
2022-01-11 09:26:44 +08:00
|
|
|
body << formatv(parserCode, "::mlir::Type", listName);
|
2021-12-08 09:24:51 +08:00
|
|
|
});
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 09:17:05 +08:00
|
|
|
void OperationFormat::genParserTypeResolution(Operator &op, MethodBody &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",
|
2021-01-07 06:08:03 +08:00
|
|
|
variable->name, constraint.getSummary())
|
2020-02-22 05:19:03 +08:00
|
|
|
<< " }\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()) {
|
2021-01-15 03:34:55 +08:00
|
|
|
if (Optional<StringRef> tform = resolver.getVarTransformer()) {
|
|
|
|
FmtContext fmtContext;
|
2021-09-30 08:47:08 +08:00
|
|
|
fmtContext.addSubst("_ctxt", "parser.getContext()");
|
2021-01-15 03:34:55 +08:00
|
|
|
if (var->isVariadic())
|
|
|
|
fmtContext.withSelf(var->name + "Types");
|
|
|
|
else
|
|
|
|
fmtContext.withSelf(var->name + "Types[0]");
|
|
|
|
body << tgfmt(*tform, &fmtContext);
|
|
|
|
} else {
|
2020-02-22 05:19:03 +08:00
|
|
|
body << var->name << "Types";
|
2021-01-15 03:34:55 +08:00
|
|
|
}
|
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.
|
2021-10-07 08:50:38 +08:00
|
|
|
if (!infersResultTypes) {
|
|
|
|
if (allResultTypes) {
|
|
|
|
body << " result.addTypes(allResultTypes);\n";
|
|
|
|
} else {
|
|
|
|
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i) {
|
|
|
|
body << " result.addTypes(";
|
|
|
|
emitTypeResolver(resultTypes[i], op.getResultName(i));
|
|
|
|
body << ");\n";
|
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 23:04:46 +08:00
|
|
|
// Emit the operand type resolutions.
|
|
|
|
genParserOperandTypeResolution(op, body, emitTypeResolver);
|
|
|
|
|
|
|
|
// Handle return type inference once all operands have been resolved
|
|
|
|
if (infersResultTypes)
|
|
|
|
body << formatv(inferReturnTypesParserCode, op.getCppClassName());
|
|
|
|
}
|
|
|
|
|
|
|
|
void OperationFormat::genParserOperandTypeResolution(
|
|
|
|
Operator &op, MethodBody &body,
|
|
|
|
function_ref<void(TypeResolution &, StringRef)> emitTypeResolver) {
|
2020-01-31 03:31:21 +08:00
|
|
|
// Early exit if there are no operands.
|
2021-12-10 23:04:46 +08:00
|
|
|
if (op.getNumOperands() == 0)
|
2020-01-31 03:31:21 +08:00
|
|
|
return;
|
|
|
|
|
2021-12-10 23:04:46 +08:00
|
|
|
// Handle the case where all operand types are grouped together with
|
|
|
|
// "types(operands)".
|
2020-01-31 03:31:21 +08:00
|
|
|
if (allOperandTypes) {
|
2021-12-10 23:04:46 +08:00
|
|
|
// If `operands` was specified, 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"
|
2020-09-15 04:01:07 +08:00
|
|
|
" return ::mlir::failure();\n";
|
2020-01-31 03:31:21 +08:00
|
|
|
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) {
|
2022-03-22 04:42:13 +08:00
|
|
|
body << "::llvm::concat<const ::mlir::OpAsmParser::UnresolvedOperand>(";
|
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"
|
2020-09-15 04:01:07 +08:00
|
|
|
<< " return ::mlir::failure();\n";
|
2020-01-31 03:31:21 +08:00
|
|
|
return;
|
|
|
|
}
|
2021-12-10 23:04:46 +08:00
|
|
|
|
|
|
|
// Handle the case where all operands are grouped together with "operands".
|
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) {
|
2021-12-25 05:25:32 +08:00
|
|
|
body << "::llvm::concat<const ::mlir::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"
|
2020-09-15 04:01:07 +08:00
|
|
|
<< " return ::mlir::failure();\n";
|
2020-01-31 03:31:21 +08:00
|
|
|
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";
|
2020-09-15 04:01:07 +08:00
|
|
|
body << ", result.operands))\n return ::mlir::failure();\n";
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
void OperationFormat::genParserRegionResolution(Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2020-09-01 03:33:55 +08:00
|
|
|
// Check for the case where all regions were parsed.
|
|
|
|
bool hasAllRegions = llvm::any_of(
|
2022-02-01 14:58:02 +08:00
|
|
|
elements, [](FormatElement *elt) { return isa<RegionsDirective>(elt); });
|
2020-09-01 03:33:55 +08:00
|
|
|
if (hasAllRegions) {
|
|
|
|
body << " result.addRegions(fullRegions);\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, handle each region individually.
|
|
|
|
for (const NamedRegion ®ion : op.getRegions()) {
|
|
|
|
if (region.isVariadic())
|
|
|
|
body << " result.addRegions(" << region.name << "Regions);\n";
|
|
|
|
else
|
|
|
|
body << " result.addRegion(std::move(" << region.name << "Region));\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
void OperationFormat::genParserSuccessorResolution(Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2020-02-22 05:20:06 +08:00
|
|
|
// Check for the case where all successors were parsed.
|
2022-02-01 14:58:02 +08:00
|
|
|
bool hasAllSuccessors = llvm::any_of(elements, [](FormatElement *elt) {
|
|
|
|
return isa<SuccessorsDirective>(elt);
|
|
|
|
});
|
2020-02-22 05:20:06 +08:00
|
|
|
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,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (!allOperands) {
|
|
|
|
if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments")) {
|
|
|
|
body << " result.addAttribute(\"operand_segment_sizes\", "
|
|
|
|
<< "parser.getBuilder().getI32VectorAttr({";
|
|
|
|
auto interleaveFn = [&](const NamedTypeConstraint &operand) {
|
|
|
|
// If the operand is variadic emit the parsed size.
|
|
|
|
if (operand.isVariableLength())
|
|
|
|
body << "static_cast<int32_t>(" << operand.name << "Operands.size())";
|
|
|
|
else
|
|
|
|
body << "1";
|
|
|
|
};
|
|
|
|
llvm::interleaveComma(op.getOperands(), body, interleaveFn);
|
|
|
|
body << "}));\n";
|
|
|
|
}
|
|
|
|
for (const NamedTypeConstraint &operand : op.getOperands()) {
|
|
|
|
if (!operand.isVariadicOfVariadic())
|
|
|
|
continue;
|
|
|
|
body << llvm::formatv(
|
|
|
|
" result.addAttribute(\"{0}\", "
|
|
|
|
"parser.getBuilder().getI32TensorAttr({1}OperandGroupSizes));\n",
|
|
|
|
operand.constraint.getVariadicOfVariadicSegmentSizeAttr(),
|
|
|
|
operand.name);
|
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
}
|
2020-09-01 03:33:36 +08:00
|
|
|
|
2020-09-15 04:01:07 +08:00
|
|
|
if (!allResultTypes &&
|
|
|
|
op.getTrait("::mlir::OpTrait::AttrSizedResultSegments")) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << " result.addAttribute(\"result_segment_sizes\", "
|
|
|
|
<< "parser.getBuilder().getI32VectorAttr({";
|
|
|
|
auto interleaveFn = [&](const NamedTypeConstraint &result) {
|
|
|
|
// If the result is variadic emit the parsed size.
|
|
|
|
if (result.isVariableLength())
|
|
|
|
body << "static_cast<int32_t>(" << result.name << "Types.size())";
|
|
|
|
else
|
|
|
|
body << "1";
|
|
|
|
};
|
|
|
|
llvm::interleaveComma(op.getResults(), body, interleaveFn);
|
|
|
|
body << "}));\n";
|
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PrinterGen
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// The code snippet used to generate a printer call for a region of an
|
|
|
|
// operation that has the SingleBlockImplicitTerminator trait.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the region.
|
|
|
|
const char *regionSingleBlockImplicitTerminatorPrinterCode = R"(
|
|
|
|
{
|
|
|
|
bool printTerminator = true;
|
|
|
|
if (auto *term = {0}.empty() ? nullptr : {0}.begin()->getTerminator()) {{
|
2020-12-18 09:10:12 +08:00
|
|
|
printTerminator = !term->getAttrDictionary().empty() ||
|
2020-09-01 03:33:55 +08:00
|
|
|
term->getNumOperands() != 0 ||
|
|
|
|
term->getNumResults() != 0;
|
[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
|
|
|
}
|
2021-11-04 06:34:13 +08:00
|
|
|
_odsPrinter.printRegion({0}, /*printEntryBlockArgs=*/true,
|
|
|
|
/*printBlockTerminators=*/printTerminator);
|
[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
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
)";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
2021-01-15 03:35:15 +08:00
|
|
|
/// The code snippet used to generate a printer call for an enum that has cases
|
|
|
|
/// that can't be represented with a keyword.
|
|
|
|
///
|
|
|
|
/// {0}: The name of the enum attribute.
|
|
|
|
/// {1}: The name of the enum attributes symbolToString function.
|
|
|
|
const char *enumAttrBeginPrinterCode = R"(
|
|
|
|
{
|
|
|
|
auto caseValue = {0}();
|
|
|
|
auto caseValueStr = {1}(caseValue);
|
|
|
|
)";
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Generate the printer for the 'attr-dict' directive.
|
|
|
|
static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body, bool withKeyword) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printOptionalAttrDict"
|
|
|
|
<< (withKeyword ? "WithKeyword" : "")
|
2021-02-26 22:14:16 +08:00
|
|
|
<< "((*this)->getAttrs(), /*elidedAttrs=*/{";
|
2020-03-06 04:40:53 +08:00
|
|
|
// Elide the variadic segment size attributes if necessary.
|
2020-09-15 04:01:07 +08:00
|
|
|
if (!fmt.allOperands &&
|
|
|
|
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"))
|
2020-03-06 04:40:53 +08:00
|
|
|
body << "\"operand_segment_sizes\", ";
|
2020-09-15 04:01:07 +08:00
|
|
|
if (!fmt.allResultTypes &&
|
|
|
|
op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
|
2020-09-01 03:33:36 +08:00
|
|
|
body << "\"result_segment_sizes\", ";
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (!fmt.inferredAttributes.empty()) {
|
|
|
|
for (const auto &attr : fmt.inferredAttributes)
|
|
|
|
body << "\"" << attr.getKey() << "\", ";
|
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
llvm::interleaveComma(
|
|
|
|
fmt.usedAttributes, body,
|
|
|
|
[&](const NamedAttribute *attr) { body << "\"" << attr->name << "\""; });
|
2020-01-31 03:31:21 +08:00
|
|
|
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.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genLiteralPrinter(StringRef value, MethodBody &body,
|
2020-01-31 03:31:21 +08:00
|
|
|
bool &shouldEmitSpace, bool &lastWasPunctuation) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter";
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
// Don't insert a space for certain punctuation.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (shouldEmitSpace && shouldEmitSpaceBefore(value, lastWasPunctuation))
|
2020-10-18 13:40:42 +08:00
|
|
|
body << " << ' '";
|
2020-01-31 03:31:21 +08:00
|
|
|
body << " << \"" << value << "\";\n";
|
|
|
|
|
|
|
|
// Insert a space after certain literals.
|
|
|
|
shouldEmitSpace =
|
|
|
|
value.size() != 1 || !StringRef("<({[").contains(value.front());
|
|
|
|
lastWasPunctuation = !(value.front() == '_' || isalpha(value.front()));
|
|
|
|
}
|
|
|
|
|
2020-10-18 13:40:42 +08:00
|
|
|
/// Generate the printer for a space. `shouldEmitSpace` and `lastWasPunctuation`
|
|
|
|
/// are set to false.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genSpacePrinter(bool value, MethodBody &body, bool &shouldEmitSpace,
|
|
|
|
bool &lastWasPunctuation) {
|
2020-11-11 15:24:16 +08:00
|
|
|
if (value) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << ' ';\n";
|
2020-11-11 15:24:16 +08:00
|
|
|
lastWasPunctuation = false;
|
2021-02-02 11:03:12 +08:00
|
|
|
} else {
|
|
|
|
lastWasPunctuation = true;
|
2020-11-11 15:24:16 +08:00
|
|
|
}
|
2020-10-18 13:40:42 +08:00
|
|
|
shouldEmitSpace = false;
|
|
|
|
}
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
/// Generate the printer for a custom directive parameter.
|
2022-02-01 14:58:02 +08:00
|
|
|
static void genCustomDirectiveParameterPrinter(FormatElement *element,
|
2021-10-20 22:08:36 +08:00
|
|
|
const Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(element)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << op.getGetterName(attr->getVar()->name) << "Attr()";
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (isa<AttrDictDirective>(element)) {
|
|
|
|
body << "getOperation()->getAttrDictionary()";
|
|
|
|
|
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << op.getGetterName(operand->getVar()->name) << "()";
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(element)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << op.getGetterName(region->getVar()->name) << "()";
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << op.getGetterName(successor->getVar()->name) << "()";
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (auto *dir = dyn_cast<RefDirective>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
genCustomDirectiveParameterPrinter(dir->getArg(), op, body);
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
auto *typeOperand = dir->getArg();
|
2021-02-10 06:32:15 +08:00
|
|
|
auto *operand = dyn_cast<OperandVariable>(typeOperand);
|
|
|
|
auto *var = operand ? operand->getVar()
|
|
|
|
: cast<ResultVariable>(typeOperand)->getVar();
|
2021-10-20 22:08:36 +08:00
|
|
|
std::string name = op.getGetterName(var->name);
|
2021-02-10 06:32:15 +08:00
|
|
|
if (var->isVariadic())
|
2021-10-20 22:08:36 +08:00
|
|
|
body << name << "().getTypes()";
|
2021-02-10 06:32:15 +08:00
|
|
|
else if (var->isOptional())
|
2021-10-20 22:08:36 +08:00
|
|
|
body << llvm::formatv("({0}() ? {0}().getType() : Type())", name);
|
2021-02-10 06:32:15 +08:00
|
|
|
else
|
2021-10-20 22:08:36 +08:00
|
|
|
body << name << "().getType()";
|
2021-02-10 06:32:15 +08:00
|
|
|
} else {
|
|
|
|
llvm_unreachable("unknown custom directive parameter");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-18 13:40:42 +08:00
|
|
|
/// Generate the printer for a custom directive.
|
2020-09-01 03:33:36 +08:00
|
|
|
static void genCustomDirectivePrinter(CustomDirective *customDir,
|
2021-11-12 09:17:05 +08:00
|
|
|
const Operator &op, MethodBody &body) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " print" << customDir->getName() << "(_odsPrinter, *this";
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *param : customDir->getArguments()) {
|
2020-09-01 03:33:36 +08:00
|
|
|
body << ", ";
|
2022-02-01 14:58:02 +08:00
|
|
|
genCustomDirectiveParameterPrinter(param, op, body);
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
body << ");\n";
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Generate the printer for a region with the given variable name.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genRegionPrinter(const Twine ®ionName, MethodBody &body,
|
2020-09-01 03:33:55 +08:00
|
|
|
bool hasImplicitTermTrait) {
|
|
|
|
if (hasImplicitTermTrait)
|
|
|
|
body << llvm::formatv(regionSingleBlockImplicitTerminatorPrinterCode,
|
|
|
|
regionName);
|
|
|
|
else
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printRegion(" << regionName << ");\n";
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
static void genVariadicRegionPrinter(const Twine ®ionListName,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body,
|
2020-09-01 03:33:55 +08:00
|
|
|
bool hasImplicitTermTrait) {
|
|
|
|
body << " llvm::interleaveComma(" << regionListName
|
2021-11-04 06:34:13 +08:00
|
|
|
<< ", _odsPrinter, [&](::mlir::Region ®ion) {\n ";
|
2020-09-01 03:33:55 +08:00
|
|
|
genRegionPrinter("region", body, hasImplicitTermTrait);
|
|
|
|
body << " });\n";
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Generate the C++ for an operand to a (*-)type directive.
|
2022-02-01 14:58:02 +08:00
|
|
|
static MethodBody &genTypeOperandPrinter(FormatElement *arg, const Operator &op,
|
2021-12-08 09:24:51 +08:00
|
|
|
MethodBody &body,
|
|
|
|
bool useArrayRef = true) {
|
2020-01-31 03:31:21 +08:00
|
|
|
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();
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (var->isVariadicOfVariadic())
|
2021-10-20 22:08:36 +08:00
|
|
|
return body << llvm::formatv("{0}().join().getTypes()",
|
|
|
|
op.getGetterName(var->name));
|
2020-01-31 03:31:21 +08:00
|
|
|
if (var->isVariadic())
|
2021-10-20 22:08:36 +08:00
|
|
|
return body << op.getGetterName(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>())",
|
2021-10-20 22:08:36 +08:00
|
|
|
op.getGetterName(var->name));
|
2021-12-08 09:24:51 +08:00
|
|
|
if (useArrayRef)
|
|
|
|
return body << "::llvm::ArrayRef<::mlir::Type>("
|
|
|
|
<< op.getGetterName(var->name) << "().getType())";
|
|
|
|
return body << op.getGetterName(var->name) << "().getType()";
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
2021-01-15 03:35:15 +08:00
|
|
|
/// Generate the printer for an enum attribute.
|
2021-10-20 22:08:36 +08:00
|
|
|
static void genEnumAttrPrinter(const NamedAttribute *var, const Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2021-01-15 03:35:15 +08:00
|
|
|
Attribute baseAttr = var->attr.getBaseAttr();
|
|
|
|
const EnumAttr &enumAttr = cast<EnumAttr>(baseAttr);
|
|
|
|
std::vector<EnumAttrCase> cases = enumAttr.getAllCases();
|
|
|
|
|
|
|
|
body << llvm::formatv(enumAttrBeginPrinterCode,
|
2021-10-20 22:08:36 +08:00
|
|
|
(var->attr.isOptional() ? "*" : "") +
|
|
|
|
op.getGetterName(var->name),
|
2021-01-15 03:35:15 +08:00
|
|
|
enumAttr.getSymbolToStringFnName());
|
|
|
|
|
|
|
|
// Get a string containing all of the cases that can't be represented with a
|
|
|
|
// keyword.
|
2022-01-27 07:30:56 +08:00
|
|
|
BitVector nonKeywordCases(cases.size());
|
2021-01-15 03:35:15 +08:00
|
|
|
bool hasStrCase = false;
|
2021-12-10 23:04:46 +08:00
|
|
|
for (auto &it : llvm::enumerate(cases)) {
|
2021-01-15 03:35:15 +08:00
|
|
|
hasStrCase = it.value().isStrCase();
|
|
|
|
if (!canFormatStringAsKeyword(it.value().getStr()))
|
|
|
|
nonKeywordCases.set(it.index());
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this is a string enum, use the case string to determine which cases
|
|
|
|
// need to use the string form.
|
|
|
|
if (hasStrCase) {
|
|
|
|
if (nonKeywordCases.any()) {
|
|
|
|
body << " if (llvm::is_contained(llvm::ArrayRef<llvm::StringRef>(";
|
|
|
|
llvm::interleaveComma(nonKeywordCases.set_bits(), body, [&](unsigned it) {
|
|
|
|
body << '"' << cases[it].getStr() << '"';
|
|
|
|
});
|
|
|
|
body << ")))\n"
|
2021-11-04 06:34:13 +08:00
|
|
|
" _odsPrinter << '\"' << caseValueStr << '\"';\n"
|
2021-01-15 03:35:15 +08:00
|
|
|
" else\n ";
|
|
|
|
}
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << caseValueStr;\n"
|
2021-01-15 03:35:15 +08:00
|
|
|
" }\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise if this is a bit enum attribute, don't allow cases that may
|
|
|
|
// overlap with other cases. For simplicity sake, only allow cases with a
|
|
|
|
// single bit value.
|
|
|
|
if (enumAttr.isBitEnum()) {
|
2021-12-10 23:04:46 +08:00
|
|
|
for (auto &it : llvm::enumerate(cases)) {
|
2021-01-15 03:35:15 +08:00
|
|
|
int64_t value = it.value().getValue();
|
|
|
|
if (value < 0 || !llvm::isPowerOf2_64(value))
|
|
|
|
nonKeywordCases.set(it.index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there are any cases that can't be used with a keyword, switch on the
|
|
|
|
// case value to determine when to print in the string form.
|
|
|
|
if (nonKeywordCases.any()) {
|
|
|
|
body << " switch (caseValue) {\n";
|
|
|
|
StringRef cppNamespace = enumAttr.getCppNamespace();
|
|
|
|
StringRef enumName = enumAttr.getEnumClassName();
|
2021-12-10 23:04:46 +08:00
|
|
|
for (auto &it : llvm::enumerate(cases)) {
|
2021-01-15 03:35:15 +08:00
|
|
|
if (nonKeywordCases.test(it.index()))
|
|
|
|
continue;
|
|
|
|
StringRef symbol = it.value().getSymbol();
|
|
|
|
body << llvm::formatv(" case {0}::{1}::{2}:\n", cppNamespace, enumName,
|
|
|
|
llvm::isDigit(symbol.front()) ? ("_" + symbol)
|
|
|
|
: symbol);
|
|
|
|
}
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << caseValueStr;\n"
|
2021-01-15 03:35:15 +08:00
|
|
|
" break;\n"
|
|
|
|
" default:\n"
|
2021-11-04 06:34:13 +08:00
|
|
|
" _odsPrinter << '\"' << caseValueStr << '\"';\n"
|
2021-01-15 03:35:15 +08:00
|
|
|
" break;\n"
|
|
|
|
" }\n"
|
|
|
|
" }\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << caseValueStr;\n"
|
2021-01-15 03:35:15 +08:00
|
|
|
" }\n";
|
|
|
|
}
|
|
|
|
|
2021-01-23 04:07:07 +08:00
|
|
|
/// Generate the check for the anchor of an optional group.
|
2022-02-01 14:58:02 +08:00
|
|
|
static void genOptionalGroupPrinterAnchor(FormatElement *anchor,
|
|
|
|
const Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2022-02-01 14:58:02 +08:00
|
|
|
TypeSwitch<FormatElement *>(anchor)
|
2021-01-23 04:07:07 +08:00
|
|
|
.Case<OperandVariable, ResultVariable>([&](auto *element) {
|
|
|
|
const NamedTypeConstraint *var = element->getVar();
|
2021-10-20 22:08:36 +08:00
|
|
|
std::string name = op.getGetterName(var->name);
|
2021-01-23 04:07:07 +08:00
|
|
|
if (var->isOptional())
|
2021-10-20 22:08:36 +08:00
|
|
|
body << " if (" << name << "()) {\n";
|
2021-01-23 04:07:07 +08:00
|
|
|
else if (var->isVariadic())
|
2021-10-20 22:08:36 +08:00
|
|
|
body << " if (!" << name << "().empty()) {\n";
|
2021-01-23 04:07:07 +08:00
|
|
|
})
|
|
|
|
.Case<RegionVariable>([&](RegionVariable *element) {
|
|
|
|
const NamedRegion *var = element->getVar();
|
2021-10-20 22:08:36 +08:00
|
|
|
std::string name = op.getGetterName(var->name);
|
2021-01-23 04:07:07 +08:00
|
|
|
// TODO: Add a check for optional regions here when ODS supports it.
|
2021-10-20 22:08:36 +08:00
|
|
|
body << " if (!" << name << "().empty()) {\n";
|
2021-01-23 04:07:07 +08:00
|
|
|
})
|
|
|
|
.Case<TypeDirective>([&](TypeDirective *element) {
|
2022-02-01 14:58:02 +08:00
|
|
|
genOptionalGroupPrinterAnchor(element->getArg(), op, body);
|
2021-01-23 04:07:07 +08:00
|
|
|
})
|
|
|
|
.Case<FunctionalTypeDirective>([&](FunctionalTypeDirective *element) {
|
2021-10-20 22:08:36 +08:00
|
|
|
genOptionalGroupPrinterAnchor(element->getInputs(), op, body);
|
2021-01-23 04:07:07 +08:00
|
|
|
})
|
|
|
|
.Case<AttributeVariable>([&](AttributeVariable *attr) {
|
|
|
|
body << " if ((*this)->getAttr(\"" << attr->getVar()->name
|
|
|
|
<< "\")) {\n";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-22 12:38:33 +08:00
|
|
|
void collect(FormatElement *element,
|
|
|
|
SmallVectorImpl<VariableElement *> &variables) {
|
|
|
|
TypeSwitch<FormatElement *>(element)
|
|
|
|
.Case([&](VariableElement *var) { variables.emplace_back(var); })
|
|
|
|
.Case([&](CustomDirective *ele) {
|
|
|
|
for (FormatElement *arg : ele->getArguments())
|
|
|
|
collect(arg, variables);
|
|
|
|
})
|
|
|
|
.Case([&](OptionalElement *ele) {
|
|
|
|
for (FormatElement *arg : ele->getThenElements())
|
|
|
|
collect(arg, variables);
|
|
|
|
for (FormatElement *arg : ele->getElseElements())
|
|
|
|
collect(arg, variables);
|
|
|
|
})
|
|
|
|
.Case([&](FunctionalTypeDirective *funcType) {
|
|
|
|
collect(funcType->getInputs(), variables);
|
|
|
|
collect(funcType->getResults(), variables);
|
|
|
|
})
|
|
|
|
.Case([&](OIListElement *oilist) {
|
|
|
|
for (ArrayRef<FormatElement *> arg : oilist->getParsingElements())
|
|
|
|
for (FormatElement *arg_ : arg)
|
|
|
|
collect(arg_, variables);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
void OperationFormat::genElementPrinter(FormatElement *element,
|
|
|
|
MethodBody &body, Operator &op,
|
|
|
|
bool &shouldEmitSpace,
|
2020-09-01 03:33:55 +08:00
|
|
|
bool &lastWasPunctuation) {
|
2020-02-22 05:19:15 +08:00
|
|
|
if (LiteralElement *literal = dyn_cast<LiteralElement>(element))
|
2022-02-01 14:58:02 +08:00
|
|
|
return genLiteralPrinter(literal->getSpelling(), body, shouldEmitSpace,
|
2020-02-22 05:19:15 +08:00
|
|
|
lastWasPunctuation);
|
|
|
|
|
2020-12-15 03:53:34 +08:00
|
|
|
// Emit a whitespace element.
|
2022-02-01 14:58:02 +08:00
|
|
|
if (auto *space = dyn_cast<WhitespaceElement>(element)) {
|
|
|
|
if (space->getValue() == "\\n") {
|
|
|
|
body << " _odsPrinter.printNewline();\n";
|
|
|
|
} else {
|
|
|
|
genSpacePrinter(!space->getValue().empty(), body, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
|
|
|
}
|
2020-12-15 03:53:34 +08:00
|
|
|
return;
|
|
|
|
}
|
2020-10-18 13:40:42 +08:00
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Emit an optional group.
|
|
|
|
if (OptionalElement *optional = dyn_cast<OptionalElement>(element)) {
|
|
|
|
// Emit the check for the presence of the anchor element.
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *anchor = optional->getAnchor();
|
2021-10-20 22:08:36 +08:00
|
|
|
genOptionalGroupPrinterAnchor(anchor, op, body);
|
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.
|
2021-03-23 09:07:09 +08:00
|
|
|
auto elements = optional->getThenElements();
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *elidedAnchorElement = nullptr;
|
2020-08-04 05:20:50 +08:00
|
|
|
auto *anchorAttr = dyn_cast<AttributeVariable>(anchor);
|
2022-02-01 14:58:02 +08:00
|
|
|
if (anchorAttr && anchorAttr != elements.front() &&
|
2020-08-04 05:20:50 +08:00
|
|
|
anchorAttr->isUnitAttr()) {
|
|
|
|
elidedAnchorElement = anchorAttr;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Emit each of the elements.
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *childElement : elements) {
|
|
|
|
if (childElement != elidedAnchorElement) {
|
|
|
|
genElementPrinter(childElement, body, op, shouldEmitSpace,
|
2020-08-04 05:20:50 +08:00
|
|
|
lastWasPunctuation);
|
|
|
|
}
|
|
|
|
}
|
2021-03-23 09:07:09 +08:00
|
|
|
body << " }";
|
|
|
|
|
|
|
|
// Emit each of the else elements.
|
|
|
|
auto elseElements = optional->getElseElements();
|
|
|
|
if (!elseElements.empty()) {
|
|
|
|
body << " else {\n";
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *childElement : elseElements) {
|
|
|
|
genElementPrinter(childElement, body, op, shouldEmitSpace,
|
2021-03-23 09:07:09 +08:00
|
|
|
lastWasPunctuation);
|
|
|
|
}
|
|
|
|
body << " }";
|
|
|
|
}
|
|
|
|
|
|
|
|
body << "\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
// Emit the OIList
|
|
|
|
if (auto *oilist = dyn_cast<OIListElement>(element)) {
|
|
|
|
genLiteralPrinter(" ", body, shouldEmitSpace, lastWasPunctuation);
|
|
|
|
for (auto clause : oilist->getClauses()) {
|
|
|
|
LiteralElement *lelement = std::get<0>(clause);
|
|
|
|
ArrayRef<FormatElement *> pelement = std::get<1>(clause);
|
|
|
|
|
2022-03-22 12:38:33 +08:00
|
|
|
SmallVector<VariableElement *> vars;
|
|
|
|
for (FormatElement *el : pelement)
|
|
|
|
collect(el, vars);
|
|
|
|
body << " if (false";
|
|
|
|
for (VariableElement *var : vars) {
|
|
|
|
TypeSwitch<FormatElement *>(var)
|
|
|
|
.Case([&](AttributeVariable *attrEle) {
|
|
|
|
body << " || " << op.getGetterName(attrEle->getVar()->name)
|
|
|
|
<< "Attr()";
|
|
|
|
})
|
|
|
|
.Case([&](OperandVariable *ele) {
|
|
|
|
if (ele->getVar()->isVariadic()) {
|
|
|
|
body << " || " << op.getGetterName(ele->getVar()->name)
|
|
|
|
<< "().size()";
|
|
|
|
} else {
|
|
|
|
body << " || " << op.getGetterName(ele->getVar()->name) << "()";
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.Case([&](ResultVariable *ele) {
|
|
|
|
if (ele->getVar()->isVariadic()) {
|
|
|
|
body << " || " << op.getGetterName(ele->getVar()->name)
|
|
|
|
<< "().size()";
|
|
|
|
} else {
|
|
|
|
body << " || " << op.getGetterName(ele->getVar()->name) << "()";
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.Case([&](RegionVariable *reg) {
|
|
|
|
body << " || " << op.getGetterName(reg->getVar()->name) << "()";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
body << ") {\n";
|
2022-02-17 12:54:10 +08:00
|
|
|
genLiteralPrinter(lelement->getSpelling(), body, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
2022-03-22 12:38:33 +08:00
|
|
|
if (oilist->getUnitAttrParsingElement(pelement) == nullptr) {
|
|
|
|
for (FormatElement *element : pelement)
|
|
|
|
genElementPrinter(element, body, op, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
2022-02-17 12:54:10 +08:00
|
|
|
}
|
|
|
|
body << " }\n";
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Emit the attribute dictionary.
|
2020-02-22 05:19:26 +08:00
|
|
|
if (auto *attrDict = dyn_cast<AttrDictDirective>(element)) {
|
2020-09-01 03:33:55 +08:00
|
|
|
genAttrDictPrinter(*this, 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)
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << ' ';\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
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.
|
2021-01-15 03:35:15 +08:00
|
|
|
if (canFormatEnumAttr(var))
|
2021-10-20 22:08:36 +08:00
|
|
|
return genEnumAttrPrinter(var, op, body);
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-09-01 03:33:46 +08:00
|
|
|
// If we are formatting as a symbol name, handle it as a symbol name.
|
|
|
|
if (shouldFormatSymbolNameAttr(var)) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printSymbolName(" << op.getGetterName(var->name)
|
2021-10-20 22:08:36 +08:00
|
|
|
<< "Attr().getValue());\n";
|
2020-09-01 03:33:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Elide the attribute type if it is buildable.
|
2020-04-04 10:20:33 +08:00
|
|
|
if (attr->getTypeBuilder())
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printAttributeWithoutType("
|
|
|
|
<< op.getGetterName(var->name) << "Attr());\n";
|
2022-01-11 09:26:44 +08:00
|
|
|
else if (attr->shouldBeQualified() ||
|
|
|
|
var->attr.getStorageType() == "::mlir::Attribute")
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printAttribute(" << op.getGetterName(var->name)
|
2021-10-20 22:08:36 +08:00
|
|
|
<< "Attr());\n";
|
2021-12-08 09:24:51 +08:00
|
|
|
else
|
|
|
|
body << "_odsPrinter.printStrippedAttrOrType("
|
|
|
|
<< op.getGetterName(var->name) << "Attr());\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(element)) {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (operand->getVar()->isVariadicOfVariadic()) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << " ::llvm::interleaveComma("
|
|
|
|
<< op.getGetterName(operand->getVar()->name)
|
2021-11-04 06:34:13 +08:00
|
|
|
<< "(), _odsPrinter, [&](const auto &operands) { _odsPrinter << "
|
|
|
|
"\"(\" << operands << "
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
"\")\"; });\n";
|
|
|
|
|
|
|
|
} else if (operand->getVar()->isOptional()) {
|
2021-10-20 22:08:36 +08:00
|
|
|
body << " if (::mlir::Value value = "
|
|
|
|
<< op.getGetterName(operand->getVar()->name) << "())\n"
|
2021-11-04 06:34:13 +08:00
|
|
|
<< " _odsPrinter << value;\n";
|
2020-04-11 05:11:45 +08:00
|
|
|
} else {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << " << op.getGetterName(operand->getVar()->name)
|
|
|
|
<< "();\n";
|
2020-04-11 05:11:45 +08:00
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(element)) {
|
|
|
|
const NamedRegion *var = region->getVar();
|
2021-10-20 22:08:36 +08:00
|
|
|
std::string name = op.getGetterName(var->name);
|
2020-09-01 03:33:55 +08:00
|
|
|
if (var->isVariadic()) {
|
2021-10-20 22:08:36 +08:00
|
|
|
genVariadicRegionPrinter(name + "()", body, hasImplicitTermTrait);
|
2020-09-01 03:33:55 +08:00
|
|
|
} else {
|
2021-10-20 22:08:36 +08:00
|
|
|
genRegionPrinter(name + "()", body, hasImplicitTermTrait);
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(element)) {
|
|
|
|
const NamedSuccessor *var = successor->getVar();
|
2021-10-20 22:08:36 +08:00
|
|
|
std::string name = op.getGetterName(var->name);
|
2020-03-06 04:48:28 +08:00
|
|
|
if (var->isVariadic())
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " ::llvm::interleaveComma(" << name << "(), _odsPrinter);\n";
|
2020-03-06 04:48:28 +08:00
|
|
|
else
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << " << name << "();\n";
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *dir = dyn_cast<CustomDirective>(element)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
genCustomDirectivePrinter(dir, op, body);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << getOperation()->getOperands();\n";
|
2020-09-01 03:33:55 +08:00
|
|
|
} else if (isa<RegionsDirective>(element)) {
|
|
|
|
genVariadicRegionPrinter("getOperation()->getRegions()", body,
|
|
|
|
hasImplicitTermTrait);
|
2020-02-22 05:20:06 +08:00
|
|
|
} else if (isa<SuccessorsDirective>(element)) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " ::llvm::interleaveComma(getOperation()->getSuccessors(), "
|
|
|
|
"_odsPrinter);\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(dir->getArg())) {
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
if (operand->getVar()->isVariadicOfVariadic()) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << llvm::formatv(
|
|
|
|
" ::llvm::interleaveComma({0}().getTypes(), _odsPrinter, "
|
|
|
|
"[&](::mlir::TypeRange types) {{ _odsPrinter << \"(\" << "
|
|
|
|
"types << \")\"; });\n",
|
|
|
|
op.getGetterName(operand->getVar()->name));
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-12-08 09:24:51 +08:00
|
|
|
const NamedTypeConstraint *var = nullptr;
|
|
|
|
{
|
2022-02-01 14:58:02 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(dir->getArg()))
|
2021-12-08 09:24:51 +08:00
|
|
|
var = operand->getVar();
|
2022-02-01 14:58:02 +08:00
|
|
|
else if (auto *operand = dyn_cast<ResultVariable>(dir->getArg()))
|
2021-12-08 09:24:51 +08:00
|
|
|
var = operand->getVar();
|
|
|
|
}
|
|
|
|
if (var && !var->isVariadicOfVariadic() && !var->isVariadic() &&
|
|
|
|
!var->isOptional()) {
|
|
|
|
std::string cppClass = var->constraint.getCPPClassName();
|
2022-01-11 09:26:44 +08:00
|
|
|
if (dir->shouldBeQualified()) {
|
|
|
|
body << " _odsPrinter << " << op.getGetterName(var->name)
|
|
|
|
<< "().getType();\n";
|
|
|
|
return;
|
|
|
|
}
|
2021-12-08 09:24:51 +08:00
|
|
|
body << " {\n"
|
|
|
|
<< " auto type = " << op.getGetterName(var->name)
|
|
|
|
<< "().getType();\n"
|
|
|
|
<< " if (auto validType = type.dyn_cast<" << cppClass << ">())\n"
|
|
|
|
<< " _odsPrinter.printStrippedAttrOrType(validType);\n"
|
|
|
|
<< " else\n"
|
|
|
|
<< " _odsPrinter << type;\n"
|
|
|
|
<< " }\n";
|
|
|
|
return;
|
|
|
|
}
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter << ";
|
2022-02-01 14:58:02 +08:00
|
|
|
genTypeOperandPrinter(dir->getArg(), op, body, /*useArrayRef=*/false)
|
2021-12-08 09:24:51 +08:00
|
|
|
<< ";\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printFunctionalType(";
|
2021-10-20 22:08:36 +08:00
|
|
|
genTypeOperandPrinter(dir->getInputs(), op, body) << ", ";
|
|
|
|
genTypeOperandPrinter(dir->getResults(), op, body) << ");\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
} else {
|
|
|
|
llvm_unreachable("unknown format element");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:31:21 +08:00
|
|
|
void OperationFormat::genPrinter(Operator &op, OpClass &opClass) {
|
2021-11-12 09:17:05 +08:00
|
|
|
auto *method = opClass.addMethod(
|
|
|
|
"void", "print",
|
|
|
|
MethodParameter("::mlir::OpAsmPrinter &", "_odsPrinter"));
|
2020-09-18 04:18:09 +08:00
|
|
|
auto &body = method->body();
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
// Flags for if we should emit a space, and if the last element was
|
|
|
|
// punctuation.
|
|
|
|
bool shouldEmitSpace = true, lastWasPunctuation = false;
|
2022-02-01 14:58:02 +08:00
|
|
|
for (FormatElement *element : elements)
|
|
|
|
genElementPrinter(element, body, op, shouldEmitSpace, lastWasPunctuation);
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2022-02-01 14:58:02 +08:00
|
|
|
// OpFormatParser
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
/// Function to find an element within the given range that has the same name as
|
|
|
|
/// 'name'.
|
2021-11-12 09:17:05 +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.
|
2022-02-01 14:58:02 +08:00
|
|
|
class OpFormatParser : public FormatParser {
|
2020-01-31 03:30:23 +08:00
|
|
|
public:
|
2022-02-01 14:58:02 +08:00
|
|
|
OpFormatParser(llvm::SourceMgr &mgr, OperationFormat &format, Operator &op)
|
|
|
|
: FormatParser(mgr, op.getLoc()[0]), fmt(format), op(op),
|
|
|
|
seenOperandTypes(op.getNumOperands()),
|
2020-01-31 03:30:23 +08:00
|
|
|
seenResultTypes(op.getNumResults()) {}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
protected:
|
|
|
|
/// Verify the format elements.
|
|
|
|
LogicalResult verify(SMLoc loc, ArrayRef<FormatElement *> elements) override;
|
|
|
|
/// Verify the arguments to a custom directive.
|
|
|
|
LogicalResult
|
|
|
|
verifyCustomDirectiveArguments(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> arguments) override;
|
|
|
|
/// Verify the elements of an optional group.
|
|
|
|
LogicalResult
|
|
|
|
verifyOptionalGroupElements(SMLoc loc, ArrayRef<FormatElement *> elements,
|
|
|
|
Optional<unsigned> anchorIndex) override;
|
|
|
|
LogicalResult verifyOptionalGroupElement(SMLoc loc, FormatElement *element,
|
|
|
|
bool isAnchor);
|
|
|
|
|
|
|
|
/// Parse an operation variable.
|
|
|
|
FailureOr<FormatElement *> parseVariableImpl(SMLoc loc, StringRef name,
|
|
|
|
Context ctx) override;
|
|
|
|
/// Parse an operation format directive.
|
|
|
|
FailureOr<FormatElement *>
|
|
|
|
parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind, Context ctx) override;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
using ElementsItT = ArrayRef<FormatElement *>::iterator;
|
2020-04-08 06:51:42 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
/// Verify the state of operation attributes within the format.
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult verifyAttributes(SMLoc loc, ArrayRef<FormatElement *> elements);
|
2020-04-08 06:51:42 +08:00
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
|
|
|
LogicalResult verifyAttributes(
|
2022-01-27 07:49:53 +08:00
|
|
|
SMLoc loc,
|
2022-02-01 14:58:02 +08:00
|
|
|
SmallVectorImpl<std::pair<ElementsItT, ElementsItT>> &iteratorStack);
|
2020-04-04 10:20:33 +08:00
|
|
|
|
|
|
|
/// Verify the state of operation operands within the format.
|
|
|
|
LogicalResult
|
2022-01-27 07:49:53 +08:00
|
|
|
verifyOperands(SMLoc loc,
|
2020-04-04 10:20:33 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Verify the state of operation regions within the format.
|
2022-01-27 07:49:53 +08:00
|
|
|
LogicalResult verifyRegions(SMLoc loc);
|
2020-09-01 03:33:55 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
/// Verify the state of operation results within the format.
|
|
|
|
LogicalResult
|
2022-01-27 07:49:53 +08:00
|
|
|
verifyResults(SMLoc loc,
|
2020-04-04 10:20:33 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
|
|
|
|
|
|
|
/// Verify the state of operation successors within the format.
|
2022-01-27 07:49:53 +08:00
|
|
|
LogicalResult verifySuccessors(SMLoc loc);
|
2020-04-04 10:20:33 +08:00
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
LogicalResult verifyOIListElements(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> elements);
|
|
|
|
|
[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,
|
2022-01-02 09:26:44 +08:00
|
|
|
const 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 the various different directives.
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> parseAttrDictDirective(SMLoc loc, Context context,
|
|
|
|
bool withKeyword);
|
|
|
|
FailureOr<FormatElement *> parseFunctionalTypeDirective(SMLoc loc,
|
|
|
|
Context context);
|
2022-02-17 12:54:10 +08:00
|
|
|
FailureOr<FormatElement *> parseOIListDirective(SMLoc loc, Context context);
|
|
|
|
LogicalResult verifyOIListParsingElement(FormatElement *element, SMLoc loc);
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> parseOperandsDirective(SMLoc loc, Context context);
|
|
|
|
FailureOr<FormatElement *> parseQualifiedDirective(SMLoc loc,
|
|
|
|
Context context);
|
|
|
|
FailureOr<FormatElement *> parseReferenceDirective(SMLoc loc,
|
|
|
|
Context context);
|
|
|
|
FailureOr<FormatElement *> parseRegionsDirective(SMLoc loc, Context context);
|
|
|
|
FailureOr<FormatElement *> parseResultsDirective(SMLoc loc, Context context);
|
|
|
|
FailureOr<FormatElement *> parseSuccessorsDirective(SMLoc loc,
|
|
|
|
Context context);
|
|
|
|
FailureOr<FormatElement *> parseTypeDirective(SMLoc loc, Context context);
|
|
|
|
FailureOr<FormatElement *> parseTypeDirectiveOperand(SMLoc loc,
|
|
|
|
bool isRefChild = false);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Fields
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
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-09-01 03:33:55 +08:00
|
|
|
bool hasAttrDict = false;
|
|
|
|
bool hasAllRegions = false, hasAllSuccessors = false;
|
2021-10-07 08:50:38 +08:00
|
|
|
bool canInferResultTypes = false;
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::SmallBitVector seenOperandTypes, seenResultTypes;
|
2020-09-01 03:33:55 +08:00
|
|
|
llvm::SmallSetVector<const NamedAttribute *, 8> seenAttrs;
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::DenseSet<const NamedTypeConstraint *> seenOperands;
|
2020-09-01 03:33:55 +08:00
|
|
|
llvm::DenseSet<const NamedRegion *> seenRegions;
|
2020-02-22 05:20:06 +08:00
|
|
|
llvm::DenseSet<const NamedSuccessor *> seenSuccessors;
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verify(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> elements) {
|
2020-01-31 03:30:23 +08:00
|
|
|
// 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;
|
2021-04-16 02:29:23 +08:00
|
|
|
for (const Trait &trait : op.getTraits()) {
|
2020-02-04 13:52:38 +08:00
|
|
|
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);
|
2022-01-05 00:28:59 +08:00
|
|
|
} else if (!op.allResultTypesKnown()) {
|
|
|
|
// This doesn't check the name directly to handle
|
|
|
|
// DeclareOpInterfaceMethods<InferTypeOpInterface>
|
|
|
|
// and the like.
|
|
|
|
// TODO: Add hasCppInterface check.
|
|
|
|
if (auto name = def.getValueAsOptionalString("cppClassName")) {
|
|
|
|
if (*name == "InferTypeOpInterface" &&
|
|
|
|
def.getValueAsString("cppNamespace") == "::mlir")
|
|
|
|
canInferResultTypes = true;
|
|
|
|
}
|
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.
|
2022-02-01 14:58:02 +08:00
|
|
|
if (failed(verifyAttributes(loc, elements)) ||
|
2020-04-04 10:20:33 +08:00
|
|
|
failed(verifyResults(loc, variableTyResolver)) ||
|
|
|
|
failed(verifyOperands(loc, variableTyResolver)) ||
|
2022-02-17 12:54:10 +08:00
|
|
|
failed(verifyRegions(loc)) || failed(verifySuccessors(loc)) ||
|
|
|
|
failed(verifyOIListElements(loc, elements)))
|
2022-02-01 14:58:02 +08:00
|
|
|
return failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
// Collect the set of used attributes in the format.
|
|
|
|
fmt.usedAttributes = seenAttrs.takeVector();
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult
|
|
|
|
OpFormatParser::verifyAttributes(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> elements) {
|
2020-04-04 10:20:33 +08:00
|
|
|
// 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.
|
2022-02-01 14:58:02 +08:00
|
|
|
SmallVector<std::pair<ElementsItT, ElementsItT>, 1> iteratorStack;
|
|
|
|
iteratorStack.emplace_back(elements.begin(), elements.end());
|
2020-04-08 06:51:42 +08:00
|
|
|
while (!iteratorStack.empty())
|
|
|
|
if (failed(verifyAttributes(loc, iteratorStack)))
|
2022-02-01 14:58:02 +08:00
|
|
|
return ::failure();
|
[mlir] Add support for VariadicOfVariadic operands
This revision adds native ODS support for VariadicOfVariadic operand
groups. An example of this is the SwitchOp, which has a variadic number
of nested operand ranges for each of the case statements, where the
number of case statements is variadic. Builtin ODS support allows for
generating proper accessors for the nested operand ranges, builder
support, and declarative format support. VariadicOfVariadic operands
are supported by providing a segment attribute to use to store the
operand groups, mapping similarly to the AttrSizedOperand trait
(but with a user defined attribute name).
`build` methods for VariadicOfVariadic operand expect inputs of the
form `ArrayRef<ValueRange>`. Accessors for the variadic ranges
return a new `OperandRangeRange` type, which represents a
contiguous range of `OperandRange`. In the declarative assembly
format, VariadicOfVariadic operands and types are by default
formatted as a comma delimited list of value lists:
`(<value>, <value>), (), (<value>)`.
Differential Revision: https://reviews.llvm.org/D107774
2021-08-24 04:23:09 +08:00
|
|
|
|
|
|
|
// Check for VariadicOfVariadic variables. The segment attribute of those
|
|
|
|
// variables will be infered.
|
|
|
|
for (const NamedTypeConstraint *var : seenOperands) {
|
|
|
|
if (var->constraint.isVariadicOfVariadic()) {
|
|
|
|
fmt.inferredAttributes.insert(
|
|
|
|
var->constraint.getVariadicOfVariadicSegmentSizeAttr());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-08 06:51:42 +08:00
|
|
|
}
|
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifyAttributes(
|
2022-01-27 07:49:53 +08:00
|
|
|
SMLoc loc,
|
2022-02-01 14:58:02 +08:00
|
|
|
SmallVectorImpl<std::pair<ElementsItT, ElementsItT>> &iteratorStack) {
|
2020-04-08 06:51:42 +08:00
|
|
|
auto &stackIt = iteratorStack.back();
|
2022-02-01 14:58:02 +08:00
|
|
|
ElementsItT &it = stackIt.first, e = stackIt.second;
|
2020-04-08 06:51:42 +08:00
|
|
|
while (it != e) {
|
2022-02-01 14:58:02 +08:00
|
|
|
FormatElement *element = *(it++);
|
2020-04-08 06:51:42 +08:00
|
|
|
|
|
|
|
// Traverse into optional groups.
|
|
|
|
if (auto *optional = dyn_cast<OptionalElement>(element)) {
|
2021-03-23 09:07:09 +08:00
|
|
|
auto thenElements = optional->getThenElements();
|
|
|
|
iteratorStack.emplace_back(thenElements.begin(), thenElements.end());
|
|
|
|
|
|
|
|
auto elseElements = optional->getElseElements();
|
|
|
|
iteratorStack.emplace_back(elseElements.begin(), elseElements.end());
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-08 06:51:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2022-02-01 14:58:02 +08:00
|
|
|
ElementsItT nextIt = nextItPair.first, nextE = nextItPair.second;
|
2020-04-08 06:51:42 +08:00
|
|
|
for (; nextIt != nextE; ++nextIt) {
|
2020-12-15 03:53:34 +08:00
|
|
|
// Skip any trailing whitespace, attribute dictionaries, or optional
|
|
|
|
// groups.
|
|
|
|
if (isa<WhitespaceElement>(*nextIt) ||
|
|
|
|
isa<AttrDictDirective>(*nextIt) || isa<OptionalElement>(*nextIt))
|
2020-04-08 06:51:42 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// We are only interested in `:` literals.
|
2022-02-01 14:58:02 +08:00
|
|
|
auto *literal = dyn_cast<LiteralElement>(*nextIt);
|
|
|
|
if (!literal || literal->getSpelling() != ":")
|
2020-04-08 06:51:42 +08:00
|
|
|
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();
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifyOperands(
|
|
|
|
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.
|
2020-09-01 03:33:55 +08:00
|
|
|
if (!fmt.allOperands && !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-09-01 03:33:55 +08:00
|
|
|
if (!builder || (fmt.allOperands && 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
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifyRegions(SMLoc loc) {
|
2020-09-01 03:33:55 +08:00
|
|
|
// Check that all of the regions are within the format.
|
|
|
|
if (hasAllRegions)
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-09-01 03:33:55 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0, e = op.getNumRegions(); i != e; ++i) {
|
|
|
|
const NamedRegion ®ion = op.getRegion(i);
|
|
|
|
if (!seenRegions.count(®ion)) {
|
|
|
|
return emitErrorAndNote(loc,
|
|
|
|
"region #" + Twine(i) + ", named '" +
|
|
|
|
region.name + "', not found",
|
|
|
|
"suggest adding a '$" + region.name +
|
|
|
|
"' directive to the custom assembly format");
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifyResults(
|
|
|
|
SMLoc loc, llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
2020-04-04 10:20:33 +08:00
|
|
|
// If we format all of the types together, there is nothing to check.
|
|
|
|
if (fmt.allResultTypes)
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
|
2021-10-07 08:50:38 +08:00
|
|
|
// If no result types are specified and we can infer them, infer all result
|
|
|
|
// types
|
|
|
|
if (op.getNumResults() > 0 && seenResultTypes.count() == 0 &&
|
|
|
|
canInferResultTypes) {
|
|
|
|
fmt.infersResultTypes = true;
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2021-10-07 08:50:38 +08:00
|
|
|
}
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
// 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
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifySuccessors(SMLoc loc) {
|
2020-04-04 10:20:33 +08:00
|
|
|
// Check that all of the successors are within the format.
|
|
|
|
if (hasAllSuccessors)
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-04-04 10:20:33 +08:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
LogicalResult
|
|
|
|
OpFormatParser::verifyOIListElements(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> elements) {
|
|
|
|
// Check that all of the successors are within the format.
|
|
|
|
SmallVector<StringRef> prohibitedLiterals;
|
|
|
|
for (FormatElement *it : elements) {
|
|
|
|
if (auto *oilist = dyn_cast<OIListElement>(it)) {
|
|
|
|
if (!prohibitedLiterals.empty()) {
|
|
|
|
// We just saw an oilist element in last iteration. Literals should not
|
|
|
|
// match.
|
|
|
|
for (LiteralElement *literal : oilist->getLiteralElements()) {
|
|
|
|
if (find(prohibitedLiterals, literal->getSpelling()) !=
|
|
|
|
prohibitedLiterals.end()) {
|
|
|
|
return emitError(
|
|
|
|
loc, "format ambiguity because " + literal->getSpelling() +
|
|
|
|
" is used in two adjacent oilist elements.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (LiteralElement *literal : oilist->getLiteralElements())
|
|
|
|
prohibitedLiterals.push_back(literal->getSpelling());
|
|
|
|
} else if (auto *literal = dyn_cast<LiteralElement>(it)) {
|
|
|
|
if (find(prohibitedLiterals, literal->getSpelling()) !=
|
|
|
|
prohibitedLiterals.end()) {
|
|
|
|
return emitError(
|
|
|
|
loc,
|
|
|
|
"format ambiguity because " + literal->getSpelling() +
|
|
|
|
" is used both in oilist element and the adjacent literal.");
|
|
|
|
}
|
|
|
|
prohibitedLiterals.clear();
|
|
|
|
} else {
|
|
|
|
prohibitedLiterals.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
void OpFormatParser::handleAllTypesMatchConstraint(
|
2020-02-04 13:52:38 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
void OpFormatParser::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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
void OpFormatParser::handleTypesMatchConstraint(
|
2020-07-07 12:40:01 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
2022-01-02 09:26:44 +08:00
|
|
|
const llvm::Record &def) {
|
2020-07-07 12:40:01 +08:00
|
|
|
StringRef lhsName = def.getValueAsString("lhs");
|
|
|
|
StringRef rhsName = def.getValueAsString("rhs");
|
|
|
|
StringRef transformer = def.getValueAsString("transformer");
|
|
|
|
if (ConstArgument arg = findSeenArg(lhsName))
|
|
|
|
variableTyResolver[rhsName] = {arg, transformer};
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
ConstArgument OpFormatParser::findSeenArg(StringRef name) {
|
2020-07-07 12:40:01 +08:00
|
|
|
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))
|
2020-09-01 03:33:55 +08:00
|
|
|
return seenAttrs.count(attr) ? attr : nullptr;
|
2020-02-22 05:19:03 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseVariableImpl(SMLoc loc, StringRef name, Context ctx) {
|
|
|
|
// Check that the parsed argument is something actually registered on the op.
|
|
|
|
// Attributes
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx == TypeDirectiveContext)
|
2021-02-10 06:32:15 +08:00
|
|
|
return emitError(
|
|
|
|
loc, "attributes cannot be used as children to a `type` directive");
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx == RefDirectiveContext) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!seenAttrs.count(attr))
|
|
|
|
return emitError(loc, "attribute '" + name +
|
|
|
|
"' must be bound before it is referenced");
|
|
|
|
} else if (!seenAttrs.insert(attr)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "attribute '" + name + "' is already bound");
|
2021-02-10 06:32:15 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<AttributeVariable>(attr);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
// Operands
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {
|
2020-09-01 03:33:55 +08:00
|
|
|
if (fmt.allOperands || !seenOperands.insert(operand).second)
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "operand '" + name + "' is already bound");
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (ctx == RefDirectiveContext && !seenOperands.count(operand)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
return emitError(loc, "operand '" + name +
|
|
|
|
"' must be bound before it is referenced");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<OperandVariable>(operand);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
// Regions
|
2020-09-01 03:33:55 +08:00
|
|
|
if (const NamedRegion *region = findArg(op.getRegions(), name)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (hasAllRegions || !seenRegions.insert(region).second)
|
|
|
|
return emitError(loc, "region '" + name + "' is already bound");
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (ctx == RefDirectiveContext && !seenRegions.count(region)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
return emitError(loc, "region '" + name +
|
|
|
|
"' must be bound before it is referenced");
|
|
|
|
} else {
|
2020-09-01 03:33:55 +08:00
|
|
|
return emitError(loc, "regions can only be used at the top level");
|
2021-02-10 06:32:15 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<RegionVariable>(region);
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
// Results.
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const auto *result = findArg(op.getResults(), name)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx != TypeDirectiveContext)
|
2021-02-10 06:32:15 +08:00
|
|
|
return emitError(loc, "result variables can can only be used as a child "
|
|
|
|
"to a 'type' directive");
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<ResultVariable>(result);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
// Successors.
|
2020-02-22 05:20:06 +08:00
|
|
|
if (const auto *successor = findArg(op.getSuccessors(), name)) {
|
2022-02-01 14:58:02 +08:00
|
|
|
if (ctx == TopLevelContext || ctx == CustomDirectiveContext) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (hasAllSuccessors || !seenSuccessors.insert(successor).second)
|
|
|
|
return emitError(loc, "successor '" + name + "' is already bound");
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (ctx == RefDirectiveContext && !seenSuccessors.count(successor)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
return emitError(loc, "successor '" + name +
|
|
|
|
"' must be bound before it is referenced");
|
|
|
|
} else {
|
2020-02-22 05:20:06 +08:00
|
|
|
return emitError(loc, "successors can only be used at the top level");
|
2021-02-10 06:32:15 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<SuccessorVariable>(successor);
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
return emitError(loc, "expected variable to refer to an argument, region, "
|
|
|
|
"result, or successor");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseDirectiveImpl(SMLoc loc, FormatToken::Kind kind,
|
|
|
|
Context ctx) {
|
|
|
|
switch (kind) {
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_attr_dict:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseAttrDictDirective(loc, ctx,
|
2020-02-22 05:19:26 +08:00
|
|
|
/*withKeyword=*/false);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_attr_dict_w_keyword:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseAttrDictDirective(loc, ctx,
|
2020-02-22 05:19:26 +08:00
|
|
|
/*withKeyword=*/true);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_functional_type:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseFunctionalTypeDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_operands:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseOperandsDirective(loc, ctx);
|
2022-01-11 09:26:44 +08:00
|
|
|
case FormatToken::kw_qualified:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseQualifiedDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_regions:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseRegionsDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_results:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseResultsDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_successors:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseSuccessorsDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_ref:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseReferenceDirective(loc, ctx);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_type:
|
2022-02-01 14:58:02 +08:00
|
|
|
return parseTypeDirective(loc, ctx);
|
2022-02-17 12:54:10 +08:00
|
|
|
case FormatToken::kw_oilist:
|
|
|
|
return parseOIListDirective(loc, ctx);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
default:
|
2022-02-01 14:58:02 +08:00
|
|
|
return emitError(loc, "unsupported directive kind");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseAttrDictDirective(SMLoc loc, Context context,
|
|
|
|
bool withKeyword) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TypeDirectiveContext)
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "'attr-dict' directive can only be used as a "
|
|
|
|
"top-level directive");
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == RefDirectiveContext) {
|
|
|
|
if (!hasAttrDict)
|
|
|
|
return emitError(loc, "'ref' of 'attr-dict' is not bound by a prior "
|
|
|
|
"'attr-dict' directive");
|
|
|
|
|
|
|
|
// Otherwise, this is a top-level context.
|
|
|
|
} else {
|
|
|
|
if (hasAttrDict)
|
|
|
|
return emitError(loc, "'attr-dict' directive has already been seen");
|
|
|
|
hasAttrDict = true;
|
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<AttrDictDirective>(withKeyword);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
LogicalResult OpFormatParser::verifyCustomDirectiveArguments(
|
|
|
|
SMLoc loc, ArrayRef<FormatElement *> arguments) {
|
|
|
|
for (FormatElement *argument : arguments) {
|
|
|
|
if (!isa<RefDirective, TypeDirective, AttrDictDirective, AttributeVariable,
|
|
|
|
OperandVariable, RegionVariable, SuccessorVariable>(argument)) {
|
|
|
|
// TODO: FormatElement should have location info attached.
|
|
|
|
return emitError(loc, "only variables and types may be used as "
|
|
|
|
"parameters to a custom directive");
|
|
|
|
}
|
|
|
|
if (auto *type = dyn_cast<TypeDirective>(argument)) {
|
|
|
|
if (!isa<OperandVariable, ResultVariable>(type->getArg())) {
|
|
|
|
return emitError(loc, "type directives within a custom directive may "
|
|
|
|
"only refer to variables");
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return success();
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseFunctionalTypeDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TopLevelContext)
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(
|
|
|
|
loc, "'functional-type' is only valid as a top-level directive");
|
|
|
|
|
|
|
|
// Parse the main operand.
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> inputs, results;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2022-02-01 14:58:02 +08:00
|
|
|
failed(inputs = parseTypeDirectiveOperand(loc)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(parseToken(FormatToken::comma,
|
|
|
|
"expected ',' after inputs argument")) ||
|
2022-02-01 14:58:02 +08:00
|
|
|
failed(results = parseTypeDirectiveOperand(loc)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2022-02-01 14:58:02 +08:00
|
|
|
return failure();
|
|
|
|
return create<FunctionalTypeDirective>(*inputs, *results);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseOperandsDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == RefDirectiveContext) {
|
|
|
|
if (!fmt.allOperands)
|
|
|
|
return emitError(loc, "'ref' of 'operands' is not bound by a prior "
|
|
|
|
"'operands' directive");
|
|
|
|
|
|
|
|
} else if (context == TopLevelContext || context == CustomDirectiveContext) {
|
2020-09-01 03:33:55 +08:00
|
|
|
if (fmt.allOperands || !seenOperands.empty())
|
|
|
|
return emitError(loc, "'operands' directive creates overlap in format");
|
|
|
|
fmt.allOperands = true;
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<OperandsDirective>();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseReferenceDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != CustomDirectiveContext)
|
|
|
|
return emitError(loc, "'ref' is only valid within a `custom` directive");
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> arg;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2022-02-01 14:58:02 +08:00
|
|
|
failed(arg = parseElement(RefDirectiveContext)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2022-02-01 14:58:02 +08:00
|
|
|
return failure();
|
2021-02-10 06:32:15 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<RefDirective>(*arg);
|
2021-02-10 06:32:15 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseRegionsDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TypeDirectiveContext)
|
2020-09-01 03:33:55 +08:00
|
|
|
return emitError(loc, "'regions' is only valid as a top-level directive");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == RefDirectiveContext) {
|
|
|
|
if (!hasAllRegions)
|
|
|
|
return emitError(loc, "'ref' of 'regions' is not bound by a prior "
|
|
|
|
"'regions' directive");
|
|
|
|
|
|
|
|
// Otherwise, this is a TopLevel directive.
|
|
|
|
} else {
|
|
|
|
if (hasAllRegions || !seenRegions.empty())
|
|
|
|
return emitError(loc, "'regions' directive creates overlap in format");
|
|
|
|
hasAllRegions = true;
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<RegionsDirective>();
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseResultsDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TypeDirectiveContext)
|
|
|
|
return emitError(loc, "'results' directive can can only be used as a child "
|
|
|
|
"to a 'type' directive");
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<ResultsDirective>();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseSuccessorsDirective(SMLoc loc, Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TypeDirectiveContext)
|
2020-02-22 05:20:06 +08:00
|
|
|
return emitError(loc,
|
|
|
|
"'successors' is only valid as a top-level directive");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == RefDirectiveContext) {
|
|
|
|
if (!hasAllSuccessors)
|
|
|
|
return emitError(loc, "'ref' of 'successors' is not bound by a prior "
|
|
|
|
"'successors' directive");
|
|
|
|
|
|
|
|
// Otherwise, this is a TopLevel directive.
|
|
|
|
} else {
|
|
|
|
if (hasAllSuccessors || !seenSuccessors.empty())
|
|
|
|
return emitError(loc, "'successors' directive creates overlap in format");
|
|
|
|
hasAllSuccessors = true;
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<SuccessorsDirective>();
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
|
2022-02-17 12:54:10 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseOIListDirective(SMLoc loc, Context context) {
|
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before oilist argument list")))
|
|
|
|
return failure();
|
|
|
|
std::vector<FormatElement *> literalElements;
|
|
|
|
std::vector<std::vector<FormatElement *>> parsingElements;
|
|
|
|
do {
|
|
|
|
FailureOr<FormatElement *> lelement = parseLiteral(context);
|
|
|
|
if (failed(lelement))
|
|
|
|
return failure();
|
|
|
|
literalElements.push_back(*lelement);
|
2022-03-07 18:12:44 +08:00
|
|
|
parsingElements.emplace_back();
|
2022-02-17 12:54:10 +08:00
|
|
|
std::vector<FormatElement *> &currParsingElements = parsingElements.back();
|
|
|
|
while (peekToken().getKind() != FormatToken::pipe &&
|
|
|
|
peekToken().getKind() != FormatToken::r_paren) {
|
|
|
|
FailureOr<FormatElement *> pelement = parseElement(context);
|
|
|
|
if (failed(pelement) ||
|
|
|
|
failed(verifyOIListParsingElement(*pelement, loc)))
|
|
|
|
return failure();
|
|
|
|
currParsingElements.push_back(*pelement);
|
|
|
|
}
|
|
|
|
if (peekToken().getKind() == FormatToken::pipe) {
|
|
|
|
consumeToken();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (peekToken().getKind() == FormatToken::r_paren) {
|
|
|
|
consumeToken();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (true);
|
|
|
|
|
|
|
|
return create<OIListElement>(std::move(literalElements),
|
|
|
|
std::move(parsingElements));
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult OpFormatParser::verifyOIListParsingElement(FormatElement *element,
|
|
|
|
SMLoc loc) {
|
2022-03-22 12:38:33 +08:00
|
|
|
SmallVector<VariableElement *> vars;
|
|
|
|
collect(element, vars);
|
|
|
|
for (VariableElement *elem : vars) {
|
|
|
|
LogicalResult res =
|
|
|
|
TypeSwitch<FormatElement *, LogicalResult>(elem)
|
|
|
|
// Only optional attributes can be within an oilist parsing group.
|
|
|
|
.Case([&](AttributeVariable *attrEle) {
|
|
|
|
if (!attrEle->getVar()->attr.isOptional() &&
|
|
|
|
!attrEle->getVar()->attr.hasDefaultValue())
|
|
|
|
return emitError(loc, "only optional attributes can be used in "
|
|
|
|
"an oilist parsing group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) operands can be within an
|
|
|
|
// oilist parsing group.
|
|
|
|
.Case([&](OperandVariable *ele) {
|
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(loc, "only variable length operands can be "
|
|
|
|
"used within an oilist parsing group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) results can be within an oilist
|
|
|
|
// parsing group.
|
|
|
|
.Case([&](ResultVariable *ele) {
|
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(loc, "only variable length results can be "
|
|
|
|
"used within an oilist parsing group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
.Case([&](RegionVariable *) { return success(); })
|
|
|
|
.Default([&](FormatElement *) {
|
|
|
|
return emitError(loc,
|
|
|
|
"only literals, types, and variables can be "
|
|
|
|
"used within an oilist group");
|
|
|
|
});
|
|
|
|
if (failed(res))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
2022-02-17 12:54:10 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> OpFormatParser::parseTypeDirective(SMLoc loc,
|
|
|
|
Context context) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TypeDirectiveContext)
|
|
|
|
return emitError(loc, "'type' cannot be used as a child of another `type`");
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
bool isRefChild = context == RefDirectiveContext;
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *> operand;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2022-02-01 14:58:02 +08:00
|
|
|
failed(operand = parseTypeDirectiveOperand(loc, isRefChild)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2022-02-01 14:58:02 +08:00
|
|
|
return failure();
|
2021-02-10 06:32:15 +08:00
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
return create<TypeDirective>(*operand);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseQualifiedDirective(SMLoc loc, Context context) {
|
|
|
|
FailureOr<FormatElement *> element;
|
2022-01-11 09:26:44 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2022-02-01 14:58:02 +08:00
|
|
|
failed(element = parseElement(context)) ||
|
2022-01-11 09:26:44 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
|
|
|
return failure();
|
2022-02-01 14:58:02 +08:00
|
|
|
return TypeSwitch<FormatElement *, FailureOr<FormatElement *>>(*element)
|
|
|
|
.Case<AttributeVariable, TypeDirective>([](auto *element) {
|
|
|
|
element->setShouldBeQualified();
|
|
|
|
return element;
|
|
|
|
})
|
|
|
|
.Default([&](auto *element) {
|
2022-02-01 18:58:27 +08:00
|
|
|
return this->emitError(
|
2022-02-01 14:58:02 +08:00
|
|
|
loc,
|
|
|
|
"'qualified' directive expects an attribute or a `type` directive");
|
|
|
|
});
|
2022-01-11 09:26:44 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
FailureOr<FormatElement *>
|
|
|
|
OpFormatParser::parseTypeDirectiveOperand(SMLoc loc, bool isRefChild) {
|
|
|
|
FailureOr<FormatElement *> result = parseElement(TypeDirectiveContext);
|
|
|
|
if (failed(result))
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
FormatElement *element = *result;
|
|
|
|
if (isa<LiteralElement>(element))
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(
|
|
|
|
loc, "'type' directive operand expects variable or directive operand");
|
|
|
|
|
2022-02-01 14:58:02 +08:00
|
|
|
if (auto *var = dyn_cast<OperandVariable>(element)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
unsigned opIdx = var->getVar() - op.operand_begin();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.test(opIdx)))
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "'type' of '" + var->getVar()->name +
|
|
|
|
"' is already bound");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (isRefChild && !(fmt.allOperandTypes || seenOperandTypes.test(opIdx)))
|
|
|
|
return emitError(loc, "'ref' of 'type($" + var->getVar()->name +
|
|
|
|
")' is not bound by a prior 'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
seenOperandTypes.set(opIdx);
|
2022-02-01 14:58:02 +08:00
|
|
|
} else if (auto *var = dyn_cast<ResultVariable>(element)) {
|
2020-01-31 03:30:23 +08:00
|
|
|
unsigned resIdx = var->getVar() - op.result_begin();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!isRefChild && (fmt.allResultTypes || seenResultTypes.test(resIdx)))
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "'type' of '" + var->getVar()->name +
|
|
|
|
"' is already bound");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (isRefChild && !(fmt.allResultTypes || seenResultTypes.test(resIdx)))
|
|
|
|
return emitError(loc, "'ref' of 'type($" + var->getVar()->name +
|
|
|
|
")' is not bound by a prior 'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
seenResultTypes.set(resIdx);
|
|
|
|
} else if (isa<OperandsDirective>(&*element)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!isRefChild && (fmt.allOperandTypes || seenOperandTypes.any()))
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "'operands' 'type' is already bound");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (isRefChild && !fmt.allOperandTypes)
|
|
|
|
return emitError(loc, "'ref' of 'type(operands)' is not bound by a prior "
|
|
|
|
"'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
fmt.allOperandTypes = true;
|
|
|
|
} else if (isa<ResultsDirective>(&*element)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!isRefChild && (fmt.allResultTypes || seenResultTypes.any()))
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(loc, "'results' 'type' is already bound");
|
2021-02-10 06:32:15 +08:00
|
|
|
if (isRefChild && !fmt.allResultTypes)
|
|
|
|
return emitError(loc, "'ref' of 'type(results)' is not bound by a prior "
|
|
|
|
"'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
fmt.allResultTypes = true;
|
|
|
|
} else {
|
|
|
|
return emitError(loc, "invalid argument to 'type' directive");
|
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
OpFormatParser::verifyOptionalGroupElements(SMLoc loc,
|
|
|
|
ArrayRef<FormatElement *> elements,
|
|
|
|
Optional<unsigned> anchorIndex) {
|
|
|
|
for (auto &it : llvm::enumerate(elements)) {
|
|
|
|
if (failed(verifyOptionalGroupElement(
|
|
|
|
loc, it.value(), anchorIndex && *anchorIndex == it.index())))
|
|
|
|
return failure();
|
|
|
|
}
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult OpFormatParser::verifyOptionalGroupElement(SMLoc loc,
|
|
|
|
FormatElement *element,
|
|
|
|
bool isAnchor) {
|
|
|
|
return TypeSwitch<FormatElement *, LogicalResult>(element)
|
|
|
|
// 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(loc, "only optional attributes can be used to "
|
|
|
|
"anchor an optional group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) operands can be within an optional
|
|
|
|
// group.
|
|
|
|
.Case([&](OperandVariable *ele) {
|
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(loc, "only variable length operands can be used "
|
|
|
|
"within an optional group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) results can be within an optional
|
|
|
|
// group.
|
|
|
|
.Case([&](ResultVariable *ele) {
|
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(loc, "only variable length results can be used "
|
|
|
|
"within an optional group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
.Case([&](RegionVariable *) {
|
|
|
|
// TODO: When ODS has proper support for marking "optional" regions, add
|
|
|
|
// a check here.
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
.Case([&](TypeDirective *ele) {
|
|
|
|
return verifyOptionalGroupElement(loc, ele->getArg(),
|
|
|
|
/*isAnchor=*/false);
|
|
|
|
})
|
|
|
|
.Case([&](FunctionalTypeDirective *ele) {
|
|
|
|
if (failed(verifyOptionalGroupElement(loc, ele->getInputs(),
|
|
|
|
/*isAnchor=*/false)))
|
|
|
|
return failure();
|
|
|
|
return verifyOptionalGroupElement(loc, ele->getResults(),
|
|
|
|
/*isAnchor=*/false);
|
|
|
|
})
|
|
|
|
// Literals, whitespace, and custom directives may be used, but they can't
|
|
|
|
// anchor the group.
|
|
|
|
.Case<LiteralElement, WhitespaceElement, CustomDirective,
|
|
|
|
FunctionalTypeDirective, OptionalElement>([&](FormatElement *) {
|
|
|
|
if (isAnchor)
|
|
|
|
return emitError(loc, "only variables and types can be used "
|
|
|
|
"to anchor an optional group");
|
|
|
|
return success();
|
|
|
|
})
|
|
|
|
.Default([&](FormatElement *) {
|
|
|
|
return emitError(loc, "only literals, types, and variables can be "
|
|
|
|
"used within an optional group");
|
|
|
|
});
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// 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(
|
2022-01-27 07:49:53 +08:00
|
|
|
llvm::MemoryBuffer::getMemBuffer(op.getAssemblyFormat()), SMLoc());
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat format(op);
|
2022-02-01 14:58:02 +08:00
|
|
|
OpFormatParser parser(mgr, format, op);
|
|
|
|
FailureOr<std::vector<FormatElement *>> elements = parser.parse();
|
|
|
|
if (failed(elements)) {
|
2020-02-04 14:14:33 +08:00
|
|
|
// 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
|
|
|
}
|
2022-02-01 14:58:02 +08:00
|
|
|
format.elements = std::move(*elements);
|
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
|
|
|
}
|