//===----------------------------------------------------------------------===// // // 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 common utilities for generating C++ from tablegen // structures. // //===----------------------------------------------------------------------===// #ifndef MLIR_TABLEGEN_CODEGENHELPERS_H #define MLIR_TABLEGEN_CODEGENHELPERS_H #include "mlir/TableGen/Constraint.h" #include "mlir/TableGen/Dialect.h" #include "mlir/TableGen/Format.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/TableGen/CodeGenHelpers.h" #include namespace llvm { class RecordKeeper; } // namespace llvm namespace mlir { namespace tblgen { class Constraint; class DagLeaf; // Format into a std::string template std::string strfmt(const char *fmt, Parameters &&...parameters) { return llvm::formatv(fmt, std::forward(parameters)...).str(); } // A helper RAII class to emit nested namespaces for a dialect. class DialectNamespaceEmitter { public: DialectNamespaceEmitter(raw_ostream &os, const Dialect &dialect) { if (!dialect) return; nsEmitter.emplace(os, dialect.getCppNamespace()); } private: std::optional nsEmitter; }; /// This class represents how an error stream string being constructed will be /// consumed. enum class ErrorStreamType { // Inside a string that's streamed into an InflightDiagnostic. InString, // Inside a string inside an OpError. InsideOpError, }; /// This class deduplicates shared operation verification code by emitting /// static functions alongside the op definitions. These methods are local to /// the definition file, and are invoked within the operation verify methods. /// An example is shown below: /// /// static LogicalResult localVerify(...) /// /// LogicalResult OpA::verify(...) { /// if (failed(localVerify(...))) /// return failure(); /// ... /// } /// /// LogicalResult OpB::verify(...) { /// if (failed(localVerify(...))) /// return failure(); /// ... /// } /// class StaticVerifierFunctionEmitter { public: /// Create a constraint uniquer with a unique prefix derived from the record /// keeper with an optional tag. StaticVerifierFunctionEmitter(raw_ostream &os, const llvm::RecordKeeper &records, StringRef tag = ""); /// Collect and unique all the constraints used by operations. void collectOpConstraints(ArrayRef opDefs); /// Collect and unique all compatible type, attribute, successor, and region /// constraints from the operations in the file and emit them at the top of /// the generated file. /// /// Constraints that do not meet the restriction that they can only reference /// `$_self` and `$_op` are not uniqued. void emitOpConstraints(); /// Unique all compatible type and attribute constraints from a pattern file /// and emit them at the top of the generated file. /// /// Constraints that do not meet the restriction that they can only reference /// `$_self`, `$_op`, and `$_builder` are not uniqued. void emitPatternConstraints(const ArrayRef constraints); /// Get the name of the static function used for the given type constraint. /// These functions are used for operand and result constraints and have the /// form: /// /// LogicalResult(Operation *op, Type type, StringRef valueKind, /// unsigned valueIndex); /// /// Pattern constraints have the form: /// /// LogicalResult(PatternRewriter &rewriter, Operation *op, Type type, /// StringRef failureStr); /// StringRef getTypeConstraintFn(const Constraint &constraint) const; /// Get the name of the static function used for the given attribute /// constraint. These functions are in the form: /// /// LogicalResult(Operation *op, Attribute attr, StringRef attrName); /// /// If a uniqued constraint was not found, this function returns std::nullopt. /// The uniqued constraints cannot be used in the context of an OpAdaptor. /// /// Pattern constraints have the form: /// /// LogicalResult(PatternRewriter &rewriter, Operation *op, Attribute attr, /// StringRef failureStr); /// std::optional getAttrConstraintFn(const Constraint &constraint) const; /// Get the name of the static function used for the given property /// constraint. These functions are in the form: /// /// LogicalResult(Operation *op, T property, StringRef propName); /// /// where T is the interface type specified in the constraint. /// If a uniqued constraint was not found, this function returns std::nullopt. /// The uniqued constraints cannot be used in the context of an OpAdaptor. /// /// Pattern constraints have the form: /// /// LogicalResult(PatternRewriter &rewriter, Operation *op, T property, /// StringRef failureStr); /// std::optional getPropConstraintFn(const Constraint &constraint) const; /// Get the name of the static function used for the given successor /// constraint. These functions are in the form: /// /// LogicalResult(Operation *op, Block *successor, StringRef successorName, /// unsigned successorIndex); /// StringRef getSuccessorConstraintFn(const Constraint &constraint) const; /// Get the name of the static function used for the given region constraint. /// These functions are in the form: /// /// LogicalResult(Operation *op, Region ®ion, StringRef regionName, /// unsigned regionIndex); /// /// The region name may be empty. StringRef getRegionConstraintFn(const Constraint &constraint) const; private: /// Emit static type constraint functions. void emitTypeConstraints(); /// Emit static attribute constraint functions. void emitAttrConstraints(); /// Emit static property constraint functions. void emitPropConstraints(); /// Emit static successor constraint functions. void emitSuccessorConstraints(); /// Emit static region constraint functions. void emitRegionConstraints(); /// Emit pattern constraints. void emitPatternConstraints(); /// Collect and unique all pattern constraints. void collectPatternConstraints(ArrayRef constraints); /// The output stream. raw_ostream &os; /// A unique label for the file currently being generated. This is used to /// ensure that the static functions have a unique name. std::string uniqueOutputLabel; /// Use a MapVector to ensure that functions are generated deterministically. using ConstraintMap = llvm::MapVector>; /// A generic function to emit constraints void emitConstraints(const ConstraintMap &constraints, StringRef selfName, const char *codeTemplate, ErrorStreamType errorStreamType); /// Assign a unique name to a unique constraint. std::string getUniqueName(StringRef kind, unsigned index); /// Unique a constraint in the map. void collectConstraint(ConstraintMap &map, StringRef kind, Constraint constraint); /// The set of type constraints used for operand and result verification in /// the current file. ConstraintMap typeConstraints; /// The set of attribute constraints used in the current file. ConstraintMap attrConstraints; /// The set of property constraints used in the current file. ConstraintMap propConstraints; /// The set of successor constraints used in the current file. ConstraintMap successorConstraints; /// The set of region constraints used in the current file. ConstraintMap regionConstraints; }; /// Escape a string using C++ encoding. E.g. foo"bar -> foo\x22bar. std::string escapeString(StringRef value); namespace detail { template struct stringifier { template static std::string apply(T &&t) { return std::string(std::forward(t)); } }; template <> struct stringifier { static std::string apply(const Twine &twine) { return twine.str(); } }; template struct stringifier> { static std::string apply(std::optional optional) { return optional ? stringifier::apply(*optional) : std::string(); } }; } // namespace detail /// Generically convert a value to a std::string. template std::string stringify(T &&t) { return detail::stringifier>>:: apply(std::forward(t)); } /// Helper to generate a C++ streaming error message from a given message. /// Message can contain '{{...}}' placeholders that are substituted with /// C-expressions via tgfmt. It would effectively convert: /// "failed to verify {{foo}}" /// into: /// "failed to verify " << bar /// where bar is the result of evaluating 'tgfmt("foo", &ctx)' at compile /// time. std::string buildErrorStreamingString( StringRef message, const FmtContext &ctx, ErrorStreamType errorStreamType = ErrorStreamType::InString); } // namespace tblgen } // namespace mlir #endif // MLIR_TABLEGEN_CODEGENHELPERS_H