//===- Pattern.h - Pattern wrapper class ------------------------*- 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 // //===----------------------------------------------------------------------===// // // Pattern wrapper class to simplify using TableGen Record defining a MLIR // Pattern. // //===----------------------------------------------------------------------===// #ifndef MLIR_TABLEGEN_PATTERN_H_ #define MLIR_TABLEGEN_PATTERN_H_ #include "mlir/Support/LLVM.h" #include "mlir/TableGen/Argument.h" #include "mlir/TableGen/EnumInfo.h" #include "mlir/TableGen/Operator.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include #include namespace llvm { class DagInit; class Init; class Record; } // namespace llvm namespace mlir { namespace tblgen { // Mapping from TableGen Record to Operator wrapper object. // // We allocate each wrapper object in heap to make sure the pointer to it is // valid throughout the lifetime of this map. This is important because this map // is shared among multiple patterns to avoid creating the wrapper object for // the same op again and again. But this map will continuously grow. using RecordOperatorMap = DenseMap>; class Pattern; // Wrapper class providing helper methods for accessing TableGen DAG leaves // used inside Patterns. This class is lightweight and designed to be used like // values. // // A TableGen DAG construct is of the syntax // `(operator, arg0, arg1, ...)`. // // This class provides getters to retrieve `arg*` as tblgen:: wrapper objects // for handy helper methods. It only works on `arg*`s that are not nested DAG // constructs. class DagLeaf { public: explicit DagLeaf(const llvm::Init *def) : def(def) {} // Returns true if this DAG leaf is not specified in the pattern. That is, it // places no further constraints/transforms and just carries over the original // value. bool isUnspecified() const; // Returns true if this DAG leaf is matching an operand. That is, it specifies // a type constraint. bool isOperandMatcher() const; // Returns true if this DAG leaf is matching an attribute. That is, it // specifies an attribute constraint. bool isAttrMatcher() const; // Returns true if this DAG leaf is matching a property. That is, it // specifies a property constraint. bool isPropMatcher() const; // Returns true if this DAG leaf is describing a property. That is, it // is a subclass of `Property` in tablegen. bool isPropDefinition() const; // Returns true if this DAG leaf is wrapping native code call. bool isNativeCodeCall() const; // Returns true if this DAG leaf is specifying a constant attribute. bool isConstantAttr() const; // Returns true if this DAG leaf is specifying a constant property. bool isConstantProp() const; // Returns true if this DAG leaf is specifying an enum case. bool isEnumCase() const; // Returns true if this DAG leaf is specifying a string attribute. bool isStringAttr() const; // Returns this DAG leaf as a constraint. Asserts if fails. Constraint getAsConstraint() const; // Returns this DAG leaf as a property constraint. Asserts if fails. This // allows access to the interface type. PropConstraint getAsPropConstraint() const; // Returns this DAG leaf as a property definition. Asserts if fails. Property getAsProperty() const; // Returns this DAG leaf as an constant attribute. Asserts if fails. ConstantAttr getAsConstantAttr() const; // Returns this DAG leaf as an constant property. Asserts if fails. ConstantProp getAsConstantProp() const; // Returns this DAG leaf as an enum case. // Precondition: isEnumCase() EnumCase getAsEnumCase() const; // Returns the matching condition template inside this DAG leaf. Assumes the // leaf is an operand/attribute matcher and asserts otherwise. std::string getConditionTemplate() const; // Returns the native code call template inside this DAG leaf. // Precondition: isNativeCodeCall() StringRef getNativeCodeTemplate() const; // Returns the number of values will be returned by the native helper // function. // Precondition: isNativeCodeCall() int getNumReturnsOfNativeCode() const; // Returns the string associated with the leaf. // Precondition: isStringAttr() std::string getStringAttr() const; void print(raw_ostream &os) const; private: friend llvm::DenseMapInfo; const void *getAsOpaquePointer() const { return def; } // Returns true if the TableGen Init `def` in this DagLeaf is a DefInit and // also a subclass of the given `superclass`. bool isSubClassOf(StringRef superclass) const; const llvm::Init *def; }; // Wrapper class providing helper methods for accessing TableGen DAG constructs // used inside Patterns. This class is lightweight and designed to be used like // values. // // A TableGen DAG construct is of the syntax // `(operator, arg0, arg1, ...)`. // // When used inside Patterns, `operator` corresponds to some dialect op, or // a known list of verbs that defines special transformation actions. This // `arg*` can be a nested DAG construct. This class provides getters to // retrieve `operator` and `arg*` as tblgen:: wrapper objects for handy helper // methods. // // A null DagNode contains a nullptr and converts to false implicitly. class DagNode { public: explicit DagNode(const llvm::DagInit *node) : node(node) {} // Implicit bool converter that returns true if this DagNode is not a null // DagNode. operator bool() const { return node != nullptr; } // Returns the symbol bound to this DAG node. StringRef getSymbol() const; // Returns the operator wrapper object corresponding to the dialect op matched // by this DAG. The operator wrapper will be queried from the given `mapper` // and created in it if not existing. Operator &getDialectOp(RecordOperatorMap *mapper) const; // Returns the number of operations recursively involved in the DAG tree // rooted from this node. int getNumOps() const; // Returns the number of immediate arguments to this DAG node. int getNumArgs() const; // Returns true if the `index`-th argument is a nested DAG construct. bool isNestedDagArg(unsigned index) const; // Gets the `index`-th argument as a nested DAG construct if possible. Returns // null DagNode otherwise. DagNode getArgAsNestedDag(unsigned index) const; // Gets the `index`-th argument as a DAG leaf. DagLeaf getArgAsLeaf(unsigned index) const; // Returns the specified name of the `index`-th argument. StringRef getArgName(unsigned index) const; // Returns true if this DAG construct means to replace with an existing SSA // value. bool isReplaceWithValue() const; // Returns whether this DAG represents the location of an op creation. bool isLocationDirective() const; // Returns whether this DAG is a return type specifier. bool isReturnTypeDirective() const; // Returns true if this DAG node is wrapping native code call. bool isNativeCodeCall() const; // Returns whether this DAG is an `either` specifier. bool isEither() const; // Returns whether this DAG is an `variadic` specifier. bool isVariadic() const; // Returns true if this DAG node is an operation. bool isOperation() const; // Returns the native code call template inside this DAG node. // Precondition: isNativeCodeCall() StringRef getNativeCodeTemplate() const; // Returns the number of values will be returned by the native helper // function. // Precondition: isNativeCodeCall() int getNumReturnsOfNativeCode() const; void print(raw_ostream &os) const; private: friend class SymbolInfoMap; friend llvm::DenseMapInfo; const void *getAsOpaquePointer() const { return node; } const llvm::DagInit *node; // nullptr means null DagNode }; // A class for maintaining information for symbols bound in patterns and // provides methods for resolving them according to specific use cases. // // Symbols can be bound to // // * Op arguments and op results in the source pattern and // * Op results in result patterns. // // Symbols can be referenced in result patterns and additional constraints to // the pattern. // // For example, in // // ``` // def : Pattern< // (SrcOp:$results1 $arg0, %arg1), // [(ResOp1:$results2), (ResOp2 $results2 (ResOp3 $arg0, $arg1))]>; // ``` // // `$argN` is bound to the `SrcOp`'s N-th argument. `$results1` is bound to // `SrcOp`. `$results2` is bound to `ResOp1`. $result2 is referenced to build // `ResOp2`. `$arg0` and `$arg1` are referenced to build `ResOp3`. // // If a symbol binds to a multi-result op and it does not have the `__N` // suffix, the symbol is expanded to represent all results generated by the // multi-result op. If the symbol has a `__N` suffix, then it will expand to // only the N-th *static* result as declared in ODS, and that can still // corresponds to multiple *dynamic* values if the N-th *static* result is // variadic. // // This class keeps track of such symbols and resolves them into their bound // values in a suitable way. class SymbolInfoMap { public: explicit SymbolInfoMap(ArrayRef loc) : loc(loc) {} // Class for information regarding a symbol. class SymbolInfo { public: // Returns a type string of a variable. std::string getVarTypeStr(StringRef name) const; // Returns a string for defining a variable named as `name` to store the // value bound by this symbol. std::string getVarDecl(StringRef name) const; // Returns a string for defining an argument which passes the reference of // the variable. std::string getArgDecl(StringRef name) const; // Returns a variable name for the symbol named as `name`. std::string getVarName(StringRef name) const; private: // Allow SymbolInfoMap to access private methods. friend class SymbolInfoMap; // Structure to uniquely distinguish different locations of the symbols. // // * If a symbol is defined as an operand of an operation, `dag` specifies // the DAG of the operation, `operandIndexOrNumValues` specifies the // operand index, and `variadicSubIndex` must be set to `std::nullopt`. // // * Properties not associated with an operation (e.g. as arguments to // native code) have their corresponding PropConstraint stored in the // `dag` field. This constraint is only used when // // * If a symbol is defined in a `variadic` DAG, `dag` specifies the DAG // of the parent operation, `operandIndexOrNumValues` specifies the // declared operand index of the variadic operand in the parent // operation. // // - If the symbol is defined as a result of `variadic` DAG, the // `variadicSubIndex` must be set to `std::nullopt`, which means that // the symbol binds to the full operand range. // // - If the symbol is defined as a operand, the `variadicSubIndex` must // be set to the index within the variadic sub-operand list. // // * If a symbol is defined in a `either` DAG, `dag` specifies the DAG // of the parent operation, `operandIndexOrNumValues` specifies the // operand index in the parent operation (not necessary the index in the // DAG). // // * If a symbol is defined as a result, specifies the number of returning // value. // // Example 1: // // def : Pat<(OpA $input0, $input1), ...>; // // $input0: (OpA, 0, nullopt) // $input1: (OpA, 1, nullopt) // // Example 2: // // def : Pat<(OpB (variadic:$input0 $input0a, $input0b), // (variadic:$input1 $input1a, $input1b, $input1c)), // ...>; // // $input0: (OpB, 0, nullopt) // $input0a: (OpB, 0, 0) // $input0b: (OpB, 0, 1) // $input1: (OpB, 1, nullopt) // $input1a: (OpB, 1, 0) // $input1b: (OpB, 1, 1) // $input1c: (OpB, 1, 2) // // Example 3: // // def : Pat<(OpC $input0, (either $input1, $input2)), ...>; // // $input0: (OpC, 0, nullopt) // $input1: (OpC, 1, nullopt) // $input2: (OpC, 2, nullopt) // // Example 4: // // def ThreeResultOp : TEST_Op<...> { // let results = (outs // AnyType:$result1, // AnyType:$result2, // AnyType:$result3 // ); // } // // def : Pat<..., // (ThreeResultOp:$result ...)>; // // $result: (nullptr, 3, nullopt) // struct DagAndConstant { // DagNode and DagLeaf are accessed by value which means it can't be used // as identifier here. Use an opaque pointer type instead. const void *dag; int operandIndexOrNumValues; std::optional variadicSubIndex; DagAndConstant(const void *dag, int operandIndexOrNumValues, std::optional variadicSubIndex) : dag(dag), operandIndexOrNumValues(operandIndexOrNumValues), variadicSubIndex(variadicSubIndex) {} bool operator==(const DagAndConstant &rhs) const { return dag == rhs.dag && operandIndexOrNumValues == rhs.operandIndexOrNumValues && variadicSubIndex == rhs.variadicSubIndex; } }; // What kind of entity this symbol represents: // * Attr: op attribute // * Prop: op property // * Operand: op operand // * Result: op result // * Value: a value not attached to an op (e.g., from NativeCodeCall) // * MultipleValues: a pack of values not attached to an op (e.g., from // NativeCodeCall). This kind supports indexing. enum class Kind : uint8_t { Attr, Prop, Operand, Result, Value, MultipleValues }; // Creates a SymbolInfo instance. `dagAndConstant` is only used for `Attr` // and `Operand` so should be std::nullopt for `Result` and `Value` kind. SymbolInfo(const Operator *op, Kind kind, std::optional dagAndConstant); // Static methods for creating SymbolInfo. static SymbolInfo getAttr(const Operator *op, int index) { return SymbolInfo(op, Kind::Attr, DagAndConstant(nullptr, index, std::nullopt)); } static SymbolInfo getAttr() { return SymbolInfo(nullptr, Kind::Attr, std::nullopt); } static SymbolInfo getProp(const Operator *op, int index) { return SymbolInfo(op, Kind::Prop, DagAndConstant(nullptr, index, std::nullopt)); } static SymbolInfo getProp(const PropConstraint *constraint) { // -1 for anthe `operandIndexOrNumValues` is a sentinel value. return SymbolInfo(nullptr, Kind::Prop, DagAndConstant(constraint, -1, std::nullopt)); } static SymbolInfo getOperand(DagNode node, const Operator *op, int operandIndex, std::optional variadicSubIndex = std::nullopt) { return SymbolInfo(op, Kind::Operand, DagAndConstant(node.getAsOpaquePointer(), operandIndex, variadicSubIndex)); } static SymbolInfo getResult(const Operator *op) { return SymbolInfo(op, Kind::Result, std::nullopt); } static SymbolInfo getValue() { return SymbolInfo(nullptr, Kind::Value, std::nullopt); } static SymbolInfo getMultipleValues(int numValues) { return SymbolInfo(nullptr, Kind::MultipleValues, DagAndConstant(nullptr, numValues, std::nullopt)); } // Returns the number of static values this symbol corresponds to. // A static value is an operand/result declared in ODS. Normally a symbol // only represents one static value, but symbols bound to op results can // represent more than one if the op is a multi-result op. int getStaticValueCount() const; // Returns a string containing the C++ expression for referencing this // symbol as a value (if this symbol represents one static value) or a value // range (if this symbol represents multiple static values). `name` is the // name of the C++ variable that this symbol bounds to. `index` should only // be used for indexing results. `fmt` is used to format each value. // `separator` is used to separate values if this is a value range. std::string getValueAndRangeUse(StringRef name, int index, const char *fmt, const char *separator) const; // Returns a string containing the C++ expression for referencing this // symbol as a value range regardless of how many static values this symbol // represents. `name` is the name of the C++ variable that this symbol // bounds to. `index` should only be used for indexing results. `fmt` is // used to format each value. `separator` is used to separate values in the // range. std::string getAllRangeUse(StringRef name, int index, const char *fmt, const char *separator) const; // The argument index (for `Attr` and `Operand` only) int getArgIndex() const { return dagAndConstant->operandIndexOrNumValues; } // The number of values in the MultipleValue int getSize() const { return dagAndConstant->operandIndexOrNumValues; } // The variadic sub-operands index (for variadic `Operand` only) std::optional getVariadicSubIndex() const { return dagAndConstant->variadicSubIndex; } const Operator *op; // The op where the bound entity belongs Kind kind; // The kind of the bound entity // The tuple of DagNode pointer and two constant values (for `Attr`, // `Operand` and the size of MultipleValue symbol). Note that operands may // be bound to the same symbol, use the DagNode and index to distinguish // them. For `Attr` and MultipleValue, the Dag part will be nullptr. std::optional dagAndConstant; // Alternative name for the symbol. It is used in case the name // is not unique. Applicable for `Operand` only. std::optional alternativeName; }; using BaseT = std::unordered_multimap; // Iterators for accessing all symbols. using iterator = BaseT::iterator; iterator begin() { return symbolInfoMap.begin(); } iterator end() { return symbolInfoMap.end(); } // Const iterators for accessing all symbols. using const_iterator = BaseT::const_iterator; const_iterator begin() const { return symbolInfoMap.begin(); } const_iterator end() const { return symbolInfoMap.end(); } // Binds the given `symbol` to the `argIndex`-th argument to the given `op`. // Returns false if `symbol` is already bound and symbols are not operands. bool bindOpArgument(DagNode node, StringRef symbol, const Operator &op, int argIndex, std::optional variadicSubIndex = std::nullopt); // Binds the given `symbol` to the results the given `op`. Returns false if // `symbol` is already bound. bool bindOpResult(StringRef symbol, const Operator &op); // A helper function for dispatching target value binding functions. bool bindValues(StringRef symbol, int numValues = 1); // Registers the given `symbol` as bound to the Value(s). Returns false if // `symbol` is already bound. bool bindValue(StringRef symbol); // Registers the given `symbol` as bound to a MultipleValue. Return false if // `symbol` is already bound. bool bindMultipleValues(StringRef symbol, int numValues); // Registers the given `symbol` as bound to an attr. Returns false if `symbol` // is already bound. bool bindAttr(StringRef symbol); // Registers the given `symbol` as bound to a property that satisfies the // given `constraint`. `constraint` must name a concrete interface type. bool bindProp(StringRef symbol, const PropConstraint &constraint); // Returns true if the given `symbol` is bound. bool contains(StringRef symbol) const; // Returns an iterator to the information of the given symbol named as `key`. const_iterator find(StringRef key) const; // Returns an iterator to the information of the given symbol named as `key`, // with index `argIndex` for operator `op`. const_iterator findBoundSymbol(StringRef key, DagNode node, const Operator &op, int argIndex, std::optional variadicSubIndex) const; const_iterator findBoundSymbol(StringRef key, const SymbolInfo &symbolInfo) const; // Returns the bounds of a range that includes all the elements which // bind to the `key`. std::pair getRangeOfEqualElements(StringRef key); // Returns number of times symbol named as `key` was used. int count(StringRef key) const; // Returns the number of static values of the given `symbol` corresponds to. // A static value is an operand/result declared in ODS. Normally a symbol only // represents one static value, but symbols bound to op results can represent // more than one if the op is a multi-result op. int getStaticValueCount(StringRef symbol) const; // Returns a string containing the C++ expression for referencing this // symbol as a value (if this symbol represents one static value) or a value // range (if this symbol represents multiple static values). `fmt` is used to // format each value. `separator` is used to separate values if `symbol` // represents a value range. std::string getValueAndRangeUse(StringRef symbol, const char *fmt = "{0}", const char *separator = ", ") const; // Returns a string containing the C++ expression for referencing this // symbol as a value range regardless of how many static values this symbol // represents. `fmt` is used to format each value. `separator` is used to // separate values in the range. std::string getAllRangeUse(StringRef symbol, const char *fmt = "{0}", const char *separator = ", ") const; // Assign alternative unique names to Operands that have equal names. void assignUniqueAlternativeNames(); // Splits the given `symbol` into a value pack name and an index. Returns the // value pack name and writes the index to `index` on success. Returns // `symbol` itself if it does not contain an index. // // We can use `name__N` to access the `N`-th value in the value pack bound to // `name`. `name` is typically the results of an multi-result op. static StringRef getValuePackName(StringRef symbol, int *index = nullptr); private: BaseT symbolInfoMap; // Pattern instantiation location. This is intended to be used as parameter // to PrintFatalError() to report errors. ArrayRef loc; }; // Wrapper class providing helper methods for accessing MLIR Pattern defined // in TableGen. This class should closely reflect what is defined as class // `Pattern` in TableGen. This class contains maps so it is not intended to be // used as values. class Pattern { public: explicit Pattern(const llvm::Record *def, RecordOperatorMap *mapper); // Returns the source pattern to match. DagNode getSourcePattern() const; // Returns the number of result patterns generated by applying this rewrite // rule. int getNumResultPatterns() const; // Returns the DAG tree root node of the `index`-th result pattern. DagNode getResultPattern(unsigned index) const; // Collects all symbols bound in the source pattern into `infoMap`. void collectSourcePatternBoundSymbols(SymbolInfoMap &infoMap); // Collects all symbols bound in result patterns into `infoMap`. void collectResultPatternBoundSymbols(SymbolInfoMap &infoMap); // Returns the op that the root node of the source pattern matches. const Operator &getSourceRootOp(); // Returns the operator wrapper object corresponding to the given `node`'s DAG // operator. Operator &getDialectOp(DagNode node); // Returns the constraints. std::vector getConstraints() const; // Returns the number of supplemental auxiliary patterns generated by applying // this rewrite rule. int getNumSupplementalPatterns() const; // Returns the DAG tree root node of the `index`-th supplemental result // pattern. DagNode getSupplementalPattern(unsigned index) const; // Returns the benefit score of the pattern. int getBenefit() const; using IdentifierLine = std::pair; // Returns the file location of the pattern (buffer identifier + line number // pair). If `forSourceOutput` is true, replace absolute paths in the buffer // identifier with just their filename so that we don't leak build paths into // the generated code. std::vector getLocation(bool forSourceOutput = false) const; // Recursively collects all bound symbols inside the DAG tree rooted // at `tree` and updates the given `infoMap`. void collectBoundSymbols(DagNode tree, SymbolInfoMap &infoMap, bool isSrcPattern); private: // Helper function to verify variable binding. void verifyBind(bool result, StringRef symbolName); // The TableGen definition of this pattern. const llvm::Record &def; // All operators. // TODO: we need a proper context manager, like MLIRContext, for managing the // lifetime of shared entities. RecordOperatorMap *recordOpMap; }; } // namespace tblgen } // namespace mlir namespace llvm { template <> struct DenseMapInfo { static unsigned getHashValue(mlir::tblgen::DagNode node) { return llvm::hash_value(node.getAsOpaquePointer()); } static bool isEqual(mlir::tblgen::DagNode lhs, mlir::tblgen::DagNode rhs) { return lhs.node == rhs.node; } }; template <> struct DenseMapInfo { static unsigned getHashValue(mlir::tblgen::DagLeaf leaf) { return llvm::hash_value(leaf.getAsOpaquePointer()); } static bool isEqual(mlir::tblgen::DagLeaf lhs, mlir::tblgen::DagLeaf rhs) { return lhs.def == rhs.def; } }; } // namespace llvm #endif // MLIR_TABLEGEN_PATTERN_H_