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
_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to