2018-12-27 20:56:03 +08:00
|
|
|
//===- OpDefinitionsGen.cpp - MLIR op definitions generator ---------------===//
|
2018-10-15 23:54:37 +08:00
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
2018-12-27 20:56:03 +08:00
|
|
|
// OpDefinitionsGen uses the description of operations to generate C++
|
|
|
|
// definitions for ops.
|
2018-10-15 23:54:37 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-12-27 20:56:03 +08:00
|
|
|
#include "mlir/TableGen/GenInfo.h"
|
2019-01-04 07:53:54 +08:00
|
|
|
#include "mlir/TableGen/Operator.h"
|
2018-12-12 19:09:11 +08:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2018-10-31 05:38:49 +08:00
|
|
|
#include "llvm/Support/FormatVariadic.h"
|
2018-10-15 23:54:37 +08:00
|
|
|
#include "llvm/Support/Signals.h"
|
|
|
|
#include "llvm/TableGen/Error.h"
|
|
|
|
#include "llvm/TableGen/Record.h"
|
|
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
2019-01-04 07:53:54 +08:00
|
|
|
using namespace mlir;
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2019-01-09 09:19:37 +08:00
|
|
|
using mlir::tblgen::Operator;
|
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
static const char *const generatedArgName = "_arg";
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2019-01-08 01:52:26 +08:00
|
|
|
// Helper macro that returns indented os.
|
|
|
|
#define OUT(X) os.indent((X))
|
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
// TODO(jpienaar): The builder body should probably be separate from the header.
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2018-10-31 05:38:49 +08:00
|
|
|
// Variation of method in FormatVariadic.h which takes a StringRef as input
|
|
|
|
// instead.
|
|
|
|
template <typename... Ts>
|
|
|
|
inline auto formatv(StringRef fmt, Ts &&... vals) -> formatv_object<decltype(
|
|
|
|
std::make_tuple(detail::build_format_adapter(std::forward<Ts>(vals))...))> {
|
|
|
|
using ParamTuple = decltype(
|
|
|
|
std::make_tuple(detail::build_format_adapter(std::forward<Ts>(vals))...));
|
|
|
|
return llvm::formatv_object<ParamTuple>(
|
|
|
|
fmt,
|
|
|
|
std::make_tuple(detail::build_format_adapter(std::forward<Ts>(vals))...));
|
|
|
|
}
|
|
|
|
|
2018-12-06 05:25:44 +08:00
|
|
|
// Returns whether the record has a value of the given name that can be returned
|
|
|
|
// via getValueAsString.
|
|
|
|
static inline bool hasStringAttribute(const Record &record,
|
|
|
|
StringRef fieldName) {
|
|
|
|
auto valueInit = record.getValueInit(fieldName);
|
|
|
|
return isa<CodeInit>(valueInit) || isa<StringInit>(valueInit);
|
|
|
|
}
|
|
|
|
|
2019-01-04 07:53:54 +08:00
|
|
|
static std::string getArgumentName(const Operator &op, int index) {
|
|
|
|
const auto &operand = op.getOperand(index);
|
|
|
|
if (operand.name)
|
|
|
|
return operand.name->getAsUnquotedString();
|
|
|
|
else
|
|
|
|
return formatv("{0}_{1}", generatedArgName, index);
|
|
|
|
}
|
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
namespace {
|
|
|
|
// Simple RAII helper for defining ifdef-undef-endif scopes.
|
|
|
|
class IfDefScope {
|
|
|
|
public:
|
|
|
|
IfDefScope(StringRef name, raw_ostream &os) : name(name), os(os) {
|
|
|
|
os << "#ifdef " << name << "\n"
|
|
|
|
<< "#undef " << name << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
~IfDefScope() { os << "\n#endif // " << name << "\n\n"; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
StringRef name;
|
|
|
|
raw_ostream &os;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// Helper class to emit a record into the given output stream.
|
|
|
|
class OpEmitter {
|
|
|
|
public:
|
|
|
|
static void emit(const Record &def, raw_ostream &os);
|
|
|
|
|
|
|
|
// Emit getters for the attributes of the operation.
|
|
|
|
void emitAttrGetters();
|
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
// Emit query methods for the named operands.
|
|
|
|
void emitNamedOperands();
|
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
// Emit builder method for the operation.
|
|
|
|
void emitBuilder();
|
|
|
|
|
2018-12-04 01:16:59 +08:00
|
|
|
// Emit method declaration for the getCanonicalizationPatterns() interface.
|
|
|
|
void emitCanonicalizationPatterns();
|
|
|
|
|
2019-01-04 17:34:16 +08:00
|
|
|
// Emit the constant folder method for the operation.
|
|
|
|
void emitConstantFolder();
|
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
// Emit the parser for the operation.
|
|
|
|
void emitParser();
|
|
|
|
|
|
|
|
// Emit the printer for the operation.
|
|
|
|
void emitPrinter();
|
|
|
|
|
|
|
|
// Emit verify method for the operation.
|
|
|
|
void emitVerifier();
|
|
|
|
|
|
|
|
// Emit the traits used by the object.
|
|
|
|
void emitTraits();
|
|
|
|
|
|
|
|
private:
|
2018-12-20 18:57:32 +08:00
|
|
|
OpEmitter(const Record &def, raw_ostream &os);
|
|
|
|
|
|
|
|
// Invokes the given function over all the namespaces of the class.
|
2019-01-04 07:53:54 +08:00
|
|
|
void mapOverClassNamespaces(function_ref<void(StringRef)> fn);
|
2018-12-20 18:57:32 +08:00
|
|
|
|
|
|
|
// The record corresponding to the op.
|
2018-10-15 23:54:37 +08:00
|
|
|
const Record &def;
|
2018-12-20 18:57:32 +08:00
|
|
|
|
2019-01-04 07:53:54 +08:00
|
|
|
// The operator being emitted.
|
|
|
|
Operator op;
|
2018-12-20 18:57:32 +08:00
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
raw_ostream &os;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
OpEmitter::OpEmitter(const Record &def, raw_ostream &os)
|
2019-01-04 07:53:54 +08:00
|
|
|
: def(def), op(def), os(os) {}
|
2018-12-20 18:57:32 +08:00
|
|
|
|
2019-01-04 07:53:54 +08:00
|
|
|
void OpEmitter::mapOverClassNamespaces(function_ref<void(StringRef)> fn) {
|
|
|
|
auto &splittedDefName = op.getSplitDefName();
|
2018-12-20 18:57:32 +08:00
|
|
|
for (auto it = splittedDefName.begin(), e = std::prev(splittedDefName.end());
|
|
|
|
it != e; ++it)
|
|
|
|
fn(*it);
|
|
|
|
}
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
void OpEmitter::emit(const Record &def, raw_ostream &os) {
|
|
|
|
OpEmitter emitter(def, os);
|
|
|
|
|
|
|
|
emitter.mapOverClassNamespaces(
|
|
|
|
[&os](StringRef ns) { os << "\nnamespace " << ns << "{\n"; });
|
2019-01-04 07:53:54 +08:00
|
|
|
os << formatv("class {0} : public Op<{0}", emitter.op.cppClassName());
|
2018-10-15 23:54:37 +08:00
|
|
|
emitter.emitTraits();
|
|
|
|
os << "> {\npublic:\n";
|
|
|
|
|
|
|
|
// Build operation name.
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "static StringRef getOperationName() { return \""
|
|
|
|
<< emitter.op.getOperationName() << "\"; };\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
emitter.emitNamedOperands();
|
2018-10-15 23:54:37 +08:00
|
|
|
emitter.emitBuilder();
|
|
|
|
emitter.emitParser();
|
|
|
|
emitter.emitPrinter();
|
|
|
|
emitter.emitVerifier();
|
|
|
|
emitter.emitAttrGetters();
|
2018-12-04 01:16:59 +08:00
|
|
|
emitter.emitCanonicalizationPatterns();
|
2019-01-04 17:34:16 +08:00
|
|
|
emitter.emitConstantFolder();
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2019-01-08 01:52:26 +08:00
|
|
|
os << "private:\n friend class ::mlir::OperationInst;\n"
|
|
|
|
<< " explicit " << emitter.op.cppClassName()
|
2019-01-04 07:53:54 +08:00
|
|
|
<< "(const OperationInst* state) : Op(state) {}\n};\n";
|
2018-12-20 18:57:32 +08:00
|
|
|
emitter.mapOverClassNamespaces(
|
|
|
|
[&os](StringRef ns) { os << "} // end namespace " << ns << "\n"; });
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpEmitter::emitAttrGetters() {
|
2019-01-10 05:50:20 +08:00
|
|
|
for (auto &namedAttr : op.getAttributes()) {
|
2019-01-10 05:50:35 +08:00
|
|
|
auto name = namedAttr.getName();
|
2019-01-10 05:50:20 +08:00
|
|
|
const auto &attr = namedAttr.attr;
|
2018-11-29 01:21:42 +08:00
|
|
|
|
2019-01-23 02:26:09 +08:00
|
|
|
// Determine the name of the attribute getter. The name matches the
|
|
|
|
// attribute name excluding dialect prefix.
|
|
|
|
StringRef getter = name;
|
|
|
|
auto it = getter.rfind('$');
|
|
|
|
if (it != StringRef::npos)
|
|
|
|
getter = getter.substr(it + 1);
|
|
|
|
|
2018-11-29 01:21:42 +08:00
|
|
|
// Emit the derived attribute body.
|
2019-01-10 05:50:20 +08:00
|
|
|
if (attr.isDerivedAttr()) {
|
2019-01-23 02:26:09 +08:00
|
|
|
OUT(2) << attr.getReturnType() << ' ' << getter << "() const {"
|
2019-01-10 05:50:20 +08:00
|
|
|
<< attr.getDerivedCodeBody() << " }\n";
|
2018-12-06 05:25:44 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-01-04 07:53:54 +08:00
|
|
|
// Emit normal emitter.
|
2019-01-23 02:26:09 +08:00
|
|
|
OUT(2) << attr.getReturnType() << ' ' << getter << "() const {\n";
|
2018-10-31 05:38:49 +08:00
|
|
|
|
|
|
|
// Return the queried attribute with the correct return type.
|
2019-01-17 02:23:21 +08:00
|
|
|
std::string attrVal = formatv("this->getAttr(\"{1}\").dyn_cast<{0}>()",
|
2019-01-08 01:52:26 +08:00
|
|
|
attr.getStorageType(), name);
|
2019-01-23 02:26:09 +08:00
|
|
|
OUT(4) << "auto attr = " << attrVal << ";\n";
|
|
|
|
if (attr.hasDefaultValue()) {
|
|
|
|
// Returns the default value if not set.
|
|
|
|
// TODO: this is inefficient, we are recreating the attribute for every
|
|
|
|
// call. This should be set instead.
|
|
|
|
OUT(4) << "if (!attr)\n";
|
|
|
|
OUT(6) << "return "
|
|
|
|
<< formatv(
|
|
|
|
attr.getConvertFromStorageCall(),
|
|
|
|
formatv(
|
|
|
|
attr.getDefaultValueTemplate(),
|
|
|
|
"mlir::Builder(this->getInstruction()->getContext())"))
|
|
|
|
<< ";\n";
|
|
|
|
}
|
|
|
|
OUT(4) << "return " << formatv(attr.getConvertFromStorageCall(), "attr")
|
2019-01-08 01:52:26 +08:00
|
|
|
<< ";\n }\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-20 18:57:32 +08:00
|
|
|
void OpEmitter::emitNamedOperands() {
|
2018-12-28 06:35:10 +08:00
|
|
|
const auto operandMethods = R"( Value *{0}() {
|
2018-12-28 20:14:52 +08:00
|
|
|
return this->getInstruction()->getOperand({1});
|
2018-12-20 18:57:32 +08:00
|
|
|
}
|
2018-12-28 06:35:10 +08:00
|
|
|
const Value *{0}() const {
|
2018-12-28 20:14:52 +08:00
|
|
|
return this->getInstruction()->getOperand({1});
|
2018-12-20 18:57:32 +08:00
|
|
|
}
|
|
|
|
)";
|
2019-01-04 07:53:54 +08:00
|
|
|
for (int i = 0, e = op.getNumOperands(); i != e; ++i) {
|
|
|
|
const auto &operand = op.getOperand(i);
|
|
|
|
if (operand.name)
|
|
|
|
os << formatv(operandMethods, operand.name->getAsUnquotedString(), i);
|
2018-12-20 18:57:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
void OpEmitter::emitBuilder() {
|
2018-12-07 04:06:00 +08:00
|
|
|
if (hasStringAttribute(def, "builder")) {
|
|
|
|
// If a custom builder is given then print that out instead.
|
|
|
|
auto builder = def.getValueAsString("builder");
|
2018-12-12 19:09:11 +08:00
|
|
|
if (!builder.empty())
|
2018-12-07 04:06:00 +08:00
|
|
|
os << builder << '\n';
|
|
|
|
}
|
2018-10-16 23:36:15 +08:00
|
|
|
|
2018-12-12 19:09:11 +08:00
|
|
|
// Generate default builders that requires all result type, operands, and
|
|
|
|
// attributes as parameters.
|
2018-12-07 04:06:00 +08:00
|
|
|
|
2018-12-12 05:59:29 +08:00
|
|
|
// We generate two builders here, one having a stand-alone parameter for
|
|
|
|
// each result type / operand / attribute, the other having an aggregated
|
|
|
|
// parameter for all result types / operands / attributes, to facilitate
|
|
|
|
// different call patterns.
|
|
|
|
|
|
|
|
// 1. Stand-alone parameters
|
|
|
|
|
2018-12-07 04:06:00 +08:00
|
|
|
std::vector<Record *> returnTypes = def.getValueAsListOfDefs("returnTypes");
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "static void build(Builder* builder, OperationState* result";
|
2018-12-07 04:06:00 +08:00
|
|
|
|
|
|
|
// Emit parameters for all return types
|
|
|
|
for (unsigned i = 0, e = returnTypes.size(); i != e; ++i)
|
|
|
|
os << ", Type returnType" << i;
|
|
|
|
|
|
|
|
// Emit parameters for all operands
|
2019-01-04 07:53:54 +08:00
|
|
|
for (int i = 0, e = op.getNumOperands(); i != e; ++i)
|
|
|
|
os << ", Value* " << getArgumentName(op, i);
|
2018-12-07 04:06:00 +08:00
|
|
|
|
|
|
|
// Emit parameters for all attributes
|
|
|
|
// TODO(antiagainst): Support default initializer for attributes
|
2019-01-10 05:50:20 +08:00
|
|
|
for (const auto &namedAttr : op.getAttributes()) {
|
|
|
|
const auto &attr = namedAttr.attr;
|
|
|
|
if (attr.isDerivedAttr())
|
2019-01-04 07:53:54 +08:00
|
|
|
break;
|
2019-01-10 05:50:35 +08:00
|
|
|
os << ", " << attr.getStorageType() << ' ' << namedAttr.getName();
|
2018-12-07 04:06:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
os << ") {\n";
|
|
|
|
|
|
|
|
// Push all result types to the result
|
|
|
|
if (!returnTypes.empty()) {
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "result->addTypes({returnType0";
|
2018-12-07 04:06:00 +08:00
|
|
|
for (unsigned i = 1, e = returnTypes.size(); i != e; ++i)
|
|
|
|
os << ", returnType" << i;
|
|
|
|
os << "});\n\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push all operands to the result
|
2019-01-04 07:53:54 +08:00
|
|
|
if (op.getNumOperands() > 0) {
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "result->addOperands({" << getArgumentName(op, 0);
|
2019-01-04 07:53:54 +08:00
|
|
|
for (int i = 1, e = op.getNumOperands(); i != e; ++i)
|
|
|
|
os << ", " << getArgumentName(op, i);
|
2018-12-07 04:06:00 +08:00
|
|
|
os << "});\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push all attributes to the result
|
2019-01-10 05:50:20 +08:00
|
|
|
for (const auto &namedAttr : op.getAttributes())
|
|
|
|
if (!namedAttr.attr.isDerivedAttr())
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << formatv("result->addAttribute(\"{0}\", {0});\n",
|
2019-01-10 05:50:35 +08:00
|
|
|
namedAttr.getName());
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "}\n";
|
2018-12-12 05:59:29 +08:00
|
|
|
|
|
|
|
// 2. Aggregated parameters
|
|
|
|
|
|
|
|
// Signature
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "static void build(Builder* builder, OperationState* result, "
|
|
|
|
<< "ArrayRef<Type> resultTypes, ArrayRef<Value*> args, "
|
|
|
|
"ArrayRef<NamedAttribute> attributes) {\n";
|
2018-12-12 05:59:29 +08:00
|
|
|
|
|
|
|
// Result types
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "assert(resultTypes.size() == " << returnTypes.size()
|
|
|
|
<< "u && \"mismatched number of return types\");\n"
|
|
|
|
<< " result->addTypes(resultTypes);\n";
|
2018-12-12 05:59:29 +08:00
|
|
|
|
|
|
|
// Operands
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "assert(args.size() == " << op.getNumOperands()
|
|
|
|
<< "u && \"mismatched number of parameters\");\n"
|
|
|
|
<< " result->addOperands(args);\n\n";
|
2018-12-12 05:59:29 +08:00
|
|
|
|
|
|
|
// Attributes
|
2019-01-04 07:53:54 +08:00
|
|
|
if (op.getNumAttributes() > 0) {
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "assert(!attributes.size() && \"no attributes expected\");\n"
|
|
|
|
<< " }\n";
|
2018-12-20 18:57:32 +08:00
|
|
|
} else {
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "assert(attributes.size() >= " << op.getNumAttributes()
|
|
|
|
<< "u && \"not enough attributes\");\n"
|
|
|
|
<< " for (const auto& pair : attributes)\n"
|
|
|
|
<< " result->addAttribute(pair.first, pair.second);\n"
|
|
|
|
<< " }\n";
|
2018-12-20 18:57:32 +08:00
|
|
|
}
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
2018-12-04 01:16:59 +08:00
|
|
|
void OpEmitter::emitCanonicalizationPatterns() {
|
2019-01-17 00:28:13 +08:00
|
|
|
if (!def.getValueAsBit("hasCanonicalizer"))
|
2018-12-04 01:16:59 +08:00
|
|
|
return;
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "static void getCanonicalizationPatterns("
|
|
|
|
<< "OwningRewritePatternList &results, MLIRContext* context);\n";
|
2018-12-04 01:16:59 +08:00
|
|
|
}
|
|
|
|
|
2019-01-04 17:34:16 +08:00
|
|
|
void OpEmitter::emitConstantFolder() {
|
|
|
|
if (!def.getValueAsBit("hasConstantFolder"))
|
|
|
|
return;
|
|
|
|
if (def.getValueAsListOfDefs("returnTypes").size() == 1) {
|
|
|
|
os << " Attribute constantFold(ArrayRef<Attribute> operands,\n"
|
|
|
|
" MLIRContext *context) const;\n";
|
|
|
|
} else {
|
|
|
|
os << " bool constantFold(ArrayRef<Attribute> operands,\n"
|
|
|
|
<< " SmallVectorImpl<Attribute> &results,\n"
|
|
|
|
<< " MLIRContext *context) const;\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 23:54:37 +08:00
|
|
|
void OpEmitter::emitParser() {
|
2018-12-06 05:25:44 +08:00
|
|
|
if (!hasStringAttribute(def, "parser"))
|
2018-10-15 23:54:37 +08:00
|
|
|
return;
|
|
|
|
os << " static bool parse(OpAsmParser *parser, OperationState *result) {"
|
2018-12-06 05:25:44 +08:00
|
|
|
<< "\n " << def.getValueAsString("parser") << "\n }\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpEmitter::emitPrinter() {
|
|
|
|
auto valueInit = def.getValueInit("printer");
|
|
|
|
CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
|
|
|
|
if (!codeInit)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto printer = codeInit->getValue();
|
|
|
|
os << " void print(OpAsmPrinter *p) const {\n"
|
|
|
|
<< " " << printer << "\n }\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpEmitter::emitVerifier() {
|
2018-10-17 00:27:39 +08:00
|
|
|
auto valueInit = def.getValueInit("verifier");
|
|
|
|
CodeInit *codeInit = dyn_cast<CodeInit>(valueInit);
|
|
|
|
bool hasCustomVerify = codeInit && !codeInit->getValue().empty();
|
2019-01-06 00:11:29 +08:00
|
|
|
if (!hasCustomVerify && op.getNumArgs() == 0)
|
2018-10-17 00:27:39 +08:00
|
|
|
return;
|
2018-10-15 23:54:37 +08:00
|
|
|
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(2) << "bool verify() const {\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
// Verify the attributes have the correct type.
|
2019-01-10 05:50:20 +08:00
|
|
|
for (const auto &namedAttr : op.getAttributes()) {
|
|
|
|
const auto &attr = namedAttr.attr;
|
|
|
|
|
|
|
|
if (attr.isDerivedAttr())
|
2019-01-04 07:53:54 +08:00
|
|
|
continue;
|
|
|
|
|
2019-01-10 05:50:35 +08:00
|
|
|
auto name = namedAttr.getName();
|
2019-01-23 02:26:09 +08:00
|
|
|
if (!attr.hasStorageType() && !attr.hasDefaultValue()) {
|
|
|
|
// TODO: Some verification can be done even without storage type.
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "if (!this->getAttr(\"" << name
|
|
|
|
<< "\")) return emitOpError(\"requires attribute '" << name
|
|
|
|
<< "'\");\n";
|
2018-12-06 05:25:44 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-01-23 02:26:09 +08:00
|
|
|
if (attr.hasDefaultValue()) {
|
|
|
|
// If the attribute has a default value, then only verify the predicate if
|
|
|
|
// set. This does effectively assume that the default value is valid.
|
|
|
|
// TODO: verify the debug value is valid (perhaps in debug mode only).
|
|
|
|
OUT(4) << "if (this->getAttr(\"" << name << "\")) {\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
OUT(6) << "if (!this->getAttr(\"" << name << "\").dyn_cast_or_null<"
|
2019-01-08 01:52:26 +08:00
|
|
|
<< attr.getStorageType() << ">()) return emitOpError(\"requires "
|
|
|
|
<< attr.getReturnType() << " attribute '" << name << "'\");\n";
|
2019-01-18 02:36:51 +08:00
|
|
|
|
|
|
|
auto attrPred = attr.getPredicate();
|
|
|
|
if (!attrPred.isNull()) {
|
2019-01-23 02:26:09 +08:00
|
|
|
OUT(6) << formatv("if (!({0})) return emitOpError(\"attribute '{1}' "
|
2019-01-18 02:36:51 +08:00
|
|
|
"failed to satisfy constraint of {2}\");\n",
|
|
|
|
formatv(attrPred.getCondition(),
|
|
|
|
formatv("this->getAttr(\"{0}\")", name)),
|
|
|
|
name, attr.getTableGenDefName());
|
|
|
|
}
|
2019-01-23 02:26:09 +08:00
|
|
|
|
|
|
|
if (attr.hasDefaultValue())
|
|
|
|
OUT(4) << "}\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
2019-01-06 00:11:29 +08:00
|
|
|
// TODO: Handle variadic.
|
|
|
|
int opIndex = 0;
|
|
|
|
for (const auto &operand : op.getOperands()) {
|
|
|
|
// TODO: Commonality between matchers could be extracted to have a more
|
|
|
|
// concise code.
|
|
|
|
if (operand.hasMatcher()) {
|
2019-01-16 02:42:21 +08:00
|
|
|
auto constraint = operand.getTypeConstraint();
|
|
|
|
auto description = constraint.getDescription();
|
|
|
|
OUT(4) << "if (!("
|
|
|
|
<< formatv(constraint.getConditionTemplate(),
|
|
|
|
"this->getInstruction()->getOperand(" + Twine(opIndex) +
|
|
|
|
")->getType()")
|
|
|
|
<< ")) {\n";
|
|
|
|
OUT(6) << "return emitOpError(\"operand #" + Twine(opIndex)
|
|
|
|
<< (description.empty() ? " type precondition failed"
|
|
|
|
: " must be " + Twine(description))
|
|
|
|
<< "\");";
|
|
|
|
OUT(4) << "}\n";
|
2019-01-06 00:11:29 +08:00
|
|
|
}
|
|
|
|
++opIndex;
|
|
|
|
}
|
|
|
|
|
2018-10-17 00:27:39 +08:00
|
|
|
if (hasCustomVerify)
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << codeInit->getValue() << "\n";
|
2018-10-17 00:27:39 +08:00
|
|
|
else
|
2019-01-08 01:52:26 +08:00
|
|
|
OUT(4) << "return false;\n";
|
|
|
|
OUT(2) << "}\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void OpEmitter::emitTraits() {
|
|
|
|
std::vector<Record *> returnTypes = def.getValueAsListOfDefs("returnTypes");
|
|
|
|
|
|
|
|
// Add return size trait.
|
|
|
|
switch (returnTypes.size()) {
|
|
|
|
case 0:
|
|
|
|
os << ", OpTrait::ZeroResult";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
os << ", OpTrait::OneResult";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
os << ", OpTrait::NResults<" << returnTypes.size() << ">::Impl";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-12-14 03:43:50 +08:00
|
|
|
// Add explicitly added traits. Note that some traits might implicitly defines
|
|
|
|
// the number of operands.
|
|
|
|
// TODO(jpienaar): Improve Trait specification to make adding them in the
|
|
|
|
// tblgen file better.
|
|
|
|
bool hasVariadicOperands = false;
|
|
|
|
bool hasAtLeastNOperands = false;
|
|
|
|
auto *recordVal = def.getValue("traits");
|
|
|
|
if (recordVal && recordVal->getValue()) {
|
|
|
|
auto traitList = dyn_cast<ListInit>(recordVal->getValue())->getValues();
|
|
|
|
for (Init *trait : traitList) {
|
2018-12-14 22:32:58 +08:00
|
|
|
std::string traitStr = trait->getAsUnquotedString();
|
|
|
|
auto ref = StringRef(traitStr).trim();
|
|
|
|
hasVariadicOperands = ref == "VariadicOperands";
|
|
|
|
hasAtLeastNOperands = ref == "AtLeastNOperands";
|
|
|
|
os << ", OpTrait::" << ref;
|
2018-12-14 03:43:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 07:53:54 +08:00
|
|
|
if ((hasVariadicOperands || hasAtLeastNOperands) && op.getNumOperands() > 0) {
|
2018-12-14 03:43:50 +08:00
|
|
|
PrintFatalError(def.getLoc(),
|
|
|
|
"Operands number definition is not consistent.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add operand size trait if defined explicitly.
|
2019-01-04 07:53:54 +08:00
|
|
|
switch (op.getNumOperands()) {
|
2018-10-15 23:54:37 +08:00
|
|
|
case 0:
|
2018-12-14 03:43:50 +08:00
|
|
|
if (!hasVariadicOperands && !hasAtLeastNOperands)
|
|
|
|
os << ", OpTrait::ZeroOperands";
|
2018-10-15 23:54:37 +08:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
os << ", OpTrait::OneOperand";
|
|
|
|
break;
|
|
|
|
default:
|
2019-01-04 07:53:54 +08:00
|
|
|
os << ", OpTrait::NOperands<" << op.getNumOperands() << ">::Impl";
|
2018-10-15 23:54:37 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-10-16 23:36:15 +08:00
|
|
|
// Add op property traits. These match the propoerties specified in the table
|
|
|
|
// with the OperationProperty specified in OperationSupport.h.
|
|
|
|
for (Record *property : def.getValueAsListOfDefs("properties")) {
|
|
|
|
if (property->getName() == "Commutative") {
|
|
|
|
os << ", OpTrait::IsCommutative";
|
|
|
|
} else if (property->getName() == "NoSideEffect") {
|
|
|
|
os << ", OpTrait::HasNoSideEffect";
|
|
|
|
}
|
|
|
|
}
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Emits the opcode enum and op classes.
|
2019-01-04 07:53:54 +08:00
|
|
|
static void emitOpClasses(const std::vector<Record *> &defs, raw_ostream &os) {
|
2018-10-15 23:54:37 +08:00
|
|
|
IfDefScope scope("GET_OP_CLASSES", os);
|
|
|
|
for (auto *def : defs)
|
|
|
|
OpEmitter::emit(*def, os);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emits a comma-separated list of the ops.
|
|
|
|
static void emitOpList(const std::vector<Record *> &defs, raw_ostream &os) {
|
|
|
|
IfDefScope scope("GET_OP_LIST", os);
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
for (auto &def : defs) {
|
|
|
|
if (!first)
|
|
|
|
os << ",";
|
2019-01-04 07:53:54 +08:00
|
|
|
os << Operator(def).qualifiedCppClassName();
|
2018-10-15 23:54:37 +08:00
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void emitOpDefinitions(const RecordKeeper &recordKeeper,
|
|
|
|
raw_ostream &os) {
|
|
|
|
emitSourceFileHeader("List of ops", os);
|
|
|
|
|
|
|
|
const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
|
|
|
|
emitOpList(defs, os);
|
2019-01-04 07:53:54 +08:00
|
|
|
emitOpClasses(defs, os);
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void emitOpDefFile(const RecordKeeper &recordKeeper, raw_ostream &os) {
|
|
|
|
emitSourceFileHeader("Op def file", os);
|
|
|
|
|
|
|
|
const auto &defs = recordKeeper.getAllDerivedDefinitions("Op");
|
|
|
|
os << "#ifndef ALL_OPS\n#define ALL_OPS(OP, NAME)\n#endif\n";
|
|
|
|
for (const auto *def : defs) {
|
|
|
|
os << "ALL_OPS(" << def->getName() << ", \""
|
2018-12-17 20:42:55 +08:00
|
|
|
<< def->getValueAsString("opName") << "\")\n";
|
2018-10-15 23:54:37 +08:00
|
|
|
}
|
|
|
|
os << "#undef ALL_OPS";
|
|
|
|
}
|
|
|
|
|
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
|
|
|
static mlir::GenRegistration
|
2018-12-27 20:56:03 +08:00
|
|
|
genOpDefinitions("gen-op-definitions", "Generate op definitions",
|
|
|
|
[](const RecordKeeper &records, raw_ostream &os) {
|
|
|
|
emitOpDefinitions(records, os);
|
|
|
|
return false;
|
|
|
|
});
|