//===- Class.h - Helper classes for 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 C++ code emission. They are only // expected to be used by MLIR TableGen backends. // // We emit the declarations and definitions into separate files: *.h.inc and // *.cpp.inc. The former is to be included in the dialect *.h and the latter for // dialect *.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_CLASS_H_ #define MLIR_TABLEGEN_CLASS_H_ #include "mlir/Support/IndentedOstream.h" #include "mlir/Support/LLVM.h" #include "mlir/TableGen/CodeGenHelpers.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" #include #include namespace mlir { namespace tblgen { class FmtObjectBase; /// This class contains a single method parameter for a C++ function. class MethodParameter { public: /// Create a method parameter with a C++ type, parameter name, and an optional /// default value. Marking a parameter as "optional" is a cosmetic effect on /// the generated code. template MethodParameter(TypeT &&type, NameT &&name, DefaultT &&defaultValue, bool optional = false) : type(stringify(std::forward(type))), name(stringify(std::forward(name))), defaultValue(stringify(std::forward(defaultValue))), optional(optional) {} /// Create a method parameter with a C++ type, parameter name, and no default /// value. template MethodParameter(TypeT &&type, NameT &&name, bool optional = false) : MethodParameter(std::forward(type), std::forward(name), /*defaultValue=*/"", optional) {} /// Write the parameter as part of a method declaration. void writeDeclTo(raw_indented_ostream &os) const; /// Write the parameter as part of a method definition. void writeDefTo(raw_indented_ostream &os) const; /// Get the C++ type. StringRef getType() const { return type; } /// Get the C++ parameter name. StringRef getName() const { return name; } /// Returns true if the parameter has a default value. bool hasDefaultValue() const { return !defaultValue.empty(); } /// Get the default value. StringRef getDefaultValue() const { return defaultValue; } /// Returns true if the parameter is optional. bool isOptional() const { return optional; } private: /// The C++ type. std::string type; /// The variable name. std::string name; /// An optional default value. The default value exists if the string is not /// empty. std::string defaultValue; /// Whether the parameter should be indicated as "optional". bool optional; }; /// This class contains a list of method parameters for constructor, class /// methods, and method signatures. class MethodParameters { public: /// Create a list of method parameters. MethodParameters(std::initializer_list parameters) : parameters(parameters) {} MethodParameters(SmallVector parameters) : parameters(std::move(parameters)) {} /// Write the parameters as part of a method declaration. void writeDeclTo(raw_indented_ostream &os) const; /// Write the parameters as part of a method definition. void writeDefTo(raw_indented_ostream &os) const; /// Determine whether this list of parameters "subsumes" another, which occurs /// when this parameter list is identical to the other and has zero or more /// additional default-valued parameters. bool subsumes(const MethodParameters &other) const; /// Return the number of parameters. unsigned getNumParameters() const { return parameters.size(); } private: /// The list of parameters. SmallVector parameters; }; /// This class contains the signature of a C++ method, including the return /// type. method name, and method parameters. class MethodSignature { public: /// Create a method signature with a return type, a method name, and a list of /// parameters. Take ownership of the list. template MethodSignature(RetTypeT &&retType, NameT &&name, SmallVector &¶meters) : returnType(stringify(std::forward(retType))), methodName(stringify(std::forward(name))), parameters(std::move(parameters)) {} /// Create a method signature with a return type, a method name, and a list of /// parameters. template MethodSignature(RetTypeT &&retType, NameT &&name, ArrayRef parameters) : MethodSignature(std::forward(retType), std::forward(name), SmallVector(parameters)) {} /// Create a method signature with a return type, a method name, and a /// variadic list of parameters. template MethodSignature(RetTypeT &&retType, NameT &&name, Parameters &&...parameters) : MethodSignature(std::forward(retType), std::forward(name), ArrayRef( {std::forward(parameters)...})) {} /// Determine whether a method with this signature makes a method with /// `other` signature redundant. This occurs if the signatures have the same /// name and this signature's parameteres subsume the other's. /// /// A method that makes another method redundant with a different return type /// can replace the other, the assumption being that the subsuming method /// provides a more resolved return type, e.g. IntegerAttr vs. Attribute. bool makesRedundant(const MethodSignature &other) const; /// Get the name of the method. StringRef getName() const { return methodName; } /// Get the return type of the method StringRef getReturnType() const { return returnType; } /// Get the number of parameters. unsigned getNumParameters() const { return parameters.getNumParameters(); } /// Write the signature as part of a method declaration. void writeDeclTo(raw_indented_ostream &os) const; /// Write the signature as part of a method definition. `namePrefix` is to be /// prepended to the method name (typically namespaces for qualifying the /// method definition). void writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const; /// Write the template parameters of the signature. void writeTemplateParamsTo(raw_indented_ostream &os) const; /// Add a template parameter. template void addTemplateParam(ParamT param) { templateParams.push_back(stringify(param)); } /// Add a list of template parameters. template void addTemplateParams(ContainerT &&container) { templateParams.insert(std::begin(container), std::end(container)); } private: /// The method's C++ return type. std::string returnType; /// The method name. std::string methodName; /// The method's parameter list. MethodParameters parameters; /// An optional list of template parameters. SmallVector templateParams; }; /// This class contains the body of a C++ method. class MethodBody { public: /// Create a method body, indicating whether it should be elided for methods /// that are declaration-only. MethodBody(bool declOnly); /// Define a move constructor to correctly initialize the streams. MethodBody(MethodBody &&other) : declOnly(other.declOnly), body(std::move(other.body)), stringOs(body), os(stringOs) {} /// Define a move assignment operator. `raw_ostream` has deleted assignment /// operators, so reinitialize the whole object. MethodBody &operator=(MethodBody &&body) { this->~MethodBody(); new (this) MethodBody(std::move(body)); return *this; } /// Write a value to the method body. template MethodBody &operator<<(ValueT &&value) { if (!declOnly) { os << std::forward(value); os.flush(); } return *this; } /// Write the method body to the output stream. The body can be written as /// part of the declaration of an inline method or just in the definition. void writeTo(raw_indented_ostream &os) const; /// Indent the output stream. MethodBody &indent() { os.indent(); return *this; } /// Unindent the output stream. MethodBody &unindent() { os.unindent(); return *this; } /// Create a delimited scope: immediately print `open`, indent if `indent` is /// true, and print `close` on object destruction. raw_indented_ostream::DelimitedScope scope(StringRef open = "", StringRef close = "", bool indent = false) { return os.scope(open, close, indent); } /// Get the underlying indented output stream. raw_indented_ostream &getStream() { return os; } private: /// Whether the body should be elided. bool declOnly; /// The body data. std::string body; /// The string output stream. llvm::raw_string_ostream stringOs; /// An indented output stream for formatting input. raw_indented_ostream os; }; /// A class declaration is a class element that appears as part of its /// declaration. class ClassDeclaration { public: virtual ~ClassDeclaration() = default; /// Kinds for LLVM-style RTTI. enum Kind { Method, UsingDeclaration, VisibilityDeclaration, Field, ExtraClassDeclaration }; /// Create a class declaration with a given kind. ClassDeclaration(Kind kind) : kind(kind) {} /// Get the class declaration kind. Kind getKind() const { return kind; } /// Write the declaration. virtual void writeDeclTo(raw_indented_ostream &os) const = 0; /// Write the definition, if any. `namePrefix` is the namespace prefix, which /// may contains a class name. virtual void writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const {} private: /// The class declaration kind. Kind kind; }; /// Base class for class declarations. template class ClassDeclarationBase : public ClassDeclaration { public: using Base = ClassDeclarationBase; ClassDeclarationBase() : ClassDeclaration(DeclKind) {} static bool classof(const ClassDeclaration *other) { return other->getKind() == DeclKind; } }; /// Class for holding an op's method for C++ code emission class Method : public ClassDeclarationBase { public: /// Properties (qualifiers) of class methods. Bitfield is used here to help /// querying properties. enum Properties { None = 0x0, Static = 0x1, Constructor = 0x2, Private = 0x4, Declaration = 0x8, Inline = 0x10, ConstexprValue = 0x20, Const = 0x40, Constexpr = ConstexprValue | Inline, StaticDeclaration = Static | Declaration, StaticInline = Static | Inline, ConstInline = Const | Inline, ConstDeclaration = Const | Declaration }; /// Create a method with a return type, a name, method properties, and a some /// parameters. The parameteres may be passed as a list or as a variadic pack. template Method(RetTypeT &&retType, NameT &&name, Properties properties, Args &&...args) : properties(properties), methodSignature(std::forward(retType), std::forward(name), std::forward(args)...), methodBody(properties & Declaration) { if (!methodPropertiesAreCompatible(properties)) { llvm::report_fatal_error( "Invalid combination of method properties specified"); } } /// Create a method with a return type, a name, method properties, and a list /// of parameters. Method(StringRef retType, StringRef name, Properties properties, std::initializer_list params) : properties(properties), methodSignature(retType, name, params), methodBody(properties & Declaration) { if (!methodPropertiesAreCompatible(properties)) { llvm::report_fatal_error( "Invalid combination of method properties specified"); } } // Define move constructor and assignment operator to prevent copying. Method(Method &&) = default; Method &operator=(Method &&) = default; /// Get the method body. MethodBody &body() { return methodBody; } /// Sets or removes the deprecation message of the method. void setDeprecated(std::optional message) { this->deprecationMessage = message; } /// Returns true if this is a static method. bool isStatic() const { return properties & Static; } /// Returns true if this is a private method. bool isPrivate() const { return properties & Private; } /// Returns true if this is an inline method. bool isInline() const { return properties & Inline; } /// Returns true if this is a constructor. bool isConstructor() const { return properties & Constructor; } /// Returns true if this class method is const. bool isConst() const { return properties & Const; } /// Returns the name of this method. StringRef getName() const { return methodSignature.getName(); } /// Returns the return type of this method StringRef getReturnType() const { return methodSignature.getReturnType(); } /// Returns if this method makes the `other` method redundant. bool makesRedundant(const Method &other) const { return methodSignature.makesRedundant(other.methodSignature); } /// Write the method declaration, including the definition if inline. void writeDeclTo(raw_indented_ostream &os) const override; /// Write the method definition. This is a no-op for inline methods. void writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const override; /// Add a template parameter. template void addTemplateParam(ParamT param); /// Add a list of template parameters. template void addTemplateParams(ContainerT &&container); protected: /// A collection of method properties. Properties properties; /// The signature of the method. MethodSignature methodSignature; /// The body of the method, if it has one. MethodBody methodBody; /// Deprecation message if the method is deprecated. std::optional deprecationMessage; /// Utility method to verify method properties correctness. [[maybe_unused]] static bool methodPropertiesAreCompatible(Properties properties); }; /// This enum describes C++ inheritance visibility. enum class Visibility { Public, Protected, Private }; /// Write "public", "protected", or "private". llvm::raw_ostream &operator<<(llvm::raw_ostream &os, mlir::tblgen::Visibility visibility); // Class for holding an op's constructor method for C++ code emission. class Constructor : public Method { public: /// Create a constructor for a given class, with method properties, and /// parameters specified either as a list of a variadic pack. template Constructor(NameT &&className, Properties properties, Args &&...args) : Method("", std::forward(className), properties, std::forward(args)...) {} /// Add member initializer to constructor initializing `name` with `value`. template void addMemberInitializer(NameT &&name, ValueT &&value) { initializers.emplace_back(stringify(std::forward(name)), stringify(std::forward(value))); } /// Write the declaration of the constructor, and its definition if inline. void writeDeclTo(raw_indented_ostream &os) const override; /// Write the definition of the constructor if it is not inline. void writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const override; /// Return true if a method is a constructor. static bool classof(const ClassDeclaration *other) { return isa(other) && cast(other)->isConstructor(); } /// Initialization of a class field in a constructor. class MemberInitializer { public: /// Create a member initializer in a constructor that initializes the class /// field `name` with `value`. MemberInitializer(std::string name, std::string value) : name(std::move(name)), value(std::move(value)) {} /// Write the member initializer. void writeTo(raw_indented_ostream &os) const; private: /// The name of the class field. std::string name; /// The value with which to initialize it. std::string value; }; private: /// The list of member initializers. SmallVector initializers; }; } // namespace tblgen } // namespace mlir /// The OR of two method properties should return method properties. Ensure that /// this function is visible to `Class`. inline constexpr mlir::tblgen::Method::Properties operator|(mlir::tblgen::Method::Properties lhs, mlir::tblgen::Method::Properties rhs) { return mlir::tblgen::Method::Properties(static_cast(lhs) | static_cast(rhs)); } inline constexpr mlir::tblgen::Method::Properties & operator|=(mlir::tblgen::Method::Properties &lhs, mlir::tblgen::Method::Properties rhs) { return lhs = mlir::tblgen::Method::Properties(static_cast(lhs) | static_cast(rhs)); } namespace mlir { namespace tblgen { template void Method::addTemplateParam(ParamT param) { // Templates imply inline. properties |= Method::Inline; methodSignature.addTemplateParam(param); } template void Method::addTemplateParams(ContainerT &&container) { // Templates imply inline. properties |= Method::Inline; methodSignature.addTemplateParam(std::forward(container)); } /// This class describes a C++ parent class declaration. class ParentClass { public: /// Create a parent class with a class name and visibility. template ParentClass(NameT &&name, Visibility visibility = Visibility::Public) : name(stringify(std::forward(name))), visibility(visibility) {} /// Add a template parameter. template void addTemplateParam(ParamT param) { templateParams.insert(stringify(param)); } /// Add a list of template parameters. template void addTemplateParams(ContainerT &&container) { templateParams.insert(std::begin(container), std::end(container)); } /// Write the parent class declaration. void writeTo(raw_indented_ostream &os) const; private: /// The fully resolved C++ name of the parent class. std::string name; /// The visibility of the parent class. Visibility visibility; /// An optional list of class template parameters. SetVector, StringSet<>> templateParams; }; /// This class describes a using-declaration for a class. E.g. /// /// using Op::Op; /// using Adaptor = OpAdaptor; /// class UsingDeclaration : public ClassDeclarationBase { public: /// Create a using declaration that either aliases `name` to `value` or /// inherits the parent methods `name. template UsingDeclaration(NameT &&name, ValueT &&value = "") : name(stringify(std::forward(name))), value(stringify(std::forward(value))) {} /// Write the using declaration. void writeDeclTo(raw_indented_ostream &os) const override; /// Add a template parameter. template void addTemplateParam(ParamT param) { templateParams.insert(stringify(param)); } /// Add a list of template parameters. template void addTemplateParams(ContainerT &&container) { templateParams.insert(std::begin(container), std::end(container)); } private: /// The name of the declaration, or a resolved name to an inherited function. std::string name; /// The type that is being aliased. Leave empty for inheriting functions. std::string value; /// An optional list of class template parameters. /// This is simply a ordered list of parameter names that are then added as /// template type parameters when the using declaration is emitted. SetVector, StringSet<>> templateParams; }; /// This class describes a class field. class Field : public ClassDeclarationBase { public: /// Create a class field with a type and variable name. template Field(TypeT &&type, NameT &&name) : type(stringify(std::forward(type))), name(stringify(std::forward(name))) {} /// Write the declaration of the field. void writeDeclTo(raw_indented_ostream &os) const override; private: /// The C++ type of the field. std::string type; /// The variable name of the class whether. std::string name; }; /// A declaration for the visibility of subsequent declarations. class VisibilityDeclaration : public ClassDeclarationBase { public: /// Create a declaration for the given visibility. VisibilityDeclaration(Visibility visibility) : visibility(visibility) {} /// Get the visibility. Visibility getVisibility() const { return visibility; } /// Write the visibility declaration. void writeDeclTo(raw_indented_ostream &os) const override; private: /// The visibility of subsequent class declarations. Visibility visibility; }; /// Unstructured extra class declarations and definitions, from TableGen /// definitions. The default visibility of extra class declarations is up to the /// owning class. class ExtraClassDeclaration : public ClassDeclarationBase { public: /// Create an extra class declaration. ExtraClassDeclaration(StringRef extraClassDeclaration, std::string extraClassDefinition = "") : ExtraClassDeclaration(extraClassDeclaration.str(), std::move(extraClassDefinition)) {} ExtraClassDeclaration(std::string extraClassDeclaration, std::string extraClassDefinition = "") : extraClassDeclaration(extraClassDeclaration), extraClassDefinition(extraClassDefinition) {} /// Write the extra class declarations. void writeDeclTo(raw_indented_ostream &os) const override; /// Write the extra class definitions. void writeDefTo(raw_indented_ostream &os, StringRef namePrefix) const override; private: /// The string of the extra class declarations. It is re-indented before /// printed. std::string extraClassDeclaration; /// The string of the extra class definitions. It is re-indented before /// printed. std::string extraClassDefinition; }; /// 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: virtual ~Class() = default; /// Explicitly delete the copy constructor. This is to work around a gcc-5 bug /// with std::is_trivially_move_constructible. Class(const Class &) = delete; /// Create a class with a name, and whether it should be declared as a `class` /// or `struct`. Also, prevent this from being mistaken as a move constructor /// candidate. template ::value>> Class(NameT &&name, bool isStruct = false) : className(stringify(std::forward(name))), isStruct(isStruct) {} /// Add a new constructor to this class and prune and constructors made /// redundant by it. Returns null if the constructor was not added. Else, /// returns a pointer to the new constructor. template Constructor *addConstructor(Args &&...args) { Method::Properties defaultProperties = Method::Constructor; // If the class has template parameters, the constructor has to be defined // inline. if (!templateParams.empty()) defaultProperties |= Method::Inline; return addConstructorAndPrune(Constructor(getClassName(), Properties | defaultProperties, std::forward(args)...)); } /// Add a new method to this class and prune any methods made redundant by it. /// Returns null if the method was not added (because an existing method would /// make it redundant). Else, returns a pointer to the new method. template Method *addMethod(RetTypeT &&retType, NameT &&name, Method::Properties properties, ArrayRef parameters) { // If the class has template parameters, then it has to be defined inline. if (!templateParams.empty()) properties |= Method::Inline; return addMethodAndPrune(Method(std::forward(retType), std::forward(name), Properties | properties, parameters)); } /// Add a method with statically-known properties. template Method *addMethod(RetTypeT &&retType, NameT &&name, ArrayRef parameters) { return addMethod(std::forward(retType), std::forward(name), Properties, parameters); } template Method *addMethod(RetTypeT &&retType, NameT &&name, Method::Properties properties, Args &&...args) { return addMethod(std::forward(retType), std::forward(name), properties | Properties, {std::forward(args)...}); } /// Add a method with statically-known properties. template Method *addMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod(std::forward(retType), std::forward(name), Properties, std::forward(args)...); } /// Add a static method. template Method *addStaticMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } /// Add an inline static method. template Method *addStaticInlineMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } /// Add an inline method. template Method *addInlineMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } /// Add a const method. template Method *addConstMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } /// Add a declaration for a method. template Method *declareMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } /// Add a declaration for a static method. template Method *declareStaticMethod(RetTypeT &&retType, NameT &&name, Args &&...args) { return addMethod( std::forward(retType), std::forward(name), std::forward(args)...); } const std::vector> &getMethods() const { return methods; } /// Add a new field to the class. Class fields added this way are always /// private. template void addField(TypeT &&type, NameT &&name) { fields.emplace_back(std::forward(type), std::forward(name)); } /// Add a parent class. ParentClass &addParent(ParentClass parent); /// Add a template parameter. template void addTemplateParam(ParamT param) { templateParams.insert(stringify(param)); } /// Add a list of template parameters. template void addTemplateParams(ContainerT &&container) { templateParams.insert(std::begin(container), std::end(container)); } /// Return the C++ name of the class. StringRef getClassName() const { return className; } /// Write the declaration of this class, all declarations, and definitions of /// inline functions. Wrap the output stream in an indented stream. void writeDeclTo(raw_ostream &rawOs) const { raw_indented_ostream os(rawOs); writeDeclTo(os); } /// Write the definitions of thiss class's out-of-line constructors and /// methods. Wrap the output stream in an indented stream. void writeDefTo(raw_ostream &rawOs) const { raw_indented_ostream os(rawOs); writeDefTo(os); } /// Write the declaration of this class, all declarations, and definitions of /// inline functions. void writeDeclTo(raw_indented_ostream &os) const; /// Write the definitions of thiss class's out-of-line constructors and /// methods. void writeDefTo(raw_indented_ostream &os) const; /// Add a declaration. The declaration is appended directly to the list of /// class declarations. template DeclT *declare(Args &&...args) { auto decl = std::make_unique(std::forward(args)...); auto *ret = decl.get(); declarations.push_back(std::move(decl)); return ret; } /// The declaration of a class needs to be "finalized". /// /// Class constructors, methods, and fields can be added in any order, /// regardless of whether they are public or private. These are stored in /// lists separate from list of declarations `declarations`. /// /// So that the generated C++ code is somewhat organised, public methods are /// declared together, and so are private methods and class fields. This /// function iterates through all the added methods and fields and organises /// them into the list of declarations, adding visibility declarations as /// needed, as follows: /// /// 1. public methods and constructors /// 2. private methods and constructors /// 3. class fields -- all are private /// /// `Class::finalize` clears the lists of pending methods and fields, and can /// be called multiple times. virtual void finalize(); protected: /// Add a new constructor if it is not made redundant by any existing /// constructors and prune and existing constructors made redundant. Constructor *addConstructorAndPrune(Constructor &&newCtor); /// Add a new method if it is not made redundant by any existing methods and /// prune and existing methods made redundant. Method *addMethodAndPrune(Method &&newMethod); /// Get the last visibility declaration. Visibility getLastVisibilityDecl() const; /// The C++ class name. std::string className; /// The list of parent classes. SmallVector parents; /// The pending list of methods and constructors. std::vector> methods; /// The pending list of private class fields. SmallVector fields; /// Whether this is a `class` or a `struct`. bool isStruct; /// A list of declarations in the class, emitted in order. std::vector> declarations; /// An optional list of class template parameters. SetVector, StringSet<>> templateParams; }; } // namespace tblgen } // namespace mlir #endif // MLIR_TABLEGEN_CLASS_H_