[Mlir-commits] [mlir] [mlir][IR] Add `getPropertyFromAttr` and `setPropertyFromAttr` methods. (PR #150060)

Fabian Mora llvmlistbot at llvm.org
Tue Jul 22 09:42:33 PDT 2025


https://github.com/fabianmcg created https://github.com/llvm/llvm-project/pull/150060

This patch adds the `getPropertyFromAttr` and `setPropertyFromAttr` methods to various places. These methods are similar to `getPropertiesFromAttr` and `setPropertiesFromAttr`, in that set a property from an attr, but they set or get props based on a name.

The reasoning behind adding these methods is that `getPropertiesFromAttr` and `setPropertiesFromAttr` are meant for getting and setting collections of props, which is not always desirable specially in language bindings like the python bindings, and can be wasteful.

Also, while it would be possible to use `setPropertiesFromAttr` to set a single prop, the method introduces additional logic to set default values if the value is not present, which is not something desirable once the op has been created. Further, `getPropertiesFromAttr` always converts all properties, which might be a wasteful process for large props.

This is also the first step in fixing https://github.com/llvm/llvm-project/issues/150009 .

>From c9128362ba8d9a6a59c3bcbcf5a57d70262ff999 Mon Sep 17 00:00:00 2001
From: Fabian Mora <fabian.mora-cordero at amd.com>
Date: Tue, 22 Jul 2025 15:21:46 +0000
Subject: [PATCH] init

---
 mlir/include/mlir/IR/ExtensibleDialect.h    |  11 ++
 mlir/include/mlir/IR/OpDefinition.h         |  21 ++++
 mlir/include/mlir/IR/Operation.h            |  17 +++
 mlir/include/mlir/IR/OperationSupport.h     |  45 ++++++++
 mlir/lib/IR/MLIRContext.cpp                 |  14 +++
 mlir/lib/IR/Operation.cpp                   |  15 +++
 mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp | 121 ++++++++++++++++----
 mlir/unittests/IR/OpPropertiesTest.cpp      |  94 +++++++++++++++
 8 files changed, 314 insertions(+), 24 deletions(-)

diff --git a/mlir/include/mlir/IR/ExtensibleDialect.h b/mlir/include/mlir/IR/ExtensibleDialect.h
index 955faaad9408b..204b3f59db1a5 100644
--- a/mlir/include/mlir/IR/ExtensibleDialect.h
+++ b/mlir/include/mlir/IR/ExtensibleDialect.h
@@ -493,6 +493,17 @@ class DynamicOpDefinition : public OperationName::Impl {
     return failure();
   }
   Attribute getPropertiesAsAttr(Operation *op) final { return {}; }
+
+  LogicalResult
+  setPropertyFromAttr(OperationName opName, OpaqueProperties properties,
+                      StringRef name, Attribute attr,
+                      function_ref<InFlightDiagnostic()> emitError) final {
+    emitError() << "extensible Dialects don't support properties";
+    return failure();
+  }
+  FailureOr<Attribute> getPropertyAsAttr(Operation *op, StringRef name) final {
+    return {};
+  }
   void copyProperties(OpaqueProperties lhs, OpaqueProperties rhs) final {}
   bool compareProperties(OpaqueProperties, OpaqueProperties) final { return false; }
   llvm::hash_code hashProperties(OpaqueProperties prop) final { return {}; }
diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h
index 883ece32967e4..2e610ec21000c 100644
--- a/mlir/include/mlir/IR/OpDefinition.h
+++ b/mlir/include/mlir/IR/OpDefinition.h
@@ -1758,6 +1758,17 @@ class Op : public OpState, public Traits<ConcreteType>... {
                         function_ref<InFlightDiagnostic()> emitError) {
     return setPropertiesFromAttribute(prop, attr, emitError);
   }
+  /// Convert the provided attribute to a property and assigned it to the
+  /// corresponding property. This default implementation forwards to a free
+  /// function `setPropertiesFromAttribute` that can be looked up with ADL in
+  /// the namespace where the properties are defined. It can also be overridden
+  /// in the derived ConcreteOp.
+  template <typename PropertiesTy>
+  static LogicalResult
+  setPropertyFromAttr(PropertiesTy &prop, StringRef name, Attribute attr,
+                      function_ref<InFlightDiagnostic()> emitError) {
+    return setPropertyFromAttribute(prop, name, attr, emitError);
+  }
   /// Convert the provided properties to an attribute. This default
   /// implementation forwards to a free function `getPropertiesAsAttribute` that
   /// can be looked up with ADL in the namespace where the properties are
@@ -1767,6 +1778,16 @@ class Op : public OpState, public Traits<ConcreteType>... {
                                        const PropertiesTy &prop) {
     return getPropertiesAsAttribute(ctx, prop);
   }
+  /// Convert the provided named property to an attribute. This default
+  /// implementation forwards to a free function `getPropertiesAsAttribute` that
+  /// can be looked up with ADL in the namespace where the properties are
+  /// defined. It can also be overridden in the derived ConcreteOp.
+  template <typename PropertiesTy>
+  static FailureOr<Attribute> getPropertyAsAttr(MLIRContext *ctx,
+                                                const PropertiesTy &prop,
+                                                StringRef name) {
+    return getPropertyAsAttribute(ctx, prop, name);
+  }
   /// Hash the provided properties. This default implementation forwards to a
   /// free function `computeHash` that can be looked up with ADL in the
   /// namespace where the properties are defined. It can also be overridden in
diff --git a/mlir/include/mlir/IR/Operation.h b/mlir/include/mlir/IR/Operation.h
index fa8a4873572ce..42ab61c055bc3 100644
--- a/mlir/include/mlir/IR/Operation.h
+++ b/mlir/include/mlir/IR/Operation.h
@@ -920,6 +920,12 @@ class alignas(8) Operation final
   /// operation. Returns an empty attribute if no properties are present.
   Attribute getPropertiesAsAttribute();
 
+  /// Return a named property converted to an attribute.
+  /// This is expensive, and mostly useful when dealing with unregistered
+  /// operations or in language bindings. Returns failure if there's no property
+  /// under such name.
+  FailureOr<Attribute> getPropertyAsAttribute(StringRef name);
+
   /// Set the properties from the provided attribute.
   /// This is an expensive operation that can fail if the attribute is not
   /// matching the expectations of the properties for this operation. This is
@@ -930,6 +936,17 @@ class alignas(8) Operation final
   setPropertiesFromAttribute(Attribute attr,
                              function_ref<InFlightDiagnostic()> emitError);
 
+  /// Set a named property from the provided attribute.
+  /// This is an expensive operation that can fail if the attribute is not
+  /// matching the expectations of the properties for this operation. This is
+  /// mostly useful for unregistered operations, used when parsing the
+  /// generic format, or in language bindings. An optional diagnostic emitter
+  /// can be passed in for richer errors, if none is passed then behavior is
+  /// undefined in error case.
+  LogicalResult
+  setPropertyFromAttribute(StringRef name, Attribute attr,
+                           function_ref<InFlightDiagnostic()> emitError);
+
   /// Copy properties from an existing other properties object. The two objects
   /// must be the same type.
   void copyProperties(OpaqueProperties rhs);
diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h
index 1ff7c56ddca38..7aadcca8f1232 100644
--- a/mlir/include/mlir/IR/OperationSupport.h
+++ b/mlir/include/mlir/IR/OperationSupport.h
@@ -139,6 +139,12 @@ class OperationName {
     setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute,
                           function_ref<InFlightDiagnostic()> emitError) = 0;
     virtual Attribute getPropertiesAsAttr(Operation *) = 0;
+    virtual LogicalResult
+    setPropertyFromAttr(OperationName, OpaqueProperties, StringRef name,
+                        Attribute,
+                        function_ref<InFlightDiagnostic()> emitError) = 0;
+    virtual FailureOr<Attribute> getPropertyAsAttr(Operation *,
+                                                   StringRef name) = 0;
     virtual void copyProperties(OpaqueProperties, OpaqueProperties) = 0;
     virtual bool compareProperties(OpaqueProperties, OpaqueProperties) = 0;
     virtual llvm::hash_code hashProperties(OpaqueProperties) = 0;
@@ -220,6 +226,11 @@ class OperationName {
     setPropertiesFromAttr(OperationName, OpaqueProperties, Attribute,
                           function_ref<InFlightDiagnostic()> emitError) final;
     Attribute getPropertiesAsAttr(Operation *) final;
+    LogicalResult
+    setPropertyFromAttr(OperationName, OpaqueProperties, StringRef name,
+                        Attribute,
+                        function_ref<InFlightDiagnostic()> emitError) final;
+    FailureOr<Attribute> getPropertyAsAttr(Operation *, StringRef name) final;
     void copyProperties(OpaqueProperties, OpaqueProperties) final;
     bool compareProperties(OpaqueProperties, OpaqueProperties) final;
     llvm::hash_code hashProperties(OpaqueProperties) final;
@@ -441,6 +452,20 @@ class OperationName {
                                             emitError);
   }
 
+  /// Return an op property converted to an Attribute.
+  FailureOr<Attribute> getOpPropertyAsAttribute(Operation *op,
+                                                StringRef name) const {
+    return getImpl()->getPropertyAsAttr(op, name);
+  }
+
+  /// Define an op property from the provided Attribute.
+  LogicalResult setOpPropertyFromAttribute(
+      OperationName opName, OpaqueProperties properties, StringRef name,
+      Attribute attr, function_ref<InFlightDiagnostic()> emitError) const {
+    return getImpl()->setPropertyFromAttr(opName, properties, name, attr,
+                                          emitError);
+  }
+
   void copyOpProperties(OpaqueProperties lhs, OpaqueProperties rhs) const {
     return getImpl()->copyProperties(lhs, rhs);
   }
@@ -650,6 +675,26 @@ class RegisteredOperationName : public OperationName {
       }
       return {};
     }
+    LogicalResult
+    setPropertyFromAttr(OperationName opName, OpaqueProperties properties,
+                        StringRef name, Attribute attr,
+                        function_ref<InFlightDiagnostic()> emitError) final {
+      if constexpr (hasProperties) {
+        auto p = properties.as<Properties *>();
+        return ConcreteOp::setPropertyFromAttr(*p, name, attr, emitError);
+      }
+      emitError() << "this operation does not support properties";
+      return failure();
+    }
+    FailureOr<Attribute> getPropertyAsAttr(Operation *op,
+                                           StringRef name) final {
+      if constexpr (hasProperties) {
+        auto concreteOp = cast<ConcreteOp>(op);
+        return ConcreteOp::getPropertyAsAttr(concreteOp->getContext(),
+                                             concreteOp.getProperties(), name);
+      }
+      return failure();
+    }
     bool compareProperties(OpaqueProperties lhs, OpaqueProperties rhs) final {
       if constexpr (hasProperties) {
         return *lhs.as<Properties *>() == *rhs.as<Properties *>();
diff --git a/mlir/lib/IR/MLIRContext.cpp b/mlir/lib/IR/MLIRContext.cpp
index 06ec1c85fb4d5..7b49a945c549b 100644
--- a/mlir/lib/IR/MLIRContext.cpp
+++ b/mlir/lib/IR/MLIRContext.cpp
@@ -901,6 +901,20 @@ Attribute
 OperationName::UnregisteredOpModel::getPropertiesAsAttr(Operation *op) {
   return *op->getPropertiesStorage().as<Attribute *>();
 }
+LogicalResult OperationName::UnregisteredOpModel::setPropertyFromAttr(
+    OperationName opName, OpaqueProperties properties, StringRef name,
+    Attribute attr, function_ref<InFlightDiagnostic()> emitError) {
+  assert(false &&
+         "`setPropertyFromAttr` doesn't work with unregistered operations.");
+  return failure();
+}
+FailureOr<Attribute>
+OperationName::UnregisteredOpModel::getPropertyAsAttr(Operation *op,
+                                                      StringRef name) {
+  assert(false &&
+         "`getPropertyAsAttr` doesn't work with unregistered operations.");
+  return failure();
+}
 void OperationName::UnregisteredOpModel::copyProperties(OpaqueProperties lhs,
                                                         OpaqueProperties rhs) {
   *lhs.as<Attribute *>() = *rhs.as<Attribute *>();
diff --git a/mlir/lib/IR/Operation.cpp b/mlir/lib/IR/Operation.cpp
index 8bcfa465e4a22..edd2efedf5a45 100644
--- a/mlir/lib/IR/Operation.cpp
+++ b/mlir/lib/IR/Operation.cpp
@@ -351,6 +351,12 @@ Attribute Operation::getPropertiesAsAttribute() {
     return *getPropertiesStorage().as<Attribute *>();
   return info->getOpPropertiesAsAttribute(this);
 }
+FailureOr<Attribute> Operation::getPropertyAsAttribute(StringRef name) {
+  std::optional<RegisteredOperationName> info = getRegisteredInfo();
+  assert(info &&
+         "`getPropertyAsAttribute` only works for registered operations.");
+  return info->getOpPropertyAsAttribute(this, name);
+}
 LogicalResult Operation::setPropertiesFromAttribute(
     Attribute attr, function_ref<InFlightDiagnostic()> emitError) {
   std::optional<RegisteredOperationName> info = getRegisteredInfo();
@@ -361,6 +367,15 @@ LogicalResult Operation::setPropertiesFromAttribute(
   return info->setOpPropertiesFromAttribute(
       this->getName(), this->getPropertiesStorage(), attr, emitError);
 }
+LogicalResult Operation::setPropertyFromAttribute(
+    StringRef name, Attribute attr,
+    function_ref<InFlightDiagnostic()> emitError) {
+  std::optional<RegisteredOperationName> info = getRegisteredInfo();
+  assert(info &&
+         "`setPropertyFromAttribute` only works for registered operations.");
+  return info->setOpPropertyFromAttribute(
+      this->getName(), this->getPropertiesStorage(), name, attr, emitError);
+}
 
 void Operation::copyProperties(OpaqueProperties rhs) {
   name.copyOpProperties(getPropertiesStorage(), rhs);
diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
index f35cfa6826388..31834554cdd43 100644
--- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp
@@ -1411,7 +1411,7 @@ void OpEmitter::genPropertiesSupport() {
     attrOrProperties.push_back(&emitHelper.getOperandSegmentsSize().value());
   if (emitHelper.getResultSegmentsSize())
     attrOrProperties.push_back(&emitHelper.getResultSegmentsSize().value());
-  auto &setPropMethod =
+  auto &setPropsMethod =
       opClass
           .addStaticMethod(
               "::llvm::LogicalResult", "setPropertiesFromAttr",
@@ -1421,12 +1421,31 @@ void OpEmitter::genPropertiesSupport() {
                   "::llvm::function_ref<::mlir::InFlightDiagnostic()>",
                   "emitError"))
           ->body();
-  auto &getPropMethod =
+  auto &getPropsMethod =
       opClass
           .addStaticMethod("::mlir::Attribute", "getPropertiesAsAttr",
                            MethodParameter("::mlir::MLIRContext *", "ctx"),
                            MethodParameter("const Properties &", "prop"))
           ->body();
+  auto &setPropMethod =
+      opClass
+          .addStaticMethod(
+              "::llvm::LogicalResult", "setPropertyFromAttr",
+              MethodParameter("Properties &", "prop"),
+              MethodParameter("llvm::StringRef", "name"),
+              MethodParameter("::mlir::Attribute", "attr"),
+              MethodParameter(
+                  "::llvm::function_ref<::mlir::InFlightDiagnostic()>",
+                  "emitError"))
+          ->body();
+  auto &getPropMethod =
+      opClass
+          .addStaticMethod("llvm::FailureOr<::mlir::Attribute>",
+                           "getPropertyAsAttr",
+                           MethodParameter("::mlir::MLIRContext *", "ctx"),
+                           MethodParameter("const Properties &", "prop"),
+                           MethodParameter("llvm::StringRef", "name"))
+          ->body();
   auto &hashMethod =
       opClass
           .addStaticMethod("llvm::hash_code", "computePropertiesHash",
@@ -1468,7 +1487,7 @@ void OpEmitter::genPropertiesSupport() {
 
   // Convert the property to the attribute form.
 
-  setPropMethod << R"decl(
+  setPropsMethod << R"decl(
   ::mlir::DictionaryAttr dict = ::llvm::dyn_cast<::mlir::DictionaryAttr>(attr);
   if (!dict) {
     emitError() << "expected DictionaryAttr to set properties";
@@ -1480,9 +1499,9 @@ void OpEmitter::genPropertiesSupport() {
                ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError) -> ::mlir::LogicalResult {{
         {0}
       };
-      {1};
+      {1}
 )decl";
-  const char *attrGetNoDefaultFmt = R"decl(;
+  const char *attrGetNoDefaultFmt = R"decl(
       if (attr && ::mlir::failed(setFromAttr(prop.{0}, attr, emitError)))
         return ::mlir::failure();
 )decl";
@@ -1515,22 +1534,33 @@ void OpEmitter::genPropertiesSupport() {
       }
 
       fctx.withBuilder(odsBuilder);
-      setPropMethod << "{\n"
+      setPropsMethod << "{\n"
+                     << formatv(
+                            propFromAttrFmt,
+                            tgfmt(prop.getConvertFromAttributeCall(),
+                                  &fctx.addSubst("_attr", propertyAttr)
+                                       .addSubst("_storage", propertyStorage)
+                                       .addSubst("_diag", propertyDiag)),
+                            getAttr);
+      if (prop.hasStorageTypeValueOverride()) {
+        setPropsMethod << formatv(attrGetDefaultFmt, name,
+                                  prop.getStorageTypeValueOverride());
+      } else if (prop.hasDefaultValue()) {
+        setPropsMethod << formatv(attrGetDefaultFmt, name,
+                                  tgfmt(prop.getDefaultValue(), &fctx));
+      } else {
+        setPropsMethod << formatv(attrGetNoDefaultFmt, name);
+      }
+      setPropsMethod << "  }\n";
+      setPropMethod << formatv("  if (name == \"{0}\") {{", name)
                     << formatv(propFromAttrFmt,
                                tgfmt(prop.getConvertFromAttributeCall(),
                                      &fctx.addSubst("_attr", propertyAttr)
                                           .addSubst("_storage", propertyStorage)
                                           .addSubst("_diag", propertyDiag)),
-                               getAttr);
-      if (prop.hasStorageTypeValueOverride()) {
-        setPropMethod << formatv(attrGetDefaultFmt, name,
-                                 prop.getStorageTypeValueOverride());
-      } else if (prop.hasDefaultValue()) {
-        setPropMethod << formatv(attrGetDefaultFmt, name,
-                                 tgfmt(prop.getDefaultValue(), &fctx));
-      } else {
-        setPropMethod << formatv(attrGetNoDefaultFmt, name);
-      }
+                               "");
+      setPropMethod << formatv(attrGetNoDefaultFmt, name);
+      setPropMethod << "    return ::mlir::success();\n";
       setPropMethod << "  }\n";
     } else {
       const auto *namedAttr =
@@ -1548,7 +1578,7 @@ void OpEmitter::genPropertiesSupport() {
         os << "   if (!attr) attr = dict.get(\"result_segment_sizes\");";
       }
 
-      setPropMethod << formatv(R"decl(
+      setPropsMethod << formatv(R"decl(
   {{
     auto &propStorage = prop.{0};
     {1}
@@ -1563,16 +1593,38 @@ void OpEmitter::genPropertiesSupport() {
     }
   }
 )decl",
-                               name, getAttr);
+                                name, getAttr);
+      setPropMethod << formatv(R"decl(
+  {{
+    auto &propStorage = prop.{0};
+    if (attr == nullptr) {{
+      propStorage = nullptr;
+      return ::mlir::success();
+    }
+    auto convertedAttr = ::llvm::dyn_cast<std::remove_reference_t<decltype(propStorage)>>(attr);
+    if (convertedAttr) {{
+      propStorage = convertedAttr;
+      return ::mlir::success();
+    } else {{
+      emitError() << "Invalid attribute `{0}` in property conversion: " << attr;
+      return ::mlir::failure();
     }
   }
-  setPropMethod << "  return ::mlir::success();\n";
+)decl",
+                               name);
+    }
+  }
+  setPropsMethod << "  return ::mlir::success();\n";
+  setPropMethod << "  return emitError() << \"`\" << name << \"` is not an op "
+                   "property\";\n";
 
   // Convert the attribute form to the property.
 
-  getPropMethod << "    ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n"
-                << "    ::mlir::Builder odsBuilder{ctx};\n";
-  const char *propToAttrFmt = R"decl(
+  getPropsMethod << "    ::mlir::SmallVector<::mlir::NamedAttribute> attrs;\n"
+                 << "    ::mlir::Builder odsBuilder{ctx};\n";
+  getPropMethod << "    ::mlir::Builder odsBuilder{ctx};\n"
+                << "    (void)odsBuilder;\n";
+  const char *propsToAttrFmt = R"decl(
     {
       const auto &propStorage = prop.{0};
       auto attr = [&]() -> ::mlir::Attribute {{
@@ -1580,6 +1632,15 @@ void OpEmitter::genPropertiesSupport() {
       }();
       attrs.push_back(odsBuilder.getNamedAttr("{0}", attr));
     }
+)decl";
+  const char *propToAttrFmt = R"decl(
+    if (name == "{0}") {
+      const auto &propStorage = prop.{0};
+      auto attr = [&]() -> ::mlir::Attribute {{
+        {1}
+      }();
+      return attr;
+    }
 )decl";
   for (const auto &attrOrProp : attrOrProperties) {
     if (const auto *namedProperty =
@@ -1587,6 +1648,11 @@ void OpEmitter::genPropertiesSupport() {
       StringRef name = namedProperty->name;
       auto &prop = namedProperty->prop;
       FmtContext fctx;
+      getPropsMethod << formatv(
+          propsToAttrFmt, name,
+          tgfmt(prop.getConvertToAttributeCall(),
+                &fctx.addSubst("_ctxt", "ctx")
+                     .addSubst("_storage", propertyStorage)));
       getPropMethod << formatv(
           propToAttrFmt, name,
           tgfmt(prop.getConvertToAttributeCall(),
@@ -1597,21 +1663,28 @@ void OpEmitter::genPropertiesSupport() {
     const auto *namedAttr =
         llvm::dyn_cast_if_present<const AttributeMetadata *>(attrOrProp);
     StringRef name = namedAttr->attrName;
-    getPropMethod << formatv(R"decl(
+    getPropsMethod << formatv(R"decl(
     {{
       const auto &propStorage = prop.{0};
       if (propStorage)
         attrs.push_back(odsBuilder.getNamedAttr("{0}",
                                        propStorage));
     }
+)decl",
+                              name);
+    getPropMethod << formatv(R"decl(
+    if (name == "{0}") {{
+      return prop.{0};
+    }
 )decl",
                              name);
   }
-  getPropMethod << R"decl(
+  getPropsMethod << R"decl(
   if (!attrs.empty())
     return odsBuilder.getDictionaryAttr(attrs);
   return {};
 )decl";
+  getPropMethod << "  return ::mlir::failure();";
 
   // Hashing for the property
 
diff --git a/mlir/unittests/IR/OpPropertiesTest.cpp b/mlir/unittests/IR/OpPropertiesTest.cpp
index 4759735d99605..12efe47c68fb0 100644
--- a/mlir/unittests/IR/OpPropertiesTest.cpp
+++ b/mlir/unittests/IR/OpPropertiesTest.cpp
@@ -78,6 +78,43 @@ setPropertiesFromAttribute(TestProperties &prop, Attribute attr,
   return success();
 }
 
+/// Convert an attribute to a TestProperties struct, optionally emit errors
+/// through the provided diagnostic if any. This is used for example during
+/// parsing with the generic format.
+static LogicalResult
+setPropertyFromAttribute(TestProperties &prop, StringRef name, Attribute attr,
+                         function_ref<InFlightDiagnostic()> emitError) {
+  if (name == "a") {
+    auto v = dyn_cast<IntegerAttr>(attr);
+    if (!v)
+      return failure();
+    prop.a = v.getValue().getSExtValue();
+    return success();
+  }
+  if (name == "b") {
+    auto v = dyn_cast<FloatAttr>(attr);
+    if (!v)
+      return failure();
+    prop.a = v.getValue().convertToFloat();
+    return success();
+  }
+  if (name == "array") {
+    auto v = dyn_cast<DenseI64ArrayAttr>(attr);
+    if (!v)
+      return failure();
+    prop.array.assign(v.asArrayRef().begin(), v.asArrayRef().end());
+    return success();
+  }
+  if (name == "label") {
+    auto v = dyn_cast<StringAttr>(attr);
+    if (!v)
+      return failure();
+    prop.label = std::make_shared<std::string>(v.getValue());
+    return success();
+  }
+  return failure();
+}
+
 /// Convert a TestProperties struct to a DictionaryAttr, this is used for
 /// example during printing with the generic format.
 static Attribute getPropertiesAsAttribute(MLIRContext *ctx,
@@ -92,6 +129,20 @@ static Attribute getPropertiesAsAttribute(MLIRContext *ctx,
   return b.getDictionaryAttr(attrs);
 }
 
+/// Convert a named property in TestProperties struct to an attribute, this is
+/// used for example during printing with the generic format.
+static FailureOr<Attribute> getPropertyAsAttribute(MLIRContext *ctx,
+                                                   const TestProperties &prop,
+                                                   StringRef name) {
+  Builder b{ctx};
+  return llvm::StringSwitch<FailureOr<Attribute>>(name)
+      .Case("a", b.getI32IntegerAttr(prop.a))
+      .Case("b", b.getF32FloatAttr(prop.b))
+      .Case("array", b.getDenseI64ArrayAttr(prop.array))
+      .Case("label", b.getStringAttr(prop.label ? *prop.label : "<nullptr>"))
+      .Default(failure());
+}
+
 inline llvm::hash_code computeHash(const TestProperties &prop) {
   // We hash `b` which is a float using its underlying array of char:
   unsigned char const *p = reinterpret_cast<unsigned char const *>(&prop.b);
@@ -419,4 +470,47 @@ TEST(OpPropertiesTest, withoutPropertiesDiscardableAttrs) {
   EXPECT_TRUE(hash(op.get()) == hash(reparsed.get()));
 }
 
+TEST(OpPropertiesTest, PropertiesAsAttrs) {
+  MLIRContext context;
+  context.getOrLoadDialect<TestOpPropertiesDialect>();
+  ParserConfig config(&context);
+  // Parse the operation with some properties.
+  OwningOpRef<Operation *> op = parseSourceString(mlirSrc, config);
+  ASSERT_TRUE(op.get() != nullptr);
+  auto opWithProp = dyn_cast<OpWithProperties>(op.get());
+  ASSERT_TRUE(opWithProp);
+  {
+    // Check the attr dict.
+    std::string output;
+    llvm::raw_string_ostream os(output);
+    opWithProp->getPropertiesAsAttribute().print(os);
+    ASSERT_STREQ("{a = -42 : i32, "
+                 "array = array<i64: 40, 41>, "
+                 "b = -4.200000e+01 : f32, "
+                 "label = \"bar foo\"}",
+                 output.c_str());
+  }
+  auto getAttr = [&](FailureOr<Attribute> attr) {
+    EXPECT_TRUE(succeeded(attr));
+    return *attr;
+  };
+  // Get and mutate single properties.
+  auto a = cast<IntegerAttr>(getAttr(op->getPropertyAsAttribute("a")));
+  auto label = cast<StringAttr>(getAttr(op->getPropertyAsAttribute("label")));
+  std::string newLabel = label.getValue().str() + " " +
+                         std::to_string(a.getValue().getSExtValue());
+  {
+    EXPECT_TRUE(succeeded(op->setPropertyFromAttribute(
+        "label", StringAttr::get(&context, newLabel),
+        [&]() { return op->emitError(); })));
+    std::string output;
+    llvm::raw_string_ostream os(output);
+    opWithProp.print(os);
+    StringRef view(output);
+    EXPECT_TRUE(view.contains("label = \"bar foo -42\""));
+  }
+  // Expect error because the named prop doesn't exist.
+  EXPECT_TRUE(failed(
+      op->setPropertyFromAttribute("s", a, [&]() { return op->emitError(); })));
+}
 } // namespace



More information about the Mlir-commits mailing list