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"
|
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"
|
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;
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Element
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents a single format element.
|
|
|
|
class Element {
|
|
|
|
public:
|
|
|
|
enum class Kind {
|
|
|
|
/// This element is a directive.
|
|
|
|
AttrDictDirective,
|
2020-09-01 03:33:36 +08:00
|
|
|
CustomDirective,
|
2020-01-31 03:30:23 +08:00
|
|
|
FunctionalTypeDirective,
|
|
|
|
OperandsDirective,
|
2021-02-10 06:32:15 +08:00
|
|
|
RefDirective,
|
2020-09-01 03:33:55 +08:00
|
|
|
RegionsDirective,
|
2020-01-31 03:30:23 +08:00
|
|
|
ResultsDirective,
|
2020-02-22 05:20:06 +08:00
|
|
|
SuccessorsDirective,
|
2020-01-31 03:30:23 +08:00
|
|
|
TypeDirective,
|
|
|
|
|
|
|
|
/// This element is a literal.
|
|
|
|
Literal,
|
|
|
|
|
2020-12-15 03:53:34 +08:00
|
|
|
/// This element is a whitespace.
|
|
|
|
Newline,
|
2020-10-18 13:40:42 +08:00
|
|
|
Space,
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This element is an variable value.
|
|
|
|
AttributeVariable,
|
|
|
|
OperandVariable,
|
2020-09-01 03:33:55 +08:00
|
|
|
RegionVariable,
|
2020-01-31 03:30:23 +08:00
|
|
|
ResultVariable,
|
2020-02-22 05:20:06 +08:00
|
|
|
SuccessorVariable,
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
/// This element is an optional element.
|
|
|
|
Optional,
|
2020-01-31 03:30:23 +08:00
|
|
|
};
|
|
|
|
Element(Kind kind) : kind(kind) {}
|
|
|
|
virtual ~Element() = default;
|
|
|
|
|
|
|
|
/// Return the kind of this element.
|
|
|
|
Kind getKind() const { return kind; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The kind of this element.
|
|
|
|
Kind kind;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// VariableElement
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents an instance of an variable element. A variable refers
|
|
|
|
/// to something registered on the operation itself, e.g. an argument, result,
|
|
|
|
/// etc.
|
|
|
|
template <typename VarT, Element::Kind kindVal>
|
|
|
|
class VariableElement : public Element {
|
|
|
|
public:
|
|
|
|
VariableElement(const VarT *var) : Element(kindVal), var(var) {}
|
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == kindVal;
|
|
|
|
}
|
|
|
|
const VarT *getVar() { return var; }
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
protected:
|
2020-01-31 03:30:23 +08:00
|
|
|
const VarT *var;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This class represents a variable that refers to an attribute argument.
|
2020-04-04 10:20:33 +08:00
|
|
|
struct AttributeVariable
|
|
|
|
: public VariableElement<NamedAttribute, Element::Kind::AttributeVariable> {
|
|
|
|
using VariableElement<NamedAttribute,
|
|
|
|
Element::Kind::AttributeVariable>::VariableElement;
|
|
|
|
|
|
|
|
/// Return the constant builder call for the type of this attribute, or None
|
|
|
|
/// if it doesn't have one.
|
|
|
|
Optional<StringRef> getTypeBuilder() const {
|
|
|
|
Optional<Type> attrType = var->attr.getValueType();
|
|
|
|
return attrType ? attrType->getBuilderCall() : llvm::None;
|
|
|
|
}
|
2020-08-04 05:20:50 +08:00
|
|
|
|
|
|
|
/// Return if this attribute refers to a UnitAttr.
|
|
|
|
bool isUnitAttr() const {
|
|
|
|
return var->attr.getBaseAttr().getAttrDefName() == "UnitAttr";
|
|
|
|
}
|
2020-04-04 10:20:33 +08:00
|
|
|
};
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// This class represents a variable that refers to an operand argument.
|
|
|
|
using OperandVariable =
|
|
|
|
VariableElement<NamedTypeConstraint, Element::Kind::OperandVariable>;
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// This class represents a variable that refers to a region.
|
|
|
|
using RegionVariable =
|
|
|
|
VariableElement<NamedRegion, Element::Kind::RegionVariable>;
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This class represents a variable that refers to a result.
|
|
|
|
using ResultVariable =
|
|
|
|
VariableElement<NamedTypeConstraint, Element::Kind::ResultVariable>;
|
2020-02-22 05:20:06 +08:00
|
|
|
|
|
|
|
/// This class represents a variable that refers to a successor.
|
|
|
|
using SuccessorVariable =
|
|
|
|
VariableElement<NamedSuccessor, Element::Kind::SuccessorVariable>;
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// DirectiveElement
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class implements single kind directives.
|
2021-11-12 09:17:05 +08:00
|
|
|
template <Element::Kind type> class DirectiveElement : public Element {
|
2020-01-31 03:30:23 +08:00
|
|
|
public:
|
|
|
|
DirectiveElement() : Element(type){};
|
|
|
|
static bool classof(const Element *ele) { return ele->getKind() == type; }
|
|
|
|
};
|
|
|
|
/// This class represents the `operands` directive. This directive represents
|
|
|
|
/// all of the operands of an operation.
|
|
|
|
using OperandsDirective = DirectiveElement<Element::Kind::OperandsDirective>;
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// This class represents the `regions` directive. This directive represents
|
|
|
|
/// all of the regions of an operation.
|
2021-01-21 08:47:00 +08:00
|
|
|
using RegionsDirective = DirectiveElement<Element::Kind::RegionsDirective>;
|
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.
|
|
|
|
using ResultsDirective = DirectiveElement<Element::Kind::ResultsDirective>;
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
/// This class represents the `successors` directive. This directive represents
|
|
|
|
/// all of the successors of an operation.
|
|
|
|
using SuccessorsDirective =
|
|
|
|
DirectiveElement<Element::Kind::SuccessorsDirective>;
|
|
|
|
|
2020-02-22 05:19:26 +08:00
|
|
|
/// This class represents the `attr-dict` directive. This directive represents
|
|
|
|
/// the attribute dictionary of the operation.
|
|
|
|
class AttrDictDirective
|
|
|
|
: public DirectiveElement<Element::Kind::AttrDictDirective> {
|
|
|
|
public:
|
|
|
|
explicit AttrDictDirective(bool withKeyword) : withKeyword(withKeyword) {}
|
|
|
|
bool isWithKeyword() const { return withKeyword; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// If the dictionary should be printed with the 'attributes' keyword.
|
|
|
|
bool withKeyword;
|
|
|
|
};
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
/// This class represents a custom format directive that is implemented by the
|
|
|
|
/// user in C++.
|
|
|
|
class CustomDirective : public Element {
|
|
|
|
public:
|
|
|
|
CustomDirective(StringRef name,
|
|
|
|
std::vector<std::unique_ptr<Element>> &&arguments)
|
|
|
|
: Element{Kind::CustomDirective}, name(name),
|
|
|
|
arguments(std::move(arguments)) {}
|
|
|
|
|
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == Kind::CustomDirective;
|
|
|
|
}
|
|
|
|
|
2021-11-14 04:06:48 +08:00
|
|
|
/// Return the name of the custom directive.
|
2020-09-01 03:33:36 +08:00
|
|
|
StringRef getName() const { return name; }
|
|
|
|
|
|
|
|
/// Return the arguments to the custom directive.
|
|
|
|
auto getArguments() const { return llvm::make_pointee_range(arguments); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The user provided name of the directive.
|
|
|
|
StringRef name;
|
|
|
|
|
|
|
|
/// The arguments to the custom directive.
|
|
|
|
std::vector<std::unique_ptr<Element>> arguments;
|
|
|
|
};
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// This class represents the `functional-type` directive. This directive takes
|
|
|
|
/// two arguments and formats them, respectively, as the inputs and results of a
|
|
|
|
/// FunctionType.
|
2020-02-22 05:19:26 +08:00
|
|
|
class FunctionalTypeDirective
|
2020-01-31 03:30:23 +08:00
|
|
|
: public DirectiveElement<Element::Kind::FunctionalTypeDirective> {
|
|
|
|
public:
|
|
|
|
FunctionalTypeDirective(std::unique_ptr<Element> inputs,
|
|
|
|
std::unique_ptr<Element> results)
|
|
|
|
: inputs(std::move(inputs)), results(std::move(results)) {}
|
|
|
|
Element *getInputs() const { return inputs.get(); }
|
|
|
|
Element *getResults() const { return results.get(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The input and result arguments.
|
|
|
|
std::unique_ptr<Element> inputs, results;
|
|
|
|
};
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
/// This class represents the `ref` directive.
|
|
|
|
class RefDirective : public DirectiveElement<Element::Kind::RefDirective> {
|
2020-01-31 03:30:23 +08:00
|
|
|
public:
|
2021-02-10 06:32:15 +08:00
|
|
|
RefDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
|
2020-01-31 03:30:23 +08:00
|
|
|
Element *getOperand() const { return operand.get(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The operand that is used to format the directive.
|
|
|
|
std::unique_ptr<Element> operand;
|
|
|
|
};
|
2020-09-18 18:13:25 +08:00
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
/// This class represents the `type` directive.
|
|
|
|
class TypeDirective : public DirectiveElement<Element::Kind::TypeDirective> {
|
2020-09-18 18:13:25 +08:00
|
|
|
public:
|
2021-02-10 06:32:15 +08:00
|
|
|
TypeDirective(std::unique_ptr<Element> arg) : operand(std::move(arg)) {}
|
2020-09-18 18:13:25 +08:00
|
|
|
Element *getOperand() const { return operand.get(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The operand that is used to format the directive.
|
|
|
|
std::unique_ptr<Element> operand;
|
|
|
|
};
|
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// LiteralElement
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents an instance of a literal element.
|
|
|
|
class LiteralElement : public Element {
|
|
|
|
public:
|
|
|
|
LiteralElement(StringRef literal)
|
2020-02-22 05:19:15 +08:00
|
|
|
: Element{Kind::Literal}, literal(literal) {}
|
2020-01-31 03:30:23 +08:00
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == Kind::Literal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the literal for this element.
|
|
|
|
StringRef getLiteral() const { return literal; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// The spelling of the literal for this element.
|
|
|
|
StringRef literal;
|
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-10-18 13:40:42 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-12-15 03:53:34 +08:00
|
|
|
// WhitespaceElement
|
2020-10-18 13:40:42 +08:00
|
|
|
|
|
|
|
namespace {
|
2020-12-15 03:53:34 +08:00
|
|
|
/// This class represents a whitespace element, e.g. newline or space. It's a
|
|
|
|
/// literal that is printed but never parsed.
|
|
|
|
class WhitespaceElement : public Element {
|
|
|
|
public:
|
|
|
|
WhitespaceElement(Kind kind) : Element{kind} {}
|
|
|
|
static bool classof(const Element *element) {
|
|
|
|
Kind kind = element->getKind();
|
|
|
|
return kind == Kind::Newline || kind == Kind::Space;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/// This class represents an instance of a newline element. It's a literal that
|
|
|
|
/// prints a newline. It is ignored by the parser.
|
|
|
|
class NewlineElement : public WhitespaceElement {
|
|
|
|
public:
|
|
|
|
NewlineElement() : WhitespaceElement(Kind::Newline) {}
|
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == Kind::Newline;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-10-18 13:40:42 +08:00
|
|
|
/// This class represents an instance of a space element. It's a literal that
|
2020-11-11 15:24:16 +08:00
|
|
|
/// prints or omits printing a space. It is ignored by the parser.
|
2020-12-15 03:53:34 +08:00
|
|
|
class SpaceElement : public WhitespaceElement {
|
2020-10-18 13:40:42 +08:00
|
|
|
public:
|
2020-12-15 03:53:34 +08:00
|
|
|
SpaceElement(bool value) : WhitespaceElement(Kind::Space), value(value) {}
|
2020-10-18 13:40:42 +08:00
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == Kind::Space;
|
|
|
|
}
|
2020-11-11 15:24:16 +08:00
|
|
|
|
|
|
|
/// Returns true if this element should print as a space. Otherwise, the
|
|
|
|
/// element should omit printing a space between the surrounding elements.
|
|
|
|
bool getValue() const { return value; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool value;
|
2020-10-18 13:40:42 +08:00
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-10-18 13:40:42 +08:00
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OptionalElement
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// This class represents a group of elements that are optionally emitted based
|
2021-03-23 09:07:09 +08:00
|
|
|
/// upon an optional variable of the operation, and a group of elements that are
|
|
|
|
/// emotted when the anchor element is not present.
|
2020-02-22 05:19:15 +08:00
|
|
|
class OptionalElement : public Element {
|
|
|
|
public:
|
2021-03-23 09:07:09 +08:00
|
|
|
OptionalElement(std::vector<std::unique_ptr<Element>> &&thenElements,
|
|
|
|
std::vector<std::unique_ptr<Element>> &&elseElements,
|
2020-11-11 15:24:16 +08:00
|
|
|
unsigned anchor, unsigned parseStart)
|
2021-03-23 09:07:09 +08:00
|
|
|
: Element{Kind::Optional}, thenElements(std::move(thenElements)),
|
|
|
|
elseElements(std::move(elseElements)), anchor(anchor),
|
2020-11-11 15:24:16 +08:00
|
|
|
parseStart(parseStart) {}
|
2020-02-22 05:19:15 +08:00
|
|
|
static bool classof(const Element *element) {
|
|
|
|
return element->getKind() == Kind::Optional;
|
|
|
|
}
|
|
|
|
|
2021-03-23 09:07:09 +08:00
|
|
|
/// Return the `then` elements of this grouping.
|
|
|
|
auto getThenElements() const {
|
|
|
|
return llvm::make_pointee_range(thenElements);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the `else` elements of this grouping.
|
|
|
|
auto getElseElements() const {
|
|
|
|
return llvm::make_pointee_range(elseElements);
|
|
|
|
}
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
/// Return the anchor of this optional group.
|
2021-03-23 09:07:09 +08:00
|
|
|
Element *getAnchor() const { return thenElements[anchor].get(); }
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-11-11 15:24:16 +08:00
|
|
|
/// Return the index of the first element that needs to be parsed.
|
|
|
|
unsigned getParseStart() const { return parseStart; }
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
private:
|
2021-03-23 09:07:09 +08:00
|
|
|
/// The child elements of `then` branch of this optional.
|
|
|
|
std::vector<std::unique_ptr<Element>> thenElements;
|
|
|
|
/// The child elements of `else` branch of this optional.
|
|
|
|
std::vector<std::unique_ptr<Element>> elseElements;
|
2020-02-22 05:19:15 +08:00
|
|
|
/// The index of the element that acts as the anchor for the optional group.
|
|
|
|
unsigned anchor;
|
2020-12-15 03:53:34 +08:00
|
|
|
/// The index of the first element that is parsed (is not a
|
|
|
|
/// WhitespaceElement).
|
2020-11-11 15:24:16 +08:00
|
|
|
unsigned parseStart;
|
2020-02-22 05:19:15 +08:00
|
|
|
};
|
2021-12-08 02:27:58 +08:00
|
|
|
} // namespace
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OperationFormat
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2020-07-07 12:40:01 +08:00
|
|
|
|
|
|
|
using ConstArgument =
|
|
|
|
llvm::PointerUnion<const NamedAttribute *, const NamedTypeConstraint *>;
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
struct OperationFormat {
|
2020-02-04 13:52:38 +08:00
|
|
|
/// This class represents a specific resolver for an operand or result type.
|
|
|
|
class TypeResolution {
|
|
|
|
public:
|
|
|
|
TypeResolution() = default;
|
|
|
|
|
|
|
|
/// Get the index into the buildable types for this type, or None.
|
|
|
|
Optional<int> getBuilderIdx() const { return builderIdx; }
|
|
|
|
void setBuilderIdx(int idx) { builderIdx = idx; }
|
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Get the variable this type is resolved to, or nullptr.
|
|
|
|
const NamedTypeConstraint *getVariable() const {
|
|
|
|
return resolver.dyn_cast<const NamedTypeConstraint *>();
|
|
|
|
}
|
|
|
|
/// Get the attribute this type is resolved to, or nullptr.
|
|
|
|
const NamedAttribute *getAttribute() const {
|
|
|
|
return resolver.dyn_cast<const NamedAttribute *>();
|
|
|
|
}
|
|
|
|
/// Get the transformer for the type of the variable, or None.
|
2020-02-22 05:19:03 +08:00
|
|
|
Optional<StringRef> getVarTransformer() const {
|
|
|
|
return variableTransformer;
|
|
|
|
}
|
2020-07-07 12:40:01 +08:00
|
|
|
void setResolver(ConstArgument arg, Optional<StringRef> transformer) {
|
|
|
|
resolver = arg;
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTransformer = transformer;
|
2020-07-07 12:40:01 +08:00
|
|
|
assert(getVariable() || getAttribute());
|
2020-02-22 05:19:03 +08:00
|
|
|
}
|
2020-02-04 13:52:38 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
/// If the type is resolved with a buildable type, this is the index into
|
|
|
|
/// 'buildableTypes' in the parent format.
|
|
|
|
Optional<int> builderIdx;
|
|
|
|
/// If the type is resolved based upon another operand or result, this is
|
2020-07-07 12:40:01 +08:00
|
|
|
/// the variable or the attribute that this type is resolved to.
|
|
|
|
ConstArgument resolver;
|
2020-02-22 05:19:03 +08:00
|
|
|
/// If the type is resolved based upon another operand or result, this is
|
|
|
|
/// a transformer to apply to the variable when resolving.
|
|
|
|
Optional<StringRef> variableTransformer;
|
2020-02-04 13:52:38 +08:00
|
|
|
};
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat(const Operator &op)
|
2021-10-07 08:50:38 +08:00
|
|
|
: allOperands(false), allOperandTypes(false), allResultTypes(false),
|
|
|
|
infersResultTypes(false) {
|
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.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genElementParser(Element *element, MethodBody &body,
|
2020-09-01 03:33:55 +08:00
|
|
|
FmtContext &attrTypeCtx);
|
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.
|
2021-11-12 09:17:05 +08:00
|
|
|
void genElementPrinter(Element *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.
|
|
|
|
std::vector<std::unique_ptr<Element>> elements;
|
|
|
|
|
|
|
|
/// A flag indicating if all operand/result types were seen. If the format
|
|
|
|
/// contains these, it can not contain individual type resolvers.
|
2020-03-06 04:40:53 +08:00
|
|
|
bool allOperands, allOperandTypes, allResultTypes;
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2021-10-07 08:50:38 +08:00
|
|
|
/// A flag indicating if this operation infers its result types
|
|
|
|
bool infersResultTypes;
|
|
|
|
|
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();
|
2020-06-26 19:20:44 +08:00
|
|
|
::mlir::OpAsmParser::OperandType operand;
|
|
|
|
::mlir::OptionalParseResult parseResult =
|
|
|
|
parser.parseOptionalOperand(operand);
|
2020-04-11 05:11:45 +08:00
|
|
|
if (parseResult.hasValue()) {
|
|
|
|
if (failed(*parseResult))
|
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
|
|
|
)";
|
|
|
|
|
|
|
|
/// 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
|
|
|
)";
|
|
|
|
|
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.
|
|
|
|
static StringRef getTypeListName(Element *arg, ArgumentLengthKind &lengthKind) {
|
2020-01-31 03:31:21 +08:00
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(arg)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = getArgumentLengthKind(operand->getVar());
|
2020-01-31 03:31:21 +08:00
|
|
|
return operand->getVar()->name;
|
|
|
|
}
|
|
|
|
if (auto *result = dyn_cast<ResultVariable>(arg)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = getArgumentLengthKind(result->getVar());
|
2020-01-31 03:31:21 +08:00
|
|
|
return result->getVar()->name;
|
|
|
|
}
|
2020-04-11 05:11:45 +08:00
|
|
|
lengthKind = ArgumentLengthKind::Variadic;
|
2020-01-31 03:31:21 +08:00
|
|
|
if (isa<OperandsDirective>(arg))
|
|
|
|
return "allOperand";
|
|
|
|
if (isa<ResultsDirective>(arg))
|
|
|
|
return "allResult";
|
|
|
|
llvm_unreachable("unknown 'type' directive argument");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Generate the parser for a literal value.
|
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.
|
2021-10-20 22:08:36 +08:00
|
|
|
static void genElementParserStorage(Element *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)) {
|
2021-03-23 09:07:09 +08:00
|
|
|
auto 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());
|
|
|
|
Element *elidedAnchorElement = nullptr;
|
|
|
|
if (anchor && anchor != &*elements.begin() && anchor->isUnitAttr())
|
|
|
|
elidedAnchorElement = anchor;
|
|
|
|
for (auto &childElement : elements)
|
|
|
|
if (&childElement != elidedAnchorElement)
|
2021-10-20 22:08:36 +08:00
|
|
|
genElementParserStorage(&childElement, op, body);
|
2021-03-23 09:07:09 +08:00
|
|
|
for (auto &childElement : optional->getElseElements())
|
2021-10-20 22:08:36 +08:00
|
|
|
genElementParserStorage(&childElement, op, body);
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
} else if (auto *custom = dyn_cast<CustomDirective>(element)) {
|
|
|
|
for (auto ¶mElement : custom->getArguments())
|
2021-10-20 22:08:36 +08:00
|
|
|
genElementParserStorage(¶mElement, op, body);
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
} else if (isa<OperandsDirective>(element)) {
|
|
|
|
body << " ::mlir::SmallVector<::mlir::OpAsmParser::OperandType, 4> "
|
|
|
|
"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()) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::SmallVector<::mlir::OpAsmParser::OperandType, 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 {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::OpAsmParser::OperandType " << name
|
|
|
|
<< "RawOperands[1];\n"
|
|
|
|
<< " ::llvm::ArrayRef<::mlir::OpAsmParser::OperandType> " << name
|
|
|
|
<< "Operands(" << name << "RawOperands);";
|
2020-03-06 04:40:53 +08:00
|
|
|
}
|
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;
|
|
|
|
StringRef name = getTypeListName(dir->getOperand(), lengthKind);
|
|
|
|
if (lengthKind != ArgumentLengthKind::Single)
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::mlir::SmallVector<::mlir::Type, 1> " << name << "Types;\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
else
|
2020-06-26 19:20:44 +08:00
|
|
|
body << llvm::formatv(" ::mlir::Type {0}RawTypes[1];\n", name)
|
|
|
|
<< llvm::formatv(
|
|
|
|
" ::llvm::ArrayRef<::mlir::Type> {0}Types({0}RawTypes);\n",
|
|
|
|
name);
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind ignored;
|
2020-06-26 19:20:44 +08:00
|
|
|
body << " ::llvm::ArrayRef<::mlir::Type> "
|
|
|
|
<< getTypeListName(dir->getInputs(), ignored) << "Types;\n";
|
|
|
|
body << " ::llvm::ArrayRef<::mlir::Type> "
|
|
|
|
<< getTypeListName(dir->getResults(), ignored) << "Types;\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
/// Generate the parser for a parameter to a custom directive.
|
2021-11-12 09:17:05 +08:00
|
|
|
static void genCustomParameterParser(Element ¶m, MethodBody &body) {
|
2020-09-01 03:33:36 +08:00
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(¶m)) {
|
|
|
|
body << attr->getVar()->name << "Attr";
|
2020-10-28 09:01:44 +08:00
|
|
|
} else if (isa<AttrDictDirective>(¶m)) {
|
|
|
|
body << "result.attributes";
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(¶m)) {
|
|
|
|
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);
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
} else if (auto *region = dyn_cast<RegionVariable>(¶m)) {
|
|
|
|
StringRef name = region->getVar()->name;
|
|
|
|
if (region->getVar()->isVariadic())
|
|
|
|
body << llvm::formatv("{0}Regions", name);
|
|
|
|
else
|
|
|
|
body << llvm::formatv("*{0}Region", name);
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *successor = dyn_cast<SuccessorVariable>(¶m)) {
|
|
|
|
StringRef name = successor->getVar()->name;
|
|
|
|
if (successor->getVar()->isVariadic())
|
|
|
|
body << llvm::formatv("{0}Successors", name);
|
|
|
|
else
|
|
|
|
body << llvm::formatv("{0}Successor", name);
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<RefDirective>(¶m)) {
|
|
|
|
genCustomParameterParser(*dir->getOperand(), body);
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(¶m)) {
|
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef listName = getTypeListName(dir->getOperand(), 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.
|
|
|
|
for (Element ¶m : dir->getArguments()) {
|
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(¶m)) {
|
[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(
|
|
|
|
" llvm::Optional<::mlir::OpAsmParser::OperandType> "
|
|
|
|
"{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(" "
|
|
|
|
"llvm::SmallVector<llvm::SmallVector<::mlir::"
|
|
|
|
"OpAsmParser::OperandType>> "
|
|
|
|
"{0}OperandGroups;\n",
|
|
|
|
var->name);
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(¶m)) {
|
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef listName = getTypeListName(dir->getOperand(), 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(
|
|
|
|
" llvm::SmallVector<llvm::SmallVector<::mlir::Type>> "
|
|
|
|
"{0}TypeGroups;\n",
|
|
|
|
listName);
|
|
|
|
}
|
2021-02-10 06:32:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<RefDirective>(¶m)) {
|
|
|
|
Element *input = dir->getOperand();
|
|
|
|
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",
|
|
|
|
"llvm::Optional<::mlir::OpAsmParser::OperandType>",
|
|
|
|
operand->getVar()->name);
|
|
|
|
|
|
|
|
} else if (auto *type = dyn_cast<TypeDirective>(input)) {
|
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef listName = getTypeListName(type->getOperand(), lengthKind);
|
|
|
|
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";
|
2021-02-10 06:32:15 +08:00
|
|
|
for (Element ¶m : dir->getArguments()) {
|
|
|
|
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.
|
|
|
|
for (Element ¶m : dir->getArguments()) {
|
|
|
|
if (auto *attr = dyn_cast<AttributeVariable>(¶m)) {
|
|
|
|
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);
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *operand = dyn_cast<OperandVariable>(¶m)) {
|
|
|
|
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());
|
|
|
|
}
|
2020-09-01 03:33:36 +08:00
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(¶m)) {
|
|
|
|
ArgumentLengthKind lengthKind;
|
|
|
|
StringRef listName = getTypeListName(dir->getOperand(), lengthKind);
|
|
|
|
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.
|
|
|
|
for (auto &element : elements)
|
2021-10-20 22:08:36 +08:00
|
|
|
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.
|
|
|
|
for (auto &element : elements)
|
|
|
|
genElementParser(element.get(), body, attrTypeCtx);
|
|
|
|
|
|
|
|
// Generate the code to resolve the operand/result types and successors now
|
|
|
|
// that they have been parsed.
|
|
|
|
genParserTypeResolution(op, body);
|
|
|
|
genParserRegionResolution(op, body);
|
|
|
|
genParserSuccessorResolution(op, body);
|
|
|
|
genParserVariadicSegmentResolution(op, body);
|
|
|
|
|
2020-09-15 04:01:07 +08:00
|
|
|
body << " return ::mlir::success();\n";
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2021-11-12 09:17:05 +08:00
|
|
|
void OperationFormat::genElementParser(Element *element, MethodBody &body,
|
2020-09-01 03:33:55 +08:00
|
|
|
FmtContext &attrTypeCtx) {
|
2020-02-22 05:19:15 +08:00
|
|
|
/// Optional Group.
|
|
|
|
if (auto *optional = dyn_cast<OptionalElement>(element)) {
|
2021-03-23 09:07:09 +08:00
|
|
|
auto elements = llvm::drop_begin(optional->getThenElements(),
|
|
|
|
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.
|
2020-07-15 04:14:14 +08:00
|
|
|
Element *firstElement = &*elements.begin();
|
|
|
|
if (auto *attrVar = dyn_cast<AttributeVariable>(firstElement)) {
|
|
|
|
genElementParser(attrVar, body, attrTypeCtx);
|
|
|
|
body << " if (" << attrVar->getVar()->name << "Attr) {\n";
|
|
|
|
} else if (auto *literal = dyn_cast<LiteralElement>(firstElement)) {
|
2020-02-22 05:19:15 +08:00
|
|
|
body << " if (succeeded(parser.parseOptional";
|
|
|
|
genLiteralParser(literal->getLiteral(), body);
|
|
|
|
body << ")) {\n";
|
2020-07-15 04:14:14 +08:00
|
|
|
} else if (auto *opVar = dyn_cast<OperandVariable>(firstElement)) {
|
2020-02-22 05:19:15 +08:00
|
|
|
genElementParser(opVar, body, attrTypeCtx);
|
|
|
|
body << " if (!" << opVar->getVar()->name << "Operands.empty()) {\n";
|
2020-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.
|
|
|
|
Element *elidedAnchorElement = nullptr;
|
|
|
|
auto *anchorAttr = dyn_cast<AttributeVariable>(optional->getAnchor());
|
|
|
|
if (anchorAttr && anchorAttr != firstElement && anchorAttr->isUnitAttr()) {
|
|
|
|
elidedAnchorElement = anchorAttr;
|
|
|
|
|
|
|
|
// Add the anchor unit attribute to the operation state.
|
|
|
|
body << " result.addAttribute(\"" << anchorAttr->getVar()->name
|
|
|
|
<< "\", parser.getBuilder().getUnitAttr());\n";
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Generate the rest of the elements normally.
|
2020-08-04 05:20:50 +08:00
|
|
|
for (Element &childElement : llvm::drop_begin(elements, 1)) {
|
|
|
|
if (&childElement != elidedAnchorElement)
|
|
|
|
genElementParser(&childElement, body, attrTypeCtx);
|
|
|
|
}
|
2021-03-23 09:07:09 +08:00
|
|
|
body << " }";
|
|
|
|
|
|
|
|
// Generate the else elements.
|
|
|
|
auto elseElements = optional->getElseElements();
|
|
|
|
if (!elseElements.empty()) {
|
|
|
|
body << " else {\n";
|
|
|
|
for (Element &childElement : elseElements)
|
|
|
|
genElementParser(&childElement, body, attrTypeCtx);
|
|
|
|
body << " }";
|
|
|
|
}
|
|
|
|
body << "\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
/// Literals.
|
|
|
|
} else if (LiteralElement *literal = dyn_cast<LiteralElement>(element)) {
|
|
|
|
body << " if (parser.parse";
|
|
|
|
genLiteralParser(literal->getLiteral(), 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 {
|
|
|
|
attrTypeStr = "Type{}";
|
|
|
|
}
|
|
|
|
if (var->attr.isOptional()) {
|
|
|
|
body << formatv(optionalAttrParserCode, var->name, attrTypeStr);
|
|
|
|
} else {
|
|
|
|
if (var->attr.getStorageType() == "::mlir::Attribute")
|
|
|
|
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;
|
|
|
|
StringRef listName = getTypeListName(dir->getOperand(), 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 {
|
|
|
|
TypeSwitch<Element *>(dir->getOperand())
|
|
|
|
.Case<OperandVariable, ResultVariable>([&](auto operand) {
|
|
|
|
body << formatv(typeParserCode,
|
|
|
|
operand->getVar()->constraint.getCPPClassName(),
|
|
|
|
listName);
|
|
|
|
})
|
|
|
|
.Default([&](auto operand) {
|
|
|
|
body << formatv(typeParserCode, "Type", listName);
|
|
|
|
});
|
|
|
|
}
|
2020-02-22 05:19:15 +08:00
|
|
|
} else if (auto *dir = dyn_cast<FunctionalTypeDirective>(element)) {
|
2020-04-11 05:11:45 +08:00
|
|
|
ArgumentLengthKind ignored;
|
2020-02-22 05:19:15 +08:00
|
|
|
body << formatv(functionalTypeParserCode,
|
|
|
|
getTypeListName(dir->getInputs(), ignored),
|
|
|
|
getTypeListName(dir->getResults(), ignored));
|
2020-01-31 03:31:21 +08:00
|
|
|
} else {
|
2020-02-22 05:19:15 +08:00
|
|
|
llvm_unreachable("unknown format element");
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::concat<const ::mlir::OpAsmParser::OperandType>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(op.getOperands(), body, [&](auto &operand) {
|
2020-01-31 03:31:21 +08:00
|
|
|
body << operand.name << "Operands";
|
|
|
|
});
|
|
|
|
body << ")";
|
|
|
|
} else {
|
|
|
|
body << op.operand_begin()->name << "Operands";
|
|
|
|
}
|
|
|
|
body << ", allOperandTypes, parser.getNameLoc(), result.operands))\n"
|
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) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::concat<const Type>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(
|
|
|
|
llvm::seq<int>(0, op.getNumOperands()), body, [&](int i) {
|
2020-06-26 19:20:44 +08:00
|
|
|
body << "::llvm::ArrayRef<::mlir::Type>(";
|
2020-04-15 05:53:28 +08:00
|
|
|
emitTypeResolver(operandTypes[i], op.getOperand(i).name);
|
|
|
|
body << ")";
|
|
|
|
});
|
2020-01-31 03:31:21 +08:00
|
|
|
body << ")";
|
|
|
|
} else {
|
2020-02-22 05:19:03 +08:00
|
|
|
emitTypeResolver(operandTypes.front(), op.getOperand(0).name);
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
body << ", allOperandLoc, result.operands))\n"
|
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(
|
|
|
|
elements, [](auto &elt) { return isa<RegionsDirective>(elt.get()); });
|
|
|
|
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.
|
|
|
|
bool hasAllSuccessors = llvm::any_of(
|
|
|
|
elements, [](auto &elt) { return isa<SuccessorsDirective>(elt.get()); });
|
|
|
|
if (hasAllSuccessors) {
|
2020-03-06 04:48:28 +08:00
|
|
|
body << " result.addSuccessors(fullSuccessors);\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, handle each successor individually.
|
|
|
|
for (const NamedSuccessor &successor : op.getSuccessors()) {
|
2020-03-06 04:48:28 +08:00
|
|
|
if (successor.isVariadic())
|
|
|
|
body << " result.addSuccessors(" << successor.name << "Successors);\n";
|
|
|
|
else
|
|
|
|
body << " result.addSuccessors(" << successor.name << "Successor);\n";
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 04:40:53 +08:00
|
|
|
void OperationFormat::genParserVariadicSegmentResolution(Operator &op,
|
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.
|
|
|
|
static void genCustomDirectiveParameterPrinter(Element *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)) {
|
2021-10-20 22:08:36 +08:00
|
|
|
genCustomDirectiveParameterPrinter(dir->getOperand(), op, body);
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
} else if (auto *dir = dyn_cast<TypeDirective>(element)) {
|
|
|
|
auto *typeOperand = dir->getOperand();
|
|
|
|
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";
|
2020-09-01 03:33:36 +08:00
|
|
|
for (Element ¶m : customDir->getArguments()) {
|
|
|
|
body << ", ";
|
2021-10-20 22:08:36 +08:00
|
|
|
genCustomDirectiveParameterPrinter(¶m, 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.
|
2021-11-12 09:17:05 +08:00
|
|
|
static MethodBody &genTypeOperandPrinter(Element *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.
|
|
|
|
llvm::BitVector nonKeywordCases(cases.size());
|
|
|
|
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.
|
2021-10-20 22:08:36 +08:00
|
|
|
static void genOptionalGroupPrinterAnchor(Element *anchor, const Operator &op,
|
2021-11-12 09:17:05 +08:00
|
|
|
MethodBody &body) {
|
2021-01-23 04:07:07 +08:00
|
|
|
TypeSwitch<Element *>(anchor)
|
|
|
|
.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) {
|
2021-10-20 22:08:36 +08:00
|
|
|
genOptionalGroupPrinterAnchor(element->getOperand(), 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";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-12 09:17:05 +08:00
|
|
|
void OperationFormat::genElementPrinter(Element *element, MethodBody &body,
|
2020-09-01 03:33:55 +08:00
|
|
|
Operator &op, bool &shouldEmitSpace,
|
|
|
|
bool &lastWasPunctuation) {
|
2020-02-22 05:19:15 +08:00
|
|
|
if (LiteralElement *literal = dyn_cast<LiteralElement>(element))
|
|
|
|
return genLiteralPrinter(literal->getLiteral(), body, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
|
|
|
|
2020-12-15 03:53:34 +08:00
|
|
|
// Emit a whitespace element.
|
2021-01-19 13:59:15 +08:00
|
|
|
if (isa<NewlineElement>(element)) {
|
2021-11-04 06:34:13 +08:00
|
|
|
body << " _odsPrinter.printNewline();\n";
|
2020-12-15 03:53:34 +08:00
|
|
|
return;
|
|
|
|
}
|
2020-11-11 15:24:16 +08:00
|
|
|
if (SpaceElement *space = dyn_cast<SpaceElement>(element))
|
|
|
|
return genSpacePrinter(space->getValue(), body, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
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.
|
|
|
|
Element *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();
|
2020-08-04 05:20:50 +08:00
|
|
|
Element *elidedAnchorElement = nullptr;
|
|
|
|
auto *anchorAttr = dyn_cast<AttributeVariable>(anchor);
|
|
|
|
if (anchorAttr && anchorAttr != &*elements.begin() &&
|
|
|
|
anchorAttr->isUnitAttr()) {
|
|
|
|
elidedAnchorElement = anchorAttr;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
// Emit each of the elements.
|
2020-08-04 05:20:50 +08:00
|
|
|
for (Element &childElement : elements) {
|
|
|
|
if (&childElement != elidedAnchorElement) {
|
2020-09-01 03:33:55 +08:00
|
|
|
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";
|
|
|
|
for (Element &childElement : elseElements) {
|
|
|
|
genElementPrinter(&childElement, body, op, shouldEmitSpace,
|
|
|
|
lastWasPunctuation);
|
|
|
|
}
|
|
|
|
body << " }";
|
|
|
|
}
|
|
|
|
|
|
|
|
body << "\n";
|
2020-02-22 05:19:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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";
|
2021-12-08 09:24:51 +08:00
|
|
|
else if (var->attr.isOptional())
|
|
|
|
body << "_odsPrinter.printAttribute(" << op.getGetterName(var->name)
|
|
|
|
<< "Attr());\n";
|
|
|
|
else if (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)) {
|
[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 (auto *operand = dyn_cast<OperandVariable>(dir->getOperand())) {
|
|
|
|
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;
|
|
|
|
{
|
|
|
|
if (auto *operand = dyn_cast<OperandVariable>(dir->getOperand()))
|
|
|
|
var = operand->getVar();
|
|
|
|
else if (auto *operand = dyn_cast<ResultVariable>(dir->getOperand()))
|
|
|
|
var = operand->getVar();
|
|
|
|
}
|
|
|
|
if (var && !var->isVariadicOfVariadic() && !var->isVariadic() &&
|
|
|
|
!var->isOptional()) {
|
|
|
|
std::string cppClass = var->constraint.getCPPClassName();
|
|
|
|
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 << ";
|
2021-12-08 09:24:51 +08:00
|
|
|
genTypeOperandPrinter(dir->getOperand(), op, body, /*useArrayRef=*/false)
|
|
|
|
<< ";\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;
|
2020-02-22 05:19:15 +08:00
|
|
|
for (auto &element : elements)
|
2020-09-01 03:33:55 +08:00
|
|
|
genElementPrinter(element.get(), body, op, shouldEmitSpace,
|
2020-02-22 05:19:15 +08:00
|
|
|
lastWasPunctuation);
|
2020-01-31 03:31:21 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// FormatParser
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
/// Function to find an element within the given range that has the same name as
|
|
|
|
/// 'name'.
|
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.
|
|
|
|
class FormatParser {
|
|
|
|
public:
|
|
|
|
FormatParser(llvm::SourceMgr &mgr, OperationFormat &format, Operator &op)
|
2021-10-16 05:39:07 +08:00
|
|
|
: lexer(mgr, op.getLoc()[0]), curToken(lexer.lexToken()), fmt(format),
|
|
|
|
op(op), seenOperandTypes(op.getNumOperands()),
|
2020-01-31 03:30:23 +08:00
|
|
|
seenResultTypes(op.getNumResults()) {}
|
|
|
|
|
|
|
|
/// Parse the operation assembly format.
|
|
|
|
LogicalResult parse();
|
|
|
|
|
|
|
|
private:
|
2021-02-10 06:32:15 +08:00
|
|
|
/// The current context of the parser when parsing an element.
|
|
|
|
enum ParserContext {
|
|
|
|
/// The element is being parsed in a "top-level" context, i.e. at the top of
|
|
|
|
/// the format or in an optional group.
|
|
|
|
TopLevelContext,
|
|
|
|
/// The element is being parsed as a custom directive child.
|
|
|
|
CustomDirectiveContext,
|
|
|
|
/// The element is being parsed as a type directive child.
|
|
|
|
TypeDirectiveContext,
|
|
|
|
/// The element is being parsed as a reference directive child.
|
|
|
|
RefDirectiveContext
|
|
|
|
};
|
|
|
|
|
2020-02-22 05:19:03 +08:00
|
|
|
/// This struct represents a type resolution instance. It includes a specific
|
|
|
|
/// type as well as an optional transformer to apply to that type in order to
|
|
|
|
/// properly resolve the type of a variable.
|
|
|
|
struct TypeResolutionInstance {
|
2020-07-07 12:40:01 +08:00
|
|
|
ConstArgument resolver;
|
2020-02-22 05:19:03 +08:00
|
|
|
Optional<StringRef> transformer;
|
|
|
|
};
|
|
|
|
|
2020-04-08 06:51:42 +08:00
|
|
|
/// An iterator over the elements of a format group.
|
|
|
|
using ElementsIterT = llvm::pointee_iterator<
|
|
|
|
std::vector<std::unique_ptr<Element>>::const_iterator>;
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
/// Verify the state of operation attributes within the format.
|
|
|
|
LogicalResult verifyAttributes(llvm::SMLoc loc);
|
2020-04-08 06:51:42 +08:00
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
|
|
|
LogicalResult verifyAttributes(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
SmallVectorImpl<std::pair<ElementsIterT, ElementsIterT>> &iteratorStack);
|
2020-04-04 10:20:33 +08:00
|
|
|
|
|
|
|
/// Verify the state of operation operands within the format.
|
|
|
|
LogicalResult
|
|
|
|
verifyOperands(llvm::SMLoc loc,
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Verify the state of operation regions within the format.
|
|
|
|
LogicalResult verifyRegions(llvm::SMLoc loc);
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
/// Verify the state of operation results within the format.
|
|
|
|
LogicalResult
|
|
|
|
verifyResults(llvm::SMLoc loc,
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
|
|
|
|
|
|
|
/// Verify the state of operation successors within the format.
|
|
|
|
LogicalResult verifySuccessors(llvm::SMLoc loc);
|
|
|
|
|
[mlir] NFC: fix trivial typo in source files
Summary: fix trivial typos in the source files
Reviewers: mravishankar, antiagainst, nicolasvasilache, herhut, rriddle, aartbik
Reviewed By: antiagainst, rriddle
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, csigg, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, bader, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76876
2020-03-27 02:51:37 +08:00
|
|
|
/// Given the values of an `AllTypesMatch` trait, check for inferable type
|
2020-02-04 13:52:38 +08:00
|
|
|
/// resolution.
|
|
|
|
void handleAllTypesMatchConstraint(
|
|
|
|
ArrayRef<StringRef> values,
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver);
|
[mlir] NFC: fix trivial typo in source files
Summary: fix trivial typos in the source files
Reviewers: mravishankar, antiagainst, nicolasvasilache, herhut, rriddle, aartbik
Reviewed By: antiagainst, rriddle
Subscribers: mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, csigg, arpith-jacob, mgester, lucyrfox, aartbik, liufengdb, Joonsoo, bader, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D76876
2020-03-27 02:51:37 +08:00
|
|
|
/// Check for inferable type resolution given all operands, and or results,
|
2020-02-04 13:52:38 +08:00
|
|
|
/// have the same type. If 'includeResults' is true, the results also have the
|
|
|
|
/// same type as all of the operands.
|
|
|
|
void handleSameTypesConstraint(
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
2020-02-04 13:52:38 +08:00
|
|
|
bool includeResults);
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Check for inferable type resolution based on another operand, result, or
|
|
|
|
/// attribute.
|
|
|
|
void handleTypesMatchConstraint(
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
|
|
|
llvm::Record def);
|
2020-02-04 13:52:38 +08:00
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
/// Returns an argument or attribute with the given name that has been seen
|
|
|
|
/// within the format.
|
|
|
|
ConstArgument findSeenArg(StringRef name);
|
2020-02-22 05:19:03 +08:00
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
/// Parse a specific element.
|
|
|
|
LogicalResult parseElement(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context);
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult parseVariable(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context);
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult parseDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context);
|
|
|
|
LogicalResult parseLiteral(std::unique_ptr<Element> &element,
|
|
|
|
ParserContext context);
|
2020-02-22 05:19:15 +08:00
|
|
|
LogicalResult parseOptional(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context);
|
2020-02-22 05:19:15 +08:00
|
|
|
LogicalResult parseOptionalChildElement(
|
|
|
|
std::vector<std::unique_ptr<Element>> &childElements,
|
|
|
|
Optional<unsigned> &anchorIdx);
|
2021-01-23 04:07:07 +08:00
|
|
|
LogicalResult verifyOptionalChildElement(Element *element,
|
|
|
|
llvm::SMLoc childLoc, bool isAnchor);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
/// Parse the various different directives.
|
|
|
|
LogicalResult parseAttrDictDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context,
|
2020-02-22 05:19:26 +08:00
|
|
|
bool withKeyword);
|
2020-09-01 03:33:36 +08:00
|
|
|
LogicalResult parseCustomDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context);
|
2020-09-01 03:33:36 +08:00
|
|
|
LogicalResult parseCustomDirectiveParameter(
|
|
|
|
std::vector<std::unique_ptr<Element>> ¶meters);
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult parseFunctionalTypeDirective(std::unique_ptr<Element> &element,
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken tok,
|
|
|
|
ParserContext context);
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult parseOperandsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context);
|
|
|
|
LogicalResult parseReferenceDirective(std::unique_ptr<Element> &element,
|
|
|
|
llvm::SMLoc loc, ParserContext context);
|
2020-09-01 03:33:55 +08:00
|
|
|
LogicalResult parseRegionsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context);
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult parseResultsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context);
|
2020-02-22 05:20:06 +08:00
|
|
|
LogicalResult parseSuccessorsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc,
|
|
|
|
ParserContext context);
|
2021-10-16 05:39:07 +08:00
|
|
|
LogicalResult parseTypeDirective(std::unique_ptr<Element> &element,
|
|
|
|
FormatToken tok, ParserContext context);
|
2020-09-18 18:13:25 +08:00
|
|
|
LogicalResult parseTypeDirectiveOperand(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
bool isRefChild = false);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Lexer Utilities
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Advance the current lexer onto the next token.
|
|
|
|
void consumeToken() {
|
2021-10-16 05:39:07 +08:00
|
|
|
assert(curToken.getKind() != FormatToken::eof &&
|
|
|
|
curToken.getKind() != FormatToken::error &&
|
2020-01-31 03:30:23 +08:00
|
|
|
"shouldn't advance past EOF or errors");
|
|
|
|
curToken = lexer.lexToken();
|
|
|
|
}
|
2021-10-16 05:39:07 +08:00
|
|
|
LogicalResult parseToken(FormatToken::Kind kind, const Twine &msg) {
|
2020-01-31 03:30:23 +08:00
|
|
|
if (curToken.getKind() != kind)
|
|
|
|
return emitError(curToken.getLoc(), msg);
|
|
|
|
consumeToken();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
LogicalResult emitError(llvm::SMLoc loc, const Twine &msg) {
|
|
|
|
lexer.emitError(loc, msg);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2020-04-04 09:17:51 +08:00
|
|
|
LogicalResult emitErrorAndNote(llvm::SMLoc loc, const Twine &msg,
|
|
|
|
const Twine ¬e) {
|
|
|
|
lexer.emitErrorAndNote(loc, msg, note);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-04-04 09:17:51 +08:00
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// Fields
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
FormatLexer lexer;
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken curToken;
|
2020-01-31 03:30:23 +08:00
|
|
|
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
|
|
|
|
|
|
|
LogicalResult FormatParser::parse() {
|
|
|
|
llvm::SMLoc loc = curToken.getLoc();
|
|
|
|
|
|
|
|
// Parse each of the format elements into the main format.
|
2021-10-16 05:39:07 +08:00
|
|
|
while (curToken.getKind() != FormatToken::eof) {
|
2020-01-31 03:30:23 +08:00
|
|
|
std::unique_ptr<Element> element;
|
2021-02-10 06:32:15 +08:00
|
|
|
if (failed(parseElement(element, TopLevelContext)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
fmt.elements.push_back(std::move(element));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the attribute dictionary is in the format.
|
|
|
|
if (!hasAttrDict)
|
2020-04-04 09:17:51 +08:00
|
|
|
return emitError(loc, "'attr-dict' directive not found in "
|
|
|
|
"custom assembly format");
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
// Check for any type traits that we can use for inferring types.
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> variableTyResolver;
|
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);
|
2021-10-07 08:50:38 +08:00
|
|
|
} else if (def.getName() == "InferTypeOpInterface" &&
|
|
|
|
!op.allResultTypesKnown()) {
|
|
|
|
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.
|
|
|
|
if (failed(verifyAttributes(loc)) ||
|
|
|
|
failed(verifyResults(loc, variableTyResolver)) ||
|
|
|
|
failed(verifyOperands(loc, variableTyResolver)) ||
|
2020-09-01 03:33:55 +08:00
|
|
|
failed(verifyRegions(loc)) || failed(verifySuccessors(loc)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::verifyAttributes(llvm::SMLoc loc) {
|
|
|
|
// Check that there are no `:` literals after an attribute without a constant
|
|
|
|
// type. The attribute grammar contains an optional trailing colon type, which
|
|
|
|
// can lead to unexpected and generally unintended behavior. Given that, it is
|
|
|
|
// better to just error out here instead.
|
|
|
|
using ElementsIterT = llvm::pointee_iterator<
|
|
|
|
std::vector<std::unique_ptr<Element>>::const_iterator>;
|
|
|
|
SmallVector<std::pair<ElementsIterT, ElementsIterT>, 1> iteratorStack;
|
|
|
|
iteratorStack.emplace_back(fmt.elements.begin(), fmt.elements.end());
|
2020-04-08 06:51:42 +08:00
|
|
|
while (!iteratorStack.empty())
|
|
|
|
if (failed(verifyAttributes(loc, iteratorStack)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-04-08 06:51:42 +08:00
|
|
|
}
|
|
|
|
/// Verify the attribute elements at the back of the given stack of iterators.
|
|
|
|
LogicalResult FormatParser::verifyAttributes(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
SmallVectorImpl<std::pair<ElementsIterT, ElementsIterT>> &iteratorStack) {
|
|
|
|
auto &stackIt = iteratorStack.back();
|
|
|
|
ElementsIterT &it = stackIt.first, e = stackIt.second;
|
|
|
|
while (it != e) {
|
|
|
|
Element *element = &*(it++);
|
|
|
|
|
|
|
|
// Traverse into optional groups.
|
|
|
|
if (auto *optional = dyn_cast<OptionalElement>(element)) {
|
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());
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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) {
|
|
|
|
ElementsIterT nextIt = nextItPair.first, nextE = nextItPair.second;
|
|
|
|
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.
|
|
|
|
auto *literal = dyn_cast<LiteralElement>(&*nextIt);
|
|
|
|
if (!literal || literal->getLiteral() != ":")
|
|
|
|
break;
|
|
|
|
|
|
|
|
// TODO: Use the location of the literal element itself.
|
|
|
|
return emitError(
|
|
|
|
loc, llvm::formatv("format ambiguity caused by `:` literal found "
|
|
|
|
"after attribute `{0}` which does not have "
|
|
|
|
"a buildable type",
|
|
|
|
prevAttr->getVar()->name));
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-08 06:51:42 +08:00
|
|
|
iteratorStack.pop_back();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
LogicalResult FormatParser::verifyOperands(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
2020-01-31 03:30:23 +08:00
|
|
|
// Check that all of the operands are within the format, and their types can
|
|
|
|
// be inferred.
|
2020-04-04 10:20:33 +08:00
|
|
|
auto &buildableTypes = fmt.buildableTypes;
|
2020-01-31 03:30:23 +08:00
|
|
|
for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i) {
|
|
|
|
NamedTypeConstraint &operand = op.getOperand(i);
|
|
|
|
|
|
|
|
// Check that the operand itself is in the format.
|
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
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
LogicalResult FormatParser::verifyRegions(llvm::SMLoc loc) {
|
|
|
|
// Check that all of the regions are within the format.
|
|
|
|
if (hasAllRegions)
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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");
|
|
|
|
}
|
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
LogicalResult FormatParser::verifyResults(
|
|
|
|
llvm::SMLoc loc,
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
|
|
|
// If we format all of the types together, there is nothing to check.
|
|
|
|
if (fmt.allResultTypes)
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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;
|
|
|
|
return ::mlir::success();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-04-04 10:20:33 +08:00
|
|
|
}
|
2020-03-06 04:40:53 +08:00
|
|
|
|
2020-04-04 10:20:33 +08:00
|
|
|
LogicalResult FormatParser::verifySuccessors(llvm::SMLoc loc) {
|
|
|
|
// Check that all of the successors are within the format.
|
|
|
|
if (hasAllSuccessors)
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::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
|
|
|
}
|
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-04 13:52:38 +08:00
|
|
|
void FormatParser::handleAllTypesMatchConstraint(
|
|
|
|
ArrayRef<StringRef> values,
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver) {
|
2020-02-04 13:52:38 +08:00
|
|
|
for (unsigned i = 0, e = values.size(); i != e; ++i) {
|
|
|
|
// Check to see if this value matches a resolved operand or result type.
|
2020-07-07 12:40:01 +08:00
|
|
|
ConstArgument arg = findSeenArg(values[i]);
|
2020-02-22 05:19:03 +08:00
|
|
|
if (!arg)
|
2020-02-04 13:52:38 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Mark this value as the type resolver for the other variables.
|
|
|
|
for (unsigned j = 0; j != i; ++j)
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[values[j]] = {arg, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
for (unsigned j = i + 1; j != e; ++j)
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[values[j]] = {arg, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FormatParser::handleSameTypesConstraint(
|
2020-02-22 05:19:03 +08:00
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
2020-02-04 13:52:38 +08:00
|
|
|
bool includeResults) {
|
|
|
|
const NamedTypeConstraint *resolver = nullptr;
|
|
|
|
int resolvedIt = -1;
|
|
|
|
|
|
|
|
// Check to see if there is an operand or result to use for the resolution.
|
|
|
|
if ((resolvedIt = seenOperandTypes.find_first()) != -1)
|
|
|
|
resolver = &op.getOperand(resolvedIt);
|
|
|
|
else if (includeResults && (resolvedIt = seenResultTypes.find_first()) != -1)
|
|
|
|
resolver = &op.getResult(resolvedIt);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Set the resolvers for each operand and result.
|
|
|
|
for (unsigned i = 0, e = op.getNumOperands(); i != e; ++i)
|
|
|
|
if (!seenOperandTypes.test(i) && !op.getOperand(i).name.empty())
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[op.getOperand(i).name] = {resolver, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
if (includeResults) {
|
|
|
|
for (unsigned i = 0, e = op.getNumResults(); i != e; ++i)
|
|
|
|
if (!seenResultTypes.test(i) && !op.getResultName(i).empty())
|
2020-02-22 05:19:03 +08:00
|
|
|
variableTyResolver[op.getResultName(i)] = {resolver, llvm::None};
|
2020-02-04 13:52:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-07 12:40:01 +08:00
|
|
|
void FormatParser::handleTypesMatchConstraint(
|
|
|
|
llvm::StringMap<TypeResolutionInstance> &variableTyResolver,
|
|
|
|
llvm::Record def) {
|
|
|
|
StringRef lhsName = def.getValueAsString("lhs");
|
|
|
|
StringRef rhsName = def.getValueAsString("rhs");
|
|
|
|
StringRef transformer = def.getValueAsString("transformer");
|
|
|
|
if (ConstArgument arg = findSeenArg(lhsName))
|
|
|
|
variableTyResolver[rhsName] = {arg, transformer};
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstArgument FormatParser::findSeenArg(StringRef name) {
|
|
|
|
if (const NamedTypeConstraint *arg = findArg(op.getOperands(), name))
|
2020-02-22 05:19:03 +08:00
|
|
|
return seenOperandTypes.test(arg - op.operand_begin()) ? arg : nullptr;
|
2020-07-07 12:40:01 +08:00
|
|
|
if (const NamedTypeConstraint *arg = findArg(op.getResults(), name))
|
2020-02-22 05:19:03 +08:00
|
|
|
return seenResultTypes.test(arg - op.result_begin()) ? arg : nullptr;
|
2020-07-07 12:40:01 +08:00
|
|
|
if (const NamedAttribute *attr = findArg(op.getAttributes(), name))
|
2020-09-01 03:33:55 +08:00
|
|
|
return seenAttrs.count(attr) ? attr : nullptr;
|
2020-02-22 05:19:03 +08:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult FormatParser::parseElement(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context) {
|
2020-01-31 03:30:23 +08:00
|
|
|
// Directives.
|
|
|
|
if (curToken.isKeyword())
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseDirective(element, context);
|
2020-01-31 03:30:23 +08:00
|
|
|
// Literals.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (curToken.getKind() == FormatToken::literal)
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseLiteral(element, context);
|
2020-02-22 05:19:15 +08:00
|
|
|
// Optionals.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (curToken.getKind() == FormatToken::l_paren)
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseOptional(element, context);
|
2020-01-31 03:30:23 +08:00
|
|
|
// Variables.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (curToken.getKind() == FormatToken::variable)
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseVariable(element, context);
|
2020-01-31 03:30:23 +08:00
|
|
|
return emitError(curToken.getLoc(),
|
2020-02-22 05:19:15 +08:00
|
|
|
"expected directive, literal, variable, or optional group");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::parseVariable(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context) {
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken varTok = curToken;
|
2020-01-31 03:30:23 +08:00
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
StringRef name = varTok.getSpelling().drop_front();
|
|
|
|
llvm::SMLoc loc = varTok.getLoc();
|
|
|
|
|
2020-02-22 05:19:03 +08:00
|
|
|
// Check that the parsed argument is something actually registered on the
|
|
|
|
// op.
|
2020-01-31 03:30:23 +08:00
|
|
|
/// Attributes
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedAttribute *attr = findArg(op.getAttributes(), name)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TypeDirectiveContext)
|
|
|
|
return emitError(
|
|
|
|
loc, "attributes cannot be used as children to a `type` directive");
|
|
|
|
if (context == RefDirectiveContext) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<AttributeVariable>(attr);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
/// Operands
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TopLevelContext || context == 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");
|
2021-02-10 06:32:15 +08:00
|
|
|
} else if (context == RefDirectiveContext && !seenOperands.count(operand)) {
|
|
|
|
return emitError(loc, "operand '" + name +
|
|
|
|
"' must be bound before it is referenced");
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
element = std::make_unique<OperandVariable>(operand);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
/// Regions
|
|
|
|
if (const NamedRegion *region = findArg(op.getRegions(), name)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TopLevelContext || context == CustomDirectiveContext) {
|
|
|
|
if (hasAllRegions || !seenRegions.insert(region).second)
|
|
|
|
return emitError(loc, "region '" + name + "' is already bound");
|
|
|
|
} else if (context == RefDirectiveContext && !seenRegions.count(region)) {
|
|
|
|
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
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
element = std::make_unique<RegionVariable>(region);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
/// Results.
|
2020-02-04 13:52:38 +08:00
|
|
|
if (const auto *result = findArg(op.getResults(), name)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TypeDirectiveContext)
|
|
|
|
return emitError(loc, "result variables can can only be used as a child "
|
|
|
|
"to a 'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<ResultVariable>(result);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
/// Successors.
|
|
|
|
if (const auto *successor = findArg(op.getSuccessors(), name)) {
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context == TopLevelContext || context == CustomDirectiveContext) {
|
|
|
|
if (hasAllSuccessors || !seenSuccessors.insert(successor).second)
|
|
|
|
return emitError(loc, "successor '" + name + "' is already bound");
|
|
|
|
} else if (context == RefDirectiveContext &&
|
|
|
|
!seenSuccessors.count(successor)) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
element = std::make_unique<SuccessorVariable>(successor);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
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
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::parseDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context) {
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken dirTok = curToken;
|
2020-01-31 03:30:23 +08:00
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
switch (dirTok.getKind()) {
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_attr_dict:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseAttrDictDirective(element, dirTok.getLoc(), context,
|
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:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseAttrDictDirective(element, dirTok.getLoc(), context,
|
2020-02-22 05:19:26 +08:00
|
|
|
/*withKeyword=*/true);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_custom:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseCustomDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_functional_type:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseFunctionalTypeDirective(element, dirTok, context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_operands:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseOperandsDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_regions:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseRegionsDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_results:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseResultsDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_successors:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseSuccessorsDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_ref:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseReferenceDirective(element, dirTok.getLoc(), context);
|
2021-10-16 05:39:07 +08:00
|
|
|
case FormatToken::kw_type:
|
2021-02-10 06:32:15 +08:00
|
|
|
return parseTypeDirective(element, dirTok, context);
|
2020-01-31 03:30:23 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
llvm_unreachable("unknown directive token");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
LogicalResult FormatParser::parseLiteral(std::unique_ptr<Element> &element,
|
|
|
|
ParserContext context) {
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken literalTok = curToken;
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TopLevelContext) {
|
|
|
|
return emitError(
|
|
|
|
literalTok.getLoc(),
|
|
|
|
"literals may only be used in a top-level section of the format");
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
StringRef value = literalTok.getSpelling().drop_front().drop_back();
|
2020-10-18 13:40:42 +08:00
|
|
|
|
2020-11-11 15:24:16 +08:00
|
|
|
// The parsed literal is a space element (`` or ` `).
|
|
|
|
if (value.empty() || (value.size() == 1 && value.front() == ' ')) {
|
|
|
|
element = std::make_unique<SpaceElement>(!value.empty());
|
2020-10-18 13:40:42 +08:00
|
|
|
return ::mlir::success();
|
|
|
|
}
|
2020-12-15 03:53:34 +08:00
|
|
|
// The parsed literal is a newline element.
|
|
|
|
if (value == "\\n") {
|
|
|
|
element = std::make_unique<NewlineElement>();
|
|
|
|
return ::mlir::success();
|
|
|
|
}
|
2020-10-18 13:40:42 +08:00
|
|
|
|
|
|
|
// Check that the parsed literal is valid.
|
2021-12-04 08:26:49 +08:00
|
|
|
if (!isValidLiteral(value, [&](Twine diag) {
|
|
|
|
(void)emitError(literalTok.getLoc(),
|
|
|
|
"expected valid literal but got '" + value +
|
|
|
|
"': " + diag);
|
|
|
|
}))
|
|
|
|
return failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<LiteralElement>(value);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:15 +08:00
|
|
|
LogicalResult FormatParser::parseOptional(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
ParserContext context) {
|
2020-02-22 05:19:15 +08:00
|
|
|
llvm::SMLoc curLoc = curToken.getLoc();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TopLevelContext)
|
2020-02-22 05:19:15 +08:00
|
|
|
return emitError(curLoc, "optional groups can only be used as top-level "
|
|
|
|
"elements");
|
|
|
|
consumeToken();
|
|
|
|
|
|
|
|
// Parse the child elements for this optional group.
|
2021-03-23 09:07:09 +08:00
|
|
|
std::vector<std::unique_ptr<Element>> thenElements, elseElements;
|
2020-02-22 05:19:15 +08:00
|
|
|
Optional<unsigned> anchorIdx;
|
|
|
|
do {
|
2021-03-23 09:07:09 +08:00
|
|
|
if (failed(parseOptionalChildElement(thenElements, anchorIdx)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2021-10-16 05:39:07 +08:00
|
|
|
} while (curToken.getKind() != FormatToken::r_paren);
|
2020-02-22 05:19:15 +08:00
|
|
|
consumeToken();
|
2021-03-23 09:07:09 +08:00
|
|
|
|
|
|
|
// Parse the `else` elements of this optional group.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (curToken.getKind() == FormatToken::colon) {
|
2021-03-23 09:07:09 +08:00
|
|
|
consumeToken();
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' to start else branch "
|
|
|
|
"of optional group")))
|
2021-03-23 09:07:09 +08:00
|
|
|
return failure();
|
|
|
|
do {
|
|
|
|
llvm::SMLoc childLoc = curToken.getLoc();
|
|
|
|
elseElements.push_back({});
|
|
|
|
if (failed(parseElement(elseElements.back(), TopLevelContext)) ||
|
|
|
|
failed(verifyOptionalChildElement(elseElements.back().get(), childLoc,
|
|
|
|
/*isAnchor=*/false)))
|
|
|
|
return failure();
|
2021-10-16 05:39:07 +08:00
|
|
|
} while (curToken.getKind() != FormatToken::r_paren);
|
2021-03-23 09:07:09 +08:00
|
|
|
consumeToken();
|
|
|
|
}
|
|
|
|
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::question,
|
|
|
|
"expected '?' after optional group")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
// The optional group is required to have an anchor.
|
|
|
|
if (!anchorIdx)
|
|
|
|
return emitError(curLoc, "optional group specified no anchor element");
|
|
|
|
|
2020-11-11 15:24:16 +08:00
|
|
|
// The first parsable element of the group must be able to be parsed in an
|
2020-02-22 05:19:15 +08:00
|
|
|
// optional fashion.
|
2021-03-23 09:07:09 +08:00
|
|
|
auto parseBegin = llvm::find_if_not(thenElements, [](auto &element) {
|
2020-12-15 03:53:34 +08:00
|
|
|
return isa<WhitespaceElement>(element.get());
|
|
|
|
});
|
2020-11-11 15:24:16 +08:00
|
|
|
Element *firstElement = parseBegin->get();
|
2020-07-15 04:14:14 +08:00
|
|
|
if (!isa<AttributeVariable>(firstElement) &&
|
2020-09-01 03:33:55 +08:00
|
|
|
!isa<LiteralElement>(firstElement) &&
|
|
|
|
!isa<OperandVariable>(firstElement) && !isa<RegionVariable>(firstElement))
|
2020-11-11 15:24:16 +08:00
|
|
|
return emitError(curLoc,
|
|
|
|
"first parsable element of an operand group must be "
|
|
|
|
"an attribute, literal, operand, or region");
|
2020-02-22 05:19:15 +08:00
|
|
|
|
2021-03-23 09:07:09 +08:00
|
|
|
auto parseStart = parseBegin - thenElements.begin();
|
|
|
|
element = std::make_unique<OptionalElement>(
|
|
|
|
std::move(thenElements), std::move(elseElements), *anchorIdx, parseStart);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-02-22 05:19:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::parseOptionalChildElement(
|
|
|
|
std::vector<std::unique_ptr<Element>> &childElements,
|
|
|
|
Optional<unsigned> &anchorIdx) {
|
|
|
|
llvm::SMLoc childLoc = curToken.getLoc();
|
|
|
|
childElements.push_back({});
|
2021-02-10 06:32:15 +08:00
|
|
|
if (failed(parseElement(childElements.back(), TopLevelContext)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-02-22 05:19:15 +08:00
|
|
|
|
|
|
|
// Check to see if this element is the anchor of the optional group.
|
2021-10-16 05:39:07 +08:00
|
|
|
bool isAnchor = curToken.getKind() == FormatToken::caret;
|
2020-02-22 05:19:15 +08:00
|
|
|
if (isAnchor) {
|
|
|
|
if (anchorIdx)
|
|
|
|
return emitError(childLoc, "only one element can be marked as the anchor "
|
|
|
|
"of an optional group");
|
|
|
|
anchorIdx = childElements.size() - 1;
|
|
|
|
consumeToken();
|
|
|
|
}
|
|
|
|
|
2021-01-23 04:07:07 +08:00
|
|
|
return verifyOptionalChildElement(childElements.back().get(), childLoc,
|
|
|
|
isAnchor);
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::verifyOptionalChildElement(Element *element,
|
|
|
|
llvm::SMLoc childLoc,
|
|
|
|
bool isAnchor) {
|
|
|
|
return TypeSwitch<Element *, LogicalResult>(element)
|
2020-02-22 05:19:15 +08:00
|
|
|
// All attributes can be within the optional group, but only optional
|
|
|
|
// attributes can be the anchor.
|
|
|
|
.Case([&](AttributeVariable *attrEle) {
|
|
|
|
if (isAnchor && !attrEle->getVar()->attr.isOptional())
|
|
|
|
return emitError(childLoc, "only optional attributes can be used to "
|
|
|
|
"anchor an optional group");
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-02-22 05:19:15 +08:00
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) operands can be within an optional
|
|
|
|
// group.
|
2021-02-10 06:32:15 +08:00
|
|
|
.Case([&](OperandVariable *ele) {
|
2020-04-11 05:11:45 +08:00
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(childLoc, "only variable length operands can be "
|
|
|
|
"used within an optional group");
|
2021-01-23 04:07:07 +08:00
|
|
|
return ::mlir::success();
|
|
|
|
})
|
|
|
|
// Only optional-like(i.e. variadic) results can be within an optional
|
|
|
|
// group.
|
2021-02-10 06:32:15 +08:00
|
|
|
.Case([&](ResultVariable *ele) {
|
2021-01-23 04:07:07 +08:00
|
|
|
if (!ele->getVar()->isVariableLength())
|
|
|
|
return emitError(childLoc, "only variable length results can be "
|
|
|
|
"used within an optional group");
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-02-22 05:19:15 +08:00
|
|
|
})
|
2021-02-10 06:32:15 +08:00
|
|
|
.Case([&](RegionVariable *) {
|
2020-09-01 03:33:55 +08:00
|
|
|
// TODO: When ODS has proper support for marking "optional" regions, add
|
|
|
|
// a check here.
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:55 +08:00
|
|
|
})
|
2021-02-10 06:32:15 +08:00
|
|
|
.Case([&](TypeDirective *ele) {
|
2021-01-23 04:07:07 +08:00
|
|
|
return verifyOptionalChildElement(ele->getOperand(), childLoc,
|
|
|
|
/*isAnchor=*/false);
|
|
|
|
})
|
2021-02-10 06:32:15 +08:00
|
|
|
.Case([&](FunctionalTypeDirective *ele) {
|
2021-01-23 04:07:07 +08:00
|
|
|
if (failed(verifyOptionalChildElement(ele->getInputs(), childLoc,
|
|
|
|
/*isAnchor=*/false)))
|
|
|
|
return failure();
|
|
|
|
return verifyOptionalChildElement(ele->getResults(), childLoc,
|
|
|
|
/*isAnchor=*/false);
|
2020-09-01 03:33:36 +08:00
|
|
|
})
|
2021-01-23 04:07:07 +08:00
|
|
|
// Literals, whitespace, and custom directives may be used, but they can't
|
|
|
|
// anchor the group.
|
|
|
|
.Case<LiteralElement, WhitespaceElement, CustomDirective,
|
2021-02-10 06:32:15 +08:00
|
|
|
FunctionalTypeDirective, OptionalElement>([&](Element *) {
|
|
|
|
if (isAnchor)
|
|
|
|
return emitError(childLoc, "only variables and types can be used "
|
|
|
|
"to anchor an optional group");
|
|
|
|
return ::mlir::success();
|
|
|
|
})
|
2020-02-22 08:09:58 +08:00
|
|
|
.Default([&](Element *) {
|
2020-02-22 05:19:15 +08:00
|
|
|
return emitError(childLoc, "only literals, types, and variables can be "
|
|
|
|
"used within an optional group");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseAttrDictDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context,
|
2020-02-22 05:19:26 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:26 +08:00
|
|
|
element = std::make_unique<AttrDictDirective>(withKeyword);
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:36 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseCustomDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
2020-09-01 03:33:36 +08:00
|
|
|
llvm::SMLoc curLoc = curToken.getLoc();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (context != TopLevelContext)
|
|
|
|
return emitError(loc, "'custom' is only valid as a top-level directive");
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// Parse the custom directive name.
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::less,
|
|
|
|
"expected '<' before custom directive name")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:36 +08:00
|
|
|
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatToken nameTok = curToken;
|
|
|
|
if (failed(parseToken(FormatToken::identifier,
|
2020-09-01 03:33:36 +08:00
|
|
|
"expected custom directive name identifier")) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(parseToken(FormatToken::greater,
|
2020-09-01 03:33:36 +08:00
|
|
|
"expected '>' after custom directive name")) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(parseToken(FormatToken::l_paren,
|
2020-09-01 03:33:36 +08:00
|
|
|
"expected '(' before custom directive parameters")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// Parse the child elements for this optional group.=
|
|
|
|
std::vector<std::unique_ptr<Element>> elements;
|
|
|
|
do {
|
|
|
|
if (failed(parseCustomDirectiveParameter(elements)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2021-10-16 05:39:07 +08:00
|
|
|
if (curToken.getKind() != FormatToken::comma)
|
2020-09-01 03:33:36 +08:00
|
|
|
break;
|
|
|
|
consumeToken();
|
|
|
|
} while (true);
|
|
|
|
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::r_paren,
|
2020-09-01 03:33:36 +08:00
|
|
|
"expected ')' after custom directive parameters")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// After parsing all of the elements, ensure that all type directives refer
|
|
|
|
// only to variables.
|
|
|
|
for (auto &ele : elements) {
|
|
|
|
if (auto *typeEle = dyn_cast<TypeDirective>(ele.get())) {
|
|
|
|
if (!isa<OperandVariable, ResultVariable>(typeEle->getOperand())) {
|
|
|
|
return emitError(curLoc, "type directives within a custom directive "
|
|
|
|
"may only refer to variables");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
element = std::make_unique<CustomDirective>(nameTok.getSpelling(),
|
|
|
|
std::move(elements));
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult FormatParser::parseCustomDirectiveParameter(
|
|
|
|
std::vector<std::unique_ptr<Element>> ¶meters) {
|
|
|
|
llvm::SMLoc childLoc = curToken.getLoc();
|
|
|
|
parameters.push_back({});
|
2021-02-10 06:32:15 +08:00
|
|
|
if (failed(parseElement(parameters.back(), CustomDirectiveContext)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-09-01 03:33:36 +08:00
|
|
|
|
|
|
|
// Verify that the element can be placed within a custom directive.
|
2021-02-10 06:32:15 +08:00
|
|
|
if (!isa<RefDirective, TypeDirective, AttrDictDirective, AttributeVariable,
|
|
|
|
OperandVariable, RegionVariable, SuccessorVariable>(
|
|
|
|
parameters.back().get())) {
|
2020-09-01 03:33:36 +08:00
|
|
|
return emitError(childLoc, "only variables and types may be used as "
|
|
|
|
"parameters to a custom directive");
|
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:36 +08:00
|
|
|
}
|
|
|
|
|
2021-10-16 05:39:07 +08:00
|
|
|
LogicalResult FormatParser::parseFunctionalTypeDirective(
|
|
|
|
std::unique_ptr<Element> &element, FormatToken tok, ParserContext context) {
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::SMLoc loc = tok.getLoc();
|
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.
|
|
|
|
std::unique_ptr<Element> inputs, results;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2020-01-31 03:30:23 +08:00
|
|
|
failed(parseTypeDirectiveOperand(inputs)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(parseToken(FormatToken::comma,
|
|
|
|
"expected ',' after inputs argument")) ||
|
2020-01-31 03:30:23 +08:00
|
|
|
failed(parseTypeDirectiveOperand(results)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<FunctionalTypeDirective>(std::move(inputs),
|
|
|
|
std::move(results));
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseOperandsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
|
|
|
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;
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<OperandsDirective>();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2021-02-10 06:32:15 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseReferenceDirective(std::unique_ptr<Element> &element,
|
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
|
|
|
if (context != CustomDirectiveContext)
|
|
|
|
return emitError(loc, "'ref' is only valid within a `custom` directive");
|
|
|
|
|
|
|
|
std::unique_ptr<Element> operand;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2021-02-10 06:32:15 +08:00
|
|
|
failed(parseElement(operand, RefDirectiveContext)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2021-02-10 06:32:15 +08:00
|
|
|
return ::mlir::failure();
|
|
|
|
|
|
|
|
element = std::make_unique<RefDirective>(std::move(operand));
|
|
|
|
return ::mlir::success();
|
|
|
|
}
|
|
|
|
|
2020-09-01 03:33:55 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseRegionsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
|
|
|
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;
|
|
|
|
}
|
2020-09-01 03:33:55 +08:00
|
|
|
element = std::make_unique<RegionsDirective>();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-09-01 03:33:55 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseResultsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
|
|
|
if (context != TypeDirectiveContext)
|
|
|
|
return emitError(loc, "'results' directive can can only be used as a child "
|
|
|
|
"to a 'type' directive");
|
2020-01-31 03:30:23 +08:00
|
|
|
element = std::make_unique<ResultsDirective>();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
2020-02-22 05:20:06 +08:00
|
|
|
LogicalResult
|
|
|
|
FormatParser::parseSuccessorsDirective(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
llvm::SMLoc loc, ParserContext context) {
|
|
|
|
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;
|
|
|
|
}
|
2020-02-22 05:20:06 +08:00
|
|
|
element = std::make_unique<SuccessorsDirective>();
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-02-22 05:20:06 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 03:30:23 +08:00
|
|
|
LogicalResult
|
2021-10-16 05:39:07 +08:00
|
|
|
FormatParser::parseTypeDirective(std::unique_ptr<Element> &element,
|
|
|
|
FormatToken tok, ParserContext context) {
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::SMLoc loc = tok.getLoc();
|
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;
|
2020-01-31 03:30:23 +08:00
|
|
|
std::unique_ptr<Element> operand;
|
2021-10-16 05:39:07 +08:00
|
|
|
if (failed(parseToken(FormatToken::l_paren,
|
|
|
|
"expected '(' before argument list")) ||
|
2021-02-10 06:32:15 +08:00
|
|
|
failed(parseTypeDirectiveOperand(operand, isRefChild)) ||
|
2021-10-16 05:39:07 +08:00
|
|
|
failed(
|
|
|
|
parseToken(FormatToken::r_paren, "expected ')' after argument list")))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2021-02-10 06:32:15 +08:00
|
|
|
|
|
|
|
element = std::make_unique<TypeDirective>(std::move(operand));
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult
|
2020-09-18 18:13:25 +08:00
|
|
|
FormatParser::parseTypeDirectiveOperand(std::unique_ptr<Element> &element,
|
2021-02-10 06:32:15 +08:00
|
|
|
bool isRefChild) {
|
2020-01-31 03:30:23 +08:00
|
|
|
llvm::SMLoc loc = curToken.getLoc();
|
2021-02-10 06:32:15 +08:00
|
|
|
if (failed(parseElement(element, TypeDirectiveContext)))
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::failure();
|
2020-01-31 03:30:23 +08:00
|
|
|
if (isa<LiteralElement>(element.get()))
|
|
|
|
return emitError(
|
|
|
|
loc, "'type' directive operand expects variable or directive operand");
|
|
|
|
|
|
|
|
if (auto *var = dyn_cast<OperandVariable>(element.get())) {
|
|
|
|
unsigned opIdx = var->getVar() - op.operand_begin();
|
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);
|
|
|
|
} else if (auto *var = dyn_cast<ResultVariable>(element.get())) {
|
|
|
|
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");
|
|
|
|
}
|
2020-09-15 04:01:07 +08:00
|
|
|
return ::mlir::success();
|
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(
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(op.getAssemblyFormat()), llvm::SMLoc());
|
2020-01-31 03:30:23 +08:00
|
|
|
OperationFormat format(op);
|
2020-02-04 14:14:33 +08:00
|
|
|
if (failed(FormatParser(mgr, format, op).parse())) {
|
|
|
|
// Exit the process if format errors are treated as fatal.
|
|
|
|
if (formatErrorIsFatal) {
|
|
|
|
// Invoke the interrupt handlers to run the file cleanup handlers.
|
|
|
|
llvm::sys::RunInterruptHandlers();
|
|
|
|
std::exit(1);
|
|
|
|
}
|
2020-01-31 03:30:23 +08:00
|
|
|
return;
|
2020-02-04 14:14:33 +08:00
|
|
|
}
|
2020-01-31 03:31:21 +08:00
|
|
|
|
|
|
|
// Generate the printer and parser based on the parsed format.
|
|
|
|
format.genParser(op, opClass);
|
|
|
|
format.genPrinter(op, opClass);
|
2020-01-31 03:30:23 +08:00
|
|
|
}
|