Skip to content

Commit 5ce5ed4

Browse files
authored
[mlir] Allow using non-attribute properties in declarative rewrite patterns (#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.
1 parent f4df9f1 commit 5ce5ed4

File tree

12 files changed

+441
-27
lines changed

12 files changed

+441
-27
lines changed

mlir/docs/DeclarativeRewrites.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ template. The string can be an arbitrary C++ expression that evaluates into some
380380
C++ object expected at the `NativeCodeCall` site (here it would be expecting an
381381
array attribute). Typically the string should be a function call.
382382

383+
In the case of properties, the return value of the `NativeCodeCall` should
384+
be in terms of the _interface_ type of a property. For example, the `NativeCodeCall`
385+
for a `StringProp` should return a `StringRef`, which will copied into the underlying
386+
`std::string`, just as if it were an argument to the operation's builder.
387+
383388
##### `NativeCodeCall` placeholders
384389

385390
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
416421
so that the matched value can be returned. In the same example, `$val` will be
417422
bound to a variable with `Attribute` type (as `I32Attr`) and the type of the
418423
second argument in `Foo()` could be `Attribute&` or `Attribute*`. Names with
419-
attribute constraints will be captured as `Attribute`s while everything else
420-
will be treated as `Value`s.
424+
attribute constraints will be captured as `Attribute`s, names with
425+
property constraints (which must have a concrete interface type) will be treated
426+
as that type, and everything else will be treated as `Value`s.
421427

422428
Positional placeholders will be substituted by the `dag` object parameters at
423429
the `NativeCodeCall` use site. For example, if we define `SomeCall :
424430
NativeCodeCall<"someFn($1, $2, $0)">` and use it like `(SomeCall $in0, $in1,
425431
$in2)`, then this will be translated into C++ call `someFn($in1, $in2, $in0)`.
426432

433+
In the case of properties, the placeholder will be bound to a value of the _interface_
434+
type of the property. For example, passing in a `StringProp` as an argument to a `NativeCodeCall` will pass a `StringRef` (as if the getter of the matched
435+
operation were called) and not a `std::string`. See
436+
`mlir/include/mlir/IR/Properties.td` for details on interface vs. storage type.
437+
427438
Positional range placeholders will be substituted by multiple `dag` object
428439
parameters at the `NativeCodeCall` use site. For example, if we define
429440
`SomeCall : NativeCodeCall<"someFn($1...)">` and use it like `(SomeCall $in0,

mlir/include/mlir/IR/Properties.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,21 @@ class ConfinedProperty<Property p, Pred pred, string newSummary = "">
401401
: ConfinedProp<p, pred, newSummary>,
402402
Deprecated<"moved to shorter name ConfinedProp">;
403403

404+
/// Defines a constant value of type `prop` to be used in pattern matching.
405+
/// When used as a constraint, forms a matcher that tests that the property is
406+
/// equal to the given value (and matches any other constraints on the property).
407+
/// The constant value is given as a string and should be of the _interface_ type
408+
/// of the attribute.
409+
///
410+
/// This requires that the given property's inference type be comparable to the
411+
/// given value with `==`, and does require specify a concrete property type.
412+
class ConstantProp<Property prop, string val>
413+
: ConfinedProp<prop,
414+
CPred<"$_self == " # val>,
415+
"constant '" # prop.summary # "': " # val> {
416+
string value = val;
417+
}
418+
404419
//===----------------------------------------------------------------------===//
405420
// Primitive property combinators
406421
//===----------------------------------------------------------------------===//

mlir/include/mlir/TableGen/Pattern.h

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,23 @@ class DagLeaf {
7373
// specifies an attribute constraint.
7474
bool isAttrMatcher() const;
7575

76+
// Returns true if this DAG leaf is matching a property. That is, it
77+
// specifies a property constraint.
78+
bool isPropMatcher() const;
79+
80+
// Returns true if this DAG leaf is describing a property. That is, it
81+
// is a subclass of `Property` in tablegen.
82+
bool isPropDefinition() const;
83+
7684
// Returns true if this DAG leaf is wrapping native code call.
7785
bool isNativeCodeCall() const;
7886

7987
// Returns true if this DAG leaf is specifying a constant attribute.
8088
bool isConstantAttr() const;
8189

90+
// Returns true if this DAG leaf is specifying a constant property.
91+
bool isConstantProp() const;
92+
8293
// Returns true if this DAG leaf is specifying an enum case.
8394
bool isEnumCase() const;
8495

@@ -88,9 +99,19 @@ class DagLeaf {
8899
// Returns this DAG leaf as a constraint. Asserts if fails.
89100
Constraint getAsConstraint() const;
90101

102+
// Returns this DAG leaf as a property constraint. Asserts if fails. This
103+
// allows access to the interface type.
104+
PropConstraint getAsPropConstraint() const;
105+
106+
// Returns this DAG leaf as a property definition. Asserts if fails.
107+
Property getAsProperty() const;
108+
91109
// Returns this DAG leaf as an constant attribute. Asserts if fails.
92110
ConstantAttr getAsConstantAttr() const;
93111

112+
// Returns this DAG leaf as an constant property. Asserts if fails.
113+
ConstantProp getAsConstantProp() const;
114+
94115
// Returns this DAG leaf as an enum case.
95116
// Precondition: isEnumCase()
96117
EnumCase getAsEnumCase() const;
@@ -279,6 +300,10 @@ class SymbolInfoMap {
279300
// the DAG of the operation, `operandIndexOrNumValues` specifies the
280301
// operand index, and `variadicSubIndex` must be set to `std::nullopt`.
281302
//
303+
// * Properties not associated with an operation (e.g. as arguments to
304+
// native code) have their corresponding PropConstraint stored in the
305+
// `dag` field. This constraint is only used when
306+
//
282307
// * If a symbol is defined in a `variadic` DAG, `dag` specifies the DAG
283308
// of the parent operation, `operandIndexOrNumValues` specifies the
284309
// declared operand index of the variadic operand in the parent
@@ -364,12 +389,20 @@ class SymbolInfoMap {
364389

365390
// What kind of entity this symbol represents:
366391
// * Attr: op attribute
392+
// * Prop: op property
367393
// * Operand: op operand
368394
// * Result: op result
369395
// * Value: a value not attached to an op (e.g., from NativeCodeCall)
370396
// * MultipleValues: a pack of values not attached to an op (e.g., from
371397
// NativeCodeCall). This kind supports indexing.
372-
enum class Kind : uint8_t { Attr, Operand, Result, Value, MultipleValues };
398+
enum class Kind : uint8_t {
399+
Attr,
400+
Prop,
401+
Operand,
402+
Result,
403+
Value,
404+
MultipleValues
405+
};
373406

374407
// Creates a SymbolInfo instance. `dagAndConstant` is only used for `Attr`
375408
// and `Operand` so should be std::nullopt for `Result` and `Value` kind.
@@ -384,6 +417,15 @@ class SymbolInfoMap {
384417
static SymbolInfo getAttr() {
385418
return SymbolInfo(nullptr, Kind::Attr, std::nullopt);
386419
}
420+
static SymbolInfo getProp(const Operator *op, int index) {
421+
return SymbolInfo(op, Kind::Prop,
422+
DagAndConstant(nullptr, index, std::nullopt));
423+
}
424+
static SymbolInfo getProp(const PropConstraint *constraint) {
425+
// -1 for anthe `operandIndexOrNumValues` is a sentinel value.
426+
return SymbolInfo(nullptr, Kind::Prop,
427+
DagAndConstant(constraint, -1, std::nullopt));
428+
}
387429
static SymbolInfo
388430
getOperand(DagNode node, const Operator *op, int operandIndex,
389431
std::optional<int> variadicSubIndex = std::nullopt) {
@@ -488,6 +530,10 @@ class SymbolInfoMap {
488530
// is already bound.
489531
bool bindAttr(StringRef symbol);
490532

533+
// Registers the given `symbol` as bound to a property that satisfies the
534+
// given `constraint`. `constraint` must name a concrete interface type.
535+
bool bindProp(StringRef symbol, const PropConstraint &constraint);
536+
491537
// Returns true if the given `symbol` is bound.
492538
bool contains(StringRef symbol) const;
493539

mlir/include/mlir/TableGen/Property.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ class Pred;
3232
// Wrapper class providing helper methods for accesing property constraint
3333
// values.
3434
class PropConstraint : public Constraint {
35+
public:
3536
using Constraint::Constraint;
3637

37-
public:
3838
static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; }
3939

4040
StringRef getInterfaceType() const;
@@ -143,6 +143,10 @@ class Property : public PropConstraint {
143143
// property constraints, this function is added for future-proofing)
144144
Property getBaseProperty() const;
145145

146+
// Returns true if this property is backed by a TableGen definition and that
147+
// definition is a subclass of `className`.
148+
bool isSubClassOf(StringRef className) const;
149+
146150
private:
147151
// Elements describing a Property, in general fetched from the record.
148152
StringRef summary;
@@ -169,6 +173,21 @@ struct NamedProperty {
169173
Property prop;
170174
};
171175

176+
// Wrapper class providing helper methods for processing constant property
177+
// values defined using the `ConstantProp` subclass of `Property`
178+
// in TableGen.
179+
class ConstantProp : public Property {
180+
public:
181+
explicit ConstantProp(const llvm::DefInit *def) : Property(def) {
182+
assert(isSubClassOf("ConstantProp"));
183+
}
184+
185+
static bool classof(Property *p) { return p->isSubClassOf("ConstantProp"); }
186+
187+
// Return the constant value of the property as an expression
188+
// that produces an interface-type constant.
189+
StringRef getValue() const;
190+
};
172191
} // namespace tblgen
173192
} // namespace mlir
174193

mlir/lib/TableGen/CodeGenHelpers.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,14 @@ static ::llvm::LogicalResult {0}(
205205

206206
/// Code for a pattern type or attribute constraint.
207207
///
208-
/// {3}: "Type type" or "Attribute attr".
209-
static const char *const patternAttrOrTypeConstraintCode = R"(
208+
/// {0}: name of function
209+
/// {1}: Condition template
210+
/// {2}: Constraint summary
211+
/// {3}: "::mlir::Type type" or "::mlirAttribute attr" or "propType prop".
212+
/// Can be "T prop" for generic property constraints.
213+
static const char *const patternConstraintCode = R"(
210214
static ::llvm::LogicalResult {0}(
211-
::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, ::mlir::{3},
215+
::mlir::PatternRewriter &rewriter, ::mlir::Operation *op, {3},
212216
::llvm::StringRef failureStr) {
213217
if (!({1})) {
214218
return rewriter.notifyMatchFailure(op, [&](::mlir::Diagnostic &diag) {
@@ -265,15 +269,31 @@ void StaticVerifierFunctionEmitter::emitPatternConstraints() {
265269
FmtContext ctx;
266270
ctx.addSubst("_op", "*op").withBuilder("rewriter").withSelf("type");
267271
for (auto &it : typeConstraints) {
268-
os << formatv(patternAttrOrTypeConstraintCode, it.second,
272+
os << formatv(patternConstraintCode, it.second,
269273
tgfmt(it.first.getConditionTemplate(), &ctx),
270-
escapeString(it.first.getSummary()), "Type type");
274+
escapeString(it.first.getSummary()), "::mlir::Type type");
271275
}
272276
ctx.withSelf("attr");
273277
for (auto &it : attrConstraints) {
274-
os << formatv(patternAttrOrTypeConstraintCode, it.second,
278+
os << formatv(patternConstraintCode, it.second,
275279
tgfmt(it.first.getConditionTemplate(), &ctx),
276-
escapeString(it.first.getSummary()), "Attribute attr");
280+
escapeString(it.first.getSummary()),
281+
"::mlir::Attribute attr");
282+
}
283+
ctx.withSelf("prop");
284+
for (auto &it : propConstraints) {
285+
PropConstraint propConstraint = cast<PropConstraint>(it.first);
286+
StringRef interfaceType = propConstraint.getInterfaceType();
287+
// Constraints that are generic over multiple interface types are
288+
// templatized under the assumption that they'll be used correctly.
289+
if (interfaceType.empty()) {
290+
interfaceType = "T";
291+
os << "template <typename T>";
292+
}
293+
os << formatv(patternConstraintCode, it.second,
294+
tgfmt(propConstraint.getConditionTemplate(), &ctx),
295+
escapeString(propConstraint.getSummary()),
296+
Twine(interfaceType) + " prop");
277297
}
278298
}
279299

@@ -367,10 +387,15 @@ void StaticVerifierFunctionEmitter::collectOpConstraints(
367387
void StaticVerifierFunctionEmitter::collectPatternConstraints(
368388
const ArrayRef<DagLeaf> constraints) {
369389
for (auto &leaf : constraints) {
370-
assert(leaf.isOperandMatcher() || leaf.isAttrMatcher());
371-
collectConstraint(
372-
leaf.isOperandMatcher() ? typeConstraints : attrConstraints,
373-
leaf.isOperandMatcher() ? "type" : "attr", leaf.getAsConstraint());
390+
assert(leaf.isOperandMatcher() || leaf.isAttrMatcher() ||
391+
leaf.isPropMatcher());
392+
Constraint constraint = leaf.getAsConstraint();
393+
if (leaf.isOperandMatcher())
394+
collectConstraint(typeConstraints, "type", constraint);
395+
else if (leaf.isAttrMatcher())
396+
collectConstraint(attrConstraints, "attr", constraint);
397+
else if (leaf.isPropMatcher())
398+
collectConstraint(propConstraints, "prop", constraint);
374399
}
375400
}
376401

0 commit comments

Comments
 (0)