-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir] Allow using non-attribute properties in declarative rewrite patterns #143071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-mlir-core @llvm/pr-subscribers-mlir-ods Author: Krzysztof Drewniak (krzysz00) Changes…tterns This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for attributes follows the handling for attributes in most cases, including in the generation of static matchers. Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type. Patch is 36.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/143071.diff 12 Files Affected:
diff --git a/mlir/docs/DeclarativeRewrites.md b/mlir/docs/DeclarativeRewrites.md
index efc1f044e1f1f..7e54085b8aa73 100644
--- a/mlir/docs/DeclarativeRewrites.md
+++ b/mlir/docs/DeclarativeRewrites.md
@@ -380,6 +380,11 @@ template. The string can be an arbitrary C++ expression that evaluates into some
C++ object expected at the `NativeCodeCall` site (here it would be expecting an
array attribute). Typically the string should be a function call.
+In the case of properties, the return value of the `NativeCodeCall` should
+be in terms of the _interface_ type of a property. For example, the `NativeCodeCall`
+for a `StringProp` should return a `StringRef`, which will copied into the underlying
+`std::string`, just as if it were an argument to the operation's builder.
+
##### `NativeCodeCall` placeholders
In `NativeCodeCall`, we can use placeholders like `$_builder`, `$N` and `$N...`.
@@ -416,14 +421,20 @@ must be either passed by reference or pointer to the variable used as argument
so that the matched value can be returned. In the same example, `$val` will be
bound to a variable with `Attribute` type (as `I32Attr`) and the type of the
second argument in `Foo()` could be `Attribute&` or `Attribute*`. Names with
-attribute constraints will be captured as `Attribute`s while everything else
-will be treated as `Value`s.
+attribute constraints will be captured as `Attribute`s, names with
+property constraints (which must have a concrete interface type) will be treated
+as that type, and everything else will be treated as `Value`s.
Positional placeholders will be substituted by the `dag` object parameters at
the `NativeCodeCall` use site. For example, if we define `SomeCall :
NativeCodeCall<"someFn($1, $2, $0)">` and use it like `(SomeCall $in0, $in1,
$in2)`, then this will be translated into C++ call `someFn($in1, $in2, $in0)`.
+In the case of properties, the placeholder will be bound to a value of the _interface_
+type of the property. Passing in a `StringProp` as an argument to a `NativeCodeCall`
+will pass a `StringRef` (as if the getter of the matched operation were called)
+and not a `std::string`.
+
Positional range placeholders will be substituted by multiple `dag` object
parameters at the `NativeCodeCall` use site. For example, if we define
`SomeCall : NativeCodeCall<"someFn($1...)">` and use it like `(SomeCall $in0,
diff --git a/mlir/include/mlir/IR/Properties.td b/mlir/include/mlir/IR/Properties.td
index 25a45489c7b53..29281b5348e26 100644
--- a/mlir/include/mlir/IR/Properties.td
+++ b/mlir/include/mlir/IR/Properties.td
@@ -400,6 +400,18 @@ class ConfinedProperty<Property p, Pred pred, string newSummary = "">
: ConfinedProp<p, pred, newSummary>,
Deprecated<"moved to shorter name ConfinedProp">;
+/// Defines a constant value of type `prop` to be used in pattern matching.
+/// When used as a constraint, forms a matcher that tests that the property is
+/// equal to the given value (and matches any other constraints on the property).
+/// The constant value is given as a string and should be of the _interface_ type
+/// of the attribute.
+class ConstantProp<Property prop, string val>
+ : ConfinedProp<prop,
+ CPred<"$_self == " # val>,
+ "constant '" # prop.summary # "': " # val> {
+ string value = val;
+}
+
//===----------------------------------------------------------------------===//
// Primitive property combinators
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/TableGen/Pattern.h b/mlir/include/mlir/TableGen/Pattern.h
index 1c9e128f0a0fb..bebfa993b2810 100644
--- a/mlir/include/mlir/TableGen/Pattern.h
+++ b/mlir/include/mlir/TableGen/Pattern.h
@@ -73,12 +73,23 @@ class DagLeaf {
// 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;
@@ -88,9 +99,18 @@ class DagLeaf {
// Returns this DAG leaf as a constraint. Asserts if fails.
Constraint getAsConstraint() const;
+ // Returns this DAG leaf as a property constraint. Asserts if fails.
+ 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;
@@ -279,6 +299,12 @@ class SymbolInfoMap {
// the DAG of the operation, `operandIndexOrNumValues` specifies the
// operand index, and `variadicSubIndex` must be set to `std::nullopt`.
//
+ // * Properties not associated with an operation (ex. as arguments to
+ // native code) have their corresponding PropConstraint stored in the
+ // `dag` field,
+ // and set `operandIndexOrNumValues` to -1 to indicate they aren't part of
+ // an operation.
+ //
// * 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
@@ -364,12 +390,20 @@ class SymbolInfoMap {
// 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, Operand, Result, Value, MultipleValues };
+ 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.
@@ -384,6 +418,15 @@ class SymbolInfoMap {
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) {
+ return SymbolInfo(nullptr, Kind::Prop,
+ DagAndConstant(constraint, -1, std::nullopt));
+ ;
+ }
static SymbolInfo
getOperand(DagNode node, const Operator *op, int operandIndex,
std::optional<int> variadicSubIndex = std::nullopt) {
@@ -488,6 +531,10 @@ class SymbolInfoMap {
// 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;
diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h
index 6af96f077efe5..81e6d85720829 100644
--- a/mlir/include/mlir/TableGen/Property.h
+++ b/mlir/include/mlir/TableGen/Property.h
@@ -32,9 +32,9 @@ class Pred;
// Wrapper class providing helper methods for accesing property constraint
// values.
class PropConstraint : public Constraint {
+public:
using Constraint::Constraint;
-public:
static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; }
StringRef getInterfaceType() const;
@@ -143,6 +143,10 @@ class Property : public PropConstraint {
// property constraints, this function is added for future-proofing)
Property getBaseProperty() const;
+ // Returns true if this property is backed by a TableGen definition and that
+ // definition is a subclass of `className`.
+ bool isSubClassOf(StringRef className) const;
+
private:
// Elements describing a Property, in general fetched from the record.
StringRef summary;
@@ -169,6 +173,21 @@ struct NamedProperty {
Property prop;
};
+// Wrapper class providing helper methods for processing constant property
+// values defined using the `ConstantProp` subclass of `Property`
+// in TableGen.
+class ConstantProp : public Property {
+public:
+ explicit ConstantProp(const llvm::DefInit *def) : Property(def) {
+ assert(isSubClassOf("ConstantProp"));
+ }
+
+ static bool classof(Property *p) { return p->isSubClassOf("ConstantProp"); }
+
+ // Return the constant value of the property as an expression
+ // that produces an interface-type constant.
+ StringRef getValue() const;
+};
} // namespace tblgen
} // namespace mlir
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index 4ce6ab1dbfce5..2c119fd680b69 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -205,10 +205,14 @@ static ::llvm::LogicalResult {0}(
/// Code for a pattern type or attribute constraint.
///
-/// {3}: "Type type" or "Attribute attr".
-static const char *const patternAttrOrTypeConstraintCode = R"(
+/// {0}: name of function
+/// {1}: Condition template
+/// {2}: Constraint summary
+/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
+/// Can be "T prop" for generic property constraints.
+static const char *const patternConstraintCode = R"(
static ::llvm::LogicalResult {0}(
- ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, ::mlir::{3},
+ ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
::llvm::StringRef failureStr) {
if (!({1})) {
return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
@@ -265,15 +269,31 @@ void StaticVerifierFunctionEmitter::emitPatternConstraints() {
FmtContext ctx;
ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
for (auto &it : typeConstraints) {
- os << formatv(patternAttrOrTypeConstraintCode, it.second,
+ os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()), "Type type");
+ escapeString(it.first.getSummary()), "::mlir::Type type");
}
ctx.withSelf("attr");
for (auto &it : attrConstraints) {
- os << formatv(patternAttrOrTypeConstraintCode, it.second,
+ os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()), "Attribute attr");
+ escapeString(it.first.getSummary()),
+ "::mlir::Attribute attr");
+ }
+ ctx.withSelf("prop");
+ for (auto &it : propConstraints) {
+ PropConstraint propConstraint = cast<PropConstraint>(it.first);
+ StringRef interfaceType = propConstraint.getInterfaceType();
+ // Constraints that are generic over multiple interface types are
+ // templatized under the assumption that they'll be used correctly.
+ if (interfaceType.empty()) {
+ interfaceType = "T";
+ os << "template <typename T>";
+ }
+ os << formatv(patternConstraintCode, it.second,
+ tgfmt(propConstraint.getConditionTemplate(), &ctx),
+ escapeString(propConstraint.getSummary()),
+ Twine(interfaceType) + " prop");
}
}
@@ -367,10 +387,15 @@ void StaticVerifierFunctionEmitter::collectOpConstraints(
void StaticVerifierFunctionEmitter::collectPatternConstraints(
const ArrayRef<DagLeaf> constraints) {
for (auto &leaf : constraints) {
- assert(leaf.isOperandMatcher() || leaf.isAttrMatcher());
- collectConstraint(
- leaf.isOperandMatcher() ? typeConstraints : attrConstraints,
- leaf.isOperandMatcher() ? "type" : "attr", leaf.getAsConstraint());
+ assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
+ leaf.isPropMatcher());
+ Constraint constraint = leaf.getAsConstraint();
+ if (leaf.isOperandMatcher())
+ collectConstraint(typeConstraints, "type", constraint);
+ else if (leaf.isAttrMatcher())
+ collectConstraint(attrConstraints, "attr", constraint);
+ else if (leaf.isPropMatcher())
+ collectConstraint(propConstraints, "prop", constraint);
}
}
diff --git a/mlir/lib/TableGen/Pattern.cpp b/mlir/lib/TableGen/Pattern.cpp
index 13541de66578d..9a3ccdaa6749b 100644
--- a/mlir/lib/TableGen/Pattern.cpp
+++ b/mlir/lib/TableGen/Pattern.cpp
@@ -51,6 +51,16 @@ bool DagLeaf::isAttrMatcher() const {
return isSubClassOf("AttrConstraint");
}
+bool DagLeaf::isPropMatcher() const {
+ // Property matchers specify a property constraint.
+ return isSubClassOf("PropConstraint");
+}
+
+bool DagLeaf::isPropDefinition() const {
+ // Property matchers specify a property definition.
+ return isSubClassOf("Property");
+}
+
bool DagLeaf::isNativeCodeCall() const {
return isSubClassOf("NativeCodeCall");
}
@@ -59,14 +69,26 @@ bool DagLeaf::isConstantAttr() const { return isSubClassOf("ConstantAttr"); }
bool DagLeaf::isEnumCase() const { return isSubClassOf("EnumCase"); }
+bool DagLeaf::isConstantProp() const { return isSubClassOf("ConstantProp"); }
+
bool DagLeaf::isStringAttr() const { return isa<llvm::StringInit>(def); }
Constraint DagLeaf::getAsConstraint() const {
- assert((isOperandMatcher() || isAttrMatcher()) &&
- "the DAG leaf must be operand or attribute");
+ assert((isOperandMatcher() || isAttrMatcher() || isPropMatcher()) &&
+ "the DAG leaf must be operand, attribute, or property");
return Constraint(cast<DefInit>(def)->getDef());
}
+PropConstraint DagLeaf::getAsPropConstraint() const {
+ assert(isPropMatcher() && "the DAG leaf must be a property matcher");
+ return PropConstraint(cast<DefInit>(def)->getDef());
+}
+
+Property DagLeaf::getAsProperty() const {
+ assert(isPropDefinition() && "the DAG leaf must be a property definition");
+ return Property(cast<DefInit>(def)->getDef());
+}
+
ConstantAttr DagLeaf::getAsConstantAttr() const {
assert(isConstantAttr() && "the DAG leaf must be constant attribute");
return ConstantAttr(cast<DefInit>(def));
@@ -77,6 +99,11 @@ EnumCase DagLeaf::getAsEnumCase() const {
return EnumCase(cast<DefInit>(def));
}
+ConstantProp DagLeaf::getAsConstantProp() const {
+ assert(isConstantProp() && "the DAG leaf must be a constant property value");
+ return ConstantProp(cast<DefInit>(def));
+}
+
std::string DagLeaf::getConditionTemplate() const {
return getAsConstraint().getConditionTemplate();
}
@@ -232,6 +259,7 @@ SymbolInfoMap::SymbolInfo::SymbolInfo(
int SymbolInfoMap::SymbolInfo::getStaticValueCount() const {
switch (kind) {
case Kind::Attr:
+ case Kind::Prop:
case Kind::Operand:
case Kind::Value:
return 1;
@@ -258,6 +286,18 @@ std::string SymbolInfoMap::SymbolInfo::getVarTypeStr(StringRef name) const {
// TODO(suderman): Use a more exact type when available.
return "::mlir::Attribute";
}
+ case Kind::Prop: {
+ if (op)
+ return cast<NamedProperty *>(op->getArg(getArgIndex()))
+ ->prop.getInterfaceType()
+ .str();
+ assert(dagAndConstant && dagAndConstant->dag &&
+ "generic properties must carry their constraint");
+ return reinterpret_cast<const DagLeaf *>(dagAndConstant->dag)
+ ->getAsPropConstraint()
+ .getInterfaceType()
+ .str();
+ }
case Kind::Operand: {
// Use operand range for captured operands (to support potential variadic
// operands).
@@ -300,6 +340,12 @@ std::string SymbolInfoMap::SymbolInfo::getValueAndRangeUse(
LLVM_DEBUG(dbgs() << repl << " (Attr)\n");
return std::string(repl);
}
+ case Kind::Prop: {
+ assert(index < 0);
+ auto repl = formatv(fmt, name);
+ LLVM_DEBUG(dbgs() << repl << " (Prop)\n");
+ return std::string(repl);
+ }
case Kind::Operand: {
assert(index < 0);
auto *operand = cast<NamedTypeConstraint *>(op->getArg(getArgIndex()));
@@ -388,10 +434,11 @@ std::string SymbolInfoMap::SymbolInfo::getAllRangeUse(
LLVM_DEBUG(dbgs() << "getAllRangeUse for '" << name << "': ");
switch (kind) {
case Kind::Attr:
+ case Kind::Prop:
case Kind::Operand: {
assert(index < 0 && "only allowed for symbol bound to result");
auto repl = formatv(fmt, name);
- LLVM_DEBUG(dbgs() << repl << " (Operand/Attr)\n");
+ LLVM_DEBUG(dbgs() << repl << " (Operand/Attr/Prop)\n");
return std::string(repl);
}
case Kind::Result: {
@@ -449,9 +496,11 @@ bool SymbolInfoMap::bindOpArgument(DagNode node, StringRef symbol,
PrintFatalError(loc, error);
}
- auto symInfo =
- isa<NamedAttribute *>(op.getArg(argIndex))
- ? SymbolInfo::getAttr(&op, argIndex)
+ Argument arg = op.getArg(argIndex);
+ SymbolInfo symInfo =
+ isa<NamedAttribute *>(arg) ? SymbolInfo::getAttr(&op, argIndex)
+ : isa<NamedProperty *>(arg)
+ ? SymbolInfo::getProp(&op, argIndex)
: SymbolInfo::getOperand(node, &op, argIndex, variadicSubIndex);
std::string key = symbol.str();
@@ -503,6 +552,13 @@ bool SymbolInfoMap::bindAttr(StringRef symbol) {
return symbolInfoMap.count(inserted->first) == 1;
}
+bool SymbolInfoMap::bindProp(StringRef symbol,
+ const PropConstraint &constraint) {
+ auto inserted =
+ symbolInfoMap.emplace(symbol.str(), SymbolInfo::getProp(&constraint));
+ return symbolInfoMap.count(inserted->first) == 1;
+}
+
bool SymbolInfoMap::contains(StringRef symbol) const {
return find(symbol) != symbolInfoMap.end();
}
@@ -774,10 +830,23 @@ void Pattern::collectBoundSymbols(DagNode tree, SymbolInfoMap &infoMap,
if (!treeArgName.empty() && treeArgName != "_") {
DagLeaf leaf = tree.getArgAsLeaf(i);
- // In (NativeCodeCall<"Foo($_self, $0, $1, $2)"> I8Attr:$a, I8:$b, $c),
+ // In (NativeCodeCall<"Foo($_self, $0, $1, $2)"> I8Attr:$a, I8:$b, $c,
+ // I8Prop:$d),
if (leaf.isUnspecified()) {
// This is case of $c, a Value without any constraints.
verifyBind(infoMap.bindValue(treeArgName), treeArgName);
+ } else if (leaf.isPropMatcher()) {
+ // This is case of $d, a binding to a certain property.
+ auto propConstraint = leaf.getAsPropConstraint();
+ if (propConstraint.getInterfaceType().empty()) {
+ PrintFatalError(&def,
+ formatv("binding symbol '{0}' in NativeCodeCall to "
+ "a property constraint without specifying "
+ "that constraint's type is unsupported",
+ treeArgName));
+ }
+ verifyBind(infoMap.bindProp(treeArgName, propConstraint),
+ treeArgName);
} else {
auto constraint = leaf.getAsConstraint();
bool isAttr = leaf.isAttrMatcher() || leaf.isEnumCase() ||
diff --git a/mlir/lib/TableGen/Property.cpp b/mlir/lib/TableGen/Property.cpp
index 9a70c1b6e8d62..47f43267cd197 100644
--- a/mlir/lib/TableGen/Property.cpp
+++ b/mlir/lib/TableGen/Property.cpp
@@ -112,3 +112,11 @@ Property Property::getBaseProperty() const {
}
return *this;
}
+
+bool Property::isSubClassOf(StringRef className) const {
+ return def && def->isSubClassOf(className);
+}
+
+StringRef ConstantProp::getValue() const {
+ return def->getValueAsString("value");
+}
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 59330fdb1bb2c..22a974fd9e1e6 100644
--- ...
[truncated]
|
@llvm/pr-subscribers-mlir Author: Krzysztof Drewniak (krzysz00) Changes…tterns This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for attributes follows the handling for attributes in most cases, including in the generation of static matchers. Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type. Patch is 36.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/143071.diff 12 Files Affected:
diff --git a/mlir/docs/DeclarativeRewrites.md b/mlir/docs/DeclarativeRewrites.md
index efc1f044e1f1f..7e54085b8aa73 100644
--- a/mlir/docs/DeclarativeRewrites.md
+++ b/mlir/docs/DeclarativeRewrites.md
@@ -380,6 +380,11 @@ template. The string can be an arbitrary C++ expression that evaluates into some
C++ object expected at the `NativeCodeCall` site (here it would be expecting an
array attribute). Typically the string should be a function call.
+In the case of properties, the return value of the `NativeCodeCall` should
+be in terms of the _interface_ type of a property. For example, the `NativeCodeCall`
+for a `StringProp` should return a `StringRef`, which will copied into the underlying
+`std::string`, just as if it were an argument to the operation's builder.
+
##### `NativeCodeCall` placeholders
In `NativeCodeCall`, we can use placeholders like `$_builder`, `$N` and `$N...`.
@@ -416,14 +421,20 @@ must be either passed by reference or pointer to the variable used as argument
so that the matched value can be returned. In the same example, `$val` will be
bound to a variable with `Attribute` type (as `I32Attr`) and the type of the
second argument in `Foo()` could be `Attribute&` or `Attribute*`. Names with
-attribute constraints will be captured as `Attribute`s while everything else
-will be treated as `Value`s.
+attribute constraints will be captured as `Attribute`s, names with
+property constraints (which must have a concrete interface type) will be treated
+as that type, and everything else will be treated as `Value`s.
Positional placeholders will be substituted by the `dag` object parameters at
the `NativeCodeCall` use site. For example, if we define `SomeCall :
NativeCodeCall<"someFn($1, $2, $0)">` and use it like `(SomeCall $in0, $in1,
$in2)`, then this will be translated into C++ call `someFn($in1, $in2, $in0)`.
+In the case of properties, the placeholder will be bound to a value of the _interface_
+type of the property. Passing in a `StringProp` as an argument to a `NativeCodeCall`
+will pass a `StringRef` (as if the getter of the matched operation were called)
+and not a `std::string`.
+
Positional range placeholders will be substituted by multiple `dag` object
parameters at the `NativeCodeCall` use site. For example, if we define
`SomeCall : NativeCodeCall<"someFn($1...)">` and use it like `(SomeCall $in0,
diff --git a/mlir/include/mlir/IR/Properties.td b/mlir/include/mlir/IR/Properties.td
index 25a45489c7b53..29281b5348e26 100644
--- a/mlir/include/mlir/IR/Properties.td
+++ b/mlir/include/mlir/IR/Properties.td
@@ -400,6 +400,18 @@ class ConfinedProperty<Property p, Pred pred, string newSummary = "">
: ConfinedProp<p, pred, newSummary>,
Deprecated<"moved to shorter name ConfinedProp">;
+/// Defines a constant value of type `prop` to be used in pattern matching.
+/// When used as a constraint, forms a matcher that tests that the property is
+/// equal to the given value (and matches any other constraints on the property).
+/// The constant value is given as a string and should be of the _interface_ type
+/// of the attribute.
+class ConstantProp<Property prop, string val>
+ : ConfinedProp<prop,
+ CPred<"$_self == " # val>,
+ "constant '" # prop.summary # "': " # val> {
+ string value = val;
+}
+
//===----------------------------------------------------------------------===//
// Primitive property combinators
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/TableGen/Pattern.h b/mlir/include/mlir/TableGen/Pattern.h
index 1c9e128f0a0fb..bebfa993b2810 100644
--- a/mlir/include/mlir/TableGen/Pattern.h
+++ b/mlir/include/mlir/TableGen/Pattern.h
@@ -73,12 +73,23 @@ class DagLeaf {
// 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;
@@ -88,9 +99,18 @@ class DagLeaf {
// Returns this DAG leaf as a constraint. Asserts if fails.
Constraint getAsConstraint() const;
+ // Returns this DAG leaf as a property constraint. Asserts if fails.
+ 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;
@@ -279,6 +299,12 @@ class SymbolInfoMap {
// the DAG of the operation, `operandIndexOrNumValues` specifies the
// operand index, and `variadicSubIndex` must be set to `std::nullopt`.
//
+ // * Properties not associated with an operation (ex. as arguments to
+ // native code) have their corresponding PropConstraint stored in the
+ // `dag` field,
+ // and set `operandIndexOrNumValues` to -1 to indicate they aren't part of
+ // an operation.
+ //
// * 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
@@ -364,12 +390,20 @@ class SymbolInfoMap {
// 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, Operand, Result, Value, MultipleValues };
+ 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.
@@ -384,6 +418,15 @@ class SymbolInfoMap {
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) {
+ return SymbolInfo(nullptr, Kind::Prop,
+ DagAndConstant(constraint, -1, std::nullopt));
+ ;
+ }
static SymbolInfo
getOperand(DagNode node, const Operator *op, int operandIndex,
std::optional<int> variadicSubIndex = std::nullopt) {
@@ -488,6 +531,10 @@ class SymbolInfoMap {
// 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;
diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h
index 6af96f077efe5..81e6d85720829 100644
--- a/mlir/include/mlir/TableGen/Property.h
+++ b/mlir/include/mlir/TableGen/Property.h
@@ -32,9 +32,9 @@ class Pred;
// Wrapper class providing helper methods for accesing property constraint
// values.
class PropConstraint : public Constraint {
+public:
using Constraint::Constraint;
-public:
static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; }
StringRef getInterfaceType() const;
@@ -143,6 +143,10 @@ class Property : public PropConstraint {
// property constraints, this function is added for future-proofing)
Property getBaseProperty() const;
+ // Returns true if this property is backed by a TableGen definition and that
+ // definition is a subclass of `className`.
+ bool isSubClassOf(StringRef className) const;
+
private:
// Elements describing a Property, in general fetched from the record.
StringRef summary;
@@ -169,6 +173,21 @@ struct NamedProperty {
Property prop;
};
+// Wrapper class providing helper methods for processing constant property
+// values defined using the `ConstantProp` subclass of `Property`
+// in TableGen.
+class ConstantProp : public Property {
+public:
+ explicit ConstantProp(const llvm::DefInit *def) : Property(def) {
+ assert(isSubClassOf("ConstantProp"));
+ }
+
+ static bool classof(Property *p) { return p->isSubClassOf("ConstantProp"); }
+
+ // Return the constant value of the property as an expression
+ // that produces an interface-type constant.
+ StringRef getValue() const;
+};
} // namespace tblgen
} // namespace mlir
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index 4ce6ab1dbfce5..2c119fd680b69 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -205,10 +205,14 @@ static ::llvm::LogicalResult {0}(
/// Code for a pattern type or attribute constraint.
///
-/// {3}: "Type type" or "Attribute attr".
-static const char *const patternAttrOrTypeConstraintCode = R"(
+/// {0}: name of function
+/// {1}: Condition template
+/// {2}: Constraint summary
+/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
+/// Can be "T prop" for generic property constraints.
+static const char *const patternConstraintCode = R"(
static ::llvm::LogicalResult {0}(
- ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, ::mlir::{3},
+ ::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
::llvm::StringRef failureStr) {
if (!({1})) {
return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
@@ -265,15 +269,31 @@ void StaticVerifierFunctionEmitter::emitPatternConstraints() {
FmtContext ctx;
ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
for (auto &it : typeConstraints) {
- os << formatv(patternAttrOrTypeConstraintCode, it.second,
+ os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()), "Type type");
+ escapeString(it.first.getSummary()), "::mlir::Type type");
}
ctx.withSelf("attr");
for (auto &it : attrConstraints) {
- os << formatv(patternAttrOrTypeConstraintCode, it.second,
+ os << formatv(patternConstraintCode, it.second,
tgfmt(it.first.getConditionTemplate(), &ctx),
- escapeString(it.first.getSummary()), "Attribute attr");
+ escapeString(it.first.getSummary()),
+ "::mlir::Attribute attr");
+ }
+ ctx.withSelf("prop");
+ for (auto &it : propConstraints) {
+ PropConstraint propConstraint = cast<PropConstraint>(it.first);
+ StringRef interfaceType = propConstraint.getInterfaceType();
+ // Constraints that are generic over multiple interface types are
+ // templatized under the assumption that they'll be used correctly.
+ if (interfaceType.empty()) {
+ interfaceType = "T";
+ os << "template <typename T>";
+ }
+ os << formatv(patternConstraintCode, it.second,
+ tgfmt(propConstraint.getConditionTemplate(), &ctx),
+ escapeString(propConstraint.getSummary()),
+ Twine(interfaceType) + " prop");
}
}
@@ -367,10 +387,15 @@ void StaticVerifierFunctionEmitter::collectOpConstraints(
void StaticVerifierFunctionEmitter::collectPatternConstraints(
const ArrayRef<DagLeaf> constraints) {
for (auto &leaf : constraints) {
- assert(leaf.isOperandMatcher() || leaf.isAttrMatcher());
- collectConstraint(
- leaf.isOperandMatcher() ? typeConstraints : attrConstraints,
- leaf.isOperandMatcher() ? "type" : "attr", leaf.getAsConstraint());
+ assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
+ leaf.isPropMatcher());
+ Constraint constraint = leaf.getAsConstraint();
+ if (leaf.isOperandMatcher())
+ collectConstraint(typeConstraints, "type", constraint);
+ else if (leaf.isAttrMatcher())
+ collectConstraint(attrConstraints, "attr", constraint);
+ else if (leaf.isPropMatcher())
+ collectConstraint(propConstraints, "prop", constraint);
}
}
diff --git a/mlir/lib/TableGen/Pattern.cpp b/mlir/lib/TableGen/Pattern.cpp
index 13541de66578d..9a3ccdaa6749b 100644
--- a/mlir/lib/TableGen/Pattern.cpp
+++ b/mlir/lib/TableGen/Pattern.cpp
@@ -51,6 +51,16 @@ bool DagLeaf::isAttrMatcher() const {
return isSubClassOf("AttrConstraint");
}
+bool DagLeaf::isPropMatcher() const {
+ // Property matchers specify a property constraint.
+ return isSubClassOf("PropConstraint");
+}
+
+bool DagLeaf::isPropDefinition() const {
+ // Property matchers specify a property definition.
+ return isSubClassOf("Property");
+}
+
bool DagLeaf::isNativeCodeCall() const {
return isSubClassOf("NativeCodeCall");
}
@@ -59,14 +69,26 @@ bool DagLeaf::isConstantAttr() const { return isSubClassOf("ConstantAttr"); }
bool DagLeaf::isEnumCase() const { return isSubClassOf("EnumCase"); }
+bool DagLeaf::isConstantProp() const { return isSubClassOf("ConstantProp"); }
+
bool DagLeaf::isStringAttr() const { return isa<llvm::StringInit>(def); }
Constraint DagLeaf::getAsConstraint() const {
- assert((isOperandMatcher() || isAttrMatcher()) &&
- "the DAG leaf must be operand or attribute");
+ assert((isOperandMatcher() || isAttrMatcher() || isPropMatcher()) &&
+ "the DAG leaf must be operand, attribute, or property");
return Constraint(cast<DefInit>(def)->getDef());
}
+PropConstraint DagLeaf::getAsPropConstraint() const {
+ assert(isPropMatcher() && "the DAG leaf must be a property matcher");
+ return PropConstraint(cast<DefInit>(def)->getDef());
+}
+
+Property DagLeaf::getAsProperty() const {
+ assert(isPropDefinition() && "the DAG leaf must be a property definition");
+ return Property(cast<DefInit>(def)->getDef());
+}
+
ConstantAttr DagLeaf::getAsConstantAttr() const {
assert(isConstantAttr() && "the DAG leaf must be constant attribute");
return ConstantAttr(cast<DefInit>(def));
@@ -77,6 +99,11 @@ EnumCase DagLeaf::getAsEnumCase() const {
return EnumCase(cast<DefInit>(def));
}
+ConstantProp DagLeaf::getAsConstantProp() const {
+ assert(isConstantProp() && "the DAG leaf must be a constant property value");
+ return ConstantProp(cast<DefInit>(def));
+}
+
std::string DagLeaf::getConditionTemplate() const {
return getAsConstraint().getConditionTemplate();
}
@@ -232,6 +259,7 @@ SymbolInfoMap::SymbolInfo::SymbolInfo(
int SymbolInfoMap::SymbolInfo::getStaticValueCount() const {
switch (kind) {
case Kind::Attr:
+ case Kind::Prop:
case Kind::Operand:
case Kind::Value:
return 1;
@@ -258,6 +286,18 @@ std::string SymbolInfoMap::SymbolInfo::getVarTypeStr(StringRef name) const {
// TODO(suderman): Use a more exact type when available.
return "::mlir::Attribute";
}
+ case Kind::Prop: {
+ if (op)
+ return cast<NamedProperty *>(op->getArg(getArgIndex()))
+ ->prop.getInterfaceType()
+ .str();
+ assert(dagAndConstant && dagAndConstant->dag &&
+ "generic properties must carry their constraint");
+ return reinterpret_cast<const DagLeaf *>(dagAndConstant->dag)
+ ->getAsPropConstraint()
+ .getInterfaceType()
+ .str();
+ }
case Kind::Operand: {
// Use operand range for captured operands (to support potential variadic
// operands).
@@ -300,6 +340,12 @@ std::string SymbolInfoMap::SymbolInfo::getValueAndRangeUse(
LLVM_DEBUG(dbgs() << repl << " (Attr)\n");
return std::string(repl);
}
+ case Kind::Prop: {
+ assert(index < 0);
+ auto repl = formatv(fmt, name);
+ LLVM_DEBUG(dbgs() << repl << " (Prop)\n");
+ return std::string(repl);
+ }
case Kind::Operand: {
assert(index < 0);
auto *operand = cast<NamedTypeConstraint *>(op->getArg(getArgIndex()));
@@ -388,10 +434,11 @@ std::string SymbolInfoMap::SymbolInfo::getAllRangeUse(
LLVM_DEBUG(dbgs() << "getAllRangeUse for '" << name << "': ");
switch (kind) {
case Kind::Attr:
+ case Kind::Prop:
case Kind::Operand: {
assert(index < 0 && "only allowed for symbol bound to result");
auto repl = formatv(fmt, name);
- LLVM_DEBUG(dbgs() << repl << " (Operand/Attr)\n");
+ LLVM_DEBUG(dbgs() << repl << " (Operand/Attr/Prop)\n");
return std::string(repl);
}
case Kind::Result: {
@@ -449,9 +496,11 @@ bool SymbolInfoMap::bindOpArgument(DagNode node, StringRef symbol,
PrintFatalError(loc, error);
}
- auto symInfo =
- isa<NamedAttribute *>(op.getArg(argIndex))
- ? SymbolInfo::getAttr(&op, argIndex)
+ Argument arg = op.getArg(argIndex);
+ SymbolInfo symInfo =
+ isa<NamedAttribute *>(arg) ? SymbolInfo::getAttr(&op, argIndex)
+ : isa<NamedProperty *>(arg)
+ ? SymbolInfo::getProp(&op, argIndex)
: SymbolInfo::getOperand(node, &op, argIndex, variadicSubIndex);
std::string key = symbol.str();
@@ -503,6 +552,13 @@ bool SymbolInfoMap::bindAttr(StringRef symbol) {
return symbolInfoMap.count(inserted->first) == 1;
}
+bool SymbolInfoMap::bindProp(StringRef symbol,
+ const PropConstraint &constraint) {
+ auto inserted =
+ symbolInfoMap.emplace(symbol.str(), SymbolInfo::getProp(&constraint));
+ return symbolInfoMap.count(inserted->first) == 1;
+}
+
bool SymbolInfoMap::contains(StringRef symbol) const {
return find(symbol) != symbolInfoMap.end();
}
@@ -774,10 +830,23 @@ void Pattern::collectBoundSymbols(DagNode tree, SymbolInfoMap &infoMap,
if (!treeArgName.empty() && treeArgName != "_") {
DagLeaf leaf = tree.getArgAsLeaf(i);
- // In (NativeCodeCall<"Foo($_self, $0, $1, $2)"> I8Attr:$a, I8:$b, $c),
+ // In (NativeCodeCall<"Foo($_self, $0, $1, $2)"> I8Attr:$a, I8:$b, $c,
+ // I8Prop:$d),
if (leaf.isUnspecified()) {
// This is case of $c, a Value without any constraints.
verifyBind(infoMap.bindValue(treeArgName), treeArgName);
+ } else if (leaf.isPropMatcher()) {
+ // This is case of $d, a binding to a certain property.
+ auto propConstraint = leaf.getAsPropConstraint();
+ if (propConstraint.getInterfaceType().empty()) {
+ PrintFatalError(&def,
+ formatv("binding symbol '{0}' in NativeCodeCall to "
+ "a property constraint without specifying "
+ "that constraint's type is unsupported",
+ treeArgName));
+ }
+ verifyBind(infoMap.bindProp(treeArgName, propConstraint),
+ treeArgName);
} else {
auto constraint = leaf.getAsConstraint();
bool isAttr = leaf.isAttrMatcher() || leaf.isEnumCase() ||
diff --git a/mlir/lib/TableGen/Property.cpp b/mlir/lib/TableGen/Property.cpp
index 9a70c1b6e8d62..47f43267cd197 100644
--- a/mlir/lib/TableGen/Property.cpp
+++ b/mlir/lib/TableGen/Property.cpp
@@ -112,3 +112,11 @@ Property Property::getBaseProperty() const {
}
return *this;
}
+
+bool Property::isSubClassOf(StringRef className) const {
+ return def && def->isSubClassOf(className);
+}
+
+StringRef ConstantProp::getValue() const {
+ return def->getValueAsString("value");
+}
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 59330fdb1bb2c..22a974fd9e1e6 100644
--- ...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just did a quick scan/couldn't dig in right now, but overall looks good, sending some initial feedback.
@@ -88,9 +99,18 @@ class DagLeaf { | |||
// Returns this DAG leaf as a constraint. Asserts if fails. | |||
Constraint getAsConstraint() const; | |||
|
|||
// Returns this DAG leaf as a property constraint. Asserts if fails. | |||
PropConstraint getAsPropConstraint() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does properties need to be treated specially compared to other constraints?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They've got an interface type attached
Ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good thanks
@@ -400,6 +400,18 @@ class ConfinedProperty<Property p, Pred pred, string newSummary = ""> | |||
: ConfinedProp<p, pred, newSummary>, | |||
Deprecated<"moved to shorter name ConfinedProp">; | |||
|
|||
/// Defines a constant value of type `prop` to be used in pattern matching. | |||
/// When used as a constraint, forms a matcher that tests that the property is | |||
/// equal to the given value (and matches any other constraints on the property). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this uses/requires that the interface type has an equality operation.
Does this also work in templated cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs updated
@@ -300,6 +340,12 @@ std::string SymbolInfoMap::SymbolInfo::getValueAndRangeUse( | |||
LLVM_DEBUG(dbgs() << repl << " (Attr)\n"); | |||
return std::string(repl); | |||
} | |||
case Kind::Prop: { | |||
assert(index < 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels confusing here as you aren't using index. What are you trying to guard against here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't entirely know, but all the other cases have this
@@ -503,6 +552,13 @@ bool SymbolInfoMap::bindAttr(StringRef symbol) { | |||
return symbolInfoMap.count(inserted->first) == 1; | |||
} | |||
|
|||
bool SymbolInfoMap::bindProp(StringRef symbol, | |||
const PropConstraint &constraint) { | |||
auto inserted = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not due to you, but this looks weird, not sure why its done this way. It inserts but then counts if inserted (its an unordered_map it seems, so don't think count can be > 1), why not just return inserted->second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an unordered multimap - so you can specifically both check for two results and - I suspect - determine what there's a conflict with
os << interfaceType << " " << argName; | ||
if (leaf.isPropDefinition()) { | ||
Property propDef = leaf.getAsProperty(); | ||
// Ensure properties that aren't zero-arg-constructable still work. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's a best-effort attempt to make that sort of thing work (since properties have a field letting you give their default value in their storage type)
…tterns This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for attributes follows the handling for attributes in most cases, including in the generation of static matchers. Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type.
142b7a6
to
c0b83f2
Compare
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/116/builds/14610 Here is the relevant piece of the build log for the reference
|
…tterns (llvm#143071) This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for properties follows the handling for attributes in most cases, including in the generation of static matchers. Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type.
…tterns (llvm#143071) This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for properties follows the handling for attributes in most cases, including in the generation of static matchers. Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type.
This commit adds support for non-attribute properties (such as StringProp and I64Prop) in declarative rewrite patterns. The handling for properties follows the handling for attributes in most cases, including in the generation of static matchers.
Constraints that are shared between multiple types are supported by making the constraint matcher a templated function, which is the equivalent to passing ::mlir::Attribute for an arbitrary C++ type.