[llvm-branch-commits] [mlir] [mlir] Unique property constraints where possible (PR #140849)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue May 20 22:48:03 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Krzysztof Drewniak (krzysz00)
<details>
<summary>Changes</summary>
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.
---
Patch is 23.43 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/140849.diff
6 Files Affected:
- (modified) mlir/include/mlir/TableGen/CodeGenHelpers.h (+21)
- (modified) mlir/include/mlir/TableGen/Property.h (+15-9)
- (modified) mlir/lib/TableGen/CodeGenHelpers.cpp (+64)
- (modified) mlir/lib/TableGen/Property.cpp (+11-7)
- (modified) mlir/test/mlir-tblgen/op-properties-predicates.td (+45-16)
- (modified) mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp (+45-29)
``````````diff
diff --git a/mlir/include/mlir/TableGen/CodeGenHelpers.h b/mlir/include/mlir/TableGen/CodeGenHelpers.h
index 465240907a3de..cf14f65b93ed2 100644
--- a/mlir/include/mlir/TableGen/CodeGenHelpers.h
+++ b/mlir/include/mlir/TableGen/CodeGenHelpers.h
@@ -153,6 +153,23 @@ class StaticVerifierFunctionEmitter {
std::optional<StringRef>
getAttrConstraintFn(const Constraint &constraint) const;
+ /// Get the name of the static function used for the given property
+ /// constraint. These functions are in the form:
+ ///
+ /// LogicalResult(Operation *op, T property, StringRef propName);
+ ///
+ /// where T is the interface type specified in the constraint.
+ /// If a uniqued constraint was not found, this function returns std::nullopt.
+ /// The uniqued constraints cannot be used in the context of an OpAdaptor.
+ ///
+ /// Pattern constraints have the form:
+ ///
+ /// LogicalResult(PatternRewriter &rewriter, Operation *op, T property,
+ /// StringRef failureStr);
+ ///
+ std::optional<StringRef>
+ getPropConstraintFn(const Constraint &constraint) const;
+
/// Get the name of the static function used for the given successor
/// constraint. These functions are in the form:
///
@@ -175,6 +192,8 @@ class StaticVerifierFunctionEmitter {
void emitTypeConstraints();
/// Emit static attribute constraint functions.
void emitAttrConstraints();
+ /// Emit static property constraint functions.
+ void emitPropConstraints();
/// Emit static successor constraint functions.
void emitSuccessorConstraints();
/// Emit static region constraint functions.
@@ -212,6 +231,8 @@ class StaticVerifierFunctionEmitter {
ConstraintMap typeConstraints;
/// The set of attribute constraints used in the current file.
ConstraintMap attrConstraints;
+ /// The set of property constraints used in the current file.
+ ConstraintMap propConstraints;
/// The set of successor constraints used in the current file.
ConstraintMap successorConstraints;
/// The set of region constraints used in the current file.
diff --git a/mlir/include/mlir/TableGen/Property.h b/mlir/include/mlir/TableGen/Property.h
index 386159191ef1f..6af96f077efe5 100644
--- a/mlir/include/mlir/TableGen/Property.h
+++ b/mlir/include/mlir/TableGen/Property.h
@@ -29,14 +29,26 @@ class Dialect;
class Type;
class Pred;
+// Wrapper class providing helper methods for accesing property constraint
+// values.
+class PropConstraint : public Constraint {
+ using Constraint::Constraint;
+
+public:
+ static bool classof(const Constraint *c) { return c->getKind() == CK_Prop; }
+
+ StringRef getInterfaceType() const;
+};
+
// Wrapper class providing helper methods for accessing MLIR Property defined
// in TableGen. This class should closely reflect what is defined as class
// `Property` in TableGen.
-class Property {
+class Property : public PropConstraint {
public:
- explicit Property(const llvm::Record *record);
+ explicit Property(const llvm::Record *def);
explicit Property(const llvm::DefInit *init);
- Property(StringRef summary, StringRef description, StringRef storageType,
+ Property(const llvm::Record *maybeDef, StringRef summary,
+ StringRef description, StringRef storageType,
StringRef interfaceType, StringRef convertFromStorageCall,
StringRef assignToStorageCall, StringRef convertToAttributeCall,
StringRef convertFromAttributeCall, StringRef parserCall,
@@ -131,13 +143,7 @@ class Property {
// property constraints, this function is added for future-proofing)
Property getBaseProperty() const;
- // Returns the TableGen definition this Property was constructed from.
- const llvm::Record &getDef() const { return *def; }
-
private:
- // The TableGen definition of this constraint.
- const llvm::Record *def;
-
// Elements describing a Property, in general fetched from the record.
StringRef summary;
StringRef description;
diff --git a/mlir/lib/TableGen/CodeGenHelpers.cpp b/mlir/lib/TableGen/CodeGenHelpers.cpp
index f4031be24dfdb..4ce6ab1dbfce5 100644
--- a/mlir/lib/TableGen/CodeGenHelpers.cpp
+++ b/mlir/lib/TableGen/CodeGenHelpers.cpp
@@ -53,6 +53,7 @@ void StaticVerifierFunctionEmitter::emitOpConstraints(
NamespaceEmitter namespaceEmitter(os, Operator(*opDefs[0]).getCppNamespace());
emitTypeConstraints();
emitAttrConstraints();
+ emitPropConstraints();
emitSuccessorConstraints();
emitRegionConstraints();
}
@@ -83,6 +84,15 @@ std::optional<StringRef> StaticVerifierFunctionEmitter::getAttrConstraintFn(
: StringRef(it->second);
}
+// Find a uniqued property constraint. Since not all property constraints can
+// be uniqued, return std::nullopt if one was not found.
+std::optional<StringRef> StaticVerifierFunctionEmitter::getPropConstraintFn(
+ const Constraint &constraint) const {
+ const auto *it = propConstraints.find(constraint);
+ return it == propConstraints.end() ? std::optional<StringRef>()
+ : StringRef(it->second);
+}
+
StringRef StaticVerifierFunctionEmitter::getSuccessorConstraintFn(
const Constraint &constraint) const {
const auto *it = successorConstraints.find(constraint);
@@ -146,6 +156,25 @@ static ::llvm::LogicalResult {0}(
}
)";
+/// Code for a property constraint. These may be called from ops only.
+/// Property constraints cannot reference anything other than `$_self` and
+/// `$_op`. {3} is the interface type of the property.
+static const char *const propConstraintCode = R"(
+ static ::llvm::LogicalResult {0}(
+ {3} prop, ::llvm::StringRef propName, llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) {{
+ if (!({1}))
+ return emitError() << "property '" << propName
+ << "' failed to satisfy constraint: {2}";
+ return ::mlir::success();
+ }
+ static ::llvm::LogicalResult {0}(
+ ::mlir::Operation *op, {3} prop, ::llvm::StringRef propName) {{
+ return {0}(prop, propName, [op]() {{
+ return op->emitOpError();
+ });
+ }
+ )";
+
/// Code for a successor constraint.
static const char *const successorConstraintCode = R"(
static ::llvm::LogicalResult {0}(
@@ -210,6 +239,20 @@ void StaticVerifierFunctionEmitter::emitAttrConstraints() {
emitConstraints(attrConstraints, "attr", attrConstraintCode);
}
+/// Unlike with the other helpers, this one has to substitute in the interface
+/// type of the property, so we can't just use the generic function.
+void StaticVerifierFunctionEmitter::emitPropConstraints() {
+ FmtContext ctx;
+ ctx.addSubst("_op", "*op").withSelf("prop");
+ for (auto &it : propConstraints) {
+ auto propConstraint = cast<PropConstraint>(it.first);
+ os << formatv(propConstraintCode, it.second,
+ tgfmt(propConstraint.getConditionTemplate(), &ctx),
+ escapeString(it.first.getSummary()),
+ propConstraint.getInterfaceType());
+ }
+}
+
void StaticVerifierFunctionEmitter::emitSuccessorConstraints() {
emitConstraints(successorConstraints, "successor", successorConstraintCode);
}
@@ -251,6 +294,20 @@ static bool canUniqueAttrConstraint(Attribute attr) {
return !StringRef(test).contains("<no-subst-found>");
}
+/// A property constraint that references anything other than itself and the
+/// current op cannot be generically extracted into a function, just as with
+/// canUnequePropConstraint(). Additionally, property constraints without
+/// an interface type specified can't be uniqued, and ones that are a literal
+/// "true" shouldn't be constrained.
+static bool canUniquePropConstraint(Property prop) {
+ FmtContext ctx;
+ auto test = tgfmt(prop.getConditionTemplate(),
+ &ctx.withSelf("prop").addSubst("_op", "*op"))
+ .str();
+ return !StringRef(test).contains("<no-subst-found>") && test != "true" &&
+ !prop.getInterfaceType().empty();
+}
+
std::string StaticVerifierFunctionEmitter::getUniqueName(StringRef kind,
unsigned index) {
return ("__mlir_ods_local_" + kind + "_constraint_" + uniqueOutputLabel +
@@ -286,6 +343,13 @@ void StaticVerifierFunctionEmitter::collectOpConstraints(
canUniqueAttrConstraint(namedAttr.attr))
collectConstraint(attrConstraints, "attr", namedAttr.attr);
}
+ /// Collect non-trivial property constraints.
+ for (const NamedProperty &namedProp : op.getProperties()) {
+ if (!namedProp.prop.getPredicate().isNull() &&
+ canUniquePropConstraint(namedProp.prop)) {
+ collectConstraint(propConstraints, "prop", namedProp.prop);
+ }
+ }
/// Collect successor constraints.
for (const NamedSuccessor &successor : op.getSuccessors()) {
if (!successor.constraint.getPredicate().isNull()) {
diff --git a/mlir/lib/TableGen/Property.cpp b/mlir/lib/TableGen/Property.cpp
index 13851167ddaf7..9a70c1b6e8d62 100644
--- a/mlir/lib/TableGen/Property.cpp
+++ b/mlir/lib/TableGen/Property.cpp
@@ -33,9 +33,13 @@ static StringRef getValueAsString(const Init *init) {
return {};
}
+StringRef PropConstraint::getInterfaceType() const {
+ return getValueAsString(def->getValueInit("interfaceType"));
+}
+
Property::Property(const Record *def)
: Property(
- getValueAsString(def->getValueInit("summary")),
+ def, getValueAsString(def->getValueInit("summary")),
getValueAsString(def->getValueInit("description")),
getValueAsString(def->getValueInit("storageType")),
getValueAsString(def->getValueInit("interfaceType")),
@@ -51,16 +55,15 @@ Property::Property(const Record *def)
getValueAsString(def->getValueInit("hashProperty")),
getValueAsString(def->getValueInit("defaultValue")),
getValueAsString(def->getValueInit("storageTypeValueOverride"))) {
- this->def = def;
assert((def->isSubClassOf("Property") || def->isSubClassOf("Attr")) &&
"must be subclass of TableGen 'Property' class");
}
Property::Property(const DefInit *init) : Property(init->getDef()) {}
-Property::Property(StringRef summary, StringRef description,
- StringRef storageType, StringRef interfaceType,
- StringRef convertFromStorageCall,
+Property::Property(const llvm::Record *maybeDef, StringRef summary,
+ StringRef description, StringRef storageType,
+ StringRef interfaceType, StringRef convertFromStorageCall,
StringRef assignToStorageCall,
StringRef convertToAttributeCall,
StringRef convertFromAttributeCall, StringRef parserCall,
@@ -69,8 +72,9 @@ Property::Property(StringRef summary, StringRef description,
StringRef writeToMlirBytecodeCall,
StringRef hashPropertyCall, StringRef defaultValue,
StringRef storageTypeValueOverride)
- : def(nullptr), summary(summary), description(description),
- storageType(storageType), interfaceType(interfaceType),
+ : PropConstraint(maybeDef, Constraint::CK_Prop), summary(summary),
+ description(description), storageType(storageType),
+ interfaceType(interfaceType),
convertFromStorageCall(convertFromStorageCall),
assignToStorageCall(assignToStorageCall),
convertToAttributeCall(convertToAttributeCall),
diff --git a/mlir/test/mlir-tblgen/op-properties-predicates.td b/mlir/test/mlir-tblgen/op-properties-predicates.td
index 9834edd0cbb57..7cd24aad97424 100644
--- a/mlir/test/mlir-tblgen/op-properties-predicates.td
+++ b/mlir/test/mlir-tblgen/op-properties-predicates.td
@@ -35,39 +35,68 @@ def OpWithPredicates : NS_Op<"op_with_predicates"> {
);
}
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1
+// CHECK-NEXT: int64_t prop
+// CHECK-NEXT: if (!((prop >= 0)))
+// CHECK: failed to satisfy constraint: non-negative int64_t
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates2
+// CHECK-NEXT: std::optional<int64_t> prop
+// CHECK-NEXT: if (!(((!prop.has_value())) || (((*(prop)) >= 0))))
+// CHECK: failed to satisfy constraint: optional non-negative int64_t
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates3
+// CHECK-NEXT: int64_t prop
+// CHECK-NEXT: if (!(((prop >= 0)) && ((prop <= 5))))
+// CHECK: failed to satisfy constraint: between 0 and 5
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates4
+// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
+// CHECK-NEXT: (!(::llvm::all_of(prop, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })))
+// CHECK: failed to satisfy constraint: array of non-negative int64_t
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates5
+// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
+// CHECK-NEXT: if (!(!((prop.empty()))))
+// CHECK: failed to satisfy constraint: non-empty array of int64_t
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates6
+// CHECK-NEXT: ::llvm::ArrayRef<int64_t> prop
+// CHECX-NEXT: if (!((::llvm::all_of(prop, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!((prop.empty())))))
+// CHECK: failed to satisfy constraint: non-empty array of non-negative int64_t
+
+// CHECK-LABEL: static ::llvm::LogicalResult __mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates7
+// CHECK-NEXT: std::optional<::llvm::ArrayRef<int64_t>> prop
+// 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()))))))
+// CHECK: failed to satisfy constraint: optional non-empty array of non-negative int64_
+
// CHECK-LABEL: OpWithPredicates::verifyInvariantsImpl()
// Note: for test readability, we capture [[maybe_unused]] into the variable maybe_unused
// CHECK: [[maybe_unused:\[\[maybe_unused\]\]]] int64_t tblgen_scalar = this->getScalar();
-// CHECK: if (!((tblgen_scalar >= 0)))
-// CHECK-NEXT: return emitOpError("property 'scalar' failed to satisfy constraint: non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1(*this, tblgen_scalar, "scalar")))
+// CHECK-NEXT: return ::mlir::failure()
// CHECK: [[maybe_unused]] std::optional<int64_t> tblgen_optional = this->getOptional();
-// CHECK: if (!(((!tblgen_optional.has_value())) || (((*(tblgen_optional)) >= 0))))
-// CHECK-NEXT: return emitOpError("property 'optional' failed to satisfy constraint: optional non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates2(*this, tblgen_optional, "optional")))
+// COM: The predicates for "scalar" and "defaulted" are uniqued
// CHECK: [[maybe_unused]] int64_t tblgen_defaulted = this->getDefaulted();
-// CHECK: if (!((tblgen_defaulted >= 0)))
-// CHECK-NEXT: return emitOpError("property 'defaulted' failed to satisfy constraint: non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates1(*this, tblgen_defaulted, "defaulted")))
// CHECK: [[maybe_unused]] int64_t tblgen_moreConstrained = this->getMoreConstrained();
-// CHECK: if (!(((tblgen_moreConstrained >= 0)) && ((tblgen_moreConstrained <= 5))))
-// CHECK-NEXT: return emitOpError("property 'moreConstrained' failed to satisfy constraint: between 0 and 5");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates3(*this, tblgen_moreConstrained, "moreConstrained")))
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_array = this->getArray();
-// CHECK: if (!(::llvm::all_of(tblgen_array, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })))
-// CHECK-NEXT: return emitOpError("property 'array' failed to satisfy constraint: array of non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates4(*this, tblgen_array, "array")))
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_nonEmptyUnconstrained = this->getNonEmptyUnconstrained();
-// CHECK: if (!(!((tblgen_nonEmptyUnconstrained.empty()))))
-// CHECK-NEXT: return emitOpError("property 'non_empty_unconstrained' failed to satisfy constraint: non-empty array of int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates5(*this, tblgen_nonEmptyUnconstrained, "non_empty_unconstrained")))
// CHECK: [[maybe_unused]] ::llvm::ArrayRef<int64_t> tblgen_nonEmptyConstrained = this->getNonEmptyConstrained();
-// CHECK: if (!((::llvm::all_of(tblgen_nonEmptyConstrained, [](const int64_t& baseStore) -> bool { return [](int64_t baseIface) -> bool { return ((baseIface >= 0)); }(baseStore); })) && (!((tblgen_nonEmptyConstrained.empty())))))
-// CHECK-NEXT: return emitOpError("property 'non_empty_constrained' failed to satisfy constraint: non-empty array of non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates6(*this, tblgen_nonEmptyConstrained, "non_empty_constrained")))
// CHECK: [[maybe_unused]] std::optional<::llvm::ArrayRef<int64_t>> tblgen_nonEmptyOptional = this->getNonEmptyOptional();
-// 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()))))))
-// CHECK-NEXT: return emitOpError("property 'non_empty_optional' failed to satisfy constraint: optional non-empty array of non-negative int64_t");
+// CHECK: if (::mlir::failed(__mlir_ods_local_prop_constraint_op2Dproperties2Dpredicates7(*this, tblgen_nonEmptyOptional, "non_empty_optional")))
// CHECK-NOT: int64_t tblgen_unconstrained
// CHECK: return ::mlir::success();
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index 373d3762cbb1a..c15f478ad9c07 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -491,28 +491,28 @@ void OpOrAdaptorHelper::computeAttrMetadata() {
}
auto makeProperty = [&](StringRef storageType, StringRef parserCall) {
- return Property(
- /*summary=*/"",
- /*description=*/"",
- /*storageType=*/storageType,
- /*interfaceType=*/"::llvm::ArrayRef<int32_t>",
- /*convertFromStorageCall=*/"$_storage",
- /*assignToStorageCall=*/
- "::llvm::copy($_value, $_storage.begin())",
- /*convertToAttributeCall=*/
- "return ::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage);",
- /*convertFromAttributeCall=*/
- "return convertFromAttribute($_storage, $_attr, $_diag);",
- /*parserCall=*/parserCall,
- /*optionalParserCall=*/"",
- /*printerCall=*/printTextualSegmentSize,
- /*readFromMlirBytecodeCall=*/readBytecodeSegmentSizeNative,
- /*writeToMlirBytecodeCall=*/writeBytecodeSegmentSizeNative,
- /*hashPropertyCall=*/
- "::llvm::hash_combine_range(std::begin($_storage), "
- "std::end($_storage));",
- /*StringRef defaultValue=*/"",
- /*storageTypeValueOverride=*/"");
+ return Property(/*maybeDef=*/nullptr,
+ /*summary=*/"",
+ /*description=*/"",
+ /*storageType=*/storageType,
+ /*interfaceType=*/"::llvm::ArrayRef<int32_t>",
+ /*convertFromStorageCall=*/"$_storage",
+ /*assignToStorageCall=*/
+ "::llvm::copy($_value, $_storage.begin())",
+ /*convertToAttributeCall=*/
+ "return ::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage);",
+ /*convertFromAttributeCall=*/
+ "return convertFromAttribute($_storage, $_attr, $_diag);",
+ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/140849
More information about the llvm-branch-commits
mailing list