| //===- 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 "mlir/TableGen/Format.h" |
| #include "llvm/ADT/Sequence.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.endswith("&") || type.endswith("*")) ? "" : " "; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // 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 << ")"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // MethodBody definitions |
| //===----------------------------------------------------------------------===// |
| |
| MethodBody::MethodBody(bool declOnly) |
| : declOnly(declOnly), stringOs(body), os(stringOs) {} |
| |
| void MethodBody::writeTo(raw_indented_ostream &os) const { |
| auto bodyRef = StringRef(body).drop_while([](char c) { return c == '\n'; }); |
| os << bodyRef; |
| if (bodyRef.empty()) |
| return; |
| if (bodyRef.back() != '\n') |
| os << "\n"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Method definitions |
| //===----------------------------------------------------------------------===// |
| |
| void Method::writeDeclTo(raw_indented_ostream &os) const { |
| 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"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Constructor definitions |
| //===----------------------------------------------------------------------===// |
| |
| void Constructor::writeDeclTo(raw_indented_ostream &os) const { |
| 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; |
| } |
| } // end namespace tblgen |
| } // end 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 { |
| 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); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Class definitions |
| //===----------------------------------------------------------------------===// |
| |
| ParentClass &Class::addParent(ParentClass parent) { |
| parents.push_back(std::move(parent)); |
| return parents.back(); |
| } |
| |
| void Class::writeDeclTo(raw_indented_ostream &os) const { |
| // 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, [](auto &decl) { |
| return isa<VisibilityDeclaration>(decl); |
| }); |
| 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)))); |
| } |