[mlir] NFC: put C++ code emission classes in their own files

This exposes thse classes so that they can be used in interfaces.

Differential Revision: https://reviews.llvm.org/D72514
This commit is contained in:
Lei Zhang 2020-01-10 11:18:08 -05:00
parent 064087581a
commit ca4a55fabb
4 changed files with 408 additions and 340 deletions

View File

@ -0,0 +1,167 @@
//===- OpClass.h - Helper classes for Op C++ code emission ------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines several classes for Op C++ code emission. They are only
// expected to be used by MLIR TableGen backends.
//
// We emit the op declaration and definition into separate files: *Ops.h.inc
// and *Ops.cpp.inc. The former is to be included in the dialect *Ops.h and
// the latter for dialect *Ops.cpp. This way provides a cleaner interface.
//
// In order to do this split, we need to track method signature and
// implementation logic separately. Signature information is used for both
// declaration and definition, while implementation logic is only for
// definition. So we have the following classes for C++ code emission.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_TABLEGEN_OPCLASS_H_
#define MLIR_TABLEGEN_OPCLASS_H_
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <string>
namespace mlir {
namespace tblgen {
class FmtObjectBase;
// Class for holding the signature of an op's method for C++ code emission
class OpMethodSignature {
public:
OpMethodSignature(StringRef retType, StringRef name, StringRef params);
// Writes the signature as a method declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the signature as the start of a method definition to the given `os`.
// `namePrefix` is the prefix to be prepended to the method name (typically
// namespaces for qualifying the method definition).
void writeDefTo(raw_ostream &os, StringRef namePrefix) const;
private:
// Returns true if the given C++ `type` ends with '&' or '*', or is empty.
static bool elideSpaceAfterType(StringRef type);
std::string returnType;
std::string methodName;
std::string parameters;
};
// Class for holding the body of an op's method for C++ code emission
class OpMethodBody {
public:
explicit OpMethodBody(bool declOnly);
OpMethodBody &operator<<(Twine content);
OpMethodBody &operator<<(int content);
OpMethodBody &operator<<(const FmtObjectBase &content);
void writeTo(raw_ostream &os) const;
private:
// Whether this class should record method body.
bool isEffective;
std::string body;
};
// Class for holding an op's method for C++ code emission
class OpMethod {
public:
// Properties (qualifiers) of class methods. Bitfield is used here to help
// querying properties.
enum Property {
MP_None = 0x0,
MP_Static = 0x1, // Static method
MP_Constructor = 0x2, // Constructor
MP_Private = 0x4, // Private method
};
OpMethod(StringRef retType, StringRef name, StringRef params,
Property property, bool declOnly);
OpMethodBody &body();
// Returns true if this is a static method.
bool isStatic() const;
// Returns true if this is a private method.
bool isPrivate() const;
// Writes the method as a declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the method as a definition to the given `os`. `namePrefix` is the
// prefix to be prepended to the method name (typically namespaces for
// qualifying the method definition).
void writeDefTo(raw_ostream &os, StringRef namePrefix) const;
private:
Property properties;
// Whether this method only contains a declaration.
bool isDeclOnly;
OpMethodSignature methodSignature;
OpMethodBody methodBody;
};
// A class used to emit C++ classes from Tablegen. Contains a list of public
// methods and a list of private fields to be emitted.
class Class {
public:
explicit Class(StringRef name);
// Creates a new method in this class.
OpMethod &newMethod(StringRef retType, StringRef name, StringRef params = "",
OpMethod::Property = OpMethod::MP_None,
bool declOnly = false);
OpMethod &newConstructor(StringRef params = "", bool declOnly = false);
// Creates a new field in this class.
void newField(StringRef type, StringRef name, StringRef defaultValue = "");
// Writes this op's class as a declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the method definitions in this op's class to the given `os`.
void writeDefTo(raw_ostream &os) const;
// Returns the C++ class name of the op.
StringRef getClassName() const { return className; }
protected:
std::string className;
SmallVector<OpMethod, 8> methods;
SmallVector<std::string, 4> fields;
};
// Class for holding an op for C++ code emission
class OpClass : public Class {
public:
explicit OpClass(StringRef name, StringRef extraClassDeclaration = "");
// Sets whether this OpClass should generate the using directive for its
// associate operand adaptor class.
void setHasOperandAdaptorClass(bool has);
// Adds an op trait.
void addTrait(Twine trait);
// Writes this op's class as a declaration to the given `os`. Redefines
// Class::writeDeclTo to also emit traits and extra class declarations.
void writeDeclTo(raw_ostream &os) const;
private:
StringRef extraClassDeclaration;
SmallVector<std::string, 4> traits;
bool hasOperandAdaptor;
};
} // namespace tblgen
} // namespace mlir
#endif // MLIR_TABLEGEN_OPCLASS_H_

View File

@ -5,6 +5,7 @@ add_llvm_library(LLVMMLIRTableGen
Dialect.cpp Dialect.cpp
Format.cpp Format.cpp
Operator.cpp Operator.cpp
OpClass.cpp
OpInterfaces.cpp OpInterfaces.cpp
OpTrait.cpp OpTrait.cpp
Pattern.cpp Pattern.cpp

View File

@ -0,0 +1,235 @@
//===- OpClass.cpp - Helper classes for Op C++ code emission --------------===//
//
// 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 "mlir/TableGen/OpClass.h"
#include "mlir/TableGen/Format.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
using namespace mlir;
//===----------------------------------------------------------------------===//
// OpMethodSignature definitions
//===----------------------------------------------------------------------===//
tblgen::OpMethodSignature::OpMethodSignature(StringRef retType, StringRef name,
StringRef params)
: returnType(retType), methodName(name), parameters(params) {}
void tblgen::OpMethodSignature::writeDeclTo(raw_ostream &os) const {
os << returnType << (elideSpaceAfterType(returnType) ? "" : " ") << methodName
<< "(" << parameters << ")";
}
void tblgen::OpMethodSignature::writeDefTo(raw_ostream &os,
StringRef namePrefix) const {
// We need to remove the default values for parameters in method definition.
// TODO(antiagainst): We are using '=' and ',' as delimiters for parameter
// initializers. This is incorrect for initializer list with more than one
// element. Change to a more robust approach.
auto removeParamDefaultValue = [](StringRef params) {
std::string result;
std::pair<StringRef, StringRef> parts;
while (!params.empty()) {
parts = params.split("=");
result.append(result.empty() ? "" : ", ");
result.append(parts.first);
params = parts.second.split(",").second;
}
return result;
};
os << returnType << (elideSpaceAfterType(returnType) ? "" : " ") << namePrefix
<< (namePrefix.empty() ? "" : "::") << methodName << "("
<< removeParamDefaultValue(parameters) << ")";
}
bool tblgen::OpMethodSignature::elideSpaceAfterType(StringRef type) {
return type.empty() || type.endswith("&") || type.endswith("*");
}
//===----------------------------------------------------------------------===//
// OpMethodBody definitions
//===----------------------------------------------------------------------===//
tblgen::OpMethodBody::OpMethodBody(bool declOnly) : isEffective(!declOnly) {}
tblgen::OpMethodBody &tblgen::OpMethodBody::operator<<(Twine content) {
if (isEffective)
body.append(content.str());
return *this;
}
tblgen::OpMethodBody &tblgen::OpMethodBody::operator<<(int content) {
if (isEffective)
body.append(std::to_string(content));
return *this;
}
tblgen::OpMethodBody &
tblgen::OpMethodBody::operator<<(const FmtObjectBase &content) {
if (isEffective)
body.append(content.str());
return *this;
}
void tblgen::OpMethodBody::writeTo(raw_ostream &os) const {
auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; });
os << bodyRef;
if (bodyRef.empty() || bodyRef.back() != '\n')
os << "\n";
}
//===----------------------------------------------------------------------===//
// OpMethod definitions
//===----------------------------------------------------------------------===//
tblgen::OpMethod::OpMethod(StringRef retType, StringRef name, StringRef params,
OpMethod::Property property, bool declOnly)
: properties(property), isDeclOnly(declOnly),
methodSignature(retType, name, params), methodBody(declOnly) {}
tblgen::OpMethodBody &tblgen::OpMethod::body() { return methodBody; }
bool tblgen::OpMethod::isStatic() const { return properties & MP_Static; }
bool tblgen::OpMethod::isPrivate() const { return properties & MP_Private; }
void tblgen::OpMethod::writeDeclTo(raw_ostream &os) const {
os.indent(2);
if (isStatic())
os << "static ";
methodSignature.writeDeclTo(os);
os << ";";
}
void tblgen::OpMethod::writeDefTo(raw_ostream &os, StringRef namePrefix) const {
if (isDeclOnly)
return;
methodSignature.writeDefTo(os, namePrefix);
os << " {\n";
methodBody.writeTo(os);
os << "}";
}
//===----------------------------------------------------------------------===//
// Class definitions
//===----------------------------------------------------------------------===//
tblgen::Class::Class(StringRef name) : className(name) {}
tblgen::OpMethod &tblgen::Class::newMethod(StringRef retType, StringRef name,
StringRef params,
OpMethod::Property property,
bool declOnly) {
methods.emplace_back(retType, name, params, property, declOnly);
return methods.back();
}
tblgen::OpMethod &tblgen::Class::newConstructor(StringRef params,
bool declOnly) {
return newMethod("", getClassName(), params, OpMethod::MP_Constructor,
declOnly);
}
void tblgen::Class::newField(StringRef type, StringRef name,
StringRef defaultValue) {
std::string varName = formatv("{0} {1}", type, name).str();
std::string field = defaultValue.empty()
? varName
: formatv("{0} = {1}", varName, defaultValue).str();
fields.push_back(std::move(field));
}
void tblgen::Class::writeDeclTo(raw_ostream &os) const {
bool hasPrivateMethod = false;
os << "class " << className << " {\n";
os << "public:\n";
for (const auto &method : methods) {
if (!method.isPrivate()) {
method.writeDeclTo(os);
os << '\n';
} else {
hasPrivateMethod = true;
}
}
os << '\n';
os << "private:\n";
if (hasPrivateMethod) {
for (const auto &method : methods) {
if (method.isPrivate()) {
method.writeDeclTo(os);
os << '\n';
}
}
os << '\n';
}
for (const auto &field : fields)
os.indent(2) << field << ";\n";
os << "};\n";
}
void tblgen::Class::writeDefTo(raw_ostream &os) const {
for (const auto &method : methods) {
method.writeDefTo(os, className);
os << "\n\n";
}
}
//===----------------------------------------------------------------------===//
// OpClass definitions
//===----------------------------------------------------------------------===//
tblgen::OpClass::OpClass(StringRef name, StringRef extraClassDeclaration)
: Class(name), extraClassDeclaration(extraClassDeclaration),
hasOperandAdaptor(true) {}
void tblgen::OpClass::setHasOperandAdaptorClass(bool has) {
hasOperandAdaptor = has;
}
// Adds the given trait to this op.
void tblgen::OpClass::addTrait(Twine trait) { traits.push_back(trait.str()); }
void tblgen::OpClass::writeDeclTo(raw_ostream &os) const {
os << "class " << className << " : public Op<" << className;
for (const auto &trait : traits)
os << ", " << trait;
os << "> {\npublic:\n";
os << " using Op::Op;\n";
if (hasOperandAdaptor)
os << " using OperandAdaptor = " << className << "OperandAdaptor;\n";
bool hasPrivateMethod = false;
for (const auto &method : methods) {
if (!method.isPrivate()) {
method.writeDeclTo(os);
os << "\n";
} else {
hasPrivateMethod = true;
}
}
// TODO: Add line control markers to make errors easier to debug.
if (!extraClassDeclaration.empty())
os << extraClassDeclaration << "\n";
if (hasPrivateMethod) {
os << "\nprivate:\n";
for (const auto &method : methods) {
if (method.isPrivate()) {
method.writeDeclTo(os);
os << "\n";
}
}
}
os << "};\n";
}

View File

@ -14,6 +14,7 @@
#include "mlir/Support/STLExtras.h" #include "mlir/Support/STLExtras.h"
#include "mlir/TableGen/Format.h" #include "mlir/TableGen/Format.h"
#include "mlir/TableGen/GenInfo.h" #include "mlir/TableGen/GenInfo.h"
#include "mlir/TableGen/OpClass.h"
#include "mlir/TableGen/OpInterfaces.h" #include "mlir/TableGen/OpInterfaces.h"
#include "mlir/TableGen/OpTrait.h" #include "mlir/TableGen/OpTrait.h"
#include "mlir/TableGen/Operator.h" #include "mlir/TableGen/Operator.h"
@ -114,6 +115,10 @@ static bool canUseUnwrappedRawValue(const tblgen::Attribute &attr) {
!attr.getConstBuilderTemplate().empty(); !attr.getConstBuilderTemplate().empty();
} }
//===----------------------------------------------------------------------===//
// Op emitter
//===----------------------------------------------------------------------===//
namespace { namespace {
// Simple RAII helper for defining ifdef-undef-endif scopes. // Simple RAII helper for defining ifdef-undef-endif scopes.
class IfDefScope { class IfDefScope {
@ -131,346 +136,6 @@ private:
}; };
} // end anonymous namespace } // end anonymous namespace
//===----------------------------------------------------------------------===//
// Classes for C++ code emission
//===----------------------------------------------------------------------===//
// We emit the op declaration and definition into separate files: *Ops.h.inc
// and *Ops.cpp.inc. The former is to be included in the dialect *Ops.h and
// the latter for dialect *Ops.cpp. This way provides a cleaner interface.
//
// In order to do this split, we need to track method signature and
// implementation logic separately. Signature information is used for both
// declaration and definition, while implementation logic is only for
// definition. So we have the following classes for C++ code emission.
namespace {
// Class for holding the signature of an op's method for C++ code emission
class OpMethodSignature {
public:
OpMethodSignature(StringRef retType, StringRef name, StringRef params);
// Writes the signature as a method declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the signature as the start of a method definition to the given `os`.
// `namePrefix` is the prefix to be prepended to the method name (typically
// namespaces for qualifying the method definition).
void writeDefTo(raw_ostream &os, StringRef namePrefix) const;
private:
// Returns true if the given C++ `type` ends with '&' or '*', or is empty.
static bool elideSpaceAfterType(StringRef type);
std::string returnType;
std::string methodName;
std::string parameters;
};
// Class for holding the body of an op's method for C++ code emission
class OpMethodBody {
public:
explicit OpMethodBody(bool declOnly);
OpMethodBody &operator<<(Twine content);
OpMethodBody &operator<<(int content);
OpMethodBody &operator<<(const FmtObjectBase &content);
void writeTo(raw_ostream &os) const;
private:
// Whether this class should record method body.
bool isEffective;
std::string body;
};
// Class for holding an op's method for C++ code emission
class OpMethod {
public:
// Properties (qualifiers) of class methods. Bitfield is used here to help
// querying properties.
enum Property {
MP_None = 0x0,
MP_Static = 0x1, // Static method
MP_Constructor = 0x2, // Constructor
MP_Private = 0x4, // Private method
};
OpMethod(StringRef retType, StringRef name, StringRef params,
Property property, bool declOnly);
OpMethodBody &body();
// Returns true if this is a static method.
bool isStatic() const;
// Returns true if this is a private method.
bool isPrivate() const;
// Writes the method as a declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the method as a definition to the given `os`. `namePrefix` is the
// prefix to be prepended to the method name (typically namespaces for
// qualifying the method definition).
void writeDefTo(raw_ostream &os, StringRef namePrefix) const;
private:
Property properties;
// Whether this method only contains a declaration.
bool isDeclOnly;
OpMethodSignature methodSignature;
OpMethodBody methodBody;
};
// A class used to emit C++ classes from Tablegen. Contains a list of public
// methods and a list of private fields to be emitted.
class Class {
public:
explicit Class(StringRef name);
// Creates a new method in this class.
OpMethod &newMethod(StringRef retType, StringRef name, StringRef params = "",
OpMethod::Property = OpMethod::MP_None,
bool declOnly = false);
OpMethod &newConstructor(StringRef params = "", bool declOnly = false);
// Creates a new field in this class.
void newField(StringRef type, StringRef name, StringRef defaultValue = "");
// Writes this op's class as a declaration to the given `os`.
void writeDeclTo(raw_ostream &os) const;
// Writes the method definitions in this op's class to the given `os`.
void writeDefTo(raw_ostream &os) const;
// Returns the C++ class name of the op.
StringRef getClassName() const { return className; }
protected:
std::string className;
SmallVector<OpMethod, 8> methods;
SmallVector<std::string, 4> fields;
};
// Class for holding an op for C++ code emission
class OpClass : public Class {
public:
explicit OpClass(StringRef name, StringRef extraClassDeclaration = "");
// Sets whether this OpClass should generate the using directive for its
// associate operand adaptor class.
void setHasOperandAdaptorClass(bool has);
// Adds an op trait.
void addTrait(Twine trait);
// Writes this op's class as a declaration to the given `os`. Redefines
// Class::writeDeclTo to also emit traits and extra class declarations.
void writeDeclTo(raw_ostream &os) const;
private:
StringRef extraClassDeclaration;
SmallVector<std::string, 4> traits;
bool hasOperandAdaptor;
};
} // end anonymous namespace
OpMethodSignature::OpMethodSignature(StringRef retType, StringRef name,
StringRef params)
: returnType(retType), methodName(name), parameters(params) {}
void OpMethodSignature::writeDeclTo(raw_ostream &os) const {
os << returnType << (elideSpaceAfterType(returnType) ? "" : " ") << methodName
<< "(" << parameters << ")";
}
void OpMethodSignature::writeDefTo(raw_ostream &os,
StringRef namePrefix) const {
// We need to remove the default values for parameters in method definition.
// TODO(antiagainst): We are using '=' and ',' as delimiters for parameter
// initializers. This is incorrect for initializer list with more than one
// element. Change to a more robust approach.
auto removeParamDefaultValue = [](StringRef params) {
std::string result;
std::pair<StringRef, StringRef> parts;
while (!params.empty()) {
parts = params.split("=");
result.append(result.empty() ? "" : ", ");
result.append(parts.first);
params = parts.second.split(",").second;
}
return result;
};
os << returnType << (elideSpaceAfterType(returnType) ? "" : " ") << namePrefix
<< (namePrefix.empty() ? "" : "::") << methodName << "("
<< removeParamDefaultValue(parameters) << ")";
}
bool OpMethodSignature::elideSpaceAfterType(StringRef type) {
return type.empty() || type.endswith("&") || type.endswith("*");
}
OpMethodBody::OpMethodBody(bool declOnly) : isEffective(!declOnly) {}
OpMethodBody &OpMethodBody::operator<<(Twine content) {
if (isEffective)
body.append(content.str());
return *this;
}
OpMethodBody &OpMethodBody::operator<<(int content) {
if (isEffective)
body.append(std::to_string(content));
return *this;
}
OpMethodBody &OpMethodBody::operator<<(const FmtObjectBase &content) {
if (isEffective)
body.append(content.str());
return *this;
}
void OpMethodBody::writeTo(raw_ostream &os) const {
auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; });
os << bodyRef;
if (bodyRef.empty() || bodyRef.back() != '\n')
os << "\n";
}
OpMethod::OpMethod(StringRef retType, StringRef name, StringRef params,
OpMethod::Property property, bool declOnly)
: properties(property), isDeclOnly(declOnly),
methodSignature(retType, name, params), methodBody(declOnly) {}
OpMethodBody &OpMethod::body() { return methodBody; }
bool OpMethod::isStatic() const { return properties & MP_Static; }
bool OpMethod::isPrivate() const { return properties & MP_Private; }
void OpMethod::writeDeclTo(raw_ostream &os) const {
os.indent(2);
if (isStatic())
os << "static ";
methodSignature.writeDeclTo(os);
os << ";";
}
void OpMethod::writeDefTo(raw_ostream &os, StringRef namePrefix) const {
if (isDeclOnly)
return;
methodSignature.writeDefTo(os, namePrefix);
os << " {\n";
methodBody.writeTo(os);
os << "}";
}
Class::Class(StringRef name) : className(name) {}
OpMethod &Class::newMethod(StringRef retType, StringRef name, StringRef params,
OpMethod::Property property, bool declOnly) {
methods.emplace_back(retType, name, params, property, declOnly);
return methods.back();
}
OpMethod &Class::newConstructor(StringRef params, bool declOnly) {
return newMethod("", getClassName(), params, OpMethod::MP_Constructor,
declOnly);
}
void Class::newField(StringRef type, StringRef name, StringRef defaultValue) {
std::string varName = formatv("{0} {1}", type, name).str();
std::string field = defaultValue.empty()
? varName
: formatv("{0} = {1}", varName, defaultValue).str();
fields.push_back(std::move(field));
}
void Class::writeDeclTo(raw_ostream &os) const {
bool hasPrivateMethod = false;
os << "class " << className << " {\n";
os << "public:\n";
for (const auto &method : methods) {
if (!method.isPrivate()) {
method.writeDeclTo(os);
os << '\n';
} else {
hasPrivateMethod = true;
}
}
os << '\n';
os << "private:\n";
if (hasPrivateMethod) {
for (const auto &method : methods) {
if (method.isPrivate()) {
method.writeDeclTo(os);
os << '\n';
}
}
os << '\n';
}
for (const auto &field : fields)
os.indent(2) << field << ";\n";
os << "};\n";
}
void Class::writeDefTo(raw_ostream &os) const {
for (const auto &method : methods) {
method.writeDefTo(os, className);
os << "\n\n";
}
}
OpClass::OpClass(StringRef name, StringRef extraClassDeclaration)
: Class(name), extraClassDeclaration(extraClassDeclaration),
hasOperandAdaptor(true) {}
void OpClass::setHasOperandAdaptorClass(bool has) { hasOperandAdaptor = has; }
// Adds the given trait to this op.
void OpClass::addTrait(Twine trait) { traits.push_back(trait.str()); }
void OpClass::writeDeclTo(raw_ostream &os) const {
os << "class " << className << " : public Op<" << className;
for (const auto &trait : traits)
os << ", " << trait;
os << "> {\npublic:\n";
os << " using Op::Op;\n";
if (hasOperandAdaptor)
os << " using OperandAdaptor = " << className << "OperandAdaptor;\n";
bool hasPrivateMethod = false;
for (const auto &method : methods) {
if (!method.isPrivate()) {
method.writeDeclTo(os);
os << "\n";
} else {
hasPrivateMethod = true;
}
}
// TODO: Add line control markers to make errors easier to debug.
if (!extraClassDeclaration.empty())
os << extraClassDeclaration << "\n";
if (hasPrivateMethod) {
os << "\nprivate:\n";
for (const auto &method : methods) {
if (method.isPrivate()) {
method.writeDeclTo(os);
os << "\n";
}
}
}
os << "};\n";
}
//===----------------------------------------------------------------------===//
// Op emitter
//===----------------------------------------------------------------------===//
namespace { namespace {
// Helper class to emit a record into the given output stream. // Helper class to emit a record into the given output stream.
class OpEmitter { class OpEmitter {