Skip to content

Commit 66a357f

Browse files
authored
[mlir] Unique property constraints where possible (#140849)
Now that `Property` is a `PropConstraint`, hook it up to the same constraint-uniquing machinery that other types of constraints use. This will primarily save on code size for types, like enums, that have inherent constraints which are shared across many operations.
1 parent 513c1cd commit 66a357f

File tree

6 files changed

+201
-61
lines changed

6 files changed

+201
-61
lines changed

mlir/include/mlir/TableGen/CodeGenHelpers.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ class StaticVerifierFunctionEmitter {
153153
std::optional<StringRef>
154154
getAttrConstraintFn(const Constraint &constraint) const;
155155

156+
/// Get the name of the static function used for the given property
157+
/// constraint. These functions are in the form:
158+
///
159+
/// LogicalResult(Operation *op, T property, StringRef propName);
160+
///
161+
/// where T is the interface type specified in the constraint.
162+
/// If a uniqued constraint was not found, this function returns std::nullopt.
163+
/// The uniqued constraints cannot be used in the context of an OpAdaptor.
164+
///
165+
/// Pattern constraints have the form:
166+
///
167+
/// LogicalResult(PatternRewriter &rewriter, Operation *op, T property,
168+
/// StringRef failureStr);
169+
///
170+
std::optional<StringRef>
171+
getPropConstraintFn(const Constraint &constraint) const;
172+
156173
/// Get the name of the static function used for the given successor
157174
/// constraint. These functions are in the form:
158175
///
@@ -175,6 +192,8 @@ class StaticVerifierFunctionEmitter {
175192
void emitTypeConstraints();
176193
/// Emit static attribute constraint functions.
177194
void emitAttrConstraints();
195+
/// Emit static property constraint functions.
196+
void emitPropConstraints();
178197
/// Emit static successor constraint functions.
179198
void emitSuccessorConstraints();
180199
/// Emit static region constraint functions.
@@ -212,6 +231,8 @@ class StaticVerifierFunctionEmitter {
212231
ConstraintMap typeConstraints;
213232
/// The set of attribute constraints used in the current file.
214233
ConstraintMap attrConstraints;
234+
/// The set of property constraints used in the current file.
235+
ConstraintMap propConstraints;
215236
/// The set of successor constraints used in the current file.
216237
ConstraintMap successorConstraints;
217238
/// The set of region constraints used in the current file.

mlir/include/mlir/TableGen/Property.h

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,26 @@ class Dialect;
2929
class Type;
3030
class Pred;
3131

32+
// Wrapper class providing helper methods for accesing property constraint
33+
// values.
34+
class PropConstraint : public Constraint {
35+
using Constraint::Constraint;
36+
37+
public:
38+
static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; }
39+
40+
StringRef getInterfaceType() const;
41+
};
42+
3243
// Wrapper class providing helper methods for accessing MLIR Property defined
3344
// in TableGen. This class should closely reflect what is defined as class
3445
// `Property` in TableGen.
35-
class Property {
46+
class Property : public PropConstraint {
3647
public:
37-
explicit Property(const llvm::Record *record);
48+
explicit Property(const llvm::Record *def);
3849
explicit Property(const llvm::DefInit *init);
39-
Property(StringRef summary, StringRef description, StringRef storageType,
50+
Property(const llvm::Record *maybeDef, StringRef summary,
51+
StringRef description, StringRef storageType,
4052
StringRef interfaceType, StringRef convertFromStorageCall,
4153
StringRef assignToStorageCall, StringRef convertToAttributeCall,
4254
StringRef convertFromAttributeCall, StringRef parserCall,
@@ -131,13 +143,7 @@ class Property {
131143
// property constraints, this function is added for future-proofing)
132144
Property getBaseProperty() const;
133145

134-
// Returns the TableGen definition this Property was constructed from.
135-
const llvm::Record &getDef() const { return *def; }
136-
137146
private:
138-
// The TableGen definition of this constraint.
139-
const llvm::Record *def;
140-
141147
// Elements describing a Property, in general fetched from the record.
142148
StringRef summary;
143149
StringRef description;

mlir/lib/TableGen/CodeGenHelpers.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void StaticVerifierFunctionEmitter::emitOpConstraints(
5353
NamespaceEmitter namespaceEmitter(os, Operator(*opDefs[0]).getCppNamespace());
5454
emitTypeConstraints();
5555
emitAttrConstraints();
56+
emitPropConstraints();
5657
emitSuccessorConstraints();
5758
emitRegionConstraints();
5859
}
@@ -83,6 +84,15 @@ std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn(
8384
: StringRef(it->second);
8485
}
8586

87+
// Find a uniqued property constraint. Since not all property constraints can
88+
// be uniqued, return std::nullopt if one was not found.
89+
std::optional<StringRef> StaticVerifierFunctionEmitter::getPropConstraintFn(
90+
const Constraint &constraint) const {
91+
const auto *it = propConstraints.find(constraint);
92+
return it == propConstraints.end() ? std::optional<StringRef>()
93+
: StringRef(it->second);
94+
}
95+
8696
StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn(
8797
const Constraint &constraint) const {
8898
const auto *it = successorConstraints.find(constraint);
@@ -146,6 +156,25 @@ static ::llvm::LogicalResult {0}(
146156
}
147157
)";
148158

159+
/// Code for a property constraint. These may be called from ops only.
160+
/// Property constraints cannot reference anything other than `$_self` and
161+
/// `$_op`. {3} is the interface type of the property.
162+
static const char *const propConstraintCode = R"(
163+
static ::llvm::LogicalResult {0}(
164+
{3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
165+
if (!({1}))
166+
return emitError() << "property '" << propName
167+
<< "' failed to satisfy constraint: {2}";
168+
return ::mlir::success();
169+
}
170+
static ::llvm::LogicalResult {0}(
171+
::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{
172+
return {0}(prop, propName, [op]() {{
173+
return op->emitOpError();
174+
});
175+
}
176+
)";
177+
149178
/// Code for a successor constraint.
150179
static const char *const successorConstraintCode = R"(
151180
static ::llvm::LogicalResult {0}(
@@ -210,6 +239,20 @@ void StaticVerifierFunctionEmitter::emitAttrConstraints() {
210239
emitConstraints(attrConstraints, "attr", attrConstraintCode);
211240
}
212241

242+
/// Unlike with the other helpers, this one has to substitute in the interface
243+
/// type of the property, so we can't just use the generic function.
244+
void StaticVerifierFunctionEmitter::emitPropConstraints() {
245+
FmtContext ctx;
246+
ctx.addSubst("_op", "*op").withSelf("prop");
247+
for (auto &it : propConstraints) {
248+
auto propConstraint = cast<PropConstraint>(it.first);
249+
os << formatv(propConstraintCode, it.second,
250+
tgfmt(propConstraint.getConditionTemplate(), &ctx),
251+
escapeString(it.first.getSummary()),
252+
propConstraint.getInterfaceType());
253+
}
254+
}
255+
213256
void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
214257
emitConstraints(successorConstraints, "successor", successorConstraintCode);
215258
}
@@ -251,6 +294,20 @@ static bool canUniqueAttrConstraint(Attribute attr) {
251294
return !StringRef(test).contains("<no-subst-found>");
252295
}
253296

297+
/// A property constraint that references anything other than itself and the
298+
/// current op cannot be generically extracted into a function, just as with
299+
/// canUnequePropConstraint(). Additionally, property constraints without
300+
/// an interface type specified can't be uniqued, and ones that are a literal
301+
/// "true" shouldn't be constrained.
302+
static bool canUniquePropConstraint(Property prop) {
303+
FmtContext ctx;
304+
auto test = tgfmt(prop.getConditionTemplate(),
305+
&ctx.withSelf("prop").addSubst("_op", "*op"))
306+
.str();
307+
return !StringRef(test).contains("<no-subst-found>") && test != "true" &&
308+
!prop.getInterfaceType().empty();
309+
}
310+
254311
std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
255312
unsigned index) {
256313
return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
@@ -286,6 +343,13 @@ void StaticVerifierFunctionEmitter::collectOpConstraints(
286343
canUniqueAttrConstraint(namedAttr.attr))
287344
collectConstraint(attrConstraints, "attr", namedAttr.attr);
288345
}
346+
/// Collect non-trivial property constraints.
347+
for (const NamedProperty &namedProp : op.getProperties()) {
348+
if (!namedProp.prop.getPredicate().isNull() &&
349+
canUniquePropConstraint(namedProp.prop)) {
350+
collectConstraint(propConstraints, "prop", namedProp.prop);
351+
}
352+
}
289353
/// Collect successor constraints.
290354
for (const NamedSuccessor &successor : op.getSuccessors()) {
291355
if (!successor.constraint.getPredicate().isNull()) {

mlir/lib/TableGen/Property.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ static StringRef getValueAsString(const Init *init) {
3333
return {};
3434
}
3535

36+
StringRef PropConstraint::getInterfaceType() const {
37+
return getValueAsString(def->getValueInit("interfaceType"));
38+
}
39+
3640
Property::Property(const Record *def)
3741
: Property(
38-
getValueAsString(def->getValueInit("summary")),
42+
def, getValueAsString(def->getValueInit("summary")),
3943
getValueAsString(def->getValueInit("description")),
4044
getValueAsString(def->getValueInit("storageType")),
4145
getValueAsString(def->getValueInit("interfaceType")),
@@ -51,16 +55,15 @@ Property::Property(const Record *def)
5155
getValueAsString(def->getValueInit("hashProperty")),
5256
getValueAsString(def->getValueInit("defaultValue")),
5357
getValueAsString(def->getValueInit("storageTypeValueOverride"))) {
54-
this->def = def;
5558
assert((def->isSubClassOf("Property") || def->isSubClassOf("Attr")) &&
5659
"must be subclass of TableGen 'Property' class");
5760
}
5861

5962
Property::Property(const DefInit *init) : Property(init->getDef()) {}
6063

61-
Property::Property(StringRef summary, StringRef description,
62-
StringRef storageType, StringRef interfaceType,
63-
StringRef convertFromStorageCall,
64+
Property::Property(const llvm::Record *maybeDef, StringRef summary,
65+
StringRef description, StringRef storageType,
66+
StringRef interfaceType, StringRef convertFromStorageCall,
6467
StringRef assignToStorageCall,
6568
StringRef convertToAttributeCall,
6669
StringRef convertFromAttributeCall, StringRef parserCall,
@@ -69,8 +72,9 @@ Property::Property(StringRef summary, StringRef description,
6972
StringRef writeToMlirBytecodeCall,
7073
StringRef hashPropertyCall, StringRef defaultValue,
7174
StringRef storageTypeValueOverride)
72-
: def(nullptr), summary(summary), description(description),
73-
storageType(storageType), interfaceType(interfaceType),
75+
: PropConstraint(maybeDef, Constraint::CK_Prop), summary(summary),
76+
description(description), storageType(storageType),
77+
interfaceType(interfaceType),
7478
convertFromStorageCall(convertFromStorageCall),
7579
assignToStorageCall(assignToStorageCall),
7680
convertToAttributeCall(convertToAttributeCall),

mlir/test/mlir-tblgen/op-properties-predicates.td

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,68 @@ def OpWithPredicates : NS_Op<"op_with_predicates"> {
3535
);
3636
}
3737

38+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1
39+
// CHECK-NEXT: int64_t prop
40+
// CHECK-NEXT: if (!((prop >= 0)))
41+
// CHECK: failed to satisfy constraint: non-negative int64_t
42+
43+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates2
44+
// CHECK-NEXT: std::optional<int64_t> prop
45+
// CHECK-NEXT: if (!(((!prop.has_value())) || (((*(prop)) >= 0))))
46+
// CHECK: failed to satisfy constraint: optional non-negative int64_t
47+
48+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates3
49+
// CHECK-NEXT: int64_t prop
50+
// CHECK-NEXT: if (!(((prop >= 0)) && ((prop <= 5))))
51+
// CHECK: failed to satisfy constraint: between 0 and 5
52+
53+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates4
54+
// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
55+
// CHECK-NEXT: (!(::llvm::all_of(prop, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })))
56+
// CHECK: failed to satisfy constraint: array of non-negative int64_t
57+
58+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates5
59+
// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
60+
// CHECK-NEXT: if (!(!((prop.empty()))))
61+
// CHECK: failed to satisfy constraint: non-empty array of int64_t
62+
63+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates6
64+
// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
65+
// CHECX-NEXT: if (!((::llvm::all_of(prop, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!((prop.empty())))))
66+
// CHECK: failed to satisfy constraint: non-empty array of non-negative int64_t
67+
68+
// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates7
69+
// CHECK-NEXT: std::optional<::llvm::ArrayRef<int64_t>> prop
70+
// CHECK-NEXT: if (!(((!prop.has_value())) || ((::llvm::all_of((*(prop)), [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!(((*(prop)).empty()))))))
71+
// CHECK: failed to satisfy constraint: optional non-empty array of non-negative int64_
72+
3873
// CHECK-LABEL: OpWithPredicates::verifyInvariantsImpl()
3974
// Note: for test readability, we capture [[maybe_unused]] into the variable maybe_unused
4075
// CHECK: [[maybe_unused:\[\[maybe_unused\]\]]] int64_t tblgen_scalar = this->getScalar();
41-
// CHECK: if (!((tblgen_scalar >= 0)))
42-
// CHECK-NEXT: return emitOpError("property 'scalar' failed to satisfy constraint: non-negative int64_t");
76+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1(*this, tblgen_scalar, "scalar")))
77+
// CHECK-NEXT: return ::mlir::failure()
4378

4479
// CHECK: [[maybe_unused]] std::optional<int64_t> tblgen_optional = this->getOptional();
45-
// CHECK: if (!(((!tblgen_optional.has_value())) || (((*(tblgen_optional)) >= 0))))
46-
// CHECK-NEXT: return emitOpError("property 'optional' failed to satisfy constraint: optional non-negative int64_t");
80+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates2(*this, tblgen_optional, "optional")))
4781

82+
// COM: The predicates for "scalar" and "defaulted" are uniqued
4883
// CHECK: [[maybe_unused]] int64_t tblgen_defaulted = this->getDefaulted();
49-
// CHECK: if (!((tblgen_defaulted >= 0)))
50-
// CHECK-NEXT: return emitOpError("property 'defaulted' failed to satisfy constraint: non-negative int64_t");
84+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1(*this, tblgen_defaulted, "defaulted")))
5185

5286
// CHECK: [[maybe_unused]] int64_t tblgen_moreConstrained = this->getMoreConstrained();
53-
// CHECK: if (!(((tblgen_moreConstrained >= 0)) && ((tblgen_moreConstrained <= 5))))
54-
// CHECK-NEXT: return emitOpError("property 'moreConstrained' failed to satisfy constraint: between 0 and 5");
87+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates3(*this, tblgen_moreConstrained, "moreConstrained")))
5588

5689
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_array = this->getArray();
57-
// CHECK: if (!(::llvm::all_of(tblgen_array, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })))
58-
// CHECK-NEXT: return emitOpError("property 'array' failed to satisfy constraint: array of non-negative int64_t");
90+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates4(*this, tblgen_array, "array")))
5991

6092
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_nonEmptyUnconstrained = this->getNonEmptyUnconstrained();
61-
// CHECK: if (!(!((tblgen_nonEmptyUnconstrained.empty()))))
62-
// CHECK-NEXT: return emitOpError("property 'non_empty_unconstrained' failed to satisfy constraint: non-empty array of int64_t");
93+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates5(*this, tblgen_nonEmptyUnconstrained, "non_empty_unconstrained")))
6394

6495
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_nonEmptyConstrained = this->getNonEmptyConstrained();
65-
// CHECK: if (!((::llvm::all_of(tblgen_nonEmptyConstrained, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!((tblgen_nonEmptyConstrained.empty())))))
66-
// CHECK-NEXT: return emitOpError("property 'non_empty_constrained' failed to satisfy constraint: non-empty array of non-negative int64_t");
96+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates6(*this, tblgen_nonEmptyConstrained, "non_empty_constrained")))
6797

6898
// CHECK: [[maybe_unused]] std::optional<::llvm::ArrayRef<int64_t>> tblgen_nonEmptyOptional = this->getNonEmptyOptional();
69-
// CHECK: (!(((!tblgen_nonEmptyOptional.has_value())) || ((::llvm::all_of((*(tblgen_nonEmptyOptional)), [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!(((*(tblgen_nonEmptyOptional)).empty()))))))
70-
// CHECK-NEXT: return emitOpError("property 'non_empty_optional' failed to satisfy constraint: optional non-empty array of non-negative int64_t");
99+
// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates7(*this, tblgen_nonEmptyOptional, "non_empty_optional")))
71100

72101
// CHECK-NOT: int64_t tblgen_unconstrained
73102
// CHECK: return ::mlir::success();

0 commit comments

Comments
 (0)