2018-12-29 04:02:08 +08:00
|
|
|
//===- Operator.cpp - Operator class --------------------------------------===//
|
|
|
|
//
|
2020-01-26 11:58:30 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-24 01:35:36 +08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-12-29 04:02:08 +08:00
|
|
|
//
|
2019-12-24 01:35:36 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-12-29 04:02:08 +08:00
|
|
|
//
|
2019-01-08 02:09:34 +08:00
|
|
|
// Operator wrapper to simplify using TableGen Record defining a MLIR Op.
|
2018-12-29 04:02:08 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/TableGen/Operator.h"
|
2019-02-21 02:50:26 +08:00
|
|
|
#include "mlir/TableGen/OpTrait.h"
|
2019-01-06 00:11:29 +08:00
|
|
|
#include "mlir/TableGen/Predicate.h"
|
2019-01-09 09:19:22 +08:00
|
|
|
#include "mlir/TableGen/Type.h"
|
2020-05-27 23:45:55 +08:00
|
|
|
#include "llvm/ADT/EquivalenceClasses.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/ADT/Sequence.h"
|
2019-12-20 08:43:35 +08:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
2020-04-15 05:53:50 +08:00
|
|
|
#include "llvm/ADT/TypeSwitch.h"
|
2020-03-01 09:11:12 +08:00
|
|
|
#include "llvm/Support/Debug.h"
|
2018-12-29 04:02:08 +08:00
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
#include "llvm/TableGen/Error.h"
|
|
|
|
#include "llvm/TableGen/Record.h"
|
|
|
|
|
2019-11-15 03:02:52 +08:00
|
|
|
#define DEBUG_TYPE "mlir-tblgen-operator"
|
|
|
|
|
2018-12-29 04:02:08 +08:00
|
|
|
using namespace mlir;
|
2019-01-09 09:19:37 +08:00
|
|
|
|
2018-12-29 04:02:08 +08:00
|
|
|
using llvm::DagInit;
|
|
|
|
using llvm::DefInit;
|
|
|
|
using llvm::Record;
|
|
|
|
|
2019-05-21 00:33:10 +08:00
|
|
|
tblgen::Operator::Operator(const llvm::Record &def)
|
|
|
|
: dialect(def.getValueAsDef("opDialect")), def(def) {
|
|
|
|
// The first `_` in the op's TableGen def name is treated as separating the
|
|
|
|
// dialect prefix and the op class name. The dialect prefix will be ignored if
|
|
|
|
// not empty. Otherwise, if def name starts with a `_`, the `_` is considered
|
|
|
|
// as part of the class name.
|
|
|
|
StringRef prefix;
|
|
|
|
std::tie(prefix, cppClassName) = def.getName().split('_');
|
|
|
|
if (prefix.empty()) {
|
|
|
|
// Class name with a leading underscore and without dialect prefix
|
2019-04-16 00:13:22 +08:00
|
|
|
cppClassName = def.getName();
|
|
|
|
} else if (cppClassName.empty()) {
|
2019-05-21 00:33:10 +08:00
|
|
|
// Class name without dialect prefix
|
|
|
|
cppClassName = prefix;
|
2019-04-16 00:13:22 +08:00
|
|
|
}
|
2018-12-29 04:02:08 +08:00
|
|
|
|
2019-04-16 00:13:22 +08:00
|
|
|
populateOpStructure();
|
2018-12-29 04:02:08 +08:00
|
|
|
}
|
|
|
|
|
2019-04-30 00:24:09 +08:00
|
|
|
std::string tblgen::Operator::getOperationName() const {
|
2019-05-21 00:33:10 +08:00
|
|
|
auto prefix = dialect.getName();
|
|
|
|
auto opName = def.getValueAsString("opName");
|
2019-04-30 00:24:09 +08:00
|
|
|
if (prefix.empty())
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(opName);
|
|
|
|
return std::string(llvm::formatv("{0}.{1}", prefix, opName));
|
2018-12-29 04:02:08 +08:00
|
|
|
}
|
|
|
|
|
2020-05-29 00:05:24 +08:00
|
|
|
std::string tblgen::Operator::getAdaptorName() const {
|
|
|
|
return std::string(llvm::formatv("{0}OperandAdaptor", getCppClassName()));
|
|
|
|
}
|
|
|
|
|
2019-05-21 00:33:10 +08:00
|
|
|
StringRef tblgen::Operator::getDialectName() const { return dialect.getName(); }
|
2019-04-16 00:13:22 +08:00
|
|
|
|
2019-05-21 00:33:10 +08:00
|
|
|
StringRef tblgen::Operator::getCppClassName() const { return cppClassName; }
|
|
|
|
|
2019-01-30 01:27:04 +08:00
|
|
|
std::string tblgen::Operator::getQualCppClassName() const {
|
2019-05-21 00:33:10 +08:00
|
|
|
auto prefix = dialect.getCppNamespace();
|
|
|
|
if (prefix.empty())
|
2020-01-29 03:23:46 +08:00
|
|
|
return std::string(cppClassName);
|
|
|
|
return std::string(llvm::formatv("{0}::{1}", prefix, cppClassName));
|
2019-01-03 08:11:42 +08:00
|
|
|
}
|
2018-12-29 04:02:08 +08:00
|
|
|
|
2019-01-30 02:03:17 +08:00
|
|
|
int tblgen::Operator::getNumResults() const {
|
|
|
|
DagInit *results = def.getValueAsDag("results");
|
|
|
|
return results->getNumArgs();
|
|
|
|
}
|
|
|
|
|
2019-04-30 14:12:40 +08:00
|
|
|
StringRef tblgen::Operator::getExtraClassDeclaration() const {
|
|
|
|
constexpr auto attr = "extraClassDeclaration";
|
|
|
|
if (def.isValueUnset(attr))
|
|
|
|
return {};
|
|
|
|
return def.getValueAsString(attr);
|
|
|
|
}
|
|
|
|
|
2019-06-03 23:03:20 +08:00
|
|
|
const llvm::Record &tblgen::Operator::getDef() const { return def; }
|
|
|
|
|
2019-07-05 17:27:39 +08:00
|
|
|
bool tblgen::Operator::skipDefaultBuilders() const {
|
|
|
|
return def.getValueAsBit("skipDefaultBuilders");
|
|
|
|
}
|
|
|
|
|
2019-06-04 20:03:42 +08:00
|
|
|
auto tblgen::Operator::result_begin() -> value_iterator {
|
|
|
|
return results.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto tblgen::Operator::result_end() -> value_iterator { return results.end(); }
|
|
|
|
|
2019-06-09 22:00:09 +08:00
|
|
|
auto tblgen::Operator::getResults() -> value_range {
|
2019-06-04 20:03:42 +08:00
|
|
|
return {result_begin(), result_end()};
|
|
|
|
}
|
|
|
|
|
2019-03-18 22:54:20 +08:00
|
|
|
tblgen::TypeConstraint
|
|
|
|
tblgen::Operator::getResultTypeConstraint(int index) const {
|
2019-02-21 02:50:26 +08:00
|
|
|
DagInit *results = def.getValueAsDag("results");
|
[TableGen] Consolidate constraint related concepts
Previously we have multiple mechanisms to specify op definition and match constraints:
TypeConstraint, AttributeConstraint, Type, Attr, mAttr, mAttrAnyOf, mPat. These variants
are not added because there are so many distinct cases we need to model; essentially,
they are all carrying a predicate. It's just an artifact of implementation.
It's quite confusing for users to grasp these variants and choose among them. Instead,
as the OpBase TableGen file, we need to strike to provide an unified mechanism. Each
dialect has the flexibility to define its own aliases if wanted.
This CL removes mAttr, mAttrAnyOf, mPat. A new base class, Constraint, is added. Now
TypeConstraint and AttrConstraint derive from Constraint. Type and Attr further derive
from TypeConstraint and AttrConstraint, respectively.
Comments are revised and examples are added to make it clear how to use constraints.
PiperOrigin-RevId: 240125076
2019-03-25 21:09:26 +08:00
|
|
|
return TypeConstraint(cast<DefInit>(results->getArg(index)));
|
2019-01-30 02:03:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
StringRef tblgen::Operator::getResultName(int index) const {
|
2019-02-21 02:50:26 +08:00
|
|
|
DagInit *results = def.getValueAsDag("results");
|
|
|
|
return results->getArgNameStr(index);
|
2019-02-19 23:15:59 +08:00
|
|
|
}
|
|
|
|
|
2020-03-07 05:55:36 +08:00
|
|
|
auto tblgen::Operator::getResultDecorators(int index) const
|
|
|
|
-> var_decorator_range {
|
|
|
|
Record *result =
|
|
|
|
cast<DefInit>(def.getValueAsDag("results")->getArg(index))->getDef();
|
|
|
|
if (!result->isSubClassOf("OpVariable"))
|
|
|
|
return var_decorator_range(nullptr, nullptr);
|
|
|
|
return *result->getValueAsListInit("decorators");
|
|
|
|
}
|
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
unsigned tblgen::Operator::getNumVariableLengthResults() const {
|
|
|
|
return llvm::count_if(results, [](const NamedTypeConstraint &c) {
|
|
|
|
return c.constraint.isVariableLength();
|
|
|
|
});
|
2019-01-30 02:03:17 +08:00
|
|
|
}
|
|
|
|
|
2020-04-11 05:11:45 +08:00
|
|
|
unsigned tblgen::Operator::getNumVariableLengthOperands() const {
|
|
|
|
return llvm::count_if(operands, [](const NamedTypeConstraint &c) {
|
|
|
|
return c.constraint.isVariableLength();
|
|
|
|
});
|
2019-02-06 21:06:11 +08:00
|
|
|
}
|
|
|
|
|
2019-10-22 11:47:49 +08:00
|
|
|
tblgen::Operator::arg_iterator tblgen::Operator::arg_begin() const {
|
|
|
|
return arguments.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
tblgen::Operator::arg_iterator tblgen::Operator::arg_end() const {
|
|
|
|
return arguments.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
tblgen::Operator::arg_range tblgen::Operator::getArgs() const {
|
|
|
|
return {arg_begin(), arg_end()};
|
|
|
|
}
|
|
|
|
|
2019-01-09 09:19:37 +08:00
|
|
|
StringRef tblgen::Operator::getArgName(int index) const {
|
2018-12-29 04:02:08 +08:00
|
|
|
DagInit *argumentValues = def.getValueAsDag("arguments");
|
|
|
|
return argumentValues->getArgName(index)->getValue();
|
|
|
|
}
|
|
|
|
|
2020-03-07 05:55:36 +08:00
|
|
|
auto tblgen::Operator::getArgDecorators(int index) const
|
|
|
|
-> var_decorator_range {
|
|
|
|
Record *arg =
|
|
|
|
cast<DefInit>(def.getValueAsDag("arguments")->getArg(index))->getDef();
|
|
|
|
if (!arg->isSubClassOf("OpVariable"))
|
|
|
|
return var_decorator_range(nullptr, nullptr);
|
|
|
|
return *arg->getValueAsListInit("decorators");
|
|
|
|
}
|
|
|
|
|
2019-11-26 09:26:16 +08:00
|
|
|
const tblgen::OpTrait *tblgen::Operator::getTrait(StringRef trait) const {
|
|
|
|
for (const auto &t : traits) {
|
2020-05-27 23:45:55 +08:00
|
|
|
if (const auto *opTrait = dyn_cast<tblgen::NativeOpTrait>(&t)) {
|
2019-02-21 02:50:26 +08:00
|
|
|
if (opTrait->getTrait() == trait)
|
2019-11-26 09:26:16 +08:00
|
|
|
return opTrait;
|
2020-05-27 23:45:55 +08:00
|
|
|
} else if (const auto *opTrait = dyn_cast<tblgen::InternalOpTrait>(&t)) {
|
2019-03-27 06:31:15 +08:00
|
|
|
if (opTrait->getTrait() == trait)
|
2019-11-26 09:26:16 +08:00
|
|
|
return opTrait;
|
2020-05-27 23:45:55 +08:00
|
|
|
} else if (const auto *opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&t)) {
|
2019-11-22 06:34:03 +08:00
|
|
|
if (opTrait->getTrait() == trait)
|
2019-11-26 09:26:16 +08:00
|
|
|
return opTrait;
|
2019-03-27 06:31:15 +08:00
|
|
|
}
|
2019-02-21 02:50:26 +08:00
|
|
|
}
|
2019-11-26 09:26:16 +08:00
|
|
|
return nullptr;
|
2019-02-15 02:54:50 +08:00
|
|
|
}
|
|
|
|
|
2020-04-05 16:03:24 +08:00
|
|
|
auto tblgen::Operator::region_begin() const -> const_region_iterator {
|
|
|
|
return regions.begin();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::region_end() const -> const_region_iterator {
|
|
|
|
return regions.end();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::getRegions() const
|
|
|
|
-> llvm::iterator_range<const_region_iterator> {
|
|
|
|
return {region_begin(), region_end()};
|
|
|
|
}
|
|
|
|
|
2019-05-31 07:50:16 +08:00
|
|
|
unsigned tblgen::Operator::getNumRegions() const { return regions.size(); }
|
|
|
|
|
|
|
|
const tblgen::NamedRegion &tblgen::Operator::getRegion(unsigned index) const {
|
|
|
|
return regions[index];
|
|
|
|
}
|
2019-05-28 23:03:46 +08:00
|
|
|
|
2020-04-05 16:03:24 +08:00
|
|
|
unsigned tblgen::Operator::getNumVariadicRegions() const {
|
|
|
|
return llvm::count_if(regions,
|
|
|
|
[](const NamedRegion &c) { return c.isVariadic(); });
|
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:50 +08:00
|
|
|
auto tblgen::Operator::successor_begin() const -> const_successor_iterator {
|
|
|
|
return successors.begin();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::successor_end() const -> const_successor_iterator {
|
|
|
|
return successors.end();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::getSuccessors() const
|
|
|
|
-> llvm::iterator_range<const_successor_iterator> {
|
|
|
|
return {successor_begin(), successor_end()};
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned tblgen::Operator::getNumSuccessors() const {
|
|
|
|
return successors.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
const tblgen::NamedSuccessor &
|
|
|
|
tblgen::Operator::getSuccessor(unsigned index) const {
|
|
|
|
return successors[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned tblgen::Operator::getNumVariadicSuccessors() const {
|
|
|
|
return llvm::count_if(successors,
|
|
|
|
[](const NamedSuccessor &c) { return c.isVariadic(); });
|
|
|
|
}
|
|
|
|
|
2019-02-21 02:50:26 +08:00
|
|
|
auto tblgen::Operator::trait_begin() const -> const_trait_iterator {
|
|
|
|
return traits.begin();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::trait_end() const -> const_trait_iterator {
|
|
|
|
return traits.end();
|
|
|
|
}
|
|
|
|
auto tblgen::Operator::getTraits() const
|
|
|
|
-> llvm::iterator_range<const_trait_iterator> {
|
|
|
|
return {trait_begin(), trait_end()};
|
|
|
|
}
|
|
|
|
|
2019-02-06 04:02:53 +08:00
|
|
|
auto tblgen::Operator::attribute_begin() const -> attribute_iterator {
|
2018-12-29 04:02:08 +08:00
|
|
|
return attributes.begin();
|
|
|
|
}
|
2019-02-06 04:02:53 +08:00
|
|
|
auto tblgen::Operator::attribute_end() const -> attribute_iterator {
|
2018-12-29 04:02:08 +08:00
|
|
|
return attributes.end();
|
|
|
|
}
|
2019-02-06 04:02:53 +08:00
|
|
|
auto tblgen::Operator::getAttributes() const
|
2019-01-09 09:19:37 +08:00
|
|
|
-> llvm::iterator_range<attribute_iterator> {
|
2018-12-29 04:02:08 +08:00
|
|
|
return {attribute_begin(), attribute_end()};
|
|
|
|
}
|
|
|
|
|
2019-06-04 20:03:42 +08:00
|
|
|
auto tblgen::Operator::operand_begin() -> value_iterator {
|
2019-01-09 09:19:37 +08:00
|
|
|
return operands.begin();
|
|
|
|
}
|
2019-06-04 20:03:42 +08:00
|
|
|
auto tblgen::Operator::operand_end() -> value_iterator {
|
2019-01-09 09:19:37 +08:00
|
|
|
return operands.end();
|
|
|
|
}
|
2019-06-09 22:00:09 +08:00
|
|
|
auto tblgen::Operator::getOperands() -> value_range {
|
2018-12-29 04:02:08 +08:00
|
|
|
return {operand_begin(), operand_end()};
|
|
|
|
}
|
|
|
|
|
2019-07-18 09:41:28 +08:00
|
|
|
auto tblgen::Operator::getArg(int index) const -> Argument {
|
2019-03-06 17:23:41 +08:00
|
|
|
return arguments[index];
|
2018-12-29 04:02:08 +08:00
|
|
|
}
|
|
|
|
|
2020-05-27 23:45:55 +08:00
|
|
|
// Mapping from result index to combined argument and result index. Arguments
|
|
|
|
// are indexed to match getArg index, while the result indexes are mapped to
|
|
|
|
// avoid overlap.
|
|
|
|
static int resultIndex(int i) { return -1 - i; }
|
|
|
|
|
|
|
|
bool tblgen::Operator::isVariadic() const {
|
|
|
|
return any_of(llvm::concat<const NamedTypeConstraint>(operands, results),
|
|
|
|
[](const NamedTypeConstraint &op) { return op.isVariadic(); });
|
|
|
|
}
|
|
|
|
|
|
|
|
void tblgen::Operator::populateTypeInferenceInfo(
|
|
|
|
const llvm::StringMap<int> &argumentsAndResultsIndex) {
|
|
|
|
// If the type inference op interface is not registered, then do not attempt
|
|
|
|
// to determine if the result types an be inferred.
|
|
|
|
auto &recordKeeper = def.getRecords();
|
|
|
|
auto *inferTrait = recordKeeper.getDef(inferTypeOpInterface);
|
|
|
|
allResultsHaveKnownTypes = false;
|
|
|
|
if (!inferTrait)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If there are no results, the skip this else the build method generated
|
|
|
|
// overlaps with another autogenerated builder.
|
|
|
|
if (getNumResults() == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Skip for ops with variadic operands/results.
|
|
|
|
// TODO: This can be relaxed.
|
|
|
|
if (isVariadic())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Skip cases currently being custom generated.
|
|
|
|
// TODO: Remove special cases.
|
|
|
|
if (getTrait("OpTrait::SameOperandsAndResultType"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We create equivalence classes of argument/result types where arguments
|
|
|
|
// and results are mapped into the same index space and indices corresponding
|
|
|
|
// to the same type are in the same equivalence class.
|
|
|
|
llvm::EquivalenceClasses<int> ecs;
|
|
|
|
resultTypeMapping.resize(getNumResults());
|
|
|
|
// Captures the argument whose type matches a given result type. Preference
|
|
|
|
// towards capturing operands first before attributes.
|
|
|
|
auto captureMapping = [&](int i) {
|
|
|
|
bool found = false;
|
|
|
|
ecs.insert(resultIndex(i));
|
|
|
|
auto mi = ecs.findLeader(resultIndex(i));
|
|
|
|
for (auto me = ecs.member_end(); mi != me; ++mi) {
|
|
|
|
if (*mi < 0) {
|
|
|
|
auto tc = getResultTypeConstraint(i);
|
|
|
|
if (tc.getBuilderCall().hasValue()) {
|
|
|
|
resultTypeMapping[i].emplace_back(tc);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-06-03 12:05:47 +08:00
|
|
|
if (getArg(*mi).is<NamedAttribute *>()) {
|
2020-05-27 23:45:55 +08:00
|
|
|
// TODO: Handle attributes.
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
resultTypeMapping[i].emplace_back(*mi);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const OpTrait &trait : traits) {
|
|
|
|
const llvm::Record &def = trait.getDef();
|
|
|
|
// If the infer type op interface was manually added, then treat it as
|
|
|
|
// intention that the op needs special handling.
|
|
|
|
// TODO: Reconsider whether to always generate, this is more conservative
|
|
|
|
// and keeps existing behavior so starting that way for now.
|
|
|
|
if (def.isSubClassOf(
|
|
|
|
llvm::formatv("{0}::Trait", inferTypeOpInterface).str()))
|
|
|
|
return;
|
|
|
|
if (const auto *opTrait = dyn_cast<tblgen::InterfaceOpTrait>(&trait))
|
|
|
|
if (opTrait->getTrait().startswith(inferTypeOpInterface))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!def.isSubClassOf("AllTypesMatch"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto values = def.getValueAsListOfStrings("values");
|
|
|
|
auto root = argumentsAndResultsIndex.lookup(values.front());
|
|
|
|
for (StringRef str : values)
|
|
|
|
ecs.unionSets(argumentsAndResultsIndex.lookup(str), root);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verifies that all output types have a corresponding known input type
|
|
|
|
// and chooses matching operand or attribute (in that order) that
|
|
|
|
// matches it.
|
|
|
|
allResultsHaveKnownTypes =
|
|
|
|
all_of(llvm::seq<int>(0, getNumResults()), captureMapping);
|
|
|
|
|
|
|
|
// If the types could be computed, then add type inference trait.
|
|
|
|
if (allResultsHaveKnownTypes)
|
|
|
|
traits.push_back(OpTrait::create(inferTrait->getDefInit()));
|
|
|
|
}
|
|
|
|
|
2019-02-19 23:15:59 +08:00
|
|
|
void tblgen::Operator::populateOpStructure() {
|
2018-12-29 04:02:08 +08:00
|
|
|
auto &recordKeeper = def.getRecords();
|
2020-05-27 23:45:55 +08:00
|
|
|
auto *typeConstraintClass = recordKeeper.getClass("TypeConstraint");
|
|
|
|
auto *attrClass = recordKeeper.getClass("Attr");
|
|
|
|
auto *derivedAttrClass = recordKeeper.getClass("DerivedAttr");
|
|
|
|
auto *opVarClass = recordKeeper.getClass("OpVariable");
|
2019-03-06 17:23:41 +08:00
|
|
|
numNativeAttributes = 0;
|
2018-12-29 04:02:08 +08:00
|
|
|
|
|
|
|
DagInit *argumentValues = def.getValueAsDag("arguments");
|
2019-11-15 03:02:52 +08:00
|
|
|
unsigned numArgs = argumentValues->getNumArgs();
|
|
|
|
|
2020-05-27 23:45:55 +08:00
|
|
|
// Mapping from name of to argument or result index. Arguments are indexed
|
|
|
|
// to match getArg index, while the results are negatively indexed.
|
|
|
|
llvm::StringMap<int> argumentsAndResultsIndex;
|
|
|
|
|
2019-03-06 17:23:41 +08:00
|
|
|
// Handle operands and native attributes.
|
2019-11-15 03:02:52 +08:00
|
|
|
for (unsigned i = 0; i != numArgs; ++i) {
|
2020-05-27 23:45:55 +08:00
|
|
|
auto *arg = argumentValues->getArg(i);
|
2019-01-30 22:05:27 +08:00
|
|
|
auto givenName = argumentValues->getArgNameStr(i);
|
2020-05-27 23:45:55 +08:00
|
|
|
auto *argDefInit = dyn_cast<DefInit>(arg);
|
2018-12-29 04:02:08 +08:00
|
|
|
if (!argDefInit)
|
|
|
|
PrintFatalError(def.getLoc(),
|
2019-02-19 23:15:59 +08:00
|
|
|
Twine("undefined type for argument #") + Twine(i));
|
2018-12-29 04:02:08 +08:00
|
|
|
Record *argDef = argDefInit->getDef();
|
2020-03-07 05:55:36 +08:00
|
|
|
if (argDef->isSubClassOf(opVarClass))
|
|
|
|
argDef = argDef->getValueAsDef("constraint");
|
2018-12-29 04:02:08 +08:00
|
|
|
|
2019-03-06 17:23:41 +08:00
|
|
|
if (argDef->isSubClassOf(typeConstraintClass)) {
|
2019-03-18 22:54:20 +08:00
|
|
|
operands.push_back(
|
2020-03-07 05:55:36 +08:00
|
|
|
NamedTypeConstraint{givenName, TypeConstraint(argDef)});
|
2019-03-06 17:23:41 +08:00
|
|
|
} else if (argDef->isSubClassOf(attrClass)) {
|
|
|
|
if (givenName.empty())
|
|
|
|
PrintFatalError(argDef->getLoc(), "attributes must be named");
|
|
|
|
if (argDef->isSubClassOf(derivedAttrClass))
|
|
|
|
PrintFatalError(argDef->getLoc(),
|
|
|
|
"derived attributes not allowed in argument list");
|
|
|
|
attributes.push_back({givenName, Attribute(argDef)});
|
|
|
|
++numNativeAttributes;
|
|
|
|
} else {
|
|
|
|
PrintFatalError(def.getLoc(), "unexpected def type; only defs deriving "
|
|
|
|
"from TypeConstraint or Attr are allowed");
|
|
|
|
}
|
2020-05-27 23:45:55 +08:00
|
|
|
if (!givenName.empty())
|
|
|
|
argumentsAndResultsIndex[givenName] = i;
|
2019-01-04 07:53:54 +08:00
|
|
|
}
|
2018-12-29 04:02:08 +08:00
|
|
|
|
2019-01-08 02:09:34 +08:00
|
|
|
// Handle derived attributes.
|
2019-01-04 07:53:54 +08:00
|
|
|
for (const auto &val : def.getValues()) {
|
|
|
|
if (auto *record = dyn_cast<llvm::RecordRecTy>(val.getType())) {
|
|
|
|
if (!record->isSubClassOf(attrClass))
|
|
|
|
continue;
|
|
|
|
if (!record->isSubClassOf(derivedAttrClass))
|
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
"unexpected Attr where only DerivedAttr is allowed");
|
|
|
|
|
|
|
|
if (record->getClasses().size() != 1) {
|
2018-12-29 04:02:08 +08:00
|
|
|
PrintFatalError(
|
|
|
|
def.getLoc(),
|
2019-01-04 07:53:54 +08:00
|
|
|
"unsupported attribute modelling, only single class expected");
|
|
|
|
}
|
2019-01-30 22:05:27 +08:00
|
|
|
attributes.push_back(
|
|
|
|
{cast<llvm::StringInit>(val.getNameInit())->getValue(),
|
|
|
|
Attribute(cast<DefInit>(val.getValue()))});
|
2018-12-29 04:02:08 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-06 21:06:11 +08:00
|
|
|
|
2019-11-15 03:02:52 +08:00
|
|
|
// Populate `arguments`. This must happen after we've finalized `operands` and
|
|
|
|
// `attributes` because we will put their elements' pointers in `arguments`.
|
|
|
|
// SmallVector may perform re-allocation under the hood when adding new
|
|
|
|
// elements.
|
|
|
|
int operandIndex = 0, attrIndex = 0;
|
|
|
|
for (unsigned i = 0; i != numArgs; ++i) {
|
|
|
|
Record *argDef = dyn_cast<DefInit>(argumentValues->getArg(i))->getDef();
|
2020-03-07 05:55:36 +08:00
|
|
|
if (argDef->isSubClassOf(opVarClass))
|
|
|
|
argDef = argDef->getValueAsDef("constraint");
|
2019-11-15 03:02:52 +08:00
|
|
|
|
|
|
|
if (argDef->isSubClassOf(typeConstraintClass)) {
|
|
|
|
arguments.emplace_back(&operands[operandIndex++]);
|
|
|
|
} else {
|
|
|
|
assert(argDef->isSubClassOf(attrClass));
|
|
|
|
arguments.emplace_back(&attributes[attrIndex++]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-19 23:15:59 +08:00
|
|
|
auto *resultsDag = def.getValueAsDag("results");
|
|
|
|
auto *outsOp = dyn_cast<DefInit>(resultsDag->getOperator());
|
|
|
|
if (!outsOp || outsOp->getDef()->getName() != "outs") {
|
|
|
|
PrintFatalError(def.getLoc(), "'results' must have 'outs' directive");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle results.
|
|
|
|
for (unsigned i = 0, e = resultsDag->getNumArgs(); i < e; ++i) {
|
|
|
|
auto name = resultsDag->getArgNameStr(i);
|
2020-03-07 05:55:36 +08:00
|
|
|
auto *resultInit = dyn_cast<DefInit>(resultsDag->getArg(i));
|
|
|
|
if (!resultInit) {
|
2019-02-19 23:15:59 +08:00
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
Twine("undefined type for result #") + Twine(i));
|
|
|
|
}
|
2020-03-07 05:55:36 +08:00
|
|
|
auto *resultDef = resultInit->getDef();
|
|
|
|
if (resultDef->isSubClassOf(opVarClass))
|
|
|
|
resultDef = resultDef->getValueAsDef("constraint");
|
[TableGen] Consolidate constraint related concepts
Previously we have multiple mechanisms to specify op definition and match constraints:
TypeConstraint, AttributeConstraint, Type, Attr, mAttr, mAttrAnyOf, mPat. These variants
are not added because there are so many distinct cases we need to model; essentially,
they are all carrying a predicate. It's just an artifact of implementation.
It's quite confusing for users to grasp these variants and choose among them. Instead,
as the OpBase TableGen file, we need to strike to provide an unified mechanism. Each
dialect has the flexibility to define its own aliases if wanted.
This CL removes mAttr, mAttrAnyOf, mPat. A new base class, Constraint, is added. Now
TypeConstraint and AttrConstraint derive from Constraint. Type and Attr further derive
from TypeConstraint and AttrConstraint, respectively.
Comments are revised and examples are added to make it clear how to use constraints.
PiperOrigin-RevId: 240125076
2019-03-25 21:09:26 +08:00
|
|
|
results.push_back({name, TypeConstraint(resultDef)});
|
2020-05-27 23:45:55 +08:00
|
|
|
if (!name.empty())
|
|
|
|
argumentsAndResultsIndex[name] = resultIndex(i);
|
2019-02-19 23:15:59 +08:00
|
|
|
}
|
|
|
|
|
2020-02-22 05:19:50 +08:00
|
|
|
// Handle successors
|
|
|
|
auto *successorsDag = def.getValueAsDag("successors");
|
|
|
|
auto *successorsOp = dyn_cast<DefInit>(successorsDag->getOperator());
|
|
|
|
if (!successorsOp || successorsOp->getDef()->getName() != "successor") {
|
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
"'successors' must have 'successor' directive");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = successorsDag->getNumArgs(); i < e; ++i) {
|
|
|
|
auto name = successorsDag->getArgNameStr(i);
|
|
|
|
auto *successorInit = dyn_cast<DefInit>(successorsDag->getArg(i));
|
|
|
|
if (!successorInit) {
|
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
Twine("undefined kind for successor #") + Twine(i));
|
|
|
|
}
|
|
|
|
Successor successor(successorInit->getDef());
|
|
|
|
|
|
|
|
// Only support variadic successors if it is the last one for now.
|
|
|
|
if (i != e - 1 && successor.isVariadic())
|
|
|
|
PrintFatalError(def.getLoc(), "only the last successor can be variadic");
|
|
|
|
successors.push_back({name, successor});
|
|
|
|
}
|
|
|
|
|
2019-12-20 08:43:35 +08:00
|
|
|
// Create list of traits, skipping over duplicates: appending to lists in
|
|
|
|
// tablegen is easy, making them unique less so, so dedupe here.
|
2020-05-27 23:45:55 +08:00
|
|
|
if (auto *traitList = def.getValueAsListInit("traits")) {
|
2019-12-20 08:43:35 +08:00
|
|
|
// This is uniquing based on pointers of the trait.
|
|
|
|
SmallPtrSet<const llvm::Init *, 32> traitSet;
|
|
|
|
traits.reserve(traitSet.size());
|
2020-05-27 23:45:55 +08:00
|
|
|
for (auto *traitInit : *traitList) {
|
2019-12-20 08:43:35 +08:00
|
|
|
// Keep traits in the same order while skipping over duplicates.
|
|
|
|
if (traitSet.insert(traitInit).second)
|
|
|
|
traits.push_back(OpTrait::create(traitInit));
|
|
|
|
}
|
|
|
|
}
|
2019-05-28 23:03:46 +08:00
|
|
|
|
2020-05-27 23:45:55 +08:00
|
|
|
populateTypeInferenceInfo(argumentsAndResultsIndex);
|
|
|
|
|
2019-05-28 23:03:46 +08:00
|
|
|
// Handle regions
|
2019-05-31 07:50:16 +08:00
|
|
|
auto *regionsDag = def.getValueAsDag("regions");
|
|
|
|
auto *regionsOp = dyn_cast<DefInit>(regionsDag->getOperator());
|
|
|
|
if (!regionsOp || regionsOp->getDef()->getName() != "region") {
|
|
|
|
PrintFatalError(def.getLoc(), "'regions' must have 'region' directive");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = regionsDag->getNumArgs(); i < e; ++i) {
|
|
|
|
auto name = regionsDag->getArgNameStr(i);
|
|
|
|
auto *regionInit = dyn_cast<DefInit>(regionsDag->getArg(i));
|
|
|
|
if (!regionInit) {
|
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
Twine("undefined kind for region #") + Twine(i));
|
|
|
|
}
|
2020-04-05 16:03:24 +08:00
|
|
|
Region region(regionInit->getDef());
|
|
|
|
if (region.isVariadic()) {
|
|
|
|
// Only support variadic regions if it is the last one for now.
|
|
|
|
if (i != e - 1)
|
|
|
|
PrintFatalError(def.getLoc(), "only the last region can be variadic");
|
|
|
|
if (name.empty())
|
|
|
|
PrintFatalError(def.getLoc(), "variadic regions must be named");
|
|
|
|
}
|
|
|
|
|
|
|
|
regions.push_back({name, region});
|
2019-05-31 07:50:16 +08:00
|
|
|
}
|
2019-11-15 03:02:52 +08:00
|
|
|
|
|
|
|
LLVM_DEBUG(print(llvm::dbgs()));
|
2018-12-29 04:02:08 +08:00
|
|
|
}
|
2019-01-06 00:11:29 +08:00
|
|
|
|
2020-05-27 23:45:55 +08:00
|
|
|
auto tblgen::Operator::getSameTypeAsResult(int index) const
|
|
|
|
-> ArrayRef<ArgOrType> {
|
|
|
|
assert(allResultTypesKnown());
|
|
|
|
return resultTypeMapping[index];
|
|
|
|
}
|
|
|
|
|
2019-02-06 04:02:53 +08:00
|
|
|
ArrayRef<llvm::SMLoc> tblgen::Operator::getLoc() const { return def.getLoc(); }
|
|
|
|
|
Start doc generation pass.
Start doc generation pass that generates simple markdown output. The output is formatted simply[1] in markdown, but this allows seeing what info we have, where we can refine the op description (e.g., the inputs is probably redundant), what info is missing (e.g., the attributes could probably have a description).
The formatting of the description is still left up to whatever was in the op definition (which luckily, due to the uniformity in the .td file, turned out well but relying on the indentation there is fragile). The mechanism to autogenerate these post changes has not been added yet either. The output file could be run through a markdown formatter too to remove extra spaces.
[1]. This is not proposal for final style :) There could also be a discussion around single doc vs multiple (per dialect, per op), whether we want a TOC, whether operands/attributes should be headings or just formatted differently ...
PiperOrigin-RevId: 230354538
2019-01-23 01:31:04 +08:00
|
|
|
bool tblgen::Operator::hasDescription() const {
|
|
|
|
return def.getValue("description") != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef tblgen::Operator::getDescription() const {
|
|
|
|
return def.getValueAsString("description");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tblgen::Operator::hasSummary() const {
|
|
|
|
return def.getValue("summary") != nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef tblgen::Operator::getSummary() const {
|
|
|
|
return def.getValueAsString("summary");
|
|
|
|
}
|
2019-11-15 03:02:52 +08:00
|
|
|
|
2020-03-25 02:57:13 +08:00
|
|
|
bool tblgen::Operator::hasAssemblyFormat() const {
|
|
|
|
auto *valueInit = def.getValueInit("assemblyFormat");
|
|
|
|
return isa<llvm::CodeInit>(valueInit) || isa<llvm::StringInit>(valueInit);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef tblgen::Operator::getAssemblyFormat() const {
|
|
|
|
return TypeSwitch<llvm::Init *, StringRef>(def.getValueInit("assemblyFormat"))
|
|
|
|
.Case<llvm::StringInit, llvm::CodeInit>(
|
|
|
|
[&](auto *init) { return init->getValue(); });
|
|
|
|
}
|
|
|
|
|
2019-11-15 03:02:52 +08:00
|
|
|
void tblgen::Operator::print(llvm::raw_ostream &os) const {
|
|
|
|
os << "op '" << getOperationName() << "'\n";
|
|
|
|
for (Argument arg : arguments) {
|
|
|
|
if (auto *attr = arg.dyn_cast<NamedAttribute *>())
|
|
|
|
os << "[attribute] " << attr->name << '\n';
|
|
|
|
else
|
|
|
|
os << "[operand] " << arg.get<NamedTypeConstraint *>()->name << '\n';
|
|
|
|
}
|
|
|
|
}
|
2020-03-07 05:55:36 +08:00
|
|
|
|
|
|
|
auto tblgen::Operator::VariableDecoratorIterator::unwrap(llvm::Init *init)
|
|
|
|
-> VariableDecorator {
|
|
|
|
return VariableDecorator(cast<llvm::DefInit>(init)->getDef());
|
|
|
|
}
|