Auto-generate op builder with TableGen

If no custom builder is supplied for an op, TableGen now generates
a default builder for it with the following signature:

  static void build(Builder *builder, OperationState* result,
                    <list-of-all-result-types>,
                    <list-of-all-operands>,
                    <list-of-all-attributes>);

PiperOrigin-RevId: 224382473
This commit is contained in:
Lei Zhang 2018-12-06 12:06:00 -08:00 committed by jpienaar
parent 13bc77045e
commit d2d7c11f19
2 changed files with 77 additions and 30 deletions
mlir
include/mlir/IR
tools/mlir-op-gen

View File

@ -144,6 +144,15 @@ class Op<string mnemonic, list<OpProperty> props = []> {
// Define the hooks used for building, parsing, printing, verification.
// Custom builder.
// If a derived class/def does not override this, then a default builder
// is generated, with the following signature:
//
// static void build(Builder* builder, OperationState* result,
// Type resultType0, Type resultType1, ...,
// SSAValue* arg0, SSAValue* arg1, ...,
// Attribute attr0, Attribute attr1, ...);
//
// Where the attributes follow the same declaration order as in the op.
code builder = ?;
// Custom parser.
@ -183,29 +192,10 @@ class Traits<list<string> Traits> {
}
class BinaryOp<string mnemonic, list<OpProperty> props> :
Op<mnemonic, props>, Operands<[Tensor, Tensor]>, Results<[Tensor]> {
// TODO(jpienaar): To autogen the builder the type of the result needs to be
// determined from the operands. That would (beyond trivial cases) require
// type propagation information.
let builder = [{
static void build(Builder *builder, OperationState *result, SSAValue *lhs,
SSAValue *rhs) {
return impl::buildBinaryOp(builder, result, lhs, rhs);
}
}];
}
Op<mnemonic, props>, Operands<[Tensor, Tensor]>, Results<[Tensor]>;
class TernaryOp<string mnemonic, list<OpProperty> props> :
Op<mnemonic, props>, Operands<[Tensor, Tensor, Tensor]>;
class UnaryOp<string mnemonic, list<OpProperty> props> :
Op<mnemonic, props>, Operands<[Tensor]>, Results<[Tensor]> {
let builder = [{
static void build(Builder *builder, OperationState *result,
SSAValue *arg) {
result->addOperands({arg});
result->types.push_back(arg->getType());
}
}];
}
Op<mnemonic, props>, Operands<[Tensor]>, Results<[Tensor]>;

View File

@ -68,6 +68,16 @@ static inline bool hasStringAttribute(const Record &record,
return isa<CodeInit>(valueInit) || isa<StringInit>(valueInit);
}
// Returns `fieldName`'s value queried from `record` if `fieldName` is set as
// an string in record; otherwise, returns `defaultVal`.
static inline StringRef getAsStringOrDefault(const Record &record,
StringRef fieldName,
StringRef defaultVal) {
return hasStringAttribute(record, fieldName)
? record.getValueAsString(fieldName)
: defaultVal;
}
namespace {
// Simple RAII helper for defining ifdef-undef-endif scopes.
class IfDefScope {
@ -209,17 +219,64 @@ void OpEmitter::emitAttrGetters() {
}
void OpEmitter::emitBuilder() {
if (!hasStringAttribute(def, "builder"))
return;
// If a custom builder is given then print that out instead.
auto builder = def.getValueAsString("builder");
if (!builder.empty()) {
os << builder << '\n';
return;
if (hasStringAttribute(def, "builder")) {
// If a custom builder is given then print that out instead.
auto builder = def.getValueAsString("builder");
if (!builder.empty()) {
os << builder << '\n';
return;
}
}
// TODO(jpienaar): Redo generating builder.
// Otherwise, generate a default builder that requires all result type,
// operands, and attributes as parameters.
std::vector<Record *> returnTypes = def.getValueAsListOfDefs("returnTypes");
std::vector<Record *> operandTypes = def.getValueAsListOfDefs("operandTypes");
os << " static void build(Builder* builder, OperationState* result";
// 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
for (unsigned i = 0, e = operandTypes.size(); i != e; ++i)
os << ", SSAValue* arg" << i;
// Emit parameters for all attributes
// TODO(antiagainst): Support default initializer for attributes
for (const auto &pair : attrs) {
const Record &attr = *pair.second;
os << ", " << getAsStringOrDefault(attr, "storageType", "Attribute").trim()
<< ' ' << pair.first->getName();
}
os << ") {\n";
// Push all result types to the result
if (!returnTypes.empty()) {
os << " result->addTypes({returnType0";
for (unsigned i = 1, e = returnTypes.size(); i != e; ++i)
os << ", returnType" << i;
os << "});\n\n";
}
// Push all operands to the result
if (!operandTypes.empty()) {
os << " result->addOperands({arg0";
for (unsigned i = 1, e = operandTypes.size(); i != e; ++i)
os << ", arg" << i;
os << "});\n";
}
// Push all attributes to the result
for (const auto &pair : attrs) {
StringRef name = pair.first->getName();
os << " result->addAttribute(\"" << name << "\", " << name << ");\n";
}
os << " }\n";
}
void OpEmitter::emitCanonicalizationPatterns() {