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

Fabian Mora llvmlistbot at llvm.org
Tue Jul 22 10:35:25 PDT 2025


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

>From dd612d6bf70e80eba41afc7e9f655d0d0280221f 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 1/2] [mlir][IR] Add `getPropertyFromAttr` and
 `setPropertyFromAttr` methods.

---
 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      | 144 ++++++++++++++++++++
 8 files changed, 364 insertions(+), 24 deletions(-)

diff --git a/mlir/include/mlir/IR/ExtensibleDialect.h b/mlir/include/mlir/IR/ExtensibleDialect.h
index 955faaad9408b..fd8ea760eb9fe 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 failure();
+  }
   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..d370f975ff2d7 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(
+  if (name == "{0}") {{
+    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..b735210ed8ee1 100644
--- a/mlir/unittests/IR/OpPropertiesTest.cpp
+++ b/mlir/unittests/IR/OpPropertiesTest.cpp
@@ -13,6 +13,9 @@
 #include "gtest/gtest.h"
 #include <optional>
 
+#include "../../test/lib/Dialect/Test/TestDialect.h"
+#include "../../test/lib/Dialect/Test/TestOps.h"
+
 using namespace mlir;
 
 namespace {
@@ -78,6 +81,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 +132,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 +473,94 @@ 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(); })));
+}
+
+TEST(OpPropertiesTest, TblgenOpProperties) {
+  MLIRContext context;
+  context.getOrLoadDialect<test::TestDialect>();
+  ParserConfig config(&context);
+  // Parse the operation with some properties.
+  OwningOpRef<Operation *> op = parseSourceString(R"mlir(
+    test.with_properties a = 32, b = "foo", c = "bar",
+      flag = true, array = [1, 2, 3, 4], array32 = [5, 6]
+    )mlir",
+                                                  config);
+  ASSERT_TRUE(op.get() != nullptr);
+  auto opWithProp = dyn_cast<test::TestOpWithProperties>(op.get());
+  ASSERT_TRUE(opWithProp);
+  {
+    // Check the attr dict.
+    std::string output;
+    llvm::raw_string_ostream os(output);
+    opWithProp->getPropertiesAsAttribute().print(os);
+    StringRef view(output);
+    EXPECT_TRUE(view.contains("a = 32"));
+    EXPECT_TRUE(view.contains("b = \"foo\""));
+  }
+  {
+    // Modify a prop.
+    std::string output;
+    llvm::raw_string_ostream os(output);
+    ASSERT_TRUE(succeeded(opWithProp->setPropertyFromAttribute(
+        "a", IntegerAttr::get(IntegerType::get(&context, 32), 42),
+        [&]() { return opWithProp->emitError(); })));
+    opWithProp->print(os);
+    StringRef view(output);
+    EXPECT_FALSE(view.contains("a = 32"));
+    EXPECT_TRUE(view.contains("a = 42"));
+    EXPECT_TRUE(view.contains("b = \"foo\""));
+  }
+  {
+    // Get a prop.
+    std::string output;
+    llvm::raw_string_ostream os(output);
+    FailureOr<Attribute> prop = opWithProp->getPropertyAsAttribute("flag");
+    ASSERT_TRUE(succeeded(prop));
+    auto flagAttr = dyn_cast_or_null<BoolAttr>(*prop);
+    ASSERT_TRUE(flagAttr);
+    EXPECT_TRUE(flagAttr.getValue());
+  }
+}
 } // namespace

>From afee7ccc7647fde829d5dcf08237f2af40e56742 Mon Sep 17 00:00:00 2001
From: Fabian Mora <fabian.mora-cordero at amd.com>
Date: Tue, 22 Jul 2025 17:35:01 +0000
Subject: [PATCH 2/2] fix typo

---
 mlir/unittests/IR/OpPropertiesTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/unittests/IR/OpPropertiesTest.cpp b/mlir/unittests/IR/OpPropertiesTest.cpp
index b735210ed8ee1..5241717a564af 100644
--- a/mlir/unittests/IR/OpPropertiesTest.cpp
+++ b/mlir/unittests/IR/OpPropertiesTest.cpp
@@ -98,7 +98,7 @@ setPropertyFromAttribute(TestProperties &prop, StringRef name, Attribute attr,
     auto v = dyn_cast<FloatAttr>(attr);
     if (!v)
       return failure();
-    prop.a = v.getValue().convertToFloat();
+    prop.b = v.getValue().convertToFloat();
     return success();
   }
   if (name == "array") {



More information about the Mlir-commits mailing list