| //===- Class.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/Class.h" | |
| #include "llvm/ADT/Twine.h" | |
| #include "llvm/Support/Debug.h" | |
| using namespace mlir; | |
| using namespace mlir::tblgen; | |
| /// Returns space to be emitted after the given C++ `type`. return "" if the | |
| /// ends with '&' or '*', or is empty, else returns " ". | |
| static StringRef getSpaceAfterType(StringRef type) { | |
| return (type.empty() || type.ends_with("&") || type.ends_with("*")) ? "" | |
| : " "; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // MethodParameter definitions | |
| //===----------------------------------------------------------------------===// | |
| void MethodParameter::writeDeclTo(raw_indented_ostream &os) const { | |
| if (optional) | |
| os << "/*optional*/"; | |
| os << type << getSpaceAfterType(type) << name; | |
| if (hasDefaultValue()) | |
| os << " = " << defaultValue; | |
| } | |
| void MethodParameter::writeDefTo(raw_indented_ostream &os) const { | |
| if (optional) | |
| os << "/*optional*/"; | |
| os << type << getSpaceAfterType(type) << name; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // MethodParameters definitions | |
| //===----------------------------------------------------------------------===// | |
| void MethodParameters::writeDeclTo(raw_indented_ostream &os) const { | |
| llvm::interleaveComma(parameters, os, | |
| [&os](auto ¶m) { param.writeDeclTo(os); }); | |
| } | |
| void MethodParameters::writeDefTo(raw_indented_ostream &os) const { | |
| llvm::interleaveComma(parameters, os, | |
| [&os](auto ¶m) { param.writeDefTo(os); }); | |
| } | |
| bool MethodParameters::subsumes(const MethodParameters &other) const { | |
| // These parameters do not subsume the others if there are fewer parameters | |
| // or their types do not match. | |
| if (parameters.size() < other.parameters.size()) | |
| return false; | |
| if (!std::equal( | |
| other.parameters.begin(), other.parameters.end(), parameters.begin(), | |
| [](auto &lhs, auto &rhs) { return lhs.getType() == rhs.getType(); })) | |
| return false; | |
| // If all the common parameters have the same type, we can elide the other | |
| // method if this method has the same number of parameters as other or if the | |
| // first paramater after the common parameters has a default value (and, as | |
| // required by C++, subsequent parameters will have default values too). | |
| return parameters.size() == other.parameters.size() || | |
| parameters[other.parameters.size()].hasDefaultValue(); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // MethodSignature definitions | |
| //===----------------------------------------------------------------------===// | |
| bool MethodSignature::makesRedundant(const MethodSignature &other) const { | |
| return methodName == other.methodName && | |
| parameters.subsumes(other.parameters); | |
| } | |
| void MethodSignature::writeDeclTo(raw_indented_ostream &os) const { | |
| os << returnType << getSpaceAfterType(returnType) << methodName << "("; | |
| parameters.writeDeclTo(os); | |
| os << ")"; | |
| } | |
| void MethodSignature::writeDefTo(raw_indented_ostream &os, | |
| StringRef namePrefix) const { | |
| os << returnType << getSpaceAfterType(returnType) << namePrefix | |
| << (namePrefix.empty() ? "" : "::") << methodName << "("; | |
| parameters.writeDefTo(os); | |
| os << ")"; | |
| } | |
| void MethodSignature::writeTemplateParamsTo( | |
| mlir::raw_indented_ostream &os) const { | |
| if (templateParams.empty()) | |
| return; | |
| os << "template <"; | |
| llvm::interleaveComma(templateParams, os, | |
| [&](StringRef param) { os << "typename " << param; }); | |
| os << ">\n"; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // MethodBody definitions | |
| //===----------------------------------------------------------------------===// | |
| MethodBody::MethodBody(bool declOnly) | |
| : declOnly(declOnly), stringOs(body), os(stringOs) {} | |
| void MethodBody::writeTo(raw_indented_ostream &os) const { | |
| auto bodyRef = StringRef(body).ltrim('\n'); | |
| os << bodyRef; | |
| if (bodyRef.empty()) | |
| return; | |
| if (bodyRef.back() != '\n') | |
| os << "\n"; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Method definitions | |
| //===----------------------------------------------------------------------===// | |
| void Method::writeDeclTo(raw_indented_ostream &os) const { | |
| methodSignature.writeTemplateParamsTo(os); | |
| if (deprecationMessage) { | |
| os << "[[deprecated(\""; | |
| os.write_escaped(*deprecationMessage); | |
| os << "\")]]\n"; | |
| } | |
| if (isStatic()) | |
| os << "static "; | |
| if (properties & ConstexprValue) | |
| os << "constexpr "; | |
| methodSignature.writeDeclTo(os); | |
| if (isConst()) | |
| os << " const"; | |
| if (!isInline()) { | |
| os << ";\n"; | |
| return; | |
| } | |
| os << " {\n"; | |
| methodBody.writeTo(os); | |
| os << "}\n\n"; | |
| } | |
| void Method::writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const { | |
| // The method has no definition to write if it is declaration only or inline. | |
| if (properties & Declaration || isInline()) | |
| return; | |
| methodSignature.writeDefTo(os, namePrefix); | |
| if (isConst()) | |
| os << " const"; | |
| os << " {\n"; | |
| methodBody.writeTo(os); | |
| os << "}\n\n"; | |
| } | |
| bool Method::methodPropertiesAreCompatible(Properties properties) { | |
| const bool isStatic = (properties & Method::Static); | |
| const bool isConstructor = (properties & Method::Constructor); | |
| // const bool isPrivate = (properties & Method::Private); | |
| const bool isDeclaration = (properties & Method::Declaration); | |
| const bool isInline = (properties & Method::Inline); | |
| const bool isConstexprValue = (properties & Method::ConstexprValue); | |
| const bool isConst = (properties & Method::Const); | |
| // Note: assert to immediately fail and thus simplify debugging. | |
| if (isStatic && isConstructor) { | |
| assert(false && "constructor cannot be static"); | |
| return false; | |
| } | |
| if (isConstructor && isConst) { // albeit constexpr is fine | |
| assert(false && "constructor cannot be const"); | |
| return false; | |
| } | |
| if (isDeclaration && isInline) { | |
| assert(false && | |
| "declaration implies no definition and thus cannot be inline"); | |
| return false; | |
| } | |
| if (isDeclaration && isConstexprValue) { | |
| assert(false && | |
| "declaration implies no definition and thus cannot be constexpr"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Constructor definitions | |
| //===----------------------------------------------------------------------===// | |
| void Constructor::writeDeclTo(raw_indented_ostream &os) const { | |
| methodSignature.writeTemplateParamsTo(os); | |
| if (properties & ConstexprValue) | |
| os << "constexpr "; | |
| methodSignature.writeDeclTo(os); | |
| if (!isInline()) { | |
| os << ";\n\n"; | |
| return; | |
| } | |
| os << ' '; | |
| if (!initializers.empty()) | |
| os << ": "; | |
| llvm::interleaveComma(initializers, os, | |
| [&](auto &initializer) { initializer.writeTo(os); }); | |
| if (!initializers.empty()) | |
| os << ' '; | |
| os << "{"; | |
| methodBody.writeTo(os); | |
| os << "}\n\n"; | |
| } | |
| void Constructor::writeDefTo(raw_indented_ostream &os, | |
| StringRef namePrefix) const { | |
| // The method has no definition to write if it is declaration only or inline. | |
| if (properties & Declaration || isInline()) | |
| return; | |
| methodSignature.writeDefTo(os, namePrefix); | |
| os << ' '; | |
| if (!initializers.empty()) | |
| os << ": "; | |
| llvm::interleaveComma(initializers, os, | |
| [&](auto &initializer) { initializer.writeTo(os); }); | |
| if (!initializers.empty()) | |
| os << ' '; | |
| os << "{"; | |
| methodBody.writeTo(os); | |
| os << "}\n\n"; | |
| } | |
| void Constructor::MemberInitializer::writeTo(raw_indented_ostream &os) const { | |
| os << name << '(' << value << ')'; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Visibility definitions | |
| //===----------------------------------------------------------------------===// | |
| namespace mlir { | |
| namespace tblgen { | |
| raw_ostream &operator<<(raw_ostream &os, Visibility visibility) { | |
| switch (visibility) { | |
| case Visibility::Public: | |
| return os << "public"; | |
| case Visibility::Protected: | |
| return os << "protected"; | |
| case Visibility::Private: | |
| return os << "private"; | |
| } | |
| return os; | |
| } | |
| } // namespace tblgen | |
| } // namespace mlir | |
| //===----------------------------------------------------------------------===// | |
| // ParentClass definitions | |
| //===----------------------------------------------------------------------===// | |
| void ParentClass::writeTo(raw_indented_ostream &os) const { | |
| os << visibility << ' ' << name; | |
| if (!templateParams.empty()) { | |
| auto scope = os.scope("<", ">", /*indent=*/false); | |
| llvm::interleaveComma(templateParams, os, | |
| [&](auto ¶m) { os << param; }); | |
| } | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // UsingDeclaration definitions | |
| //===----------------------------------------------------------------------===// | |
| void UsingDeclaration::writeDeclTo(raw_indented_ostream &os) const { | |
| if (!templateParams.empty()) { | |
| os << "template <"; | |
| llvm::interleaveComma(templateParams, os, [&](StringRef paramName) { | |
| os << "typename " << paramName; | |
| }); | |
| os << ">\n"; | |
| } | |
| os << "using " << name; | |
| if (!value.empty()) | |
| os << " = " << value; | |
| os << ";\n"; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Field definitions | |
| //===----------------------------------------------------------------------===// | |
| void Field::writeDeclTo(raw_indented_ostream &os) const { | |
| os << type << ' ' << name << ";\n"; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // VisibilityDeclaration definitions | |
| //===----------------------------------------------------------------------===// | |
| void VisibilityDeclaration::writeDeclTo(raw_indented_ostream &os) const { | |
| os.unindent(); | |
| os << visibility << ":\n"; | |
| os.indent(); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // ExtraClassDeclaration definitions | |
| //===----------------------------------------------------------------------===// | |
| void ExtraClassDeclaration::writeDeclTo(raw_indented_ostream &os) const { | |
| os.printReindented(extraClassDeclaration); | |
| } | |
| void ExtraClassDeclaration::writeDefTo(raw_indented_ostream &os, | |
| StringRef namePrefix) const { | |
| os.printReindented(extraClassDefinition); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Class definitions | |
| //===----------------------------------------------------------------------===// | |
| ParentClass &Class::addParent(ParentClass parent) { | |
| parents.push_back(std::move(parent)); | |
| return parents.back(); | |
| } | |
| void Class::writeDeclTo(raw_indented_ostream &os) const { | |
| if (!templateParams.empty()) { | |
| os << "template <"; | |
| llvm::interleaveComma(templateParams, os, | |
| [&](StringRef param) { os << "typename " << param; }); | |
| os << ">\n"; | |
| } | |
| // Declare the class. | |
| os << (isStruct ? "struct" : "class") << ' ' << className << ' '; | |
| // Declare the parent classes, if any. | |
| if (!parents.empty()) { | |
| os << ": "; | |
| llvm::interleaveComma(parents, os, | |
| [&](auto &parent) { parent.writeTo(os); }); | |
| os << ' '; | |
| } | |
| auto classScope = os.scope("{\n", "};\n", /*indent=*/true); | |
| // Print all the class declarations. | |
| for (auto &decl : declarations) | |
| decl->writeDeclTo(os); | |
| } | |
| void Class::writeDefTo(raw_indented_ostream &os) const { | |
| // Print all the definitions. | |
| for (auto &decl : declarations) | |
| decl->writeDefTo(os, className); | |
| } | |
| void Class::finalize() { | |
| // Sort the methods by public and private. Remove them from the pending list | |
| // of methods. | |
| SmallVector<std::unique_ptr<Method>> publicMethods, privateMethods; | |
| for (auto &method : methods) { | |
| if (method->isPrivate()) | |
| privateMethods.push_back(std::move(method)); | |
| else | |
| publicMethods.push_back(std::move(method)); | |
| } | |
| methods.clear(); | |
| // If the last visibility declaration wasn't `public`, add one that is. Then, | |
| // declare the public methods. | |
| if (!publicMethods.empty() && getLastVisibilityDecl() != Visibility::Public) | |
| declare<VisibilityDeclaration>(Visibility::Public); | |
| for (auto &method : publicMethods) | |
| declarations.push_back(std::move(method)); | |
| // If the last visibility declaration wasn't `private`, add one that is. Then, | |
| // declare the private methods. | |
| if (!privateMethods.empty() && getLastVisibilityDecl() != Visibility::Private) | |
| declare<VisibilityDeclaration>(Visibility::Private); | |
| for (auto &method : privateMethods) | |
| declarations.push_back(std::move(method)); | |
| // All fields added to the pending list are private and declared at the bottom | |
| // of the class. If the last visibility declaration wasn't `private`, add one | |
| // that is, then declare the fields. | |
| if (!fields.empty() && getLastVisibilityDecl() != Visibility::Private) | |
| declare<VisibilityDeclaration>(Visibility::Private); | |
| for (auto &field : fields) | |
| declare<Field>(std::move(field)); | |
| fields.clear(); | |
| } | |
| Visibility Class::getLastVisibilityDecl() const { | |
| auto reverseDecls = llvm::reverse(declarations); | |
| auto it = llvm::find_if(reverseDecls, llvm::IsaPred<VisibilityDeclaration>); | |
| return it == reverseDecls.end() | |
| ? (isStruct ? Visibility::Public : Visibility::Private) | |
| : cast<VisibilityDeclaration>(**it).getVisibility(); | |
| } | |
| Method *insertAndPruneMethods(std::vector<std::unique_ptr<Method>> &methods, | |
| std::unique_ptr<Method> newMethod) { | |
| if (llvm::any_of(methods, [&](auto &method) { | |
| return method->makesRedundant(*newMethod); | |
| })) | |
| return nullptr; | |
| llvm::erase_if(methods, [&](auto &method) { | |
| return newMethod->makesRedundant(*method); | |
| }); | |
| methods.push_back(std::move(newMethod)); | |
| return methods.back().get(); | |
| } | |
| Method *Class::addMethodAndPrune(Method &&newMethod) { | |
| return insertAndPruneMethods(methods, | |
| std::make_unique<Method>(std::move(newMethod))); | |
| } | |
| Constructor *Class::addConstructorAndPrune(Constructor &&newCtor) { | |
| return dyn_cast_or_null<Constructor>(insertAndPruneMethods( | |
| methods, std::make_unique<Constructor>(std::move(newCtor)))); | |
| } |